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 23 AstroTest::AstroTest(): astro(NULL), gc(NULL) { 24 } 25 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 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 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 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 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 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 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 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 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 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