1 /*
2 * Copyright (C) 2020 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.navigation.inertial.calibration;
17
18 import com.irurueta.navigation.inertial.BodyKinematics;
19 import com.irurueta.units.Time;
20 import com.irurueta.units.TimeConverter;
21 import com.irurueta.units.TimeUnit;
22
23 import java.io.Serial;
24 import java.io.Serializable;
25 import java.util.Objects;
26
27 /**
28 * Contains a body kinematics measurement (accelerometer + gyroscope) along with the
29 * corresponding timestamp when measure was made.
30 * Notice that timestamp does not need to be absolute.
31 * Usually timestamps are used in sequences of measurements of body kinematics, where
32 * the first measurement can have any timestamp value (e.g. zero), and hence the subsequent
33 * measurements will have timestamps relative to the first one.
34 */
35 public class TimedBodyKinematics implements Serializable, Cloneable {
36
37 /**
38 * Serialization version. This is used to ensure compatibility of deserialization of permanently stored serialized
39 * instances.
40 */
41 @Serial
42 private static final long serialVersionUID = 0L;
43
44 /**
45 * Current body kinematics measurement. Contains accelerometer and gyroscope measurements.
46 */
47 private BodyKinematics kinematics;
48
49 /**
50 * Timestamp value expressed in seconds.
51 */
52 private double timestampSeconds;
53
54 /**
55 * Constructor.
56 */
57 public TimedBodyKinematics() {
58 }
59
60 /**
61 * Constructor.
62 *
63 * @param kinematics current body kinematics measurement.
64 */
65 public TimedBodyKinematics(final BodyKinematics kinematics) {
66 this.kinematics = kinematics;
67 }
68
69 /**
70 * Constructor.
71 *
72 * @param timestampSeconds timestamp value expressed in seconds.
73 */
74 public TimedBodyKinematics(final double timestampSeconds) {
75 this.timestampSeconds = timestampSeconds;
76 }
77
78 /**
79 * Constructor.
80 *
81 * @param timestamp timestamp value.
82 */
83 public TimedBodyKinematics(final Time timestamp) {
84 timestampSeconds = convertTime(timestamp);
85 }
86
87 /**
88 * Constructor.
89 *
90 * @param kinematics current body kinematics measurement.
91 * @param timestampSeconds timestamp value expressed in seconds.
92 */
93 public TimedBodyKinematics(final BodyKinematics kinematics, final double timestampSeconds) {
94 this(kinematics);
95 this.timestampSeconds = timestampSeconds;
96 }
97
98 /**
99 * Constructor.
100 *
101 * @param kinematics current body kinematics measurement.
102 * @param timestamp timestamp value.
103 */
104 public TimedBodyKinematics(final BodyKinematics kinematics, final Time timestamp) {
105 this(timestamp);
106 this.kinematics = kinematics;
107 }
108
109 /**
110 * Constructor.
111 *
112 * @param input instance to copy data from.
113 */
114 public TimedBodyKinematics(final TimedBodyKinematics input) {
115 copyFrom(input);
116 }
117
118 /**
119 * Gets current body kinematics measurement. Contains accelerometer and gyroscope
120 * measurements.
121 *
122 * @return current body kinematics measurement.
123 */
124 public BodyKinematics getKinematics() {
125 return kinematics;
126 }
127
128 /**
129 * Sets current body kinematics measurement. Contains accelerometer and gyroscope
130 * measurements.
131 *
132 * @param kinematics current body kinematics measurement to be set.
133 */
134 public void setKinematics(final BodyKinematics kinematics) {
135 this.kinematics = kinematics;
136 }
137
138 /**
139 * Gets timestamp value expressed in seconds.
140 *
141 * @return timestamp value expressed in seconds.
142 */
143 public double getTimestampSeconds() {
144 return timestampSeconds;
145 }
146
147 /**
148 * Sets timestamp value expressed in seconds.
149 *
150 * @param timestampSeconds timestamp value expressed in seconds.
151 */
152 public void setTimestampSeconds(final double timestampSeconds) {
153 this.timestampSeconds = timestampSeconds;
154 }
155
156 /**
157 * Gets timestamp value.
158 *
159 * @return a new timestamp instance.
160 */
161 public Time getTimestamp() {
162 return new Time(timestampSeconds, TimeUnit.SECOND);
163 }
164
165 /**
166 * Gets timestamp value.
167 *
168 * @param result instance where result data will be stored.
169 */
170 public void getTimestamp(final Time result) {
171 result.setValue(timestampSeconds);
172 result.setUnit(TimeUnit.SECOND);
173 }
174
175 /**
176 * Sets timestamp.
177 *
178 * @param timestamp timestamp to be set.
179 */
180 public void setTimestamp(final Time timestamp) {
181 timestampSeconds = convertTime(timestamp);
182 }
183
184 /**
185 * Copies data of provided instance into this instance.
186 *
187 * @param input instance to copy data from.
188 */
189 public void copyFrom(final TimedBodyKinematics input) {
190 if (input.kinematics != null) {
191 if (kinematics == null) {
192 kinematics = new BodyKinematics(input.kinematics);
193 } else {
194 kinematics.copyFrom(input.kinematics);
195 }
196 } else {
197 kinematics = null;
198 }
199
200 timestampSeconds = input.timestampSeconds;
201 }
202
203 /**
204 * Copies this instance data into provided instance.
205 *
206 * @param output destination instance where data will be copied to.
207 */
208 public void copyTo(final TimedBodyKinematics output) {
209 output.copyFrom(this);
210 }
211
212 /**
213 * Computes and returns hash code for this instance. Hash codes are almost unique
214 * values that are useful for fast classification and storage of objects in collections.
215 *
216 * @return Hash code.
217 */
218 @Override
219 public int hashCode() {
220 return Objects.hash(kinematics, timestampSeconds);
221 }
222
223 /**
224 * Checks if provided instance has exactly the same contents as this instance.
225 *
226 * @param other instance to be compared.
227 * @return true if both instances are considered to be equal, false otherwise.
228 */
229 public boolean equals(final TimedBodyKinematics other) {
230 return equals(other, 0.0);
231 }
232
233 /**
234 * Checks if provided instance has contents similar to this instance up to provided
235 * threshold value.
236 *
237 * @param other instance to be compared.
238 * @param threshold maximum allowed difference between kinematics and timestamp values.
239 * @return true if both instances are considered to be equal (up to provided
240 * threshold), false otherwise.
241 */
242 public boolean equals(final TimedBodyKinematics other, final double threshold) {
243 if (other == null) {
244 return false;
245 }
246
247 return ((other.kinematics == null && kinematics == null)
248 || (kinematics != null && kinematics.equals(other.kinematics, threshold)))
249 && Math.abs(other.timestampSeconds - timestampSeconds) <= threshold;
250 }
251
252 /**
253 * Checks if provided object is a TimedBodyKinematics instance having exactly the same
254 * contents as this instance.
255 *
256 * @param obj object to be compared.
257 * @return true if both objects are considered to be equal, false otherwise.
258 */
259 @Override
260 public boolean equals(final Object obj) {
261 if (this == obj) {
262 return true;
263 }
264 if (obj == null || getClass() != obj.getClass()) {
265 return false;
266 }
267 final var other = (TimedBodyKinematics) obj;
268 return equals(other);
269 }
270
271 /**
272 * Makes a copy of this instance.
273 *
274 * @return a copy of this instance.
275 * @throws CloneNotSupportedException if clone fails for some reason.
276 */
277 @Override
278 protected Object clone() throws CloneNotSupportedException {
279 final var result = (TimedBodyKinematics) super.clone();
280 copyTo(result);
281 return result;
282 }
283
284 /**
285 * Converts provided time instance to seconds.
286 *
287 * @param time timestamp to be converted.
288 * @return converted value expressed in seconds.
289 */
290 private static double convertTime(final Time time) {
291 return TimeConverter.convert(time.getValue().doubleValue(), time.getUnit(), TimeUnit.SECOND);
292 }
293 }