• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /********************************************************************
2  * COPYRIGHT:
3  * Copyright (c) 1996-2006, International Business Machines Corporation and
4  * others. All Rights Reserved.
5  ********************************************************************/
6 
7 /* Test CalendarAstronomer for C++ */
8 
9 #include "unicode/utypes.h"
10 #include "string.h"
11 #include "unicode/locid.h"
12 
13 #if !UCONFIG_NO_FORMATTING
14 
15 #include "astro.h"
16 #include "astrotst.h"
17 #include "gregoimp.h" // for Math
18 #include "unicode/simpletz.h"
19 
20 
21 static const double DAY_MS = 24.*60.*60.*1000.;
22 
23 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
24 
AstroTest()25 AstroTest::AstroTest(): astro(NULL), gc(NULL) {
26 }
27 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)28 void AstroTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
29 {
30     if (exec) logln("TestSuite AstroTest");
31     switch (index) {
32       // CASE(0,FooTest);
33       CASE(0,TestSolarLongitude);
34       CASE(1,TestLunarPosition);
35       CASE(2,TestCoordinates);
36       CASE(3,TestCoverage);
37       CASE(4,TestSunriseTimes);
38       CASE(5,TestBasics);
39     default: name = ""; break;
40     }
41 }
42 
43 #undef CASE
44 
45 #define ASSERT_OK(x)   if(U_FAILURE(x)) { errln("%s:%d: %s\n", __FILE__, __LINE__, u_errorName(x)); return; }
46 
47 
initAstro(UErrorCode & status)48 void AstroTest::initAstro(UErrorCode &status) {
49   if(U_FAILURE(status)) return;
50 
51   if((astro != NULL) || (gc != NULL)) {
52     errln("Err: initAstro() called twice!");
53     closeAstro(status);
54     if(U_SUCCESS(status)) {
55       status = U_INTERNAL_PROGRAM_ERROR;
56     }
57   }
58 
59   if(U_FAILURE(status)) return;
60 
61   astro = new CalendarAstronomer();
62   gc = Calendar::createInstance(TimeZone::getGMT()->clone(), status);
63 }
64 
closeAstro(UErrorCode &)65 void AstroTest::closeAstro(UErrorCode &/*status*/) {
66   if(astro != NULL) {
67     delete astro;
68     astro = NULL;
69   }
70   if(gc != NULL) {
71     delete gc;
72     gc = NULL;
73   }
74 }
75 
TestSolarLongitude(void)76 void AstroTest::TestSolarLongitude(void) {
77   UErrorCode status = U_ZERO_ERROR;
78   initAstro(status);
79   ASSERT_OK(status);
80 
81   struct {
82     int32_t d[5]; double f ;
83   } tests[] = {
84     { { 1980, 7, 27, 0, 00 },  124.114347 },
85     { { 1988, 7, 27, 00, 00 },  124.187732 }
86   };
87 
88   logln("");
89   for (uint32_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
90     gc->clear();
91     gc->set(tests[i].d[0], tests[i].d[1]-1, tests[i].d[2], tests[i].d[3], tests[i].d[4]);
92 
93     astro->setDate(gc->getTime(status));
94 
95     double longitude = astro->getSunLongitude();
96     //longitude = 0;
97     CalendarAstronomer::Equatorial result;
98     astro->getSunPosition(result);
99     logln((UnicodeString)"Sun position is " + result.toString() + (UnicodeString)";  " /* + result.toHmsString()*/ + " Sun longitude is " + longitude );
100   }
101   closeAstro(status);
102   ASSERT_OK(status);
103 }
104 
105 
106 
TestLunarPosition(void)107 void AstroTest::TestLunarPosition(void) {
108   UErrorCode status = U_ZERO_ERROR;
109   initAstro(status);
110   ASSERT_OK(status);
111 
112   static const double tests[][7] = {
113     { 1979, 2, 26, 16, 00,  0, 0 }
114   };
115   logln("");
116 
117   for (int32_t i = 0; i < (int32_t)(sizeof(tests)/sizeof(tests[0])); i++) {
118     gc->clear();
119     gc->set((int32_t)tests[i][0], (int32_t)tests[i][1]-1, (int32_t)tests[i][2], (int32_t)tests[i][3], (int32_t)tests[i][4]);
120     astro->setDate(gc->getTime(status));
121 
122     const CalendarAstronomer::Equatorial& result = astro->getMoonPosition();
123     logln((UnicodeString)"Moon position is " + result.toString() + (UnicodeString)";  " /* + result->toHmsString()*/);
124   }
125 
126   closeAstro(status);
127   ASSERT_OK(status);
128 }
129 
130 
131 
TestCoordinates(void)132 void AstroTest::TestCoordinates(void) {
133   UErrorCode status = U_ZERO_ERROR;
134   initAstro(status);
135   ASSERT_OK(status);
136 
137   CalendarAstronomer::Equatorial result;
138   astro->eclipticToEquatorial(result, 139.686111 * CalendarAstronomer::PI / 180.0, 4.875278* CalendarAstronomer::PI / 180.0);
139   logln((UnicodeString)"result is " + result.toString() + (UnicodeString)";  " /* + result.toHmsString()*/ );
140   closeAstro(status);
141   ASSERT_OK(status);
142 }
143 
144 
145 
TestCoverage(void)146 void AstroTest::TestCoverage(void) {
147   UErrorCode status = U_ZERO_ERROR;
148   initAstro(status);
149   ASSERT_OK(status);
150   GregorianCalendar *cal = new GregorianCalendar(1958, UCAL_AUGUST, 15,status);
151   UDate then = cal->getTime(status);
152   CalendarAstronomer *myastro = new CalendarAstronomer(then);
153   ASSERT_OK(status);
154 
155   //Latitude:  34 degrees 05' North
156   //Longitude:  118 degrees 22' West
157   double laLat = 34 + 5./60, laLong = 360 - (118 + 22./60);
158   CalendarAstronomer *myastro2 = new CalendarAstronomer(laLong, laLat);
159 
160   double eclLat = laLat * CalendarAstronomer::PI / 360;
161   double eclLong = laLong * CalendarAstronomer::PI / 360;
162 
163   CalendarAstronomer::Ecliptic ecl(eclLat, eclLong);
164   CalendarAstronomer::Equatorial eq;
165   CalendarAstronomer::Horizon hor;
166 
167   logln("ecliptic: " + ecl.toString());
168   CalendarAstronomer *myastro3 = new CalendarAstronomer();
169   myastro3->setJulianDay((4713 + 2000) * 365.25);
170 
171   CalendarAstronomer *astronomers[] = {
172     myastro, myastro2, myastro3, myastro2 // check cache
173   };
174 
175   for (uint32_t i = 0; i < sizeof(astronomers)/sizeof(astronomers[0]); ++i) {
176     CalendarAstronomer *anAstro = astronomers[i];
177 
178     //logln("astro: " + astro);
179     logln((UnicodeString)"   date: " + anAstro->getTime());
180     logln((UnicodeString)"   cent: " + anAstro->getJulianCentury());
181     logln((UnicodeString)"   gw sidereal: " + anAstro->getGreenwichSidereal());
182     logln((UnicodeString)"   loc sidereal: " + anAstro->getLocalSidereal());
183     logln((UnicodeString)"   equ ecl: " + (anAstro->eclipticToEquatorial(eq,ecl)).toString());
184     logln((UnicodeString)"   equ long: " + (anAstro->eclipticToEquatorial(eq, eclLong)).toString());
185     logln((UnicodeString)"   horiz: " + (anAstro->eclipticToHorizon(hor, eclLong)).toString());
186     logln((UnicodeString)"   sunrise: " + (anAstro->getSunRiseSet(TRUE)));
187     logln((UnicodeString)"   sunset: " + (anAstro->getSunRiseSet(FALSE)));
188     logln((UnicodeString)"   moon phase: " + anAstro->getMoonPhase());
189     logln((UnicodeString)"   moonrise: " + (anAstro->getMoonRiseSet(TRUE)));
190     logln((UnicodeString)"   moonset: " + (anAstro->getMoonRiseSet(FALSE)));
191     logln((UnicodeString)"   prev summer solstice: " + (anAstro->getSunTime(CalendarAstronomer::SUMMER_SOLSTICE(), FALSE)));
192     logln((UnicodeString)"   next summer solstice: " + (anAstro->getSunTime(CalendarAstronomer::SUMMER_SOLSTICE(), TRUE)));
193     logln((UnicodeString)"   prev full moon: " + (anAstro->getMoonTime(CalendarAstronomer::FULL_MOON(), FALSE)));
194     logln((UnicodeString)"   next full moon: " + (anAstro->getMoonTime(CalendarAstronomer::FULL_MOON(), TRUE)));
195   }
196 
197   delete myastro2;
198   delete myastro3;
199   delete myastro;
200   delete cal;
201 
202   closeAstro(status);
203   ASSERT_OK(status);
204 }
205 
206 
207 
TestSunriseTimes(void)208 void AstroTest::TestSunriseTimes(void) {
209   UErrorCode status = U_ZERO_ERROR;
210   initAstro(status);
211   ASSERT_OK(status);
212 
213   //  logln("Sunrise/Sunset times for San Jose, California, USA");
214   //  CalendarAstronomer *astro2 = new CalendarAstronomer(-121.55, 37.20);
215   //  TimeZone *tz = TimeZone::createTimeZone("America/Los_Angeles");
216 
217   // We'll use a table generated by the UNSO website as our reference
218   // From: http://aa.usno.navy.mil/
219   //-Location: W079 25, N43 40
220   //-Rise and Set for the Sun for 2001
221   //-Zone:  4h West of Greenwich
222   int32_t USNO[] = {
223     6,59, 19,45,
224     6,57, 19,46,
225     6,56, 19,47,
226     6,54, 19,48,
227     6,52, 19,49,
228     6,50, 19,51,
229     6,48, 19,52,
230     6,47, 19,53,
231     6,45, 19,54,
232     6,43, 19,55,
233     6,42, 19,57,
234     6,40, 19,58,
235     6,38, 19,59,
236     6,36, 20, 0,
237     6,35, 20, 1,
238     6,33, 20, 3,
239     6,31, 20, 4,
240     6,30, 20, 5,
241     6,28, 20, 6,
242     6,27, 20, 7,
243     6,25, 20, 8,
244     6,23, 20,10,
245     6,22, 20,11,
246     6,20, 20,12,
247     6,19, 20,13,
248     6,17, 20,14,
249     6,16, 20,16,
250     6,14, 20,17,
251     6,13, 20,18,
252     6,11, 20,19,
253   };
254 
255   logln("Sunrise/Sunset times for Toronto, Canada");
256   // long = 79 25", lat = 43 40"
257   CalendarAstronomer *astro3 = new CalendarAstronomer(-(79+25/60), 43+40/60);
258 
259   // As of ICU4J 2.8 the ICU4J time zones implement pass-through
260   // to the underlying JDK.  Because of variation in the
261   // underlying JDKs, we have to use a fixed-offset
262   // SimpleTimeZone to get consistent behavior between JDKs.
263   // The offset we want is [-18000000, 3600000] (raw, dst).
264   // [aliu 10/15/03]
265 
266   // TimeZone tz = TimeZone.getTimeZone("America/Montreal");
267   TimeZone *tz = new SimpleTimeZone(-18000000 + 3600000, "Montreal(FIXED)");
268 
269   GregorianCalendar *cal = new GregorianCalendar(tz->clone(), Locale::getUS(), status);
270   GregorianCalendar *cal2 = new GregorianCalendar(tz->clone(), Locale::getUS(), status);
271   cal->clear();
272   cal->set(UCAL_YEAR, 2001);
273   cal->set(UCAL_MONTH, UCAL_APRIL);
274   cal->set(UCAL_DAY_OF_MONTH, 1);
275   cal->set(UCAL_HOUR_OF_DAY, 12); // must be near local noon for getSunRiseSet to work
276 
277   DateFormat *df_t  = DateFormat::createTimeInstance(DateFormat::MEDIUM,Locale::getUS());
278   DateFormat *df_d  = DateFormat::createDateInstance(DateFormat::MEDIUM,Locale::getUS());
279   DateFormat *df_dt = DateFormat::createDateTimeInstance(DateFormat::MEDIUM, DateFormat::MEDIUM, Locale::getUS());
280   if(!df_t || !df_d || !df_dt) {
281     errln("couldn't create dateformats.");
282     return;
283   }
284   df_t->adoptTimeZone(tz->clone());
285   df_d->adoptTimeZone(tz->clone());
286   df_dt->adoptTimeZone(tz->clone());
287 
288   for (int32_t i=0; i < 30; i++) {
289     logln("setDate\n");
290     astro3->setDate(cal->getTime(status));
291     logln("getRiseSet(TRUE)\n");
292     UDate sunrise = astro3->getSunRiseSet(TRUE);
293     logln("getRiseSet(FALSE)\n");
294     UDate sunset  = astro3->getSunRiseSet(FALSE);
295     logln("end of getRiseSet\n");
296 
297     cal2->setTime(cal->getTime(status), status);
298     cal2->set(UCAL_SECOND,      0);
299     cal2->set(UCAL_MILLISECOND, 0);
300 
301     cal2->set(UCAL_HOUR_OF_DAY, USNO[4*i+0]);
302     cal2->set(UCAL_MINUTE,      USNO[4*i+1]);
303     UDate exprise = cal2->getTime(status);
304     cal2->set(UCAL_HOUR_OF_DAY, USNO[4*i+2]);
305     cal2->set(UCAL_MINUTE,      USNO[4*i+3]);
306     UDate expset = cal2->getTime(status);
307     // Compute delta of what we got to the USNO data, in seconds
308     int32_t deltarise = (int32_t)uprv_fabs((sunrise - exprise) / 1000);
309     int32_t deltaset = (int32_t)uprv_fabs((sunset - expset) / 1000);
310 
311     // Allow a deviation of 0..MAX_DEV seconds
312     // It would be nice to get down to 60 seconds, but at this
313     // point that appears to be impossible without a redo of the
314     // algorithm using something more advanced than Duffett-Smith.
315     int32_t MAX_DEV = 180;
316     UnicodeString s1, s2, s3, s4, s5;
317     if (deltarise > MAX_DEV || deltaset > MAX_DEV) {
318       if (deltarise > MAX_DEV) {
319         errln("FAIL: (rise) " + df_d->format(cal->getTime(status),s1) +
320               ", Sunrise: " + df_dt->format(sunrise, s2) +
321               " (USNO " + df_t->format(exprise,s3) +
322               " d=" + deltarise + "s)");
323       } else {
324         logln(df_d->format(cal->getTime(status),s1) +
325               ", Sunrise: " + df_dt->format(sunrise,s2) +
326               " (USNO " + df_t->format(exprise,s3) + ")");
327       }
328       s1.remove(); s2.remove(); s3.remove(); s4.remove(); s5.remove();
329       if (deltaset > MAX_DEV) {
330         errln("FAIL: (set)  " + df_d->format(cal->getTime(status),s1) +
331               ", Sunset:  " + df_dt->format(sunset,s2) +
332               " (USNO " + df_t->format(expset,s3) +
333               " d=" + deltaset + "s)");
334       } else {
335         logln(df_d->format(cal->getTime(status),s1) +
336               ", Sunset: " + df_dt->format(sunset,s2) +
337               " (USNO " + df_t->format(expset,s3) + ")");
338       }
339     } else {
340       logln(df_d->format(cal->getTime(status),s1) +
341             ", Sunrise: " + df_dt->format(sunrise,s2) +
342             " (USNO " + df_t->format(exprise,s3) + ")" +
343             ", Sunset: " + df_dt->format(sunset,s4) +
344             " (USNO " + df_t->format(expset,s5) + ")");
345     }
346     cal->add(UCAL_DATE, 1, status);
347   }
348 
349   //        CalendarAstronomer a = new CalendarAstronomer(-(71+5/60), 42+37/60);
350   //        cal.clear();
351   //        cal.set(cal.YEAR, 1986);
352   //        cal.set(cal.MONTH, cal.MARCH);
353   //        cal.set(cal.DATE, 10);
354   //        cal.set(cal.YEAR, 1988);
355   //        cal.set(cal.MONTH, cal.JULY);
356   //        cal.set(cal.DATE, 27);
357   //        a.setDate(cal.getTime());
358   //        long r = a.getSunRiseSet2(true);
359   delete astro3;
360   delete tz;
361   delete cal;
362   delete cal2;
363   delete df_t;
364   delete df_d;
365   delete df_dt;
366   closeAstro(status);
367   ASSERT_OK(status);
368 }
369 
370 
371 
TestBasics(void)372 void AstroTest::TestBasics(void) {
373   UErrorCode status = U_ZERO_ERROR;
374   initAstro(status);
375   ASSERT_OK(status);
376 
377   // Check that our JD computation is the same as the book's (p. 88)
378   GregorianCalendar *cal3 = new GregorianCalendar(TimeZone::getGMT()->clone(), Locale::getUS(), status);
379   DateFormat *d3 = DateFormat::createDateTimeInstance(DateFormat::MEDIUM,DateFormat::MEDIUM,Locale::getUS());
380   d3->setTimeZone(*TimeZone::getGMT());
381   cal3->clear();
382   cal3->set(UCAL_YEAR, 1980);
383   cal3->set(UCAL_MONTH, UCAL_JULY);
384   cal3->set(UCAL_DATE, 2);
385   logln("cal3[a]=%.1lf, d=%d\n", cal3->getTime(status), cal3->get(UCAL_JULIAN_DAY,status));
386   {
387     UnicodeString s;
388     logln(UnicodeString("cal3[a] = ") + d3->format(cal3->getTime(status),s));
389   }
390   cal3->clear();
391   cal3->set(UCAL_YEAR, 1980);
392   cal3->set(UCAL_MONTH, UCAL_JULY);
393   cal3->set(UCAL_DATE, 27);
394   logln("cal3=%.1lf, d=%d\n", cal3->getTime(status), cal3->get(UCAL_JULIAN_DAY,status));
395 
396   ASSERT_OK(status);
397   {
398     UnicodeString s;
399     logln(UnicodeString("cal3 = ") + d3->format(cal3->getTime(status),s));
400   }
401   astro->setTime(cal3->getTime(status));
402   double jd = astro->getJulianDay() - 2447891.5;
403   double exp = -3444.;
404   if (jd == exp) {
405     UnicodeString s;
406     logln(d3->format(cal3->getTime(status),s) + " => " + jd);
407   } else {
408     UnicodeString s;
409     errln("FAIL: " + d3->format(cal3->getTime(status), s) + " => " + jd +
410           ", expected " + exp);
411   }
412 
413   //        cal3.clear();
414   //        cal3.set(cal3.YEAR, 1990);
415   //        cal3.set(cal3.MONTH, Calendar.JANUARY);
416   //        cal3.set(cal3.DATE, 1);
417   //        cal3.add(cal3.DATE, -1);
418   //        astro.setDate(cal3.getTime());
419   //        astro.foo();
420 
421   delete cal3;
422   delete d3;
423   ASSERT_OK(status);
424   closeAstro(status);
425   ASSERT_OK(status);
426 
427 }
428 
429 
430 // TODO: try finding next new moon after  07/28/1984 16:00 GMT
431 
432 
433 #endif
434 
435 
436 
437