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