View Javadoc
1   /*
2    * Copyright (C) 2016 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.ar.slam;
17  
18  import com.irurueta.algebra.ArrayUtils;
19  import com.irurueta.algebra.Matrix;
20  import com.irurueta.algebra.WrongSizeException;
21  import com.irurueta.geometry.Quaternion;
22  import com.irurueta.geometry.RotationUtils;
23  
24  /**
25   * Utility class to predict rotations.
26   */
27  public class QuaternionPredictor {
28      /**
29       * Number of components on angular speed.
30       */
31      public static final int ANGULAR_SPEED_COMPONENTS = 3;
32  
33      /**
34       * Constructor.
35       */
36      private QuaternionPredictor() {
37      }
38  
39      /**
40       * Predicts the updated quaternion after a rotation in body frame expressed
41       * by the rate of rotation along axes x,y,z (roll, pitch, yaw).
42       *
43       * @param q           quaternion to be updated.
44       * @param wx          angular speed in x-axis (roll axis). Expressed in rad/s.
45       * @param wy          angular speed in y-axis (pitch axis). Expressed in rad/s.
46       * @param wz          angular speed in z-axis (yaw axis). Expressed in rad/s.
47       * @param dt          time interval to compute prediction expressed in seconds.
48       * @param exactMethod true to use exact method, false to use "Tustin"
49       *                    method.
50       * @param result      instance where updated quaternion is stored.
51       * @param jacobianQ   jacobian wrt input quaternion. Must be 4x4.
52       * @param jacobianW   jacobian wrt angular speed. Must be 4x3.
53       * @throws IllegalArgumentException if any of provided jacobians does not
54       *                                  have proper size.
55       * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
56       */
57      public static void predict(
58              final Quaternion q, final double wx, final double wy, final double wz,
59              final double dt, final boolean exactMethod, final Quaternion result, final Matrix jacobianQ,
60              final Matrix jacobianW) {
61  
62          if (jacobianQ != null && (jacobianQ.getRows() != Quaternion.N_PARAMS
63                  || jacobianQ.getColumns() != Quaternion.N_PARAMS)) {
64              throw new IllegalArgumentException("jacobian wrt q must be 4x4");
65          }
66          if (jacobianW != null && (jacobianW.getRows() != Quaternion.N_PARAMS
67                  || jacobianW.getColumns() != ANGULAR_SPEED_COMPONENTS)) {
68              throw new IllegalArgumentException("jacobian wrt w must be 4x3");
69          }
70  
71          final var w = new double[]{wx, wy, wz};
72  
73          if (exactMethod) {
74              // exact jacobians and rotation
75              ArrayUtils.multiplyByScalar(w, dt, w);
76              Quaternion.rotationVectorToQuaternion(w, result, jacobianW);
77              Matrix jacobianQ2 = null;
78              if (jacobianW != null) {
79                  jacobianW.multiplyByScalar(dt);
80                  try {
81                      jacobianQ2 = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
82                  } catch (final WrongSizeException ignore) {
83                      // never happens
84                  }
85              }
86              Quaternion.product(q, result, result, jacobianQ, jacobianQ2);
87  
88              if (jacobianW != null && jacobianQ2 != null) {
89                  try {
90                      jacobianQ2.multiply(jacobianW);
91                      jacobianW.copyFrom(jacobianQ2);
92                  } catch (final WrongSizeException ignore) {
93                      // never happens
94                  }
95              }
96          } else {
97              // tustin integration - fits with jacobians
98              var skewW = RotationUtils.w2omega(w);
99              try {
100                 final var qMatrix = new Matrix(Quaternion.N_PARAMS, 1);
101                 qMatrix.setElementAtIndex(0, q.getA());
102                 qMatrix.setElementAtIndex(1, q.getB());
103                 qMatrix.setElementAtIndex(2, q.getC());
104                 qMatrix.setElementAtIndex(3, q.getD());
105                 skewW.multiply(qMatrix);
106             } catch (final WrongSizeException ignore) {
107                 // never happens
108             }
109 
110             skewW.multiplyByScalar(0.5 * dt);
111 
112             result.setA(q.getA() + skewW.getElementAtIndex(0));
113             result.setB(q.getB() + skewW.getElementAtIndex(1));
114             result.setC(q.getC() + skewW.getElementAtIndex(2));
115             result.setD(q.getD() + skewW.getElementAtIndex(3));
116 
117             if (jacobianQ != null) {
118                 try {
119                     // reset w values to reuse the same array as they might have been
120                     // modified
121                     w[0] = wx;
122                     w[1] = wy;
123                     w[2] = wz;
124 
125                     skewW = RotationUtils.w2omega(w);
126                     jacobianQ.copyFrom(Matrix.identity(Quaternion.N_PARAMS, Quaternion.N_PARAMS));
127                     jacobianQ.add(skewW);
128                     jacobianQ.multiplyByScalar(0.5 * dt);
129                 } catch (final WrongSizeException ignore) {
130                     // never happens
131                 }
132             }
133 
134             if (jacobianW != null) {
135                 RotationUtils.quaternionToConjugatedPiMatrix(q, jacobianW);
136                 jacobianW.multiplyByScalar(0.5 * dt);
137             }
138         }
139     }
140 
141     /**
142      * Predicts the updated quaternion after a rotation in body frame expressed
143      * by the rate of rotation along axes x, y, z (roll, pitch, yaw).
144      *
145      * @param q           quaternion to be updated.
146      * @param w           array containing angular speed in the 3 axis (x = roll,
147      *                    y = pitch, z = yaw). Expressed in rad/se. Must have length 3
148      * @param dt          time interval to compute prediction expressed in seconds.
149      * @param exactMethod true to use exact method, false to use "Tustin"
150      *                    method.
151      * @param result      instance where update quaternion is stored.
152      * @param jacobianQ   jacobian wrt quaternion. Must be 4x4.
153      * @param jacobianW   jacobian wrt angular speed. Must be 4x3.
154      * @throws IllegalArgumentException if any of provided jacobians does not
155      *                                  have proper size, or w does not have length 3.
156      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
157      */
158     public static void predict(
159             final Quaternion q, final double[] w, final double dt,
160             final boolean exactMethod, final Quaternion result, final Matrix jacobianQ, final Matrix jacobianW) {
161         if (w.length != ANGULAR_SPEED_COMPONENTS) {
162             throw new IllegalArgumentException("w must have length 3");
163         }
164         predict(q, w[0], w[1], w[2], dt, exactMethod, result, jacobianQ, jacobianW);
165     }
166 
167     /**
168      * Predicts the updated quaternion after a rotation in body frame expressed
169      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) using
170      * exact method.
171      *
172      * @param q         quaternion to be updated.
173      * @param wx        angular speed in x-axis (roll axis). Expressed in rad/s.
174      * @param wy        angular speed in y-axis (pitch axis). Expressed in rad/s.
175      * @param wz        angular speed in z-axis (yaw axis). Expressed in rad/s.
176      * @param dt        time interval to compute prediction expressed in seconds.
177      * @param result    instance where update quaternion is stored.
178      * @param jacobianQ jacobian wrt quaternion. Must be 4x4.
179      * @param jacobianW jacobian wrt angular speed. Must be 4x3.
180      * @throws IllegalArgumentException if any of provided jacobians does not
181      *                                  have proper size.
182      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
183      */
184     public static void predict(
185             final Quaternion q, final double wx, final double wy, final double wz, final double dt,
186             final Quaternion result, final Matrix jacobianQ, final Matrix jacobianW) {
187         predict(q, wx, wy, wz, dt, true, result, jacobianQ, jacobianW);
188     }
189 
190     /**
191      * Predicts the updated quaternion after a rotation in body frame expressed
192      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) using exact
193      * method.
194      *
195      * @param q         quaternion to be updated.
196      * @param w         array containing angular speed in the 3 axis (x = roll,
197      *                  y = pitch, z = yaw). Expressed in rad/se. Must have length 3
198      * @param dt        time interval to compute prediction expressed in seconds.
199      * @param result    instance where update quaternion is stored.
200      * @param jacobianQ jacobian wrt quaternion. Must be 4x4.
201      * @param jacobianW jacobian wrt angular speed. Must be 4x3.
202      * @throws IllegalArgumentException if any of provided jacobians does not
203      *                                  have proper size.
204      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
205      */
206     public static void predict(
207             final Quaternion q, final double[] w, final double dt, final Quaternion result, final Matrix jacobianQ,
208             final Matrix jacobianW) {
209         predict(q, w, dt, true, result, jacobianQ, jacobianW);
210     }
211 
212     /**
213      * Predicts the updated quaternion after a rotation in body frame expressed
214      * by the rate of rotation along axes x, y, z (roll, pitch, yaw).
215      *
216      * @param q           quaternion to be updated.
217      * @param wx          angular speed in x-axis (roll axis). Expressed in rad/s.
218      * @param wy          angular speed in y-axis (pitch axis). Expressed in rad/s.
219      * @param wz          angular speed in z-axis (yaw axis). Expressed in rad/s.
220      * @param dt          time interval to compute prediction expressed in seconds.
221      * @param exactMethod true to use exact method, false to use "Tustin"
222      *                    method.
223      * @param result      instance where update quaternion is stored.
224      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
225      */
226     public static void predict(
227             final Quaternion q, final double wx, final double wy, final double wz, final double dt,
228             final boolean exactMethod, final Quaternion result) {
229         predict(q, wx, wy, wz, dt, exactMethod, result, null, null);
230     }
231 
232     /**
233      * Predicts the updated quaternion after a rotation in body frame expressed
234      * by the rate of rotation along axes x, y, z (roll, pitch, yaw).
235      *
236      * @param q           quaternion to be updated.
237      * @param w           array containing angular speed in the 3 axis (x = roll,
238      *                    y = pitch, z = yaw). Expressed in rad/se. Must have length 3
239      * @param dt          time interval to compute prediction expressed in seconds.
240      * @param exactMethod true to use exact method, false to use "Tustin"
241      *                    method.
242      * @param result      instance where update quaternion is stored.
243      * @throws IllegalArgumentException if w does not have length 3.
244      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
245      */
246     public static void predict(
247             final Quaternion q, final double[] w, final double dt, final boolean exactMethod, final Quaternion result) {
248         predict(q, w, dt, exactMethod, result, null, null);
249     }
250 
251     /**
252      * Predicts the updated quaternion after a rotation in body frame expressed
253      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) using exact
254      * method.
255      *
256      * @param q      quaternion to be updated.
257      * @param wx     angular speed in x-axis (roll axis). Expressed in rad/s.
258      * @param wy     angular speed in y-axis (pitch axis). Expressed in rad/s.
259      * @param wz     angular speed in z-axis (yaw axis). Expressed in rad/s.
260      * @param dt     time interval to compute prediction expressed in seconds.
261      * @param result instance where update quaternion is stored.
262      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
263      */
264     public static void predict(
265             final Quaternion q, final double wx, final double wy, final double wz, final double dt,
266             final Quaternion result) {
267         predict(q, wx, wy, wz, dt, result, null, null);
268     }
269 
270     /**
271      * Predicts the updated quaternion after a rotation in body frame expressed
272      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) using exact
273      * method.
274      *
275      * @param q      quaternion to be updated.
276      * @param w      array containing angular speed in the 3 axis (x = roll,
277      *               y = pitch, z = yaw). Expressed in rad/se. Must have length 3
278      * @param dt     time interval to compute prediction expressed in seconds.
279      * @param result instance where update quaternion is stored.
280      * @throws IllegalArgumentException if w does not have length 3
281      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
282      */
283     public static void predict(
284             final Quaternion q, final double[] w, final double dt, final Quaternion result) {
285         predict(q, w, dt, result, null, null);
286     }
287 
288     /**
289      * Predicts the updated quaternion after a rotation in body frame expressed
290      * by the rate of rotation along axes x, y, z (roll, pitch, yaw).
291      *
292      * @param q           quaternion to be updated.
293      * @param wx          angular speed in x-axis (roll axis). Expressed in rad/s.
294      * @param wy          angular speed in y-axis (pitch axis). Expressed in rad/s.
295      * @param wz          angular speed in z-axis (yaw axis). Expressed in rad/s.
296      * @param dt          time interval to compute prediction expressed in seconds.
297      * @param exactMethod true to use exact method, false to use "Tustin"
298      *                    method.
299      * @param jacobianQ   jacobian wrt quaternion. Must be 4x4.
300      * @param jacobianW   jacobian wrt angular speed. Must be 4x3.
301      * @return a new quaternion containing updated quaternion.
302      * @throws IllegalArgumentException if any of provided jacobians does not
303      *                                  have proper size.
304      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
305      */
306     public static Quaternion predict(
307             final Quaternion q, final double wx, final double wy, final double wz, final double dt,
308             final boolean exactMethod, final Matrix jacobianQ, final Matrix jacobianW) {
309         final var result = new Quaternion();
310         predict(q, wx, wy, wz, dt, exactMethod, result, jacobianQ, jacobianW);
311         return result;
312     }
313 
314     /**
315      * Predicts the updated quaternion after a rotation in body frame expressed
316      * by the rate of rotation along axes x, y, z (roll, pitch, yaw).
317      *
318      * @param q           quaternion to be updated.
319      * @param w           array containing angular speed in the 3 axis (x = roll,
320      *                    y = pitch, z = yaw). Expressed in rad/se. Must have length 3
321      * @param dt          time interval to compute prediction expressed in seconds.
322      * @param exactMethod true to use exact method, false to use "Tustin"
323      *                    method.
324      * @param jacobianQ   jacobian wrt quaternion. Must be 4x4.
325      * @param jacobianW   jacobian wrt angular speed. Must be 4x3.
326      * @return a new quaternion containing updated quaternion.
327      * @throws IllegalArgumentException if any of provided jacobians does not
328      *                                  have proper size or w does not have length 3.
329      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
330      */
331     public static Quaternion predict(
332             final Quaternion q, final double[] w, final double dt, final boolean exactMethod, final Matrix jacobianQ,
333             final Matrix jacobianW) {
334         final var result = new Quaternion();
335         predict(q, w, dt, exactMethod, result, jacobianQ, jacobianW);
336         return result;
337     }
338 
339     /**
340      * Predicts the updated quaternion after a rotation in body frame expressed
341      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) using exact
342      * method.
343      *
344      * @param q         quaternion to be updated.
345      * @param wx        angular speed in x-axis (roll axis). Expressed in rad/s.
346      * @param wy        angular speed in y-axis (pitch axis). Expressed in rad/s.
347      * @param wz        angular speed in z-axis (yaw axis). Expressed in rad/s.
348      * @param dt        time interval to compute prediction expressed in seconds.
349      * @param jacobianQ jacobian wrt quaternion. Must be 4x4.
350      * @param jacobianW jacobian wrt angular speed. Must be 4x3.
351      * @return a new quaternion containing updated quaternion.
352      * @throws IllegalArgumentException if any of provided jacobians does not
353      *                                  have proper size.
354      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
355      */
356     public static Quaternion predict(
357             final Quaternion q, final double wx, final double wy, final double wz, final double dt,
358             final Matrix jacobianQ, final Matrix jacobianW) {
359         final var result = new Quaternion();
360         predict(q, wx, wy, wz, dt, result, jacobianQ, jacobianW);
361         return result;
362     }
363 
364     /**
365      * Predicts the updated quaternion after a rotation in body frame expressed
366      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) using exact
367      * method.
368      *
369      * @param q         quaternion to be updated.
370      * @param w         array containing angular speed in the 3 axis (x = roll,
371      *                  y = pitch, z = yaw). Expressed in rad/se. Must have length 3
372      * @param dt        time interval to compute prediction expressed in seconds.
373      * @param jacobianQ jacobian wrt quaternion. Must be 4x4.
374      * @param jacobianW jacobian wrt angular speed. Must be 4x3.
375      * @return a new quaternion containing updated quaternion.
376      * @throws IllegalArgumentException if any of provided jacobians does not
377      *                                  have proper size.
378      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
379      */
380     public static Quaternion predict(
381             final Quaternion q, final double[] w, final double dt, final Matrix jacobianQ, final Matrix jacobianW) {
382         final var result = new Quaternion();
383         predict(q, w, dt, result, jacobianQ, jacobianW);
384         return result;
385     }
386 
387     /**
388      * Predicts the updated quaternion after a rotation in body frame expressed
389      * by the rate of rotation along axes x, y, z (roll, pitch, yaw).
390      *
391      * @param q           quaternion to be updated.
392      * @param wx          angular speed in x-axis (roll axis). Expressed in rad/s.
393      * @param wy          angular speed in y-axis (pitch axis). Expressed in rad/s.
394      * @param wz          angular speed in z-axis (yaw axis). Expressed in rad/s.
395      * @param dt          time interval to compute prediction expressed in seconds.
396      * @param exactMethod true to use exact method, false to use "Tustin"
397      *                    method.
398      * @return a new quaternion containing updated quaternion.
399      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
400      */
401     public static Quaternion predict(
402             final Quaternion q, final double wx, final double wy, final double wz, final double dt,
403             final boolean exactMethod) {
404         final var result = new Quaternion();
405         predict(q, wx, wy, wz, dt, exactMethod, result);
406         return result;
407     }
408 
409     /**
410      * Predicts the updated quaternion after a rotation in body frame expressed
411      * by the rate of rotation along axes x, y, z (roll, pitch, yaw).
412      *
413      * @param q           quaternion to be updated.
414      * @param w           array containing angular speed in the 3 axis (x = roll,
415      *                    y = pitch, z = yaw). Expressed in rad/se. Must have length 3
416      * @param dt          time interval to compute prediction expressed in seconds.
417      * @param exactMethod true to use exact method, false to use "Tustin"
418      *                    method.
419      * @return a new quaternion containing updated quaternion.
420      * @throws IllegalArgumentException if w does not have length 3
421      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
422      */
423     public static Quaternion predict(final Quaternion q, final double[] w, final double dt, final boolean exactMethod) {
424         final var result = new Quaternion();
425         predict(q, w, dt, exactMethod, result);
426         return result;
427     }
428 
429     /**
430      * Predicts the updated quaternion after a rotation in body frame expressed
431      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) using exact
432      * method.
433      *
434      * @param q  quaternion to be updated.
435      * @param wx angular speed in x-axis (roll axis). Expressed in rad/s.
436      * @param wy angular speed in y-axis (pitch axis). Expressed in rad/s.
437      * @param wz angular speed in z-axis (yaw axis). Expressed in rad/s.
438      * @param dt time interval to compute prediction expressed in seconds.
439      * @return a new quaternion containing updated quaternion.
440      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
441      */
442     public static Quaternion predict(final Quaternion q, final double wx, final double wy, final double wz,
443                                      final double dt) {
444         final var result = new Quaternion();
445         predict(q, wx, wy, wz, dt, result);
446         return result;
447     }
448 
449     /**
450      * Predicts the updated quaternion after a rotation in body frame expressed
451      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) using exact
452      * method.
453      *
454      * @param q  quaternion to be updated.
455      * @param w  array containing angular speed in the 3 axis (x = roll,
456      *           y = pitch, z = yaw). Expressed in rad/se. Must have length 3
457      * @param dt time interval to compute prediction expressed in seconds.
458      * @return a new quaternion containing updated quaternion.
459      * @throws IllegalArgumentException if w does not have length 3.
460      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
461      */
462     public static Quaternion predict(final Quaternion q, final double[] w, final double dt) {
463         final var result = new Quaternion();
464         predict(q, w, dt, result);
465         return result;
466     }
467 
468     /**
469      * Predicts the updated quaternion after a rotation in body frame expressed
470      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
471      * time interval of 1 second.
472      *
473      * @param q           quaternion to be updated.
474      * @param wx          angular speed in x-axis (roll axis). Expressed in rad/s.
475      * @param wy          angular speed in y-axis (pitch axis). Expressed in rad/s.
476      * @param wz          angular speed in z-axis (yaw axis). Expressed in rad/s.
477      * @param exactMethod true to use exact method, false to use "Tustin"
478      *                    method.
479      * @param result      instance where update quaternion is stored.
480      * @param jacobianQ   jacobian wrt quaternion. Must be 4x4.
481      * @param jacobianW   jacobian wrt angular speed. Must be 4x3.
482      * @throws IllegalArgumentException if any of provided jacobians does not
483      *                                  have proper size.
484      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
485      */
486     public static void predict(
487             final Quaternion q, final double wx, final double wy, final double wz, final boolean exactMethod,
488             final Quaternion result, final Matrix jacobianQ, final Matrix jacobianW) {
489         predict(q, wx, wy, wz, 1.0, exactMethod, result, jacobianQ, jacobianW);
490     }
491 
492     /**
493      * Predicts the updated quaternion after a rotation in body frame expressed
494      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
495      * time interval of 1 second.
496      *
497      * @param q           quaternion to be updated.
498      * @param w           array containing angular speed in the 3 axis (x = roll,
499      *                    y = pitch, z = yaw). Expressed in rad/se. Must have length 3
500      * @param exactMethod true to use exact method, false to use "Tustin"
501      *                    method.
502      * @param result      instance where update quaternion is stored.
503      * @param jacobianQ   jacobian wrt quaternion. Must be 4x4.
504      * @param jacobianW   jacobian wrt angular speed. Must be 4x3.
505      * @throws IllegalArgumentException if any of provided jacobians does not
506      *                                  have proper size.
507      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
508      */
509     public static void predict(
510             final Quaternion q, final double[] w, final boolean exactMethod, final Quaternion result,
511             final Matrix jacobianQ, final Matrix jacobianW) {
512         predict(q, w, 1.0, exactMethod, result, jacobianQ, jacobianW);
513     }
514 
515     /**
516      * Predicts the updated quaternion after a rotation in body frame expressed
517      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
518      * time interval of 1 second and exact method.
519      *
520      * @param q         quaternion to be updated.
521      * @param wx        angular speed in x-axis (roll axis). Expressed in rad/s.
522      * @param wy        angular speed in y-axis (pitch axis). Expressed in rad/s.
523      * @param wz        angular speed in z-axis (yaw axis). Expressed in rad/s.
524      * @param result    instance where update quaternion is stored.
525      * @param jacobianQ jacobian wrt quaternion. Must be 4x4.
526      * @param jacobianW jacobian wrt angular speed. Must be 4x3.
527      * @throws IllegalArgumentException if any of provided jacobians does not
528      *                                  have proper size.
529      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
530      */
531     public static void predict(
532             final Quaternion q, final double wx, final double wy, final double wz, final Quaternion result,
533             final Matrix jacobianQ, final Matrix jacobianW) {
534         predict(q, wx, wy, wz, 1.0, result, jacobianQ, jacobianW);
535     }
536 
537     /**
538      * Predicts the updated quaternion after a rotation in body frame expressed
539      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
540      * time interval of 1 second and exact method.
541      *
542      * @param q         quaternion to be updated.
543      * @param w         array containing angular speed in the 3 axis (x = roll,
544      *                  y = pitch, z = yaw). Expressed in rad/se. Must have length 3
545      * @param result    instance where update quaternion is stored.
546      * @param jacobianQ jacobian wrt quaternion. Must be 4x4.
547      * @param jacobianW jacobian wrt angular speed. Must be 4x3.
548      * @throws IllegalArgumentException if any of provided jacobians does not
549      *                                  have proper size.
550      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
551      */
552     public static void predict(
553             final Quaternion q, final double[] w, final Quaternion result, final Matrix jacobianQ,
554             final Matrix jacobianW) {
555         predict(q, w, 1.0, result, jacobianQ, jacobianW);
556     }
557 
558     /**
559      * Predicts the updated quaternion after a rotation in body frame expressed
560      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
561      * time interval of 1 second.
562      *
563      * @param q           quaternion to be updated.
564      * @param wx          angular speed in x-axis (roll axis). Expressed in rad/s.
565      * @param wy          angular speed in y-axis (pitch axis). Expressed in rad/s.
566      * @param wz          angular speed in z-axis (yaw axis). Expressed in rad/s.
567      * @param exactMethod true to use exact method, false to use "Tustin"
568      *                    method.
569      * @param result      instance where update quaternion is stored.
570      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
571      */
572     public static void predict(
573             final Quaternion q, final double wx, final double wy, final double wz, final boolean exactMethod,
574             final Quaternion result) {
575         predict(q, wx, wy, wz, 1.0, exactMethod, result);
576     }
577 
578     /**
579      * Predicts the updated quaternion after a rotation in body frame expressed
580      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
581      * time interval of 1 second.
582      *
583      * @param q           quaternion to be updated.
584      * @param w           array containing angular speed in the 3 axis (x = roll,
585      *                    y = pitch, z = yaw). Expressed in rad/se. Must have length 3
586      * @param exactMethod true to use exact method, false to use "Tustin"
587      *                    method.
588      * @param result      instance where update quaternion is stored.
589      * @throws IllegalArgumentException if any of provided jacobians does not
590      *                                  have proper size.
591      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
592      */
593     public static void predict(
594             final Quaternion q, final double[] w, final boolean exactMethod, final Quaternion result) {
595         predict(q, w, 1.0, exactMethod, result);
596     }
597 
598     /**
599      * Predicts the updated quaternion after a rotation in body frame expressed
600      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
601      * time interval of 1 second and exact method.
602      *
603      * @param q      quaternion to be updated.
604      * @param wx     angular speed in x-axis (roll axis). Expressed in rad/s.
605      * @param wy     angular speed in y-axis (pitch axis). Expressed in rad/s.
606      * @param wz     angular speed in z-axis (yaw axis). Expressed in rad/s.
607      * @param result instance where update quaternion is stored.
608      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
609      */
610     public static void predict(
611             final Quaternion q, final double wx, final double wy, final double wz, final Quaternion result) {
612         predict(q, wx, wy, wz, 1.0, result);
613     }
614 
615     /**
616      * Predicts the updated quaternion after a rotation in body frame expressed
617      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
618      * time interval of 1 second and exact method.
619      *
620      * @param q      quaternion to be updated.
621      * @param w      array containing angular speed in the 3 axis (x = roll,
622      *               y = pitch, z = yaw). Expressed in rad/se. Must have length 3
623      * @param result instance where update quaternion is stored.
624      * @throws IllegalArgumentException if any of provided jacobians does not
625      *                                  have proper size.
626      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
627      */
628     public static void predict(final Quaternion q, final double[] w, final Quaternion result) {
629         predict(q, w, 1.0, result);
630     }
631 
632     /**
633      * Predicts the updated quaternion after a rotation in body frame expressed
634      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
635      * time interval of 1 second.
636      *
637      * @param q           quaternion to be updated.
638      * @param wx          angular speed in x-axis (roll axis). Expressed in rad/s.
639      * @param wy          angular speed in y-axis (pitch axis). Expressed in rad/s.
640      * @param wz          angular speed in z-axis (yaw axis). Expressed in rad/s.
641      * @param exactMethod true to use exact method, false to use "Tustin"
642      *                    method.
643      * @param jacobianQ   jacobian wrt quaternion. Must be 4x4.
644      * @param jacobianW   jacobian wrt angular speed. Must be 4x3.
645      * @return a new quaternion containing updated quaternion.
646      * @throws IllegalArgumentException if any of provided jacobians does not
647      *                                  have proper size.
648      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
649      */
650     public static Quaternion predict(
651             final Quaternion q, final double wx, final double wy, final double wz, final boolean exactMethod,
652             final Matrix jacobianQ, final Matrix jacobianW) {
653         return predict(q, wx, wy, wz, 1.0, exactMethod, jacobianQ, jacobianW);
654     }
655 
656     /**
657      * Predicts the updated quaternion after a rotation in body frame expressed
658      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
659      * time interval of 1 second.
660      *
661      * @param q           quaternion to be updated.
662      * @param w           array containing angular speed in the 3 axis (x = roll,
663      *                    y = pitch, z = yaw). Expressed in rad/se. Must have length 3
664      * @param exactMethod true to use exact method, false to use "Tustin"
665      *                    method.
666      * @param jacobianQ   jacobian wrt quaternion. Must be 4x4.
667      * @param jacobianW   jacobian wrt angular speed. Must be 4x3.
668      * @return a new quaternion containing updated quaternion.
669      * @throws IllegalArgumentException if any of provided jacobians does not
670      *                                  have proper size.
671      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
672      */
673     public static Quaternion predict(
674             final Quaternion q, final double[] w, final boolean exactMethod, final Matrix jacobianQ,
675             final Matrix jacobianW) {
676         return predict(q, w, 1.0, exactMethod, jacobianQ, jacobianW);
677     }
678 
679     /**
680      * Predicts the updated quaternion after a rotation in body frame expressed
681      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
682      * time interval of 1 second and exact method.
683      *
684      * @param q         quaternion to be updated.
685      * @param wx        angular speed in x-axis (roll axis). Expressed in rad/s.
686      * @param wy        angular speed in y-axis (pitch axis). Expressed in rad/s.
687      * @param wz        angular speed in z-axis (yaw axis). Expressed in rad/s.
688      * @param jacobianQ jacobian wrt quaternion. Must be 4x4.
689      * @param jacobianW jacobian wrt angular speed. Must be 4x3.
690      * @return a new quaternion containing updated quaternion.
691      * @throws IllegalArgumentException if any of provided jacobians does not
692      *                                  have proper size.
693      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
694      */
695     public static Quaternion predict(
696             final Quaternion q, final double wx, final double wy, final double wz, final Matrix jacobianQ,
697             final Matrix jacobianW) {
698         return predict(q, wx, wy, wz, 1.0, jacobianQ, jacobianW);
699     }
700 
701     /**
702      * Predicts the updated quaternion after a rotation in body frame expressed
703      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
704      * time interval of 1 second and exact method.
705      *
706      * @param q         quaternion to be updated.
707      * @param w         array containing angular speed in the 3 axis (x = roll,
708      *                  y = pitch, z = yaw). Expressed in rad/se. Must have length 3
709      * @param jacobianQ jacobian wrt quaternion. Must be 4x4.
710      * @param jacobianW jacobian wrt angular speed. Must be 4x3.
711      * @return a new quaternion containing updated quaternion.
712      * @throws IllegalArgumentException if any of provided jacobians does not
713      *                                  have proper size.
714      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
715      */
716     public static Quaternion predict(final Quaternion q, final double[] w, final Matrix jacobianQ,
717                                      final Matrix jacobianW) {
718         return predict(q, w, 1.0, jacobianQ, jacobianW);
719     }
720 
721     /**
722      * Predicts the updated quaternion after a rotation in body frame expressed
723      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
724      * time interval of 1 second.
725      *
726      * @param q           quaternion to be updated.
727      * @param wx          angular speed in x-axis (roll axis). Expressed in rad/s.
728      * @param wy          angular speed in y-axis (pitch axis). Expressed in rad/s.
729      * @param wz          angular speed in z-axis (yaw axis). Expressed in rad/s.
730      * @param exactMethod true to use exact method, false to use "Tustin"
731      *                    method.
732      * @return a new quaternion containing updated quaternion.
733      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
734      */
735     public static Quaternion predict(
736             final Quaternion q, final double wx, final double wy, final double wz, final boolean exactMethod) {
737         return predict(q, wx, wy, wz, 1.0, exactMethod);
738     }
739 
740     /**
741      * Predicts the updated quaternion after a rotation in body frame expressed
742      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
743      * time interval of 1 second.
744      *
745      * @param q           quaternion to be updated.
746      * @param w           array containing angular speed in the 3 axis (x = roll,
747      *                    y = pitch, z = yaw). Expressed in rad/se. Must have length 3
748      * @param exactMethod true to use exact method, false to use "Tustin"
749      *                    method.
750      * @return a new quaternion containing updated quaternion.
751      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
752      */
753     public static Quaternion predict(final Quaternion q, final double[] w, final boolean exactMethod) {
754         return predict(q, w, 1.0, exactMethod);
755     }
756 
757     /**
758      * Predicts the updated quaternion after a rotation in body frame expressed
759      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
760      * time interval of 1 second and exact method.
761      *
762      * @param q  quaternion to be updated.
763      * @param wx angular speed in x-axis (roll axis). Expressed in rad/s.
764      * @param wy angular speed in y-axis (pitch axis). Expressed in rad/s.
765      * @param wz angular speed in z-axis (yaw axis). Expressed in rad/s.
766      * @return a new quaternion containing updated quaternion.
767      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
768      */
769     public static Quaternion predict(final Quaternion q, final double wx, final double wy, final double wz) {
770         return predict(q, wx, wy, wz, 1.0);
771     }
772 
773     /**
774      * Predicts the updated quaternion after a rotation in body frame expressed
775      * by the rate of rotation along axes x, y, z (roll, pitch, yaw) assuming a
776      * time interval of 1 second and exact method.
777      *
778      * @param q quaternion to be updated.
779      * @param w array containing angular speed in the 3 axis (x = roll,
780      *          y = pitch, z = yaw). Expressed in rad/se. Must have length 3
781      * @return a new quaternion containing updated quaternion.
782      * @throws IllegalArgumentException if any of provided jacobians does not
783      *                                  have proper size.
784      * @see <a href="https://github.com/joansola/slamtb">qpredict.m at https://github.com/joansola/slamtb</a>
785      */
786     public static Quaternion predict(final Quaternion q, final double[] w) {
787         return predict(q, w, 1.0);
788     }
789 
790     /**
791      * Predicts the updated quaternion after a rotation in body frame expressed
792      * by provided rotation dq and by provided rate of rotation along axes
793      * x,y,z (roll, pitch, yaw).
794      *
795      * @param q          quaternion to be updated.
796      * @param dq         adjustment of rotation to be combined with input quaternion.
797      * @param wx         angular speed in x-axis (roll axis). Expressed in rad/s.
798      * @param wy         angular speed in y-axis (pitch axis). Expressed in rad/s.
799      * @param wz         angular speed in z-axis (yaw axis). Expressed in rad/s.
800      * @param dt         time interval to compute prediction expressed in seconds.
801      * @param result     instance where updated quaternion is stored.
802      * @param jacobianQ  jacobian wrt input quaternion. Must be 4x4.
803      * @param jacobianDQ jacobian wrt dq quaternion. Must be 4x4.
804      * @param jacobianW  jacobian wrt angular speed. Must be 4x3.
805      * @throws IllegalArgumentException if any of provided jacobians does not
806      *                                  have proper size.
807      */
808     public static void predictWithRotationAdjustment(
809             final Quaternion q, final Quaternion dq, final double wx, final double wy, final double wz, final double dt,
810             final Quaternion result, final Matrix jacobianQ, final Matrix jacobianDQ, final Matrix jacobianW) {
811 
812         if (jacobianQ != null && (jacobianQ.getRows() != Quaternion.N_PARAMS
813                 || jacobianQ.getColumns() != Quaternion.N_PARAMS)) {
814             throw new IllegalArgumentException("jacobian wrt q must be 4x4");
815         }
816         if (jacobianDQ != null && (jacobianDQ.getRows() != Quaternion.N_PARAMS
817                 || jacobianDQ.getColumns() != Quaternion.N_PARAMS)) {
818             throw new IllegalArgumentException("jacobian wrt dq must be 4x4");
819         }
820         if (jacobianW != null && (jacobianW.getRows() != Quaternion.N_PARAMS
821                 || jacobianW.getColumns() != ANGULAR_SPEED_COMPONENTS)) {
822             throw new IllegalArgumentException("jacobian wrt w must be 4x3");
823         }
824 
825         final var w = new double[]{wx, wy, wz};
826 
827         ArrayUtils.multiplyByScalar(w, dt, w);
828         Quaternion.rotationVectorToQuaternion(w, result, jacobianW);
829         Matrix jacobianQ2 = null;
830         if (jacobianW != null) {
831             jacobianW.multiplyByScalar(dt);
832         }
833         if (jacobianW != null) {
834             try {
835                 jacobianQ2 = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
836             } catch (final WrongSizeException ignore) {
837                 // never happens
838             }
839         }
840         Quaternion.product(dq, result, result, jacobianDQ, jacobianQ2);
841 
842         Matrix jacobianQ3 = null;
843         if (jacobianDQ != null || jacobianW != null) {
844             try {
845                 jacobianQ3 = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
846             } catch (final WrongSizeException ignore) {
847                 // never happens
848             }
849         }
850         Quaternion.product(q, result, result, jacobianQ, jacobianQ3);
851 
852         // chain rule
853         if (jacobianQ3 != null) {
854             if (jacobianDQ != null) {
855                 try {
856                     final var tmp = jacobianQ3.multiplyAndReturnNew(jacobianDQ);
857                     jacobianDQ.copyFrom(tmp);
858                 } catch (final WrongSizeException ignore) {
859                     // never happens
860                 }
861             }
862 
863             // chain rule (jacobianW is already multiplied by dt)
864             if (jacobianW != null && jacobianQ2 != null) {
865                 try {
866                     jacobianQ3.multiply(jacobianQ2);
867                     jacobianQ3.multiply(jacobianW);
868                     jacobianW.copyFrom(jacobianQ3);
869                 } catch (final WrongSizeException ignore) {
870                     // never happens
871                 }
872             }
873         }
874     }
875 
876     /**
877      * Predicts the updated quaternion after a rotation in body frame expressed
878      * by provided rotation dq and by provided rate of rotation along axes x, y,
879      * z (roll, pitch, yaw).
880      *
881      * @param q          quaternion to be updated.
882      * @param dq         adjustment of rotation to be combined with input quaternion.
883      * @param w          angular speed (x, y, z axes). Expressed in rad/s. Must have
884      *                   length 3.
885      * @param dt         time interval to compute prediction expressed in seconds.
886      * @param result     instance where updated quaternion is stored.
887      * @param jacobianQ  jacobian wrt input quaternion. Must be 4x4.
888      * @param jacobianDQ jacobian wrt dq quaternion. Must be 4x4.
889      * @param jacobianW  jacobian wrt angular speed. Must be 4x3.
890      * @throws IllegalArgumentException if any of provided jacobians does not
891      *                                  have proper size or if w does not have length 3.
892      */
893     public static void predictWithRotationAdjustment(
894             final Quaternion q, final Quaternion dq, final double[] w, final double dt, final Quaternion result,
895             final Matrix jacobianQ, final Matrix jacobianDQ, final Matrix jacobianW) {
896         if (w.length != ANGULAR_SPEED_COMPONENTS) {
897             throw new IllegalArgumentException("w must have length 3");
898         }
899         predictWithRotationAdjustment(q, dq, w[0], w[1], w[2], dt, result, jacobianQ, jacobianDQ, jacobianW);
900     }
901 
902     /**
903      * Predicts the updated quaternion after a rotation in body frame expressed
904      * by provided rotation dq and by provided rate of rotation along axes
905      * x, y, z (roll, pitch, yaw).
906      *
907      * @param q      quaternion to be updated.
908      * @param dq     adjustment of rotation to be combined with input quaternion.
909      * @param wx     angular speed in x-axis (roll axis). Expressed in rad/s.
910      * @param wy     angular speed in y-axis (pitch axis). Expressed in rad/s.
911      * @param wz     angular speed in z-axis (yaw axis). Expressed in rad/s.
912      * @param dt     time interval to compute prediction expressed in seconds.
913      * @param result instance where updated quaternion is stored.
914      */
915     public static void predictWithRotationAdjustment(
916             final Quaternion q, final Quaternion dq, final double wx, final double wy, final double wz, final double dt,
917             final Quaternion result) {
918         predictWithRotationAdjustment(q, dq, wx, wy, wz, dt, result, null, null, null);
919     }
920 
921     /**
922      * Predicts the updated quaternion after a rotation in body frame expressed
923      * by provided rotation dq and by provided rate of rotation along axes x,y,z
924      * (roll, pitch, yaw).
925      *
926      * @param q      quaternion to be updated.
927      * @param dq     adjustment of rotation to be combined with input quaternion.
928      * @param w      angular speed (x, y, z axes). Expressed in rad/s. Must have
929      *               length 3.
930      * @param dt     time interval to compute prediction expressed in seconds.
931      * @param result instance where updated quaternion is stored.
932      * @throws IllegalArgumentException if w does not have length 3.
933      */
934     public static void predictWithRotationAdjustment(
935             final Quaternion q, final Quaternion dq, final double[] w, final double dt, final Quaternion result) {
936         predictWithRotationAdjustment(q, dq, w, dt, result, null, null, null);
937     }
938 
939     /**
940      * Predicts the updated quaternion after a rotation in body frame expressed
941      * by provided rotation dq and by provided rate of rotation along axes x,y,z
942      * (roll, pitch, yaw).
943      *
944      * @param q          quaternion to be updated.
945      * @param dq         adjustment of rotation to be combined with input quaternion.
946      * @param wx         angular speed in x-axis (roll axis). Expressed in rad/s.
947      * @param wy         angular speed in y-axis (pitch axis). Expressed in rad/s.
948      * @param wz         angular speed in z-axis (yaw axis). Expressed in rad/s.
949      * @param dt         time interval to compute prediction expressed in seconds.
950      * @param jacobianQ  jacobian wrt input quaternion. Must be 4x4.
951      * @param jacobianDQ jacobian wrt dq quaternion. Must be 4x4.
952      * @param jacobianW  jacobian wrt angular speed. Must be 4x3.
953      * @return a new updated quaternion.
954      * @throws IllegalArgumentException if any of provided jacobians does not
955      *                                  have proper size.
956      */
957     public static Quaternion predictWithRotationAdjustment(
958             final Quaternion q, final Quaternion dq, final double wx, final double wy, final double wz, final double dt,
959             final Matrix jacobianQ, final Matrix jacobianDQ, final Matrix jacobianW) {
960         final var result = new Quaternion();
961         predictWithRotationAdjustment(q, dq, wx, wy, wz, dt, result, jacobianQ, jacobianDQ, jacobianW);
962         return result;
963     }
964 
965     /**
966      * Predicts the updated quaternion after a rotation in body frame expressed
967      * by provided rotation dq and by provided rate of rotation along axes x,y,z
968      * (roll, pitch, yaw).
969      *
970      * @param q          quaternion to be updated.
971      * @param dq         adjustment of rotation to be combined with input quaternion.
972      * @param w          angular speed (x,y,z axes). Expressed in rad/s. Must have length
973      *                   3.
974      * @param dt         time interval to compute prediction expressed in seconds.
975      * @param jacobianQ  jacobian wrt input quaternion. Must be 4x4.
976      * @param jacobianDQ jacobian wrt dq quaternion. Must be 4x4.
977      * @param jacobianW  jacobian wrt angular speed. Must be 4x3.
978      * @return a new updated quaternion.
979      * @throws IllegalArgumentException if any of provided jacobians does not
980      *                                  have proper size.
981      */
982     public static Quaternion predictWithRotationAdjustment(
983             final Quaternion q, final Quaternion dq, final double[] w, final double dt, final Matrix jacobianQ,
984             final Matrix jacobianDQ, final Matrix jacobianW) {
985         final var result = new Quaternion();
986         predictWithRotationAdjustment(q, dq, w, dt, result, jacobianQ, jacobianDQ, jacobianW);
987         return result;
988     }
989 
990     /**
991      * Predicts the updated quaternion after a rotation in body frame expressed
992      * by provided rotation dq and by provided rate of rotation along axes x,y,z
993      * (roll, pitch, yaw).
994      *
995      * @param q  quaternion to be updated.
996      * @param dq adjustment of rotation to be combined with input quaternion.
997      * @param wx angular speed in x-axis (roll axis). Expressed in rad/s.
998      * @param wy angular speed in y-axis (pitch axis). Expressed in rad/s.
999      * @param wz angular speed in z-axis (yaw axis). Expressed in rad/s.
1000      * @param dt time interval to compute prediction expressed in seconds.
1001      * @return a new updated quaternion.
1002      */
1003     public static Quaternion predictWithRotationAdjustment(
1004             final Quaternion q, final Quaternion dq, final double wx, final double wy, final double wz,
1005             final double dt) {
1006         final var result = new Quaternion();
1007         predictWithRotationAdjustment(q, dq, wx, wy, wz, dt, result);
1008         return result;
1009     }
1010 
1011     /**
1012      * Predicts the updated quaternion after a rotation in body frame expressed
1013      * by provided rotation dq and by provided rate of rotation along axes x,y,z
1014      * (roll, pitch, yaw).
1015      *
1016      * @param q  quaternion to be updated.
1017      * @param dq adjustment of rotation to be combined with input quaternion.
1018      * @param w  angular speed (x, y, z axes). Expressed in rad/s. Must have
1019      *           length 3.
1020      * @param dt time interval to compute prediction expressed in seconds.
1021      * @return a new updated quaternion.
1022      * @throws IllegalArgumentException if w does not have length 3.
1023      */
1024     public static Quaternion predictWithRotationAdjustment(
1025             final Quaternion q, final Quaternion dq, final double[] w, final double dt) {
1026         final var result = new Quaternion();
1027         predictWithRotationAdjustment(q, dq, w, dt, result);
1028         return result;
1029     }
1030 }