1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4 * COPYRIGHT:
5 * Copyright (c) 1996-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
8
9 /* Test CalendarAstronomer for C++ */
10
11 #include "unicode/utypes.h"
12 #include "string.h"
13 #include "unicode/locid.h"
14
15 #if !UCONFIG_NO_FORMATTING
16
17 #include "astro.h"
18 #include "astrotst.h"
19 #include "cmemory.h"
20 #include "gregoimp.h" // for Math
21 #include "unicode/simpletz.h"
22
23
24 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
25
AstroTest()26 AstroTest::AstroTest(): gc(nullptr) {
27 }
28
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)29 void AstroTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
30 {
31 if (exec) logln("TestSuite AstroTest");
32 switch (index) {
33 // CASE(0,FooTest);
34 CASE(0,TestSolarLongitude);
35 CASE(1,TestLunarPosition);
36 CASE(2,TestCoordinates);
37 CASE(3,TestCoverage);
38 CASE(4,TestBasics);
39 CASE(5,TestMoonAge);
40 default: name = ""; break;
41 }
42 }
43
44 #undef CASE
45
46 #define ASSERT_OK(x) UPRV_BLOCK_MACRO_BEGIN { \
47 if(U_FAILURE(x)) { \
48 dataerrln("%s:%d: %s\n", __FILE__, __LINE__, u_errorName(x)); \
49 return; \
50 } \
51 } UPRV_BLOCK_MACRO_END
52
53
init(UErrorCode & status)54 void AstroTest::init(UErrorCode &status) {
55 if(U_FAILURE(status)) return;
56
57 if(gc != nullptr) {
58 dataerrln("Err: init() called twice!");
59 close(status);
60 if(U_SUCCESS(status)) {
61 status = U_INTERNAL_PROGRAM_ERROR;
62 }
63 }
64
65 if(U_FAILURE(status)) return;
66
67 gc = Calendar::createInstance(TimeZone::getGMT()->clone(), status);
68 }
69
close(UErrorCode &)70 void AstroTest::close(UErrorCode &/*status*/) {
71 if(gc != nullptr) {
72 delete gc;
73 gc = nullptr;
74 }
75 }
76
TestSolarLongitude()77 void AstroTest::TestSolarLongitude() {
78 UErrorCode status = U_ZERO_ERROR;
79 init(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 < UPRV_LENGTHOF(tests); 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 CalendarAstronomer astro(gc->getTime(status));
95
96 astro.getSunLongitude();
97 }
98 close(status);
99 ASSERT_OK(status);
100 }
101
102
103
TestLunarPosition()104 void AstroTest::TestLunarPosition() {
105 UErrorCode status = U_ZERO_ERROR;
106 init(status);
107 ASSERT_OK(status);
108
109 static const double tests[][7] = {
110 { 1979, 2, 26, 16, 00, 0, 0 }
111 };
112 logln("");
113
114 for (int32_t i = 0; i < UPRV_LENGTHOF(tests); i++) {
115 gc->clear();
116 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]);
117 CalendarAstronomer astro(gc->getTime(status));
118
119 const CalendarAstronomer::Equatorial& result = astro.getMoonPosition();
120 logln((UnicodeString)"Moon position is " + result.toString() + (UnicodeString)"; " /* + result->toHmsString()*/);
121 }
122
123 close(status);
124 ASSERT_OK(status);
125 }
126
127
128
TestCoordinates()129 void AstroTest::TestCoordinates() {
130 UErrorCode status = U_ZERO_ERROR;
131 init(status);
132 ASSERT_OK(status);
133
134 CalendarAstronomer::Equatorial result;
135 CalendarAstronomer astro;
136 astro.eclipticToEquatorial(result, 139.686111 * CalendarAstronomer::PI / 180.0, 4.875278* CalendarAstronomer::PI / 180.0);
137 logln((UnicodeString)"result is " + result.toString() + (UnicodeString)"; " /* + result.toHmsString()*/ );
138 close(status);
139 ASSERT_OK(status);
140 }
141
142
143
TestCoverage()144 void AstroTest::TestCoverage() {
145 UErrorCode status = U_ZERO_ERROR;
146 init(status);
147 ASSERT_OK(status);
148 GregorianCalendar *cal = new GregorianCalendar(1958, UCAL_AUGUST, 15,status);
149 UDate then = cal->getTime(status);
150 CalendarAstronomer *myastro = new CalendarAstronomer(then);
151 ASSERT_OK(status);
152
153 //Latitude: 34 degrees 05' North
154 //Longitude: 118 degrees 22' West
155 double laLat = 34 + 5./60, laLong = 360 - (118 + 22./60);
156
157 double eclLat = laLat * CalendarAstronomer::PI / 360;
158 double eclLong = laLong * CalendarAstronomer::PI / 360;
159
160 CalendarAstronomer::Equatorial eq;
161
162 CalendarAstronomer *astronomers[] = {
163 myastro, myastro, myastro // check cache
164 };
165
166 for (uint32_t i = 0; i < UPRV_LENGTHOF(astronomers); ++i) {
167 CalendarAstronomer *anAstro = astronomers[i];
168
169 //logln("astro: " + astro);
170 logln((UnicodeString)" date: " + anAstro->getTime());
171 logln((UnicodeString)" equ ecl: " + (anAstro->eclipticToEquatorial(eq,eclLat,eclLong)).toString());
172 }
173
174 delete myastro;
175 delete cal;
176
177 close(status);
178 ASSERT_OK(status);
179 }
180
TestBasics()181 void AstroTest::TestBasics() {
182 UErrorCode status = U_ZERO_ERROR;
183 init(status);
184 if (U_FAILURE(status)) {
185 dataerrln("Got error: %s", u_errorName(status));
186 return;
187 }
188
189 // Check that our JD computation is the same as the book's (p. 88)
190 GregorianCalendar cal3(TimeZone::getGMT()->clone(), Locale::getUS(), status);
191 LocalPointer<DateFormat> d3(DateFormat::createDateTimeInstance(DateFormat::MEDIUM,DateFormat::MEDIUM,Locale::getUS()));
192 if (d3.isNull()) {
193 dataerrln("Got error: %s", u_errorName(status));
194 close(status);
195 return;
196 }
197 d3->setTimeZone(*TimeZone::getGMT());
198 cal3.clear();
199 cal3.set(UCAL_YEAR, 1980);
200 cal3.set(UCAL_MONTH, UCAL_JULY);
201 cal3.set(UCAL_DATE, 2);
202 logln("cal3[a]=%.1lf, d=%d\n", cal3.getTime(status), cal3.get(UCAL_JULIAN_DAY,status));
203 {
204 UnicodeString s;
205 logln(UnicodeString("cal3[a] = ") + d3->format(cal3.getTime(status),s));
206 }
207 cal3.clear();
208 cal3.set(UCAL_YEAR, 1980);
209 cal3.set(UCAL_MONTH, UCAL_JULY);
210 cal3.set(UCAL_DATE, 27);
211 logln("cal3=%.1lf, d=%d\n", cal3.getTime(status), cal3.get(UCAL_JULIAN_DAY,status));
212
213 ASSERT_OK(status);
214 {
215 UnicodeString s;
216 logln(UnicodeString("cal3 = ") + d3->format(cal3.getTime(status),s));
217 }
218 CalendarAstronomer astro(cal3.getTime(status));
219 double jd = astro.getJulianDay() - 2447891.5;
220 double exp = -3444.;
221 if (jd == exp) {
222 UnicodeString s;
223 logln(d3->format(cal3.getTime(status),s) + " => " + jd);
224 } else {
225 UnicodeString s;
226 errln("FAIL: " + d3->format(cal3.getTime(status), s) + " => " + jd +
227 ", expected " + exp);
228 }
229
230 // cal3.clear();
231 // cal3.set(cal3.YEAR, 1990);
232 // cal3.set(cal3.MONTH, Calendar.JANUARY);
233 // cal3.set(cal3.DATE, 1);
234 // cal3.add(cal3.DATE, -1);
235 // astro.setDate(cal3.getTime());
236 // astro.foo();
237
238 ASSERT_OK(status);
239 close(status);
240 ASSERT_OK(status);
241
242 }
243
TestMoonAge()244 void AstroTest::TestMoonAge(){
245 UErrorCode status = U_ZERO_ERROR;
246 init(status);
247 ASSERT_OK(status);
248
249 // more testcases are around the date 05/20/2012
250 //ticket#3785 UDate ud0 = 1337557623000.0;
251 static const double testcase[][10] = {{2012, 5, 20 , 16 , 48, 59},
252 {2012, 5, 20 , 16 , 47, 34},
253 {2012, 5, 21, 00, 00, 00},
254 {2012, 5, 20, 14, 55, 59},
255 {2012, 5, 21, 7, 40, 40},
256 {2023, 9, 25, 10,00, 00},
257 {2008, 7, 7, 15, 00, 33},
258 {1832, 9, 24, 2, 33, 41 },
259 {2016, 1, 31, 23, 59, 59},
260 {2099, 5, 20, 14, 55, 59}
261 };
262 // Moon phase angle - Got from http://www.moonsystem.to/checkupe.htm
263 static const double angle[] = {356.8493418421329, 356.8386760059673, 0.09625415252237701, 355.9986960782416, 3.5714026601303317, 124.26906744384183, 59.80247650195558,
264 357.54163205513123, 268.41779281511094, 4.82340276581624};
265 static const double precision = CalendarAstronomer::PI/32;
266 for (int32_t i = 0; i < UPRV_LENGTHOF(testcase); i++) {
267 gc->clear();
268 logln((UnicodeString)"CASE["+i+"]: Year "+(int32_t)testcase[i][0]+" Month "+(int32_t)testcase[i][1]+" Day "+
269 (int32_t)testcase[i][2]+" Hour "+(int32_t)testcase[i][3]+" Minutes "+(int32_t)testcase[i][4]+
270 " Seconds "+(int32_t)testcase[i][5]);
271 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]);
272 CalendarAstronomer astro(gc->getTime(status));
273 double expectedAge = (angle[i]*CalendarAstronomer::PI)/180;
274 double got = astro.getMoonAge();
275 //logln(testString);
276 if(!(got>expectedAge-precision && got<expectedAge+precision)){
277 errln((UnicodeString)"FAIL: expected " + expectedAge +
278 " got " + got);
279 }else{
280 logln((UnicodeString)"PASS: expected " + expectedAge +
281 " got " + got);
282 }
283 }
284 close(status);
285 ASSERT_OK(status);
286 }
287
288
289 // TODO: try finding next new moon after 07/28/1984 16:00 GMT
290
291
292 #endif
293
294
295
296