Home | History | Annotate | Download | only in intltest
      1 /********************************************************************
      2  * COPYRIGHT:
      3  * Copyright (c) 1996-2009, 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)) { errln("%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