1   /* Copyright 2002-2022 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.frames;
18  
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.io.ObjectInputStream;
24  import java.io.ObjectOutputStream;
25  
26  import org.hipparchus.CalculusFieldElement;
27  import org.hipparchus.geometry.euclidean.threed.Rotation;
28  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
29  import org.hipparchus.geometry.euclidean.threed.Vector3D;
30  import org.junit.Assert;
31  import org.junit.Before;
32  import org.junit.Test;
33  import org.orekit.Utils;
34  import org.orekit.data.DataContext;
35  import org.orekit.data.PolynomialNutation;
36  import org.orekit.time.AbsoluteDate;
37  import org.orekit.time.DateComponents;
38  import org.orekit.time.FieldAbsoluteDate;
39  import org.orekit.time.TimeComponents;
40  import org.orekit.time.TimeScalesFactory;
41  import org.orekit.utils.Constants;
42  import org.orekit.utils.IERSConventions;
43  import org.orekit.utils.PVCoordinates;
44  
45  
46  public class MODProviderTest {
47  
48      @Test
49      public void testEuler1976() {
50  
51          TransformProvider eulerBasedProvider = new TransformProvider() {
52              private static final long serialVersionUID = 1L;
53              private final PolynomialNutation zetaA =
54                      new PolynomialNutation(0.0,
55                              2306.2181 * Constants.ARC_SECONDS_TO_RADIANS,
56                              0.30188   * Constants.ARC_SECONDS_TO_RADIANS,
57                              0.017998  * Constants.ARC_SECONDS_TO_RADIANS);
58              private final PolynomialNutation thetaA =
59                      new PolynomialNutation(0.0,
60                              2004.3109 * Constants.ARC_SECONDS_TO_RADIANS,
61                              -0.42665  * Constants.ARC_SECONDS_TO_RADIANS,
62                              -0.041833 * Constants.ARC_SECONDS_TO_RADIANS);
63              private final PolynomialNutation zA =
64                      new PolynomialNutation(0.0,
65                              2306.2181 * Constants.ARC_SECONDS_TO_RADIANS,
66                              1.09468   * Constants.ARC_SECONDS_TO_RADIANS,
67                              0.018203  * Constants.ARC_SECONDS_TO_RADIANS);
68  
69              public Transform getTransform(AbsoluteDate date) {
70                  final double tc = IERSConventions.IERS_1996.evaluateTC(date);
71                  final Rotation r1 = new Rotation(Vector3D.PLUS_K,  zA.value(tc), RotationConvention.VECTOR_OPERATOR);
72                  final Rotation r2 = new Rotation(Vector3D.PLUS_J, -thetaA.value(tc), RotationConvention.VECTOR_OPERATOR);
73                  final Rotation r3 = new Rotation(Vector3D.PLUS_K,  zetaA.value(tc), RotationConvention.VECTOR_OPERATOR);
74                  return new Transform(date, r1.compose(r2.compose(r3,
75                                                                   RotationConvention.VECTOR_OPERATOR),
76                                                        RotationConvention.VECTOR_OPERATOR));
77              }
78  
79              public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
80                  throw new UnsupportedOperationException("never called in this test");
81              }
82  
83          };
84  
85          MODProvider modProvider = new MODProvider(IERSConventions.IERS_1996,
86                  DataContext.getDefault().getTimeScales());
87  
88          for (double dt = -5 * Constants.JULIAN_YEAR; dt < 5 * Constants.JULIAN_YEAR; dt += 10 * Constants.JULIAN_DAY) {
89              AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt);
90              Transform t = new Transform(date,
91                                          modProvider.getTransform(date).getInverse(),
92                                          eulerBasedProvider.getTransform(date));
93              Assert.assertEquals(0, t.getRotation().getAngle(), 1.01e-11);
94          }
95  
96      }
97  
98      @Test
99      public void testEuler2000() {
100 
101         // this alternate representation of the transform
102         // is from equation 33 in IERS conventions 2003
103         TransformProvider eulerBasedProvider = new TransformProvider() {
104             private static final long serialVersionUID = 1L;
105             private final PolynomialNutation zetaA =
106                     new PolynomialNutation(   2.5976176 * Constants.ARC_SECONDS_TO_RADIANS,
107                                            2306.0809506 * Constants.ARC_SECONDS_TO_RADIANS,
108                                               0.3019015 * Constants.ARC_SECONDS_TO_RADIANS,
109                                               0.0179663 * Constants.ARC_SECONDS_TO_RADIANS,
110                                              -0.0000327 * Constants.ARC_SECONDS_TO_RADIANS,
111                                              -0.0000002 * Constants.ARC_SECONDS_TO_RADIANS);
112             private final PolynomialNutation thetaA =
113                     new PolynomialNutation(0.0,
114                                            2004.1917476 * Constants.ARC_SECONDS_TO_RADIANS,
115                                              -0.4269353 * Constants.ARC_SECONDS_TO_RADIANS,
116                                              -0.0418251 * Constants.ARC_SECONDS_TO_RADIANS,
117                                              -0.0000601 * Constants.ARC_SECONDS_TO_RADIANS,
118                                              -0.0000001 * Constants.ARC_SECONDS_TO_RADIANS);
119             private final PolynomialNutation zA =
120                     new PolynomialNutation(  -2.5976176 * Constants.ARC_SECONDS_TO_RADIANS,
121                                            2306.0803226 * Constants.ARC_SECONDS_TO_RADIANS,
122                                               1.0947790 * Constants.ARC_SECONDS_TO_RADIANS,
123                                               0.0182273 * Constants.ARC_SECONDS_TO_RADIANS,
124                                               0.0000470 * Constants.ARC_SECONDS_TO_RADIANS,
125                                              -0.0000003 * Constants.ARC_SECONDS_TO_RADIANS);
126 
127             public Transform getTransform(AbsoluteDate date) {
128                 final double tc = IERSConventions.IERS_2003.evaluateTC(date);
129                 final Rotation r1 = new Rotation(Vector3D.PLUS_K,  zA.value(tc), RotationConvention.VECTOR_OPERATOR);
130                 final Rotation r2 = new Rotation(Vector3D.PLUS_J, -thetaA.value(tc), RotationConvention.VECTOR_OPERATOR);
131                 final Rotation r3 = new Rotation(Vector3D.PLUS_K,  zetaA.value(tc), RotationConvention.VECTOR_OPERATOR);
132                 return new Transform(date, r1.compose(r2.compose(r3,
133                                                                  RotationConvention.VECTOR_OPERATOR),
134                                                       RotationConvention.VECTOR_OPERATOR));
135             }
136 
137             public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
138                 throw new UnsupportedOperationException("never called in this test");
139             }
140 
141         };
142 
143         MODProvider modProvider = new MODProvider(IERSConventions.IERS_2003,
144                 DataContext.getDefault().getTimeScales());
145 
146         for (double dt = -Constants.JULIAN_CENTURY; dt < Constants.JULIAN_CENTURY; dt += 50 * Constants.JULIAN_DAY) {
147             AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt);
148             Transform t = new Transform(date,
149                                         modProvider.getTransform(date).getInverse(),
150                                         eulerBasedProvider.getTransform(date));
151             Assert.assertEquals(0, t.getRotation().getAngle(), 6.6e-13);
152         }
153 
154     }
155 
156     @Test
157     public void testAASReferenceLEO() {
158 
159         // this reference test has been extracted from the following paper:
160         // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
161         // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
162         // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
163         AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 04, 06),
164                                            new TimeComponents(07, 51, 28.386009),
165                                            TimeScalesFactory.getUTC());
166 
167         Transform tt = FramesFactory.getGCRF().getTransformTo(FramesFactory.getMOD(IERSConventions.IERS_1996), t0);
168         //GCRF iau76 w corr
169         PVCoordinates pvGCRFiau76 =
170             new PVCoordinates(new Vector3D(5102508.9579, 6123011.4007, 6378136.9282),
171                               new Vector3D(-4743.220157, 790.536497, 5533.755727));
172         double norm = pvGCRFiau76.getPosition().getNorm();
173 
174         //MOD iau76 w corr
175         PVCoordinates pvMODiau76Wcorr =
176             new PVCoordinates(new Vector3D(5094028.3745, 6127870.8164, 6380248.5164),
177                               new Vector3D(-4746.263052, 786.014045, 5531.790562));
178 
179         checkPV(pvMODiau76Wcorr, tt.transformPVCoordinates(pvGCRFiau76), 9e-12 * norm, 7.3e-7);
180 
181         Transform tf = FramesFactory.getEME2000().getTransformTo(FramesFactory.getMOD(false), t0);
182         //J2000 iau76
183         PVCoordinates pvJ2000iau76 =
184             new PVCoordinates(new Vector3D(5102509.6000, 6123011.5200, 6378136.3000),
185                               new Vector3D(-4743.219600, 790.536600, 5533.756190));
186         //MOD iau76
187         PVCoordinates pvMODiau76 =
188             new PVCoordinates(new Vector3D(5094029.0167, 6127870.9363, 6380247.8885),
189                               new Vector3D(-4746.262495, 786.014149, 5531.791025));
190         checkPV(pvMODiau76, tf.transformPVCoordinates(pvJ2000iau76), 9e-12 * norm, 3.1e-7);
191 
192     }
193 
194     @Test
195     public void testAASReferenceGEO() {
196 
197         // this reference test has been extracted from the following paper:
198         // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
199         // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
200         // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
201         AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 06, 01),
202                                            TimeComponents.H00,
203                                            TimeScalesFactory.getUTC());
204 
205         Transform tt = FramesFactory.getGCRF().getTransformTo(FramesFactory.getMOD(IERSConventions.IERS_1996), t0);
206         //GCRF iau76 w corr
207         PVCoordinates pvGCRFiau76 =
208             new PVCoordinates(new Vector3D(-40588150.3649, -11462167.0282, 27143.2028),
209                               new Vector3D(834.787457, -2958.305691, -1.172994));
210         double norm = pvGCRFiau76.getPosition().getNorm();
211 
212         //MOD iau76 w corr
213         PVCoordinates pvMODiau76Wcorr =
214             new PVCoordinates(new Vector3D(-40576822.6395, -11502231.5015, 9733.7842),
215                               new Vector3D(837.708020, -2957.480117, -0.814253));
216         checkPV(pvMODiau76Wcorr, tt.transformPVCoordinates(pvGCRFiau76), 9e-12 * norm, 6.9e-7);
217 
218         Transform tf = FramesFactory.getEME2000().getTransformTo(FramesFactory.getMOD(false), t0);
219         //J2000 iau76
220         PVCoordinates pvJ2000iau76 =
221             new PVCoordinates(new Vector3D(-40588150.3620, -11462167.0280, 27147.6490),
222                               new Vector3D(834.787457, -2958.305691, -1.173016));
223         //MOD iau76
224         PVCoordinates pvMODiau76 =
225             new PVCoordinates(new Vector3D(-40576822.6385, -11502231.5013, 9738.2304),
226                               new Vector3D(837.708020, -2957.480118, -0.814275));
227         checkPV(pvMODiau76, tf.transformPVCoordinates(pvJ2000iau76), 9e-12 * norm, 6.9e-7);
228 
229     }
230 
231     @Test
232     public void testSofaPmat76() {
233 
234         // the reference value has been computed using the March 2012 version of the SOFA library
235         // http://www.iausofa.org/2012_0301_C.html, with the following code
236         //
237         //        double utc1, utc2, tai1, tai2, tt1, tt2, rmatp[3][3];
238         //
239         //        // 2004-02-14:00:00:00Z, MJD = 53049, UT1-UTC = -0.4093509
240         //        utc1  = DJM0 + 53049.0;
241         //        utc2  = 0.0;
242         //        iauUtctai(utc1, utc2, &tai1, &tai2);
243         //        iauTaitt(tai1, tai2, &tt1, &tt2);
244         //
245         //        iauPmat76(tt1, tt2, rmatp);
246         //
247         //        printf("iauPmat76(%.20g, %.20g,rmatp)\n"
248         //               "  --> %.20g %.20g %.20g\n"
249         //               "      %.20g %.20g %.20g\n"
250         //               "      %.20g %.20g %.20g\n",
251         //               tt1, tt2,
252         //               rmatp[0][0], rmatp[0][1], rmatp[0][2],
253         //               rmatp[1][0], rmatp[1][1], rmatp[1][2],
254         //               rmatp[2][0], rmatp[2][1], rmatp[2][2]);
255         //
256         // the output of this test reads:
257         //      iauPmat76(2453049.5, 0.00074287037037037029902,rmatp)
258         //        --> 0.9999994956729996165 -0.00092111268696996330928 -0.00040025637336518803469
259         //            0.00092111268696944094067 0.99999957577560194544 -1.843419633938077413e-07
260         //            0.00040025637336639019806 -1.8433935312187383064e-07 0.99999991989739756004
261 
262         // the SOFA routine iauPmat76 uses the Euler angles zetaA, thetaA and zA, whereas
263         // Orekit uses the "canonical 4-rotation method" with angles epsilon0, psiA, omegaA
264         // and chiA. As seen in the thresholds of the test testEuler1976 above, the two methods
265         // drift with respect to each other at a rate about 2e-12 radians/year (i.e. about 0.42
266         // microarcseconds per year) on the time range around J2000.0). We see the same models
267         // difference in this test as in the testEuler1976 above.
268         // Note that the difference is much smaller for IERS 2003 (see another test below)
269         // because the Euler models for these conventions match much better the 4-rotation angles
270 
271         AbsoluteDate date = new AbsoluteDate(2004, 2, 14, TimeScalesFactory.getUTC());
272         Frame mod  = FramesFactory.getFrame(Predefined.MOD_CONVENTIONS_1996);
273         Frame gcrf = FramesFactory.getFrame(Predefined.GCRF);
274         checkRotation(new double[][] {
275             { 0.9999994956729996165,     -0.00092111268696996330928, -0.00040025637336518803469 },
276             { 0.00092111268696944094067,  0.99999957577560194544,    -1.843419633938077413e-07  },
277             { 0.00040025637336639019806, -1.8433935312187383064e-07,  0.99999991989739756004    }
278 
279         }, gcrf.getTransformTo(mod, date), 9e-12);
280 
281     }
282 
283     @Test
284     public void testSofaBp00() {
285 
286         // the reference value has been computed using the March 2012 version of the SOFA library
287         // http://www.iausofa.org/2012_0301_C.html, with the following code
288         //
289         //        double utc1, utc2, tai1, tai2, tt1, tt2, rb[3][3], rp[3][3], rbp[3][3];
290         //
291         //        // 2004-02-14:00:00:00Z, MJD = 53049, UT1-UTC = -0.4093509
292         //        utc1  = DJM0 + 53049.0;
293         //        utc2  = 0.0;
294         //        iauUtctai(utc1, utc2, &tai1, &tai2);
295         //        iauTaitt(tai1, tai2, &tt1, &tt2);
296         //
297         //        iauBp00(tt1, tt2, rb, rp, rbp);
298         //
299         //        printf("iauBp00(%.20g, %.20g, rb, rp, rbp)\n"
300         //               "  rb  --> %.20g %.20g %.20g\n          %.20g %.20g %.20g\n          %.20g %.20g %.20g\n"
301         //               "  rp  --> %.20g %.20g %.20g\n          %.20g %.20g %.20g\n          %.20g %.20g %.20g\n"
302         //               "  rbp --> %.20g %.20g %.20g\n          %.20g %.20g %.20g\n          %.20g %.20g %.20g\n",
303         //               tt1, tt2,
304         //               rb[0][0],  rb[0][1],  rb[0][2],
305         //               rb[1][0],  rb[1][1],  rb[1][2],
306         //               rb[2][0],  rb[2][1],  rb[2][2],
307         //               rp[0][0],  rp[0][1],  rp[0][2],
308         //               rp[1][0],  rp[1][1],  rp[1][2],
309         //               rp[2][0],  rp[2][1],  rp[2][2],
310         //               rbp[0][0], rbp[0][1], rbp[0][2],
311         //               rbp[1][0], rbp[1][1], rbp[1][2],
312         //               rbp[2][0], rbp[2][1], rbp[2][2]);
313         //
314         // the output of this test reads:
315         //        iauBp00(2453049.5, 0.00074287037037037029902, rb, rp, rbp)
316         //        rb  --> 0.99999999999999422684 -7.0782797441991980175e-08 8.0562171469761337802e-08
317         //                7.0782794778573375197e-08 0.99999999999999689138 3.3060414542221364117e-08
318         //                -8.0562173809869716745e-08 -3.3060408839805516801e-08 0.99999999999999622524
319         //        rp  --> 0.99999949573309343531 -0.00092105778423522924759 -0.00040023257863225548568
320         //                0.00092105778625203805956 0.99999957582617116092 -1.7927962069881782439e-07
321         //                0.00040023257399096032498 -1.8935780260465051583e-07 0.99999991990692216337
322         //        rbp --> 0.99999949570013624278 -0.00092112855376512230675 -0.00040015204695196122638
323         //                0.00092112856903123034591 0.99999957576097886491 -1.4614501776464880046e-07
324         //                0.00040015201181019732432 -2.2244653837776004327e-07 0.99999991993915571253
325 
326         AbsoluteDate date = new AbsoluteDate(2004, 2, 14, TimeScalesFactory.getUTC());
327         Frame mod  = FramesFactory.getFrame(Predefined.MOD_CONVENTIONS_2003);
328         Frame gcrf = FramesFactory.getFrame(Predefined.GCRF);
329         checkRotation(new double[][] {
330             { 0.99999949570013624278,    -0.00092112855376512230675, -0.00040015204695196122638 },
331             { 0.00092112856903123034591,  0.99999957576097886491,    -1.4614501776464880046e-07 },
332             { 0.00040015201181019732432, -2.2244653837776004327e-07,  0.99999991993915571253    }
333 
334         }, gcrf.getTransformTo(mod, date), 4.0e-16);
335 
336     }
337 
338     @Test
339     public void testSofaBp06() {
340 
341         // the reference value has been computed using the March 2012 version of the SOFA library
342         // http://www.iausofa.org/2012_0301_C.html, with the following code
343         //
344         //        double utc1, utc2, tai1, tai2, tt1, tt2, rb[3][3], rp[3][3], rbp[3][3];
345         //
346         //        // 2004-02-14:00:00:00Z, MJD = 53049, UT1-UTC = -0.4093509
347         //        utc1  = DJM0 + 53049.0;
348         //        utc2  = 0.0;
349         //        iauUtctai(utc1, utc2, &tai1, &tai2);
350         //        iauTaitt(tai1, tai2, &tt1, &tt2);
351         //
352         //        iauBp06(tt1, tt2, rb, rp, rbp);
353         //
354         //        printf("iauBp06(%.20g, %.20g, rb, rp, rbp)\n"
355         //               "  rb  --> %.20g %.20g %.20g\n          %.20g %.20g %.20g\n          %.20g %.20g %.20g\n"
356         //               "  rp  --> %.20g %.20g %.20g\n          %.20g %.20g %.20g\n          %.20g %.20g %.20g\n"
357         //               "  rbp --> %.20g %.20g %.20g\n          %.20g %.20g %.20g\n          %.20g %.20g %.20g\n",
358         //               tt1, tt2,
359         //               rb[0][0],  rb[0][1],  rb[0][2],
360         //               rb[1][0],  rb[1][1],  rb[1][2],
361         //               rb[2][0],  rb[2][1],  rb[2][2],
362         //               rp[0][0],  rp[0][1],  rp[0][2],
363         //               rp[1][0],  rp[1][1],  rp[1][2],
364         //               rp[2][0],  rp[2][1],  rp[2][2],
365         //               rbp[0][0], rbp[0][1], rbp[0][2],
366         //               rbp[1][0], rbp[1][1], rbp[1][2],
367         //               rbp[2][0], rbp[2][1], rbp[2][2]);
368         //
369         // the output of this test reads:
370         //      iauBp06(2453049.5, 0.00074287037037037029902, rb, rp, rbp)
371         //        rb  --> 0.99999999999999411582 -7.0783689609715561276e-08 8.0562139776131860839e-08
372         //                7.0783686946376762683e-08 0.99999999999999689138 3.3059437354321374869e-08
373         //                -8.0562142116200574817e-08 -3.3059431692183949281e-08 0.99999999999999622524
374         //        rp  --> 0.99999949573328705821 -0.00092105756953249828464 -0.00040023258864902886648
375         //                0.00092105757159046265214 0.99999957582636889164 -1.7917675327629716543e-07
376         //                0.00040023258391302344184 -1.8946059324815925032e-07 0.99999991990691827759
377         //        rbp --> 0.99999949570032919954 -0.00092112833995494939818 -0.00040015205699952106997
378         //                0.00092112835526181411488 0.9999995757611759295 -1.4604312753574433259e-07
379         //                0.00040015202176394668336 -2.2254835219115420841e-07 0.99999991993915182675
380 
381         AbsoluteDate date = new AbsoluteDate(2004, 2, 14, TimeScalesFactory.getUTC());
382         Frame mod  = FramesFactory.getFrame(Predefined.MOD_CONVENTIONS_2010);
383         Frame gcrf = FramesFactory.getFrame(Predefined.GCRF);
384         checkRotation(new double[][] {
385             { 0.99999949570032919954,    -0.00092112833995494939818, -0.00040015205699952106997 },
386             { 0.00092112835526181411488,  0.9999995757611759295,     -1.4604312753574433259e-07 },
387             { 0.00040015202176394668336, -2.2254835219115420841e-07,  0.99999991993915182675    }
388 
389         }, gcrf.getTransformTo(mod, date), 1.1e-12);
390 
391     }
392 
393     @Test
394     public void testMOD1976vs2006() {
395 
396         final Frame mod1976 = FramesFactory.getMOD(IERSConventions.IERS_1996);
397         final Frame mod2006 = FramesFactory.getMOD(IERSConventions.IERS_2010);
398         for (double dt = 0; dt < 10 * Constants.JULIAN_YEAR; dt += 10 * Constants.JULIAN_DAY) {
399             AbsoluteDate date = new AbsoluteDate(AbsoluteDate.J2000_EPOCH, dt);
400             double delta = mod1976.getTransformTo(mod2006, date).getRotation().getAngle();
401             // MOD2006 and MOD2000 are similar to about 33 milli-arcseconds between 2000 and 2010
402             Assert.assertEquals(0.0, delta, 2.0e-7);
403         }
404     }
405 
406     @Test
407     public void testMOD2000vs2006() {
408 
409         final Frame mod2000 = FramesFactory.getMOD(IERSConventions.IERS_2003);
410         final Frame mod2006 = FramesFactory.getMOD(IERSConventions.IERS_2010);
411         for (double dt = 0; dt < 10 * Constants.JULIAN_YEAR; dt += 10 * Constants.JULIAN_DAY) {
412             AbsoluteDate date = new AbsoluteDate(AbsoluteDate.J2000_EPOCH, dt);
413             double delta = mod2000.getTransformTo(mod2006, date).getRotation().getAngle();
414             // MOD2006 and MOD2000 are similar to about 0.15 milli-arcseconds between 2000 and 2010
415             Assert.assertEquals(0.0, delta, 7.2e-10);
416         }
417     }
418 
419     @Test
420     public void testSerialization() throws IOException, ClassNotFoundException {
421         MODProvider provider = new MODProvider(IERSConventions.IERS_2010,
422                 DataContext.getDefault().getTimeScales());
423 
424         ByteArrayOutputStream bos = new ByteArrayOutputStream();
425         ObjectOutputStream    oos = new ObjectOutputStream(bos);
426         oos.writeObject(provider);
427 
428         Assert.assertTrue(bos.size() > 150);
429         Assert.assertTrue(bos.size() < 250);
430 
431         ByteArrayInputStream  bis = new ByteArrayInputStream(bos.toByteArray());
432         ObjectInputStream     ois = new ObjectInputStream(bis);
433         MODProvider deserialized  = (MODProvider) ois.readObject();
434         for (double dt = 0; dt < Constants.JULIAN_DAY; dt += 3600) {
435             AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt);
436             Transform expectedIdentity = new Transform(date,
437                                                        provider.getTransform(date).getInverse(),
438                                                        deserialized.getTransform(date));
439             Assert.assertEquals(0.0, expectedIdentity.getTranslation().getNorm(), 1.0e-15);
440             Assert.assertEquals(0.0, expectedIdentity.getRotation().getAngle(),   1.0e-15);
441         }
442 
443     }
444 
445     @Before
446     public void setUp() {
447         Utils.setDataRoot("compressed-data");
448     }
449 
450     private void checkPV(PVCoordinates reference,
451                          PVCoordinates result,
452                          double positionThreshold,
453                          double velocityThreshold) {
454 
455         Vector3D dP = result.getPosition().subtract(reference.getPosition());
456         Vector3D dV = result.getVelocity().subtract(reference.getVelocity());
457         Assert.assertEquals(0, dP.getNorm(), positionThreshold);
458         Assert.assertEquals(0, dV.getNorm(), velocityThreshold);
459     }
460 
461     private void checkRotation(double[][] reference, Transform t, double epsilon) {
462         double[][] mat = t.getRotation().getMatrix();
463         for (int i = 0; i < 3; ++i) {
464             for (int j = 0; j < 3; ++j) {
465                 Assert.assertEquals(reference[i][j], mat[i][j], epsilon);
466             }
467         }
468     }
469 
470 }