You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
488 lines
16 KiB
488 lines
16 KiB
// © 2016 and later: Unicode, Inc. and others.
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
|
/********************************************************************
|
|
* COPYRIGHT:
|
|
* Copyright (c) 1996-2016, International Business Machines Corporation and
|
|
* others. All Rights Reserved.
|
|
********************************************************************/
|
|
|
|
/* Test CalendarAstronomer for C++ */
|
|
|
|
#include "unicode/utypes.h"
|
|
#include "string.h"
|
|
#include "unicode/locid.h"
|
|
|
|
#if !UCONFIG_NO_FORMATTING
|
|
|
|
#include "astro.h"
|
|
#include "astrotst.h"
|
|
#include "cmemory.h"
|
|
#include "gregoimp.h" // for Math
|
|
#include "unicode/simpletz.h"
|
|
|
|
|
|
#define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
|
|
|
|
AstroTest::AstroTest(): astro(NULL), gc(NULL) {
|
|
}
|
|
|
|
void AstroTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
|
|
{
|
|
if (exec) logln("TestSuite AstroTest");
|
|
switch (index) {
|
|
// CASE(0,FooTest);
|
|
CASE(0,TestSolarLongitude);
|
|
CASE(1,TestLunarPosition);
|
|
CASE(2,TestCoordinates);
|
|
CASE(3,TestCoverage);
|
|
CASE(4,TestSunriseTimes);
|
|
CASE(5,TestBasics);
|
|
CASE(6,TestMoonAge);
|
|
default: name = ""; break;
|
|
}
|
|
}
|
|
|
|
#undef CASE
|
|
|
|
#define ASSERT_OK(x) UPRV_BLOCK_MACRO_BEGIN { \
|
|
if(U_FAILURE(x)) { \
|
|
dataerrln("%s:%d: %s\n", __FILE__, __LINE__, u_errorName(x)); \
|
|
return; \
|
|
} \
|
|
} UPRV_BLOCK_MACRO_END
|
|
|
|
|
|
void AstroTest::initAstro(UErrorCode &status) {
|
|
if(U_FAILURE(status)) return;
|
|
|
|
if((astro != NULL) || (gc != NULL)) {
|
|
dataerrln("Err: initAstro() called twice!");
|
|
closeAstro(status);
|
|
if(U_SUCCESS(status)) {
|
|
status = U_INTERNAL_PROGRAM_ERROR;
|
|
}
|
|
}
|
|
|
|
if(U_FAILURE(status)) return;
|
|
|
|
astro = new CalendarAstronomer();
|
|
gc = Calendar::createInstance(TimeZone::getGMT()->clone(), status);
|
|
}
|
|
|
|
void AstroTest::closeAstro(UErrorCode &/*status*/) {
|
|
if(astro != NULL) {
|
|
delete astro;
|
|
astro = NULL;
|
|
}
|
|
if(gc != NULL) {
|
|
delete gc;
|
|
gc = NULL;
|
|
}
|
|
}
|
|
|
|
void AstroTest::TestSolarLongitude(void) {
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
initAstro(status);
|
|
ASSERT_OK(status);
|
|
|
|
struct {
|
|
int32_t d[5]; double f ;
|
|
} tests[] = {
|
|
{ { 1980, 7, 27, 0, 00 }, 124.114347 },
|
|
{ { 1988, 7, 27, 00, 00 }, 124.187732 }
|
|
};
|
|
|
|
logln("");
|
|
for (uint32_t i = 0; i < UPRV_LENGTHOF(tests); i++) {
|
|
gc->clear();
|
|
gc->set(tests[i].d[0], tests[i].d[1]-1, tests[i].d[2], tests[i].d[3], tests[i].d[4]);
|
|
|
|
astro->setDate(gc->getTime(status));
|
|
|
|
double longitude = astro->getSunLongitude();
|
|
//longitude = 0;
|
|
CalendarAstronomer::Equatorial result;
|
|
astro->getSunPosition(result);
|
|
logln((UnicodeString)"Sun position is " + result.toString() + (UnicodeString)"; " /* + result.toHmsString()*/ + " Sun longitude is " + longitude );
|
|
}
|
|
closeAstro(status);
|
|
ASSERT_OK(status);
|
|
}
|
|
|
|
|
|
|
|
void AstroTest::TestLunarPosition(void) {
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
initAstro(status);
|
|
ASSERT_OK(status);
|
|
|
|
static const double tests[][7] = {
|
|
{ 1979, 2, 26, 16, 00, 0, 0 }
|
|
};
|
|
logln("");
|
|
|
|
for (int32_t i = 0; i < UPRV_LENGTHOF(tests); i++) {
|
|
gc->clear();
|
|
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]);
|
|
astro->setDate(gc->getTime(status));
|
|
|
|
const CalendarAstronomer::Equatorial& result = astro->getMoonPosition();
|
|
logln((UnicodeString)"Moon position is " + result.toString() + (UnicodeString)"; " /* + result->toHmsString()*/);
|
|
}
|
|
|
|
closeAstro(status);
|
|
ASSERT_OK(status);
|
|
}
|
|
|
|
|
|
|
|
void AstroTest::TestCoordinates(void) {
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
initAstro(status);
|
|
ASSERT_OK(status);
|
|
|
|
CalendarAstronomer::Equatorial result;
|
|
astro->eclipticToEquatorial(result, 139.686111 * CalendarAstronomer::PI / 180.0, 4.875278* CalendarAstronomer::PI / 180.0);
|
|
logln((UnicodeString)"result is " + result.toString() + (UnicodeString)"; " /* + result.toHmsString()*/ );
|
|
closeAstro(status);
|
|
ASSERT_OK(status);
|
|
}
|
|
|
|
|
|
|
|
void AstroTest::TestCoverage(void) {
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
initAstro(status);
|
|
ASSERT_OK(status);
|
|
GregorianCalendar *cal = new GregorianCalendar(1958, UCAL_AUGUST, 15,status);
|
|
UDate then = cal->getTime(status);
|
|
CalendarAstronomer *myastro = new CalendarAstronomer(then);
|
|
ASSERT_OK(status);
|
|
|
|
//Latitude: 34 degrees 05' North
|
|
//Longitude: 118 degrees 22' West
|
|
double laLat = 34 + 5./60, laLong = 360 - (118 + 22./60);
|
|
CalendarAstronomer *myastro2 = new CalendarAstronomer(laLong, laLat);
|
|
|
|
double eclLat = laLat * CalendarAstronomer::PI / 360;
|
|
double eclLong = laLong * CalendarAstronomer::PI / 360;
|
|
|
|
CalendarAstronomer::Ecliptic ecl(eclLat, eclLong);
|
|
CalendarAstronomer::Equatorial eq;
|
|
CalendarAstronomer::Horizon hor;
|
|
|
|
logln("ecliptic: " + ecl.toString());
|
|
CalendarAstronomer *myastro3 = new CalendarAstronomer();
|
|
myastro3->setJulianDay((4713 + 2000) * 365.25);
|
|
|
|
CalendarAstronomer *astronomers[] = {
|
|
myastro, myastro2, myastro3, myastro2 // check cache
|
|
};
|
|
|
|
for (uint32_t i = 0; i < UPRV_LENGTHOF(astronomers); ++i) {
|
|
CalendarAstronomer *anAstro = astronomers[i];
|
|
|
|
//logln("astro: " + astro);
|
|
logln((UnicodeString)" date: " + anAstro->getTime());
|
|
logln((UnicodeString)" cent: " + anAstro->getJulianCentury());
|
|
logln((UnicodeString)" gw sidereal: " + anAstro->getGreenwichSidereal());
|
|
logln((UnicodeString)" loc sidereal: " + anAstro->getLocalSidereal());
|
|
logln((UnicodeString)" equ ecl: " + (anAstro->eclipticToEquatorial(eq,ecl)).toString());
|
|
logln((UnicodeString)" equ long: " + (anAstro->eclipticToEquatorial(eq, eclLong)).toString());
|
|
logln((UnicodeString)" horiz: " + (anAstro->eclipticToHorizon(hor, eclLong)).toString());
|
|
logln((UnicodeString)" sunrise: " + (anAstro->getSunRiseSet(TRUE)));
|
|
logln((UnicodeString)" sunset: " + (anAstro->getSunRiseSet(FALSE)));
|
|
logln((UnicodeString)" moon phase: " + anAstro->getMoonPhase());
|
|
logln((UnicodeString)" moonrise: " + (anAstro->getMoonRiseSet(TRUE)));
|
|
logln((UnicodeString)" moonset: " + (anAstro->getMoonRiseSet(FALSE)));
|
|
logln((UnicodeString)" prev summer solstice: " + (anAstro->getSunTime(CalendarAstronomer::SUMMER_SOLSTICE(), FALSE)));
|
|
logln((UnicodeString)" next summer solstice: " + (anAstro->getSunTime(CalendarAstronomer::SUMMER_SOLSTICE(), TRUE)));
|
|
logln((UnicodeString)" prev full moon: " + (anAstro->getMoonTime(CalendarAstronomer::FULL_MOON(), FALSE)));
|
|
logln((UnicodeString)" next full moon: " + (anAstro->getMoonTime(CalendarAstronomer::FULL_MOON(), TRUE)));
|
|
}
|
|
|
|
delete myastro2;
|
|
delete myastro3;
|
|
delete myastro;
|
|
delete cal;
|
|
|
|
closeAstro(status);
|
|
ASSERT_OK(status);
|
|
}
|
|
|
|
|
|
|
|
void AstroTest::TestSunriseTimes(void) {
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
initAstro(status);
|
|
ASSERT_OK(status);
|
|
|
|
// logln("Sunrise/Sunset times for San Jose, California, USA");
|
|
// CalendarAstronomer *astro2 = new CalendarAstronomer(-121.55, 37.20);
|
|
// TimeZone *tz = TimeZone::createTimeZone("America/Los_Angeles");
|
|
|
|
// We'll use a table generated by the UNSO website as our reference
|
|
// From: http://aa.usno.navy.mil/
|
|
//-Location: W079 25, N43 40
|
|
//-Rise and Set for the Sun for 2001
|
|
//-Zone: 4h West of Greenwich
|
|
int32_t USNO[] = {
|
|
6,59, 19,45,
|
|
6,57, 19,46,
|
|
6,56, 19,47,
|
|
6,54, 19,48,
|
|
6,52, 19,49,
|
|
6,50, 19,51,
|
|
6,48, 19,52,
|
|
6,47, 19,53,
|
|
6,45, 19,54,
|
|
6,43, 19,55,
|
|
6,42, 19,57,
|
|
6,40, 19,58,
|
|
6,38, 19,59,
|
|
6,36, 20, 0,
|
|
6,35, 20, 1,
|
|
6,33, 20, 3,
|
|
6,31, 20, 4,
|
|
6,30, 20, 5,
|
|
6,28, 20, 6,
|
|
6,27, 20, 7,
|
|
6,25, 20, 8,
|
|
6,23, 20,10,
|
|
6,22, 20,11,
|
|
6,20, 20,12,
|
|
6,19, 20,13,
|
|
6,17, 20,14,
|
|
6,16, 20,16,
|
|
6,14, 20,17,
|
|
6,13, 20,18,
|
|
6,11, 20,19,
|
|
};
|
|
|
|
logln("Sunrise/Sunset times for Toronto, Canada");
|
|
// long = 79 25", lat = 43 40"
|
|
CalendarAstronomer astro3(-(79+25/60), 43+40/60);
|
|
|
|
// As of ICU4J 2.8 the ICU4J time zones implement pass-through
|
|
// to the underlying JDK. Because of variation in the
|
|
// underlying JDKs, we have to use a fixed-offset
|
|
// SimpleTimeZone to get consistent behavior between JDKs.
|
|
// The offset we want is [-18000000, 3600000] (raw, dst).
|
|
// [aliu 10/15/03]
|
|
|
|
// TimeZone tz = TimeZone.getTimeZone("America/Montreal");
|
|
SimpleTimeZone tz(-18000000 + 3600000, "Montreal(FIXED)");
|
|
|
|
GregorianCalendar cal(tz.clone(), Locale::getUS(), status);
|
|
GregorianCalendar cal2(tz.clone(), Locale::getUS(), status);
|
|
cal.clear();
|
|
cal.set(UCAL_YEAR, 2001);
|
|
cal.set(UCAL_MONTH, UCAL_APRIL);
|
|
cal.set(UCAL_DAY_OF_MONTH, 1);
|
|
cal.set(UCAL_HOUR_OF_DAY, 12); // must be near local noon for getSunRiseSet to work
|
|
|
|
LocalPointer<DateFormat> df_t(DateFormat::createTimeInstance(DateFormat::MEDIUM,Locale::getUS()));
|
|
LocalPointer<DateFormat> df_d(DateFormat::createDateInstance(DateFormat::MEDIUM,Locale::getUS()));
|
|
LocalPointer<DateFormat> df_dt(DateFormat::createDateTimeInstance(DateFormat::MEDIUM, DateFormat::MEDIUM, Locale::getUS()));
|
|
if(!df_t.isValid() || !df_d.isValid() || !df_dt.isValid()) {
|
|
dataerrln("couldn't create dateformats.");
|
|
closeAstro(status);
|
|
return;
|
|
}
|
|
df_t->adoptTimeZone(tz.clone());
|
|
df_d->adoptTimeZone(tz.clone());
|
|
df_dt->adoptTimeZone(tz.clone());
|
|
|
|
for (int32_t i=0; i < 30; i++) {
|
|
logln("setDate\n");
|
|
astro3.setDate(cal.getTime(status));
|
|
logln("getRiseSet(TRUE)\n");
|
|
UDate sunrise = astro3.getSunRiseSet(TRUE);
|
|
logln("getRiseSet(FALSE)\n");
|
|
UDate sunset = astro3.getSunRiseSet(FALSE);
|
|
logln("end of getRiseSet\n");
|
|
|
|
cal2.setTime(cal.getTime(status), status);
|
|
cal2.set(UCAL_SECOND, 0);
|
|
cal2.set(UCAL_MILLISECOND, 0);
|
|
|
|
cal2.set(UCAL_HOUR_OF_DAY, USNO[4*i+0]);
|
|
cal2.set(UCAL_MINUTE, USNO[4*i+1]);
|
|
UDate exprise = cal2.getTime(status);
|
|
cal2.set(UCAL_HOUR_OF_DAY, USNO[4*i+2]);
|
|
cal2.set(UCAL_MINUTE, USNO[4*i+3]);
|
|
UDate expset = cal2.getTime(status);
|
|
// Compute delta of what we got to the USNO data, in seconds
|
|
int32_t deltarise = (int32_t)uprv_fabs((sunrise - exprise) / 1000);
|
|
int32_t deltaset = (int32_t)uprv_fabs((sunset - expset) / 1000);
|
|
|
|
// Allow a deviation of 0..MAX_DEV seconds
|
|
// It would be nice to get down to 60 seconds, but at this
|
|
// point that appears to be impossible without a redo of the
|
|
// algorithm using something more advanced than Duffett-Smith.
|
|
int32_t MAX_DEV = 180;
|
|
UnicodeString s1, s2, s3, s4, s5;
|
|
if (deltarise > MAX_DEV || deltaset > MAX_DEV) {
|
|
if (deltarise > MAX_DEV) {
|
|
errln("FAIL: (rise) " + df_d->format(cal.getTime(status),s1) +
|
|
", Sunrise: " + df_dt->format(sunrise, s2) +
|
|
" (USNO " + df_t->format(exprise,s3) +
|
|
" d=" + deltarise + "s)");
|
|
} else {
|
|
logln(df_d->format(cal.getTime(status),s1) +
|
|
", Sunrise: " + df_dt->format(sunrise,s2) +
|
|
" (USNO " + df_t->format(exprise,s3) + ")");
|
|
}
|
|
s1.remove(); s2.remove(); s3.remove(); s4.remove(); s5.remove();
|
|
if (deltaset > MAX_DEV) {
|
|
errln("FAIL: (set) " + df_d->format(cal.getTime(status),s1) +
|
|
", Sunset: " + df_dt->format(sunset,s2) +
|
|
" (USNO " + df_t->format(expset,s3) +
|
|
" d=" + deltaset + "s)");
|
|
} else {
|
|
logln(df_d->format(cal.getTime(status),s1) +
|
|
", Sunset: " + df_dt->format(sunset,s2) +
|
|
" (USNO " + df_t->format(expset,s3) + ")");
|
|
}
|
|
} else {
|
|
logln(df_d->format(cal.getTime(status),s1) +
|
|
", Sunrise: " + df_dt->format(sunrise,s2) +
|
|
" (USNO " + df_t->format(exprise,s3) + ")" +
|
|
", Sunset: " + df_dt->format(sunset,s4) +
|
|
" (USNO " + df_t->format(expset,s5) + ")");
|
|
}
|
|
cal.add(UCAL_DATE, 1, status);
|
|
}
|
|
|
|
// CalendarAstronomer a = new CalendarAstronomer(-(71+5/60), 42+37/60);
|
|
// cal.clear();
|
|
// cal.set(cal.YEAR, 1986);
|
|
// cal.set(cal.MONTH, cal.MARCH);
|
|
// cal.set(cal.DATE, 10);
|
|
// cal.set(cal.YEAR, 1988);
|
|
// cal.set(cal.MONTH, cal.JULY);
|
|
// cal.set(cal.DATE, 27);
|
|
// a.setDate(cal.getTime());
|
|
// long r = a.getSunRiseSet2(true);
|
|
closeAstro(status);
|
|
ASSERT_OK(status);
|
|
}
|
|
|
|
|
|
|
|
void AstroTest::TestBasics(void) {
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
initAstro(status);
|
|
if (U_FAILURE(status)) {
|
|
dataerrln("Got error: %s", u_errorName(status));
|
|
return;
|
|
}
|
|
|
|
// Check that our JD computation is the same as the book's (p. 88)
|
|
GregorianCalendar cal3(TimeZone::getGMT()->clone(), Locale::getUS(), status);
|
|
LocalPointer<DateFormat> d3(DateFormat::createDateTimeInstance(DateFormat::MEDIUM,DateFormat::MEDIUM,Locale::getUS()));
|
|
if (d3.isNull()) {
|
|
dataerrln("Got error: %s", u_errorName(status));
|
|
closeAstro(status);
|
|
return;
|
|
}
|
|
d3->setTimeZone(*TimeZone::getGMT());
|
|
cal3.clear();
|
|
cal3.set(UCAL_YEAR, 1980);
|
|
cal3.set(UCAL_MONTH, UCAL_JULY);
|
|
cal3.set(UCAL_DATE, 2);
|
|
logln("cal3[a]=%.1lf, d=%d\n", cal3.getTime(status), cal3.get(UCAL_JULIAN_DAY,status));
|
|
{
|
|
UnicodeString s;
|
|
logln(UnicodeString("cal3[a] = ") + d3->format(cal3.getTime(status),s));
|
|
}
|
|
cal3.clear();
|
|
cal3.set(UCAL_YEAR, 1980);
|
|
cal3.set(UCAL_MONTH, UCAL_JULY);
|
|
cal3.set(UCAL_DATE, 27);
|
|
logln("cal3=%.1lf, d=%d\n", cal3.getTime(status), cal3.get(UCAL_JULIAN_DAY,status));
|
|
|
|
ASSERT_OK(status);
|
|
{
|
|
UnicodeString s;
|
|
logln(UnicodeString("cal3 = ") + d3->format(cal3.getTime(status),s));
|
|
}
|
|
astro->setTime(cal3.getTime(status));
|
|
double jd = astro->getJulianDay() - 2447891.5;
|
|
double exp = -3444.;
|
|
if (jd == exp) {
|
|
UnicodeString s;
|
|
logln(d3->format(cal3.getTime(status),s) + " => " + jd);
|
|
} else {
|
|
UnicodeString s;
|
|
errln("FAIL: " + d3->format(cal3.getTime(status), s) + " => " + jd +
|
|
", expected " + exp);
|
|
}
|
|
|
|
// cal3.clear();
|
|
// cal3.set(cal3.YEAR, 1990);
|
|
// cal3.set(cal3.MONTH, Calendar.JANUARY);
|
|
// cal3.set(cal3.DATE, 1);
|
|
// cal3.add(cal3.DATE, -1);
|
|
// astro.setDate(cal3.getTime());
|
|
// astro.foo();
|
|
|
|
ASSERT_OK(status);
|
|
closeAstro(status);
|
|
ASSERT_OK(status);
|
|
|
|
}
|
|
|
|
void AstroTest::TestMoonAge(void){
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
initAstro(status);
|
|
ASSERT_OK(status);
|
|
|
|
// more testcases are around the date 05/20/2012
|
|
//ticket#3785 UDate ud0 = 1337557623000.0;
|
|
static const double testcase[][10] = {{2012, 5, 20 , 16 , 48, 59},
|
|
{2012, 5, 20 , 16 , 47, 34},
|
|
{2012, 5, 21, 00, 00, 00},
|
|
{2012, 5, 20, 14, 55, 59},
|
|
{2012, 5, 21, 7, 40, 40},
|
|
{2023, 9, 25, 10,00, 00},
|
|
{2008, 7, 7, 15, 00, 33},
|
|
{1832, 9, 24, 2, 33, 41 },
|
|
{2016, 1, 31, 23, 59, 59},
|
|
{2099, 5, 20, 14, 55, 59}
|
|
};
|
|
// Moon phase angle - Got from http://www.moonsystem.to/checkupe.htm
|
|
static const double angle[] = {356.8493418421329, 356.8386760059673, 0.09625415252237701, 355.9986960782416, 3.5714026601303317, 124.26906744384183, 59.80247650195558,
|
|
357.54163205513123, 268.41779281511094, 4.82340276581624};
|
|
static const double precision = CalendarAstronomer::PI/32;
|
|
for (int32_t i = 0; i < UPRV_LENGTHOF(testcase); i++) {
|
|
gc->clear();
|
|
logln((UnicodeString)"CASE["+i+"]: Year "+(int32_t)testcase[i][0]+" Month "+(int32_t)testcase[i][1]+" Day "+
|
|
(int32_t)testcase[i][2]+" Hour "+(int32_t)testcase[i][3]+" Minutes "+(int32_t)testcase[i][4]+
|
|
" Seconds "+(int32_t)testcase[i][5]);
|
|
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]);
|
|
astro->setDate(gc->getTime(status));
|
|
double expectedAge = (angle[i]*CalendarAstronomer::PI)/180;
|
|
double got = astro->getMoonAge();
|
|
//logln(testString);
|
|
if(!(got>expectedAge-precision && got<expectedAge+precision)){
|
|
errln((UnicodeString)"FAIL: expected " + expectedAge +
|
|
" got " + got);
|
|
}else{
|
|
logln((UnicodeString)"PASS: expected " + expectedAge +
|
|
" got " + got);
|
|
}
|
|
}
|
|
closeAstro(status);
|
|
ASSERT_OK(status);
|
|
}
|
|
|
|
|
|
// TODO: try finding next new moon after 07/28/1984 16:00 GMT
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|