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.frames.ECEFFrame;
19 import com.irurueta.navigation.frames.NEDFrame;
20 import com.irurueta.navigation.frames.converters.ECEFtoNEDFrameConverter;
21 import com.irurueta.navigation.frames.converters.NEDtoECEFFrameConverter;
22 import com.irurueta.navigation.inertial.BodyMagneticFluxDensity;
23 import com.irurueta.navigation.inertial.wmm.WMMEarthMagneticFluxDensityEstimator;
24
25 import java.io.Serial;
26 import java.io.Serializable;
27 import java.util.Date;
28 import java.util.GregorianCalendar;
29 import java.util.Objects;
30
31 /**
32 * Contains a body magnetic flux density along with the corresponding frame
33 * (position, orientation and velocity) where the measurement was made.
34 */
35 public class FrameBodyMagneticFluxDensity 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 magnetic flux density. Contains magnetometer measurements.
46 */
47 private BodyMagneticFluxDensity magneticFluxDensity;
48
49 /**
50 * Contains current body position, velocity (which will typically be zero)
51 * and orientation resolved around ECEF axes.
52 */
53 private ECEFFrame frame;
54
55 /**
56 * Contains year expressed in decimal format.
57 */
58 private double year;
59
60 /**
61 * Constructor.
62 */
63 public FrameBodyMagneticFluxDensity() {
64 }
65
66 /**
67 * Constructor.
68 *
69 * @param magneticFluxDensity current body magnetic flux density.
70 */
71 public FrameBodyMagneticFluxDensity(final BodyMagneticFluxDensity magneticFluxDensity) {
72 this.magneticFluxDensity = magneticFluxDensity;
73 }
74
75 /**
76 * Constructor.
77 *
78 * @param frame current ECEF frame associated to measurement.
79 */
80 public FrameBodyMagneticFluxDensity(final ECEFFrame frame) {
81 this.frame = frame;
82 }
83
84 /**
85 * Constructor.
86 *
87 * @param frame current NED frame associated to measurement. Internally it
88 * will be converted to its corresponding ECEF frame.
89 */
90 public FrameBodyMagneticFluxDensity(final NEDFrame frame) {
91 setNedFrame(frame);
92 }
93
94 /**
95 * Constructor.
96 *
97 * @param magneticFluxDensity current body magnetic flux density.
98 * @param frame current ECEF frame associated to measurement.
99 */
100 public FrameBodyMagneticFluxDensity(final BodyMagneticFluxDensity magneticFluxDensity, final ECEFFrame frame) {
101 this(magneticFluxDensity);
102 this.frame = frame;
103 }
104
105 /**
106 * Constructor.
107 *
108 * @param magneticFluxDensity current body magnetic flux density.
109 * @param frame current NED frame associated to measurement.
110 * Internally it will be converted to its
111 * corresponding ECEF frame.
112 */
113 public FrameBodyMagneticFluxDensity(final BodyMagneticFluxDensity magneticFluxDensity, final NEDFrame frame) {
114 this(magneticFluxDensity);
115 setNedFrame(frame);
116 }
117
118 /**
119 * Constructor.
120 *
121 * @param year time expressed as decimal year.
122 */
123 public FrameBodyMagneticFluxDensity(final double year) {
124 this.year = year;
125 }
126
127 /**
128 * Constructor.
129 *
130 * @param magneticFluxDensity current body magnetic flux density.
131 * @param year time expressed as decimal year.
132 */
133 public FrameBodyMagneticFluxDensity(final BodyMagneticFluxDensity magneticFluxDensity, final double year) {
134 this.magneticFluxDensity = magneticFluxDensity;
135 this.year = year;
136 }
137
138 /**
139 * Constructor.
140 *
141 * @param frame current ECEF frame associated to measurement.
142 * @param year time expressed as decimal year.
143 */
144 public FrameBodyMagneticFluxDensity(final ECEFFrame frame, final double year) {
145 this.frame = frame;
146 this.year = year;
147 }
148
149 /**
150 * Constructor.
151 *
152 * @param frame current NED frame associated to measurement. Internally it
153 * will be converted to its corresponding ECEF frame.
154 * @param year time expressed as decimal year.
155 */
156 public FrameBodyMagneticFluxDensity(final NEDFrame frame, final double year) {
157 setNedFrame(frame);
158 this.year = year;
159 }
160
161 /**
162 * Constructor.
163 *
164 * @param magneticFluxDensity current body magnetic flux density.
165 * @param frame current ECEF frame associated to measurement.
166 * @param year time expressed as decimal year.
167 */
168 public FrameBodyMagneticFluxDensity(
169 final BodyMagneticFluxDensity magneticFluxDensity, final ECEFFrame frame, final double year) {
170 this(magneticFluxDensity);
171 this.frame = frame;
172 this.year = year;
173 }
174
175 /**
176 * Constructor.
177 *
178 * @param magneticFluxDensity current body magnetic flux density.
179 * @param frame current NED frame associated to measurement.
180 * Internally it will be converted to its
181 * corresponding ECEF frame.
182 * @param year time expressed as decimal year.
183 */
184 public FrameBodyMagneticFluxDensity(
185 final BodyMagneticFluxDensity magneticFluxDensity, final NEDFrame frame, final double year) {
186 this(magneticFluxDensity);
187 setNedFrame(frame);
188 this.year = year;
189 }
190
191 /**
192 * Constructor.
193 *
194 * @param time a timestamp.
195 */
196 public FrameBodyMagneticFluxDensity(final Date time) {
197 this(convertTime(time));
198 }
199
200 /**
201 * Constructor.
202 *
203 * @param magneticFluxDensity current body magnetic flux density.
204 * @param time a timestamp.
205 */
206 public FrameBodyMagneticFluxDensity(final BodyMagneticFluxDensity magneticFluxDensity, final Date time) {
207 this(magneticFluxDensity, convertTime(time));
208 }
209
210 /**
211 * Constructor.
212 *
213 * @param frame current ECEF frame associated to measurement.
214 * @param time a timestamp.
215 */
216 public FrameBodyMagneticFluxDensity(final ECEFFrame frame, final Date time) {
217 this(frame, convertTime(time));
218 }
219
220 /**
221 * Constructor.
222 *
223 * @param frame current NED frame associated to measurement. Internally it
224 * will be converted to its corresponding ECEF frame.
225 * @param time a timestamp.
226 */
227 public FrameBodyMagneticFluxDensity(final NEDFrame frame, final Date time) {
228 this(frame, convertTime(time));
229 }
230
231 /**
232 * Constructor.
233 *
234 * @param magneticFluxDensity current body magnetic flux density.
235 * @param frame current ECEF frame associated to measurement.
236 * @param time a timestamp.
237 */
238 public FrameBodyMagneticFluxDensity(
239 final BodyMagneticFluxDensity magneticFluxDensity, final ECEFFrame frame, final Date time) {
240 this(magneticFluxDensity, frame, convertTime(time));
241 }
242
243 /**
244 * Constructor.
245 *
246 * @param magneticFluxDensity current body magnetic flux density.
247 * @param frame current NED frame associated to measurement.
248 * Internally it will be converted to its
249 * corresponding ECEF frame.
250 * @param time a timestamp.
251 */
252 public FrameBodyMagneticFluxDensity(
253 final BodyMagneticFluxDensity magneticFluxDensity, final NEDFrame frame, final Date time) {
254 this(magneticFluxDensity, frame, convertTime(time));
255 }
256
257 /**
258 * Constructor.
259 *
260 * @param calendar calendar containing a timestamp.
261 */
262 public FrameBodyMagneticFluxDensity(final GregorianCalendar calendar) {
263 this(convertTime(calendar));
264 }
265
266 /**
267 * Constructor.
268 *
269 * @param magneticFluxDensity current body magnetic flux density.
270 * @param calendar calendar containing a timestamp.
271 */
272 public FrameBodyMagneticFluxDensity(
273 final BodyMagneticFluxDensity magneticFluxDensity, final GregorianCalendar calendar) {
274 this(magneticFluxDensity, convertTime(calendar));
275 }
276
277 /**
278 * Constructor.
279 *
280 * @param frame current ECEF frame associated to measurement.
281 * @param calendar calendar containing a timestamp.
282 */
283 public FrameBodyMagneticFluxDensity(final ECEFFrame frame, final GregorianCalendar calendar) {
284 this(frame, convertTime(calendar));
285 }
286
287 /**
288 * Constructor.
289 *
290 * @param frame current NED frame associated to measurement. Internally it
291 * will be converted to its corresponding ECEF frame.
292 * @param calendar calendar containing a timestamp.
293 */
294 public FrameBodyMagneticFluxDensity(final NEDFrame frame, final GregorianCalendar calendar) {
295 this(frame, convertTime(calendar));
296 }
297
298 /**
299 * Constructor.
300 *
301 * @param magneticFluxDensity current body magnetic flux density.
302 * @param frame current ECEF frame associated to measurement.
303 * @param calendar calendar containing a timestamp.
304 */
305 public FrameBodyMagneticFluxDensity(
306 final BodyMagneticFluxDensity magneticFluxDensity, final ECEFFrame frame,
307 final GregorianCalendar calendar) {
308 this(magneticFluxDensity, frame, convertTime(calendar));
309 }
310
311 /**
312 * Constructor.
313 *
314 * @param magneticFluxDensity current body magnetic flux density.
315 * @param frame current NED frame associated to measurement.
316 * Internally it will be converted to its
317 * corresponding ECEF frame.
318 * @param calendar calendar containing a timestamp.
319 */
320 public FrameBodyMagneticFluxDensity(
321 final BodyMagneticFluxDensity magneticFluxDensity, final NEDFrame frame,
322 final GregorianCalendar calendar) {
323 this(magneticFluxDensity, frame, convertTime(calendar));
324 }
325
326 /**
327 * Constructor.
328 *
329 * @param input instance to copy data from.
330 */
331 public FrameBodyMagneticFluxDensity(final FrameBodyMagneticFluxDensity input) {
332 copyFrom(input);
333 }
334
335 /**
336 * Gets current body magnetic flux density. Contains magnetometer
337 * measurements.
338 *
339 * @return current body magnetic flux density.
340 */
341 public BodyMagneticFluxDensity getMagneticFluxDensity() {
342 return magneticFluxDensity;
343 }
344
345 /**
346 * Sets current body magnetic flux density. Contains magnetometer
347 * measurements.
348 *
349 * @param magneticFluxDensity current body magnetic flux density.
350 */
351 public void setMagneticFluxDensity(final BodyMagneticFluxDensity magneticFluxDensity) {
352 this.magneticFluxDensity = magneticFluxDensity;
353 }
354
355 /**
356 * Gets current body position (which will typically remain constant),
357 * velocity(which will typically be zero) and orientation (which usually
358 * changes with each measurement to perform calibration of a single device)
359 * resolved around ECEF axes associated to body magnetic flux density
360 * measurement.
361 *
362 * @return current ECEF frame associated to body magnetic flux density
363 * measurement or null if not available.
364 */
365 public ECEFFrame getFrame() {
366 return frame;
367 }
368
369 /**
370 * Sets current body position (which will typically remain constant),
371 * velocity (which will typically be zero) and orientation (which usually
372 * changes with each measurement to perform calibration of a single
373 * device) resolved around ECEF axes associated to body magnetic flux
374 * density measurement.
375 *
376 * @param frame current ECEF frame.
377 */
378 public void setFrame(final ECEFFrame frame) {
379 this.frame = frame;
380 }
381
382 /**
383 * Gets current body position (which will typically remain constant),
384 * velocity (which will typically be zero) and orientation (which usually
385 * changes with each measurement to perform calibration of a single
386 * device) resolved around NED axes associated to body magnetic flux
387 * density measurement.
388 *
389 * @return current NED frame associated to body magnetic flux density
390 * measurement or null if not available.
391 */
392 public NEDFrame getNedFrame() {
393 return frame != null ? ECEFtoNEDFrameConverter.convertECEFtoNEDAndReturnNew(frame) : null;
394 }
395
396 /**
397 * Gets current body position (which will typically remain constant),
398 * velocity (which will typically be zero) and orientation (which usually
399 * changes with each measurement to perform calibration of a single
400 * device) resolved around NED axes associated to body magnetic flux
401 * density measurement.
402 *
403 * @param result instance where result data will be stored if available.
404 * @return true if result instance was updated, false otherwise.
405 */
406 public boolean getNedFrame(final NEDFrame result) {
407 if (frame != null) {
408 ECEFtoNEDFrameConverter.convertECEFtoNED(frame, result);
409 return true;
410 } else {
411 return false;
412 }
413 }
414
415 /**
416 * Sets current body position (which will typically remain constant),
417 * velocity (which will typically be zero) and orientation (which usually
418 * changes with each measurement to perform calibration of a single
419 * device) resolved around NED axes associated to body magnetic flux
420 * density measurement.
421 * <p>
422 * This method will internally store the corresponding ECEF frame to provided
423 * NED frame value.
424 *
425 * @param nedFrame current NED frame associated to body magnetic flux
426 * density measurement to be set.
427 */
428 public void setNedFrame(final NEDFrame nedFrame) {
429 if (nedFrame != null) {
430 if (frame != null) {
431 NEDtoECEFFrameConverter.convertNEDtoECEF(nedFrame, frame);
432 } else {
433 frame = NEDtoECEFFrameConverter.convertNEDtoECEFAndReturnNew(nedFrame);
434 }
435 } else {
436 frame = null;
437 }
438 }
439
440 /**
441 * Gets year expressed in decimal format.
442 *
443 * @return year expressed in decimal format.
444 */
445 public double getYear() {
446 return year;
447 }
448
449 /**
450 * Sets year expressed in decimal format.
451 *
452 * @param year year expressed in decimal format.
453 */
454 public void setYear(final double year) {
455 this.year = year;
456 }
457
458 /**
459 * Sets decimal year from provided date instance.
460 *
461 * @param date a date instance containing a timestamp.
462 */
463 public void setTime(final Date date) {
464 year = convertTime(date);
465 }
466
467 /**
468 * Sets decimal year from provided calendar instance.
469 *
470 * @param calendar a calendar instance containing a timestamp.
471 */
472 public void setTime(final GregorianCalendar calendar) {
473 year = convertTime(calendar);
474 }
475
476 /**
477 * Converts a time instant contained ina date object to a
478 * decimal year.
479 *
480 * @param date a time instance to be converted.
481 * @return converted value expressed in decimal years.
482 */
483 public static double convertTime(final Date date) {
484 final var calendar = new GregorianCalendar();
485 calendar.setTime(date);
486 return convertTime(calendar);
487 }
488
489 /**
490 * Converts a time instant contained in a gregorian calendar to a
491 * decimal year.
492 *
493 * @param calendar calendar containing a specific instant to be
494 * converted.
495 * @return converted value expressed in decimal years.
496 */
497 public static double convertTime(final GregorianCalendar calendar) {
498 return WMMEarthMagneticFluxDensityEstimator.convertTime(calendar);
499 }
500
501 /**
502 * Copies data of provided instance into this instance.
503 *
504 * @param input instance to copy dara from.
505 */
506 public void copyFrom(final FrameBodyMagneticFluxDensity input) {
507 if (input.magneticFluxDensity != null) {
508 if (magneticFluxDensity == null) {
509 magneticFluxDensity = new BodyMagneticFluxDensity(input.magneticFluxDensity);
510 } else {
511 magneticFluxDensity.copyFrom(input.magneticFluxDensity);
512 }
513 } else {
514 magneticFluxDensity = null;
515 }
516
517 if (input.frame != null) {
518 if (frame == null) {
519 frame = new ECEFFrame(input.frame);
520 } else {
521 frame.copyFrom(input.frame);
522 }
523 } else {
524 frame = null;
525 }
526
527 year = input.year;
528 }
529
530 /**
531 * Copies this instance data into provided instance.
532 *
533 * @param output destination instance where data will be copied to.
534 */
535 public void copyTo(final FrameBodyMagneticFluxDensity output) {
536 output.copyFrom(this);
537 }
538
539 /**
540 * Computes and returns hash code for this instance. Hash codes are almost unique
541 * values that are useful for fast classification and storage of objects in collections.
542 *
543 * @return Hash code.
544 */
545 @Override
546 public int hashCode() {
547 return Objects.hash(magneticFluxDensity, frame, year);
548 }
549
550 /**
551 * Checks if provided instance has exactly the same contents as this instance.
552 *
553 * @param other instance to be compared.
554 * @return true if both instances are considered to be equal, false otherwise.
555 */
556 public boolean equals(final FrameBodyMagneticFluxDensity other) {
557 return equals(other, 0.0);
558 }
559
560 /**
561 * Checks if provided instance has contents similar to this instance up to provided
562 * threshold value.
563 *
564 * @param other instance to be compared.
565 * @param threshold maximum allowed difference between magnetic flux density and
566 * frame values.
567 * @return true if both instances are considered to be equal (up to provided
568 * threshold), false otherwise.
569 */
570 public boolean equals(final FrameBodyMagneticFluxDensity other, final double threshold) {
571 if (other == null) {
572 return false;
573 }
574
575 return ((other.magneticFluxDensity == null && magneticFluxDensity == null)
576 || (magneticFluxDensity != null && magneticFluxDensity.equals(other.magneticFluxDensity, threshold)))
577 && ((other.frame == null && frame == null)
578 || (frame != null && frame.equals(other.frame, threshold)))
579 && Math.abs(other.year - year) <= threshold;
580 }
581
582 /**
583 * Checks if provided object is a FrameBodyMagneticFluxDensity instance
584 * having exactly the same contents as this instance.
585 *
586 * @param obj object to be compared.
587 * @return true if both objects are considered to be equal, false otherwise.
588 */
589 @Override
590 public boolean equals(final Object obj) {
591 if (this == obj) {
592 return true;
593 }
594 if (obj == null || getClass() != obj.getClass()) {
595 return false;
596 }
597 final var other = (FrameBodyMagneticFluxDensity) obj;
598 return equals(other);
599 }
600
601 /**
602 * Makes a copy of this instance.
603 *
604 * @return a copy of this instance.
605 * @throws CloneNotSupportedException if clone fails for some reason.
606 */
607 @Override
608 protected Object clone() throws CloneNotSupportedException {
609 final var result = (FrameBodyMagneticFluxDensity) super.clone();
610 copyTo(result);
611 return result;
612 }
613 }