View Javadoc
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 }