1   /* Copyright 2022-2025 Thales Alenia Space
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.frames;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.orekit.time.FieldAbsoluteDate;
21  
22  import java.util.LinkedHashMap;
23  import java.util.Map;
24  import java.util.concurrent.locks.ReentrantLock;
25  import java.util.function.Function;
26  
27  /** Thread-safe cached provider for frame transforms.
28   * <p>
29   * This provider is based on a thread-safe Least Recently Used cache
30   * using date as it access key, hence saving computation time on
31   * transform building.
32   * </p>
33   * <p>
34   * This class is thread-safe.
35   * </p>
36   * @param <T> type of the field elements
37   * @author Luc Maisonobe
38   * @since 13.0.3
39   */
40  public class FieldCachedTransformProvider<T extends CalculusFieldElement<T>> {
41  
42      /** Origin frame. */
43      private final Frame origin;
44  
45      /** Destination frame. */
46      private final Frame destination;
47  
48      /** Number of transforms kept in the date-based cache. */
49      private final int cacheSize;
50  
51      /** Generator for full transforms. */
52      private final Function<FieldAbsoluteDate<T>, FieldTransform<T>> fullGenerator;
53  
54      /** Generator for kinematic transforms. */
55      private final Function<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> kinematicGenerator;
56  
57      /** Generator for static transforms. */
58      private final Function<FieldAbsoluteDate<T>, FieldStaticTransform<T>> staticGenerator;
59  
60      /** Lock for concurrent access. */
61      private final ReentrantLock lock;
62  
63      /** Transforms LRU cache. */
64      private final Map<FieldAbsoluteDate<T>, FieldTransform<T>> fullCache;
65  
66      /** Transforms LRU cache. */
67      private final Map<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> kinematicCache;
68  
69      /** Transforms LRU cache. */
70      private final Map<FieldAbsoluteDate<T>, FieldStaticTransform<T>> staticCache;
71  
72      /**
73       * Simple constructor.
74       * @param origin             origin frame
75       * @param destination        destination frame
76       * @param fullGenerator      generator for full transforms
77       * @param kinematicGenerator generator for kinematic transforms
78       * @param staticGenerator    generator for static transforms
79       * @param cacheSize          number of transforms kept in the date-based cache
80       */
81      public FieldCachedTransformProvider(final Frame origin, final Frame destination,
82                                          final Function<FieldAbsoluteDate<T>, FieldTransform<T>> fullGenerator,
83                                          final Function<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> kinematicGenerator,
84                                          final Function<FieldAbsoluteDate<T>, FieldStaticTransform<T>> staticGenerator,
85                                          final int cacheSize) {
86  
87          this.origin             = origin;
88          this.destination        = destination;
89          this.cacheSize          = cacheSize;
90          this.fullGenerator      = fullGenerator;
91          this.kinematicGenerator = kinematicGenerator;
92          this.staticGenerator    = staticGenerator;
93          this.lock               = new ReentrantLock();
94  
95          // cache for full transforms
96          this.fullCache = new LinkedHashMap<FieldAbsoluteDate<T>, FieldTransform<T>>(cacheSize, 0.75f, true) {
97              /** {@inheritDoc} */
98              @Override
99              protected boolean removeEldestEntry(final Map.Entry<FieldAbsoluteDate<T>, FieldTransform<T>> eldest) {
100                 return size() > cacheSize;
101             }
102         };
103 
104         // cache for kinematic transforms
105         this.kinematicCache = new LinkedHashMap<FieldAbsoluteDate<T>, FieldKinematicTransform<T>>(cacheSize, 0.75f, true) {
106             /** {@inheritDoc} */
107             @Override
108             protected boolean removeEldestEntry(final Map.Entry<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> eldest) {
109                 return size() > cacheSize;
110             }
111         };
112 
113         // cache for static transforms
114         this.staticCache = new LinkedHashMap<FieldAbsoluteDate<T>, FieldStaticTransform<T>>(cacheSize, 0.75f, true) {
115             /** {@inheritDoc} */
116             @Override
117             protected boolean removeEldestEntry(final Map.Entry<FieldAbsoluteDate<T>, FieldStaticTransform<T>> eldest) {
118                 return size() > cacheSize;
119             }
120         };
121 
122     }
123 
124     /** Get origin frame.
125      * @return origin frame
126      */
127     public Frame getOrigin() {
128         return origin;
129     }
130 
131     /** Get destination frame.
132      * @return destination frame
133      */
134     public Frame getDestination() {
135         return destination;
136     }
137 
138     /** Get the nmber of transforms kept in the date-based cache.
139      * @return nmber of transforms kept in the date-based cache
140      */
141     public int getCacheSize() {
142         return cacheSize;
143     }
144 
145     /** Get the {@link Transform} corresponding to specified date.
146      * @param date current date
147      * @return transform at specified date
148      */
149     public FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
150         lock.lock();
151         try {
152             return fullCache.computeIfAbsent(date, fullGenerator);
153         } finally {
154             lock.unlock();
155         }
156     }
157 
158     /** Get the {@link Transform} corresponding to specified date.
159      * @param date current date
160      * @return transform at specified date
161      */
162     public FieldKinematicTransform<T> getKinematicTransform(final FieldAbsoluteDate<T> date) {
163         lock.lock();
164         try {
165             return kinematicCache.computeIfAbsent(date, kinematicGenerator);
166         } finally {
167             lock.unlock();
168         }
169     }
170 
171     /** Get the {@link Transform} corresponding to specified date.
172      * @param date current date
173      * @return transform at specified date
174      */
175     public FieldStaticTransform<T> getStaticTransform(final FieldAbsoluteDate<T> date) {
176         lock.lock();
177         try {
178             return staticCache.computeIfAbsent(date, staticGenerator);
179         } finally {
180             lock.unlock();
181         }
182     }
183 
184 }