Home | History | Annotate | Download | only in intltest
      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