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.hipparchus.Field;
21 import org.orekit.time.AbsoluteDate;
22 import org.orekit.time.FieldAbsoluteDate;
23
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.locks.ReentrantReadWriteLock;
27 import java.util.function.Function;
28
29 /** Cache for frame transforms.
30 * <p>
31 * This class is thread-safe.
32 * </p>
33 * @author Luc Maisonobe
34 * @since 13.1
35 */
36 class PeerCache {
37
38 /** Origin frame. */
39 private final Frame origin;
40
41 /** Cache for transforms with peer frame. */
42 private volatile CachedTransformProvider cache;
43
44 /** Lock for peer frame cache. */
45 private final ReentrantReadWriteLock lock;
46
47 /** Cache for transforms with peer frame. */
48 private volatile Map<Field<? extends CalculusFieldElement<?>>, FieldCachedTransformProvider<?>> fieldCaches;
49
50 /** create an instance not associated with any peer.
51 * @param origin origin frame
52 */
53 PeerCache(final Frame origin) {
54 this.origin = origin;
55 this.cache = null;
56 this.fieldCaches = null;
57 this.lock = new ReentrantReadWriteLock();
58 }
59
60 /** Associate a cache with a peer frame, caching transforms.
61 * <p>
62 * The cache is a LRU cache (Least Recently Used), so entries remain in
63 * the cache if they are used frequently, and only older entries
64 * that have not been accessed for a while will be expunged.
65 * </p>
66 * <p>
67 * If a peer was already associated with this frame, it will be overridden.
68 * </p>
69 * <p>
70 * Peering is unidirectional, i.e. if frameA is peered with frameB,
71 * then frameB may be peered with another frameC or no frame at all.
72 * This allows several frames to be peered with a pivot one (typically
73 * Earth frame and many topocentric frames all peered with one inertial frame).
74 * </p>
75 * @param peer peer frame (if null, cache is cleared)
76 * @param cacheSize number of transforms kept in the date-based cache
77 */
78 public void setPeerCaching(final Frame peer, final int cacheSize) {
79
80 lock.writeLock().lock();
81 try {
82
83 if (peer == null) {
84 // clear peering
85 cache = null;
86 fieldCaches = null;
87 }
88
89 // caching for regular dates
90 cache = createCache(peer, cacheSize);
91
92 // caching for field dates
93 fieldCaches = new ConcurrentHashMap<>();
94 } finally {
95 lock.writeLock().unlock();
96 }
97
98 }
99
100 /** Get the peer associated to this frame.
101 * @return peer associated with this frame, null if not peered at all
102 */
103 Frame getPeer() {
104 lock.readLock().lock();
105 try {
106 return cache == null ? null : cache.getDestination();
107 } finally {
108 lock.readLock().unlock();
109 }
110 }
111
112 /** Get the cached transform provider associated with this destination.
113 * @param destination destination frame to which we want to transform vectors
114 * @return cached transform provider, or null if destination is not the instance peer
115 */
116 CachedTransformProvider getCachedTransformProvider(final Frame destination) {
117 lock.readLock().lock();
118 try {
119 if (cache == null || cache.getDestination() != destination) {
120 return null;
121 } else {
122 return cache;
123 }
124 } finally {
125 lock.readLock().unlock();
126 }
127 }
128
129 /** Get the cached transform provider associated with this destination.
130 * @param <T> the type of the field elements
131 * @param destination destination frame to which we want to transform vectors
132 * @param field field elements belong to
133 * @return cached transform provider, or null if destination is not the instance peer
134 */
135 @SuppressWarnings("unchecked")
136 <T extends CalculusFieldElement<T>> FieldCachedTransformProvider<T> getCachedTransformProvider(final Frame destination,
137 final Field<T> field) {
138 lock.readLock().lock();
139 try {
140 if (cache == null || cache.getDestination() != destination) {
141 return null;
142 } else {
143 @SuppressWarnings("unchedked")
144 final FieldCachedTransformProvider<T> tp =
145 (FieldCachedTransformProvider<T>) fieldCaches.computeIfAbsent(field,
146 f -> createCache(destination,
147 cache.getCacheSize(),
148 field));
149 return tp;
150 }
151 } finally {
152 lock.readLock().unlock();
153 }
154 }
155
156 /** Create cache.
157 * @param peer peer frame
158 * @param cacheSize number of transforms kept in the date-based cache
159 * @return built cache
160 * @since 13.0.3
161 */
162 private CachedTransformProvider createCache(final Frame peer, final int cacheSize) {
163 final Function<AbsoluteDate, Transform> fullGenerator =
164 date -> origin.getTransformTo(peer,
165 Transform.IDENTITY,
166 frame -> frame.getTransformProvider().getTransform(date),
167 (t1, t2) -> new Transform(date, t1, t2),
168 Transform::getInverse);
169 final Function<AbsoluteDate, KinematicTransform> kinematicGenerator =
170 date -> origin.getTransformTo(peer,
171 KinematicTransform.getIdentity(),
172 frame -> frame.getTransformProvider().getTransform(date),
173 (t1, t2) -> KinematicTransform.compose(date, t1, t2),
174 KinematicTransform::getInverse);
175 final Function<AbsoluteDate, StaticTransform> staticGenerator =
176 date -> origin.getTransformTo(peer,
177 StaticTransform.getIdentity(),
178 frame -> frame.getTransformProvider().getTransform(date),
179 (t1, t2) -> StaticTransform.compose(date, t1, t2),
180 StaticTransform::getInverse);
181 return new CachedTransformProvider(origin, peer,
182 fullGenerator, kinematicGenerator, staticGenerator,
183 cacheSize);
184 }
185
186 /** Create field cache.
187 * @param <T> type of the field elements
188 * @param peer peer frame
189 * @param cacheSize number of transforms kept in the date-based cache
190 * @param field field elements belong to
191 * @return built cache
192 * @since 13.0.3
193 */
194 private <T extends CalculusFieldElement<T>> FieldCachedTransformProvider<T>
195 createCache(final Frame peer, final int cacheSize, final Field<T> field) {
196 final Function<FieldAbsoluteDate<T>, FieldTransform<T>> fullGenerator =
197 d -> origin.getTransformTo(peer,
198 FieldTransform.getIdentity(field),
199 frame -> frame.getTransformProvider().getTransform(d),
200 (FieldTransform<T> t1, FieldTransform<T> t2) -> new FieldTransform<>(d, t1, t2),
201 FieldTransform::getInverse);
202 final Function<FieldAbsoluteDate<T>, FieldKinematicTransform<T>> kinematicGenerator =
203 d -> origin.getTransformTo(peer,
204 FieldKinematicTransform.getIdentity(field),
205 frame -> frame.getTransformProvider().getTransform(d),
206 (t1, t2) -> FieldKinematicTransform.compose(d, t1, t2),
207 FieldKinematicTransform::getInverse);
208 final Function<FieldAbsoluteDate<T>, FieldStaticTransform<T>> staticGenerator =
209 d -> origin.getTransformTo(peer,
210 FieldStaticTransform.getIdentity(field),
211 frame -> frame.getTransformProvider().getTransform(d),
212 (t1, t2) -> FieldStaticTransform.compose(d, t1, t2),
213 FieldStaticTransform::getInverse);
214 return new FieldCachedTransformProvider<>(origin, peer,
215 fullGenerator, kinematicGenerator, staticGenerator,
216 cacheSize);
217 }
218
219 }