add holiday in AA lunar calendar.

This commit is contained in:
Chen Wei
2014-03-12 09:57:46 +08:00
parent c08c8e0db9
commit 7322476333
4 changed files with 121 additions and 178 deletions

109
aa.py
View File

@@ -120,72 +120,7 @@ IAU2000BNutationTable = array([
[ 1, 1, 2, -2, 2, 1290, 0, 0, -556, 0, 0 ] [ 1, 1, 2, -2, 2, 1290, 0, 0, -556, 0, 0 ]
]) ])
# The abridged elp-2000 table from A&A p309, TABLE 45.A # Truncated VSOP87D tables
moon_tableA = [
[ 0, 0, 1, 0, 6288744, -20905355 ],
[ 2, 0, -1, 0, 1274027, -3699111 ],
[ 2, 0, 0, 0, 658314, -2955968 ],
[ 0, 0, 2, 0, 213618, -569925 ],
[ 0, 1, 0, 0, -185116, 48888 ],
[ 0, 0, 0, 2, -114332, -3149 ],
[ 2, 0, -2, 0, 58793, 246158 ],
[ 2, -1, -1, 0, 57066, -152138 ],
[ 2, 0, 1, 0, 53322, -170733 ],
[ 2, -1, 0, 0, 45758, -204586 ],
[ 0, 1, -1, 0, -40923, -129620 ],
[ 1, 0, 0, 0, -34720, 108743 ],
[ 0, 1, 1, 0, -30383, 104755 ],
[ 2, 0, 0, -2, 15327, 10321 ],
[ 0, 0, 1, 2, -12528, 0 ],
[ 0, 0, 1, -2, 10980, 79661 ],
[ 4, 0, -1, 0, 10675, -34782 ],
[ 0, 0, 3, 0, 10034, -23210 ],
[ 4, 0, -2, 0, 8548, -21636 ],
[ 2, 1, -1, 0, -7888, 24208 ],
[ 2, 1, 0, 0, -6766, 30824 ],
[ 1, 0, -1, 0, -5163, -8379 ],
[ 1, 1, 0, 0, 4987, -16675 ],
[ 2, -1, 1, 0, 4036, -12831 ],
[ 2, 0, 2, 0, 3994, -10445 ],
[ 4, 0, 0, 0, 3861, -11650 ],
[ 2, 0, -3, 0, 3665, 14403 ],
[ 0, 1, -2, 0, -2689, -7003 ],
[ 2, 0, -1, 2, -2602, 0 ],
[ 2, -1, -2, 0, 2390, 10056 ],
[ 1, 0, 1, 0, -2348, 6322 ],
[ 2, -2, 0, 0, 2236, -9884 ],
[ 0, 1, 2, 0, -2120, 5751 ],
[ 0, 2, 0, 0, -2069, 0 ],
[ 2, -2, -1, 0, 2048, -4950 ],
[ 2, 0, 1, -2, -1773, 4130 ],
[ 2, 0, 0, 2, -1595, 0 ],
[ 4, -1, -1, 0, 1215, -3958 ],
[ 0, 0, 2, 2, -1110, 0 ],
[ 3, 0, -1, 0, -892, 3258 ],
[ 2, 1, 1, 0, -810, 2616 ],
[ 4, -1, -2, 0, 759, -1897 ],
[ 0, 2, -1, 0, -713, -2117 ],
[ 2, 2, -1, 0, -700, 2354 ],
[ 2, 1, -2, 0, 691, 0 ],
[ 2, -1, 0, -2, 596, 0 ],
[ 4, 0, 1, 0, 549, -1423 ],
[ 0, 0, 4, 0, 537, -1117 ],
[ 4, -1, 0, 0, 520, -1571 ],
[ 1, 0, -2, 0, -487, -1739 ],
[ 2, 1, 0, -2, -399, 0 ],
[ 0, 0, 2, -2, -381, -4421 ],
[ 1, 1, 1, 0, 351, 0 ],
[ 3, 0, -2, 0, -340, 0 ],
[ 4, 0, -3, 0, 330, 0 ],
[ 2, -1, 2, 0, 327, 0 ],
[ 0, 2, 1, 0, -323, 1165 ],
[ 1, 1, -1, 0, 299, 0 ],
[ 2, 0, 3, 0, 294, 0 ],
[ 2, 0, -1, -2, 0, 8752 ]
]
# VSOP87D tables
earth_L0 = array([ earth_L0 = array([
[ 1.75347045673, 0, 0 ], [ 1.75347045673, 0, 0 ],
[ 0.03341656456, 4.66925680417, 6283.0758499914 ], [ 0.03341656456, 4.66925680417, 6283.0758499914 ],
@@ -830,10 +765,6 @@ earth_R5 = array([
# 0 terms retained # 0 terms retained
]) ])
# table for LEA-406 moon solution. Those terms are linear combination # table for LEA-406 moon solution. Those terms are linear combination
# of integer multipliers of 14 variables (Arg_j_, j=1,14): # of integer multipliers of 14 variables (Arg_j_, j=1,14):
# Delaunay variables l, l', F, D; # Delaunay variables l, l', F, D;
@@ -842,7 +773,7 @@ earth_R5 = array([
# and the general precession in longitude p_A_. # and the general precession in longitude p_A_.
# terms of 3rd-degree and 4th-degree are ignored # terms of 3rd-degree and 4th-degree are ignored
# in arcsec # in arcsec
# generated with threshold 0.05, average error Moon = 0.73", max 1.5" # average error Moon = 0.73", max 1.5"
M_ARG = array([ M_ARG = array([
# 226 terms # 226 terms
[ 485868.249036, 1717915923.21779990, 31.87920 ], [ 485868.249036, 1717915923.21779990, 31.87920 ],
@@ -1550,9 +1481,6 @@ CV = M_PHASE * DEG2RAD
C_V, CT_V, CTT_V = hsplit(CV, 3) C_V, CT_V, CTT_V = hsplit(CV, 3)
A_V, AT_V, ATT_V = hsplit(M_AMP, 3) A_V, AT_V, ATT_V = hsplit(M_AMP, 3)
# for VSOP2010 # for VSOP2010
# Mean Longituee J2000 (radian) # Mean Longituee J2000 (radian)
ci0 = [ ci0 = [
@@ -1761,12 +1689,9 @@ class VSOP2010():
def vsopLx(vsopterms, t): def vsopLx(vsopterms, t):
''' helper function for calculate VSOP87 ''' ''' helper function for calculate VSOP87 '''
lx = vsopterms[:, 0] * cos(vsopterms[:, 1] + vsopterms[:, 2] * t) lx = vsopterms[:, 0] * cos(vsopterms[:, 1] + vsopterms[:, 2] * t)
#for vsopterm in vsopterms:
# A = vsopterm[0]
# B = vsopterm[1]
# C = vsopterm[2]
# Lx += A * math.cos(B + C * t)
return sum(lx) return sum(lx)
@@ -1884,7 +1809,7 @@ def f_msangle(jd, angle):
Arg: Arg:
jd: time in JDTT jd: time in JDTT
Return: Return:
angle in radians, convert to -PI to + PI range angle in radians, convert to -pi to +pi range
''' '''
return npitopi(apparentmoon(jd, ignorenutation=True) return npitopi(apparentmoon(jd, ignorenutation=True)
@@ -1897,7 +1822,8 @@ def solarterm(year, angle):
The Sun's moving speed on ecliptical longitude is 0.04 argsecond / second, The Sun's moving speed on ecliptical longitude is 0.04 argsecond / second,
The accuracy of abridged VSOP is 1", nutation by IAU2000B is 0.001" The mean error of truncated VSOP is less than 0.1", nutation by IAU2000B is
0.001"
Args: Args:
year: the year in integer year: the year in integer
@@ -1907,7 +1833,7 @@ def solarterm(year, angle):
''' '''
# mean error when compare apparentsun to NASA(1900-2100) is 0.14" # mean error when compare apparentsun to NASA(1900-2100) is 0.05"
# 0.000000005 radians = 0.001" # 0.000000005 radians = 0.001"
ERROR = 0.000000005 ERROR = 0.000000005
@@ -1917,7 +1843,6 @@ def solarterm(year, angle):
# we searching for # we searching for
est_vejd = g2jd(year, 3, 20.5) est_vejd = g2jd(year, 3, 20.5)
x0 = est_vejd + angle * 360.0 / 365.24 # estimate x0 = est_vejd + angle * 360.0 / 365.24 # estimate
#x0 -= solarangle(x0, r) / SUN_SPEED # further closing
x1 = x0 + 0.5 x1 = x0 + 0.5
return rootbysecand(f_solarangle, r, x0, x1, precision=ERROR) return rootbysecand(f_solarangle, r, x0, x1, precision=ERROR)
@@ -1927,11 +1852,11 @@ def newmoon(jd):
''' search newmoon near a given date. ''' search newmoon near a given date.
Angle between Sun-Moon has been converted to [-pi, pi] range so the Angle between Sun-Moon has been converted to [-pi, pi] range so the
function msangle is continuous in that range. Use Secand method to find function f_msangle is continuous in that range. Use Secand method to find
root. root.
newmoon in 5 iterations, if the start is close enough, as the searching of Test shows newmoon can be found in 5 iterations, if the start is close
next newmoon usualy does, it may use only 3 iterations. enough, it may use only 3 iterations.
Arg: Arg:
jd: in JDTT jd: in JDTT
@@ -1941,7 +1866,7 @@ def newmoon(jd):
''' '''
# 0.0000001 radians is about 0.02 arcsecond, mean error of apparentmoon # 0.0000001 radians is about 0.02 arcsecond, mean error of apparentmoon
# when compared to JPL Horizon is about 4 arcsecond # when compared to JPL Horizon is about 0.7 arcsecond
ERROR = 0.0000001 ERROR = 0.0000001
# initilize x0 to the day close to newmoon # initilize x0 to the day close to newmoon
@@ -2003,7 +1928,6 @@ def apparentsun(jde, ignorenutation=False):
geolong += nutation(jde) geolong += nutation(jde)
labbr = lightabbr_high(jde) labbr = lightabbr_high(jde)
#print 'labbr = %s' % fmtdeg(math.degrees(labbr))
geolong += labbr geolong += labbr
return normrad(geolong) return normrad(geolong)
@@ -2493,11 +2417,8 @@ def nutation(jde):
# Long-term harmonic development of lunar ephemeris. # Long-term harmonic development of lunar ephemeris.
# Kudryavtsev S.M. <Astron. Astrophys. 471, 1069 (2007)> # Kudryavtsev S.M. <Astron. Astrophys. 471, 1069 (2007)>
# #
# the tables M_AMP, M_PHASE, M_ARG are imported from aa_full_table
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
FRM = [785939.924268, 1732564372.3047, -5.279, .006665, -5.522e-5 ] FRM = [785939.924268, 1732564372.3047, -5.279, .006665, -5.522e-5 ]
RADEG = 1.7453292519943296e-2
RASEC = 4.8481368110953599e-6
RE_FORTRAN_FMT = re.compile(r'(\d*)([A-Z]+)(\d*)\.?\d*') RE_FORTRAN_FMT = re.compile(r'(\d*)([A-Z]+)(\d*)\.?\d*')
def fortran_parsefmt(fmt): def fortran_parsefmt(fmt):
@@ -2686,9 +2607,9 @@ class LEA406():
self.A_V =array(A_V) self.A_V =array(A_V)
self.AT_V =array(AT_V) self.AT_V =array(AT_V)
self.ATT_V =array(ATT_V) self.ATT_V =array(ATT_V)
self.C_V =array(C_V) * RADEG self.C_V =array(C_V) * DEG2RAD
self.CT_V =array(CT_V) * RADEG self.CT_V =array(CT_V) * DEG2RAD
self.CTT_V =array(CTT_V) * RADEG self.CTT_V =array(CTT_V) * DEG2RAD
self.F0_V = array(F0_V) * 3600 self.F0_V = array(F0_V) * 3600
self.F1_V = array(F1_V) self.F1_V = array(F1_V)

View File

@@ -130,12 +130,9 @@ IAU2000BNutationTable = array([
def vsopLx(vsopterms, t): def vsopLx(vsopterms, t):
''' helper function for calculate VSOP87 ''' ''' helper function for calculate VSOP87 '''
lx = vsopterms[:, 0] * cos(vsopterms[:, 1] + vsopterms[:, 2] * t) lx = vsopterms[:, 0] * cos(vsopterms[:, 1] + vsopterms[:, 2] * t)
#for vsopterm in vsopterms:
# A = vsopterm[0]
# B = vsopterm[1]
# C = vsopterm[2]
# Lx += A * math.cos(B + C * t)
return sum(lx) return sum(lx)
@@ -253,7 +250,7 @@ def f_msangle(jd, angle):
Arg: Arg:
jd: time in JDTT jd: time in JDTT
Return: Return:
angle in radians, convert to -PI to + PI range angle in radians, convert to -pi to +pi range
''' '''
return npitopi(apparentmoon(jd, ignorenutation=True) return npitopi(apparentmoon(jd, ignorenutation=True)
@@ -266,7 +263,7 @@ def solarterm(year, angle):
The Sun's moving speed on ecliptical longitude is 0.04 argsecond / second, The Sun's moving speed on ecliptical longitude is 0.04 argsecond / second,
The accuracy of abridged VSOP is 1", nutation by IAU2000B is 0.001" The accuracy of nutation by IAU2000B is 0.001"
Args: Args:
year: the year in integer year: the year in integer
@@ -276,7 +273,7 @@ def solarterm(year, angle):
''' '''
# mean error when compare apparentsun to NASA(1900-2100) is 0.14" # mean error when compare apparentsun to NASA(1900-2100) is 0.05"
# 0.000000005 radians = 0.001" # 0.000000005 radians = 0.001"
ERROR = 0.000000005 ERROR = 0.000000005
@@ -296,11 +293,11 @@ def newmoon(jd):
''' search newmoon near a given date. ''' search newmoon near a given date.
Angle between Sun-Moon has been converted to [-pi, pi] range so the Angle between Sun-Moon has been converted to [-pi, pi] range so the
function msangle is continuous in that range. Use Secand method to find function f_msangle is continuous in that range. Use Secand method to find
root. root.
newmoon in 5 iterations, if the start is close enough, as the searching of Test shows newmoon can be found in 5 iterations, if the start is close
next newmoon usualy does, it may use only 3 iterations. enough, it may use only 3 iterations.
Arg: Arg:
jd: in JDTT jd: in JDTT
@@ -310,7 +307,7 @@ def newmoon(jd):
''' '''
# 0.0000001 radians is about 0.02 arcsecond, mean error of apparentmoon # 0.0000001 radians is about 0.02 arcsecond, mean error of apparentmoon
# when compared to JPL Horizon is about 4 arcsecond # when compared to JPL Horizon is about 0.7 arcsecond
ERROR = 0.0000001 ERROR = 0.0000001
# initilize x0 to the day close to newmoon # initilize x0 to the day close to newmoon
@@ -372,7 +369,6 @@ def apparentsun(jde, ignorenutation=False):
geolong += nutation(jde) geolong += nutation(jde)
labbr = lightabbr_high(jde) labbr = lightabbr_high(jde)
#print 'labbr = %s' % fmtdeg(math.degrees(labbr))
geolong += labbr geolong += labbr
return normrad(geolong) return normrad(geolong)

View File

@@ -401,12 +401,12 @@ def verify_lunarcalendar():
hko.append((x[0], '%s %s' % (x[1], x[2]))) hko.append((x[0], '%s %s' % (x[1], x[2])))
else: else:
hko.append((x[0], x[1])) hko.append((x[0], x[1]))
aalc = cn_lunarcal(start) aalc = cn_lunarcal(start)
if start == 1900:
for x in aalc:
print x[0], x[1]
for i in xrange(len(aalc)): for i in xrange(len(aalc)):
aaday, aaldate = aalc[i]['date'], aalc[i]['lunardate'] aaday, aaldate = aalc[i]['date'], aalc[i]['lunardate']
if aalc[i]['jieqi']:
aaldate = '%s %s' % (aalc[i]['lunardate'], aalc[i]['jieqi'])
hkoday, hkoldate = hko[i] hkoday, hkoldate = hko[i]
#print aaday, aaldate #print aaday, aaldate
if aaday != hkoday or aaldate != hkoldate: if aaday != hkoday or aaldate != hkoldate:

View File

@@ -1,6 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' generate Chinese Lunar Calendar by astronomical algorithms. Also mark the
Chinese Traditional Holiday based on luar calendar '''
__license__ = 'BSD' __license__ = 'BSD'
__copyright__ = '2014, Chen Wei <weichen302@gmail.com>' __copyright__ = '2014, Chen Wei <weichen302@gmail.com>'
__version__ = '0.0.3' __version__ = '0.0.3'
@@ -176,6 +179,84 @@ def scan_leap(clc):
return clc return clc
def mark_lunarcal_day(clcmonth):
''' expand to whole year, mark the day of month and lunar calendar date in
Chinese'''
stdays= {} # days have solar terms
for d in clcmonth:
if d['astro'] != 'newmoon':
stdays[d['date']] = d['astro']
# expand to whole year
start = clcmonth[0]['date']
yearend = clcmonth[-1]['date'] + 1
lcdays = []
while start < yearend:
# scan the month start belongs
for d in clcmonth:
if d['date'] > start:
break
if d['astro'] == 'newmoon':
monthstart = d['date']
mname = d['month']
day = {'date': start, 'month': mname, 'jieqi': None, 'holiday': None}
day['day'] = int(start + 1 - monthstart)
if start in stdays:
day['jieqi'] = CN_SOLARTERM[stdays[start]]
if day['day'] == 1:
day['lunardate'] = CN_MON[day['month']]
else:
day['lunardate'] = CN_DAY[day['day']]
lcdays.append(day)
start += 1
return lcdays
def mark_holiday(clcdays):
''' mark Chinese Traditional Holiday
腊八节(腊月初八) 除夕(腊月的最后一天) 春节(一月一日)
元宵节(一月十五日) 寒食节(清明的前一天) 端午节(五月初五)
七夕节(七月初七) 中元节(七月十五日) 中秋节(八月十五日)
重阳节(九月九日) 下元节(十月十五日)
'''
for i in xrange(len(clcdays)):
m, d = clcdays[i]['month'], clcdays[i]['day']
if m == 12 and d == 8:
clcdays[i]['holiday'] = u'腊八'
elif m == 1 and d == 1:
clcdays[i]['holiday'] = u'春节'
clcdays[i - 1]['holiday'] = u'除夕'
elif m == 1 and d == 15:
clcdays[i]['holiday'] = u'元宵'
elif m == 5 and d == 5:
clcdays[i]['holiday'] = u'端午'
elif m == 7 and d == 7:
clcdays[i]['holiday'] = u'七夕'
elif m == 7 and d == 15:
clcdays[i]['holiday'] = u'中元'
elif m == 8 and d == 15:
clcdays[i]['holiday'] = u'中秋'
elif m == 9 and d == 9:
clcdays[i]['holiday'] = u'重阳'
elif m == 10 and d == 15:
clcdays[i]['holiday'] = u'下元'
if clcdays[i]['jieqi'] == u'清明':
clcdays[i - 1]['holiday'] = u'寒食'
return clcdays
def search_lunarcal(year): def search_lunarcal(year):
''' search JieQi and Newmoon, step 1 ''' search JieQi and Newmoon, step 1
@@ -192,61 +273,12 @@ def search_lunarcal(year):
clc = find_astro(year) clc = find_astro(year)
clcmonth = mark_lunarcal_month(clc) clcmonth = mark_lunarcal_month(clc)
ystart = clcmonth[0]['date'] clcdays = mark_lunarcal_day(clcmonth)
yend = clcmonth[-1]['date'] + 1 clcdays = mark_holiday(clcdays)
#debug
#print
#for x in clcmonth:
# print jdftime(x['date']), x['astro'], x['date']
FLAG_NEWMOON = 1
FLAG_ST = 2
output = {} output = {}
while ystart < yend: for d in clcdays:
flag = 0 output[d['date']] = d
# scan the month ystart belongs
for d in clcmonth:
if d['date'] > ystart:
break
if d['astro'] == 'newmoon':
monthstart = d['date']
mname = d['month']
# scan if the day happens to be the begining of month, or has ST, so we
# can choose the output date format accordingly. The day will be month
# name if it is the day 1 of a month; if it also has solarterm, then
# the name of solarterm will be append to month name; if it is not day
# 1 but has solarterm, then only solarterm will be displayed; if it is
# not begining of the month, or has solarterm, then only the date will
# be showed.
for d in clcmonth:
if d['date'] > ystart:
break
day = int(ystart + 1 - monthstart)
# the day we looking for is in the solarterms and newmoon table
if d['date'] == ystart:
if d['astro'] == 'newmoon':
flag |= FLAG_NEWMOON
else:
flag |= FLAG_ST
angle = d['astro']
if flag == (FLAG_NEWMOON | FLAG_ST):
label = '%s %s' % (CN_MON[mname], CN_SOLARTERM[angle])
elif flag == FLAG_NEWMOON:
label = CN_MON[mname]
elif flag == FLAG_ST:
label = '%s %s' % (CN_DAY[day], CN_SOLARTERM[angle])
else:
#print fmtjde2ut(jd,ut=False), s, nm, day
label = CN_DAY[day]
output[ystart] = label
ystart += 1
CALCACHE[year] = output # cache it for future use CALCACHE[year] = output # cache it for future use
CALCACHE['cached'].append(year) CALCACHE['cached'].append(year)
@@ -263,8 +295,8 @@ def cn_lunarcal(year):
Because there might be a leap month after this Winter Solstic, which can Because there might be a leap month after this Winter Solstic, which can
only be found by compute Calendar of next year, for example, 2033 has a only be found by compute Calendar of next year, for example, 2033 has a
leap 11, calendar for this and next year are computed and combined, then leap 11 that belongs to the next year. Calendar for this and next year are
trim to fit into this year scale. computed and combined, then trim to fit into scale of this year.
''' '''
@@ -276,27 +308,21 @@ def cn_lunarcal(year):
start = jdptime('%s-%s-%s' % (year, 1, 1), '%y-%m-%d') start = jdptime('%s-%s-%s' % (year, 1, 1), '%y-%m-%d')
end = jdptime('%s-%s-%s' % (year, 12, 31), '%y-%m-%d') end = jdptime('%s-%s-%s' % (year, 12, 31), '%y-%m-%d')
lc = [] lc = []
for jd, mname in cal0.iteritems(): for jd, day in cal0.iteritems():
day['date'] = jdftime(jd, '%y-%m-%d', ut=False)
if jd >= start and jd <= end: if jd >= start and jd <= end:
lc.append(( lc.append((jd, day))
jdftime(jd, '%y-%m-%d', ut=False),
mname))
lc.sort() lc.sort()
res = [x[1] for x in lc]
# convert to format that accepted by ical generator return res
rows = []
for x in lc:
rows.append({'date': x[0], 'lunardate': x[1],
'holiday': None, 'jieqi': None})
#sql = ('select date, lunardate, holiday, jieqi from ical '
return rows
def main(): def main():
a = cn_lunarcal(2033) a = cn_lunarcal(2033)
for x in a: for x in a:
print x['date'], x['lunardate'] print x['date'], x['lunardate'], x['jieqi'], x['holiday']
if __name__ == "__main__": if __name__ == "__main__":