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