• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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