"""
Use Swiss ephemeris to calculate tithi, nakshatra, etc.
"""
from __future__ import division
from math import floor, ceil
from collections import namedtuple as struct
import swisseph as swe
Date = struct('Date', ['year', 'month', 'day'])
Place = struct('Location', ['latitude', 'longitude', 'timezone'])
from_dms = lambda degs, mins, secs: degs + mins/60 + secs/3600
def to_dms(deg):
d = int(deg)
mins = (deg - d) * 60
m = int(mins)
s = int(round((mins - m) * 60))
return [d, m, s]
def unwrap_angles(angles):
"""Add 360 to those elements in the input list so that
all elements are sorted in ascending order."""
result = angles
for i in range(1, len(angles)):
if result[i] < result[i-1]: result[i] += 360
assert(result == sorted(result))
return result
def inverse_lagrange(x, y, ya):
"""Given two lists x and y, find the value of x = xa when y = ya, i.e., f(xa) = ya"""
assert(len(x) == len(y))
total = 0
for i in range(len(x)):
numer = 1
denom = 1
for j in range(len(x)):
if j != i:
numer *= (ya - y[j])
denom *= (y[i] - y[j])
total += numer * x[i] / denom
return total
gregorian_to_jd = lambda date: swe.julday(date.year, date.month, date.day, 0.0)
jd_to_gregorian = lambda jd: swe.revjul(jd, swe.GREG_CAL)
def solar_longitude(jd):
"""Solar longitude at given instant (julian day) jd"""
data = swe.calc_ut(jd, swe.SUN, flag = swe.FLG_SWIEPH)
return data[0]
def lunar_longitude(jd):
"""Lunar longitude at given instant (julian day) jd"""
data = swe.calc_ut(jd, swe.MOON, flag = swe.FLG_SWIEPH)
return data[0]
def lunar_latitude(jd):
"""Lunar latitude at given instant (julian day) jd"""
data = swe.calc_ut(jd, swe.MOON, flag = swe.FLG_SWIEPH)
return data[1]
def sunrise(jd, place):
"""Sunrise when centre of disc is at horizon for given date and place"""
lat, lon, tz = place
result = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi=swe.BIT_DISC_CENTER + swe.CALC_RISE)
rise = result[1][0]
return [rise + tz/24., to_dms((rise - jd) * 24 + tz)]
def sunset(jd, place):
"""Sunset when centre of disc is at horizon for given date and place"""
lat, lon, tz = place
result = swe.rise_trans(jd - tz/24, swe.SUN, lon, lat, rsmi=swe.BIT_DISC_CENTER + swe.CALC_SET)
setting = result[1][0]
return [setting + tz/24., to_dms((setting - jd) * 24 + tz)]
def moonrise(jd, place):
"""Moonrise when centre of disc is at horizon for given date and place"""
lat, lon, tz = place
result = swe.rise_trans(jd - tz/24, swe.MOON, lon, lat, rsmi=swe.BIT_DISC_CENTER + swe.CALC_RISE)
rise = result[1][0]
return to_dms((rise - jd) * 24 + tz)
def moonset(jd, place):
"""Moonset when centre of disc is at horizon for given date and place"""
lat, lon, tz = place
result = swe.rise_trans(jd - tz/24, swe.MOON, lon, lat, rsmi=swe.BIT_DISC_CENTER + swe.CALC_SET)
setting = result[1][0]
return to_dms((setting - jd) * 24 + tz)
def tithi(jd, place):
"""Tithi at sunrise for given date and place. Also returns tithi's end time."""
tz = place.timezone
rise = sunrise(jd, place)[0] - tz / 24
moon_phase = lunar_phase(rise)
today = ceil(moon_phase / 12)
degrees_left = today * 12 - moon_phase
offsets = [0.25, 0.5, 0.75, 1.0]
lunar_long_diff = [ (lunar_longitude(rise + t) - lunar_longitude(rise)) % 360 for t in offsets ]
solar_long_diff = [ (solar_longitude(rise + t) - solar_longitude(rise)) % 360 for t in offsets ]
relative_motion = [ moon - sun for (moon, sun) in zip(lunar_long_diff, solar_long_diff) ]
y = relative_motion
x = offsets
approx_end = inverse_lagrange(x, y, degrees_left)
ends = (rise + approx_end -jd) * 24 + tz
answer = [int(today), to_dms(ends)]
moon_phase_tmrw = lunar_phase(rise + 1)
tomorrow = ceil(moon_phase_tmrw / 12)
isSkipped = (tomorrow - today) % 30 > 1
if isSkipped:
leap_tithi = today + 1
degrees_left = leap_tithi * 12 - moon_phase
approx_end = inverse_lagrange(x, y, degrees_left)
ends = (rise + approx_end -jd) * 24 + place.timezone
answer += [int(leap_tithi), to_dms(ends)]
return answer
def nakshatra(jd, place):
"""Current nakshatra as of julian day (jd)
1 = Asvini, 2 = Bharani, ..., 27 = Revati
"""
swe.set_sid_mode(swe.SIDM_LAHIRI)
lat, lon, tz = place
rise = sunrise(jd, place)[0] - tz / 24.
offsets = [0.0, 0.25, 0.5, 0.75, 1.0]
longitudes = [ (lunar_longitude(rise + t) - swe.get_ayanamsa_ut(rise)) % 360 for t in offsets]
nak = ceil(longitudes[0] * 27 / 360)
y = unwrap_angles(longitudes)
x = offsets
approx_end = inverse_lagrange(x, y, nak * 360 / 27)
ends = (rise - jd + approx_end) * 24 + tz
answer = [int(nak), to_dms(ends)]
nak_tmrw = ceil(longitudes[-1] * 27 / 360)
isSkipped = (nak_tmrw - nak) % 27 > 1
if isSkipped:
leap_nak = nak + 1
approx_end = inverse_lagrange(offsets, longitudes, leap_nak * 360 / 27)
ends = (rise - jd + approx_end) * 24 + tz
answer += [int(leap_nak), to_dms(ends)]
return answer
def yoga(jd, place):
"""Yoga at given jd and place.
1 = Vishkambha, 2 = Priti, ..., 27 = Vaidhrti
"""
swe.set_sid_mode(swe.SIDM_LAHIRI)
lat, lon, tz = place
rise = sunrise(jd, place)[0] - tz / 24.
lunar_long = (lunar_longitude(rise) - swe.get_ayanamsa_ut(rise)) % 360
solar_long = (solar_longitude(rise) - swe.get_ayanamsa_ut(rise)) % 360
total = (lunar_long + solar_long) % 360
yog = ceil(total * 27 / 360)
degrees_left = yog * (360 / 27) - total
offsets = [0.25, 0.5, 0.75, 1.0]
lunar_long_diff = [ (lunar_longitude(rise + t) - lunar_longitude(rise)) % 360 for t in offsets ]
solar_long_diff = [ (solar_longitude(rise + t) - solar_longitude(rise)) % 360 for t in offsets ]
total_motion = [ moon + sun for (moon, sun) in zip(lunar_long_diff, solar_long_diff) ]
y = total_motion
x = offsets
approx_end = inverse_lagrange(x, y, degrees_left)
ends = (rise + approx_end - jd) * 24 + tz
answer = [int(yog), to_dms(ends)]
lunar_long_tmrw = (lunar_longitude(rise + 1) - swe.get_ayanamsa_ut(rise + 1)) % 360
solar_long_tmrw = (solar_longitude(rise + 1) - swe.get_ayanamsa_ut(rise + 1)) % 360
total_tmrw = (lunar_long_tmrw + solar_long_tmrw) % 360
tomorrow = ceil(total_tmrw * 27 / 360)
isSkipped = (tomorrow - yog) % 27 > 1
if isSkipped:
leap_yog = yog + 1
degrees_left = leap_yog * (360 / 27) - total
approx_end = inverse_lagrange(x, y, degrees_left)
ends = (rise + approx_end - jd) * 24 + tz
answer += [int(leap_yog), to_dms(ends)]
return answer
def karana(jd, place):
"""Returns the karana and their ending times. (from 1 to 60)"""
rise = sunrise(jd, place)[0]
solar_long = solar_longitude(rise)
lunar_long = lunar_longitude(rise)
moon_phase = (lunar_long - solar_long) % 360
today = ceil(moon_phase / 6)
degrees_left = today * 6 - moon_phase
return [int(today)]
def vaara(jd):
"""Weekday for given Julian day. 0 = Sunday, 1 = Monday,..., 6 = Saturday"""
return int(ceil(jd + 1) % 7)
def masa(jd, place):
"""Returns lunar month and if it is adhika or not.
1 = Chaitra, 2 = Vaisakha, ..., 12 = Phalguna"""
ti = tithi(jd, place)[0]
critical = sunrise(jd, place)[0]
last_new_moon = new_moon(critical, ti, -1)
next_new_moon = new_moon(critical, ti, +1)
this_solar_month = raasi(last_new_moon)
next_solar_month = raasi(next_new_moon)
is_leap_month = (this_solar_month == next_solar_month)
maasa = this_solar_month + 1
if maasa > 12: maasa = (maasa % 12)
return [int(maasa), is_leap_month]
ahargana = lambda jd: jd - 588465.5
def elapsed_year(jd, maasa_num):
sidereal_year = 365.25636
ahar = ahargana(jd)
kali = int((ahar + (4 - maasa_num) * 30) / sidereal_year)
saka = kali - 3179
vikrama = saka + 135
return kali, saka
def new_moon(jd, tithi_, opt = -1):
"""Returns JDN, where
opt = -1: JDN < jd such that lunar_phase(JDN) = 360 degrees
opt = +1: JDN >= jd such that lunar_phase(JDN) = 360 degrees
"""
if opt == -1: start = jd - tithi_
if opt == +1: start = jd + (30 - tithi_)
x = [ -2 + offset/4 for offset in range(17) ]
y = [lunar_phase(start + i) for i in x]
y = unwrap_angles(y)
y0 = inverse_lagrange(x, y, 360)
return start + y0
def raasi(jd):
"""Zodiac of given jd. 1 = Mesha, ... 12 = Meena"""
swe.set_sid_mode(swe.SIDM_LAHIRI)
s = solar_longitude(jd)
solar_nirayana = (solar_longitude(jd) - swe.get_ayanamsa_ut(jd)) % 360
return ceil(solar_nirayana / 30.)
def lunar_phase(jd):
solar_long = solar_longitude(jd)
lunar_long = lunar_longitude(jd)
moon_phase = (lunar_long - solar_long) % 360
return moon_phase
def samvatsara(jd, maasa_num):
kali = elapsed_year(jd, maasa_num)[0]
if kali >= 4009: kali = (kali - 14) % 60
samvat = (kali + 27 + int((kali * 211 - 108) / 18000)) % 60
return samvat
def ritu(masa_num):
"""0 = Vasanta,...,5 = Shishira"""
return (masa_num - 1) // 2
def day_duration(jd, place):
srise = sunrise(jd, place)[0]
sset = sunset(jd, place)[0]
diff = (sset - srise) * 24
return [diff, to_dms(diff)]
def all_tests():
print(moonrise(date2, bangalore))
print(moonset(date2, bangalore))
print(sunrise(date2, bangalore)[1])
print(sunset(date2, bangalore)[1])
assert(vaara(date2) == 5)
print(sunrise(date4, shillong)[1])
assert(karana(date2, helsinki) == [14])
return
def tithi_tests():
feb3 = gregorian_to_jd(Date(2013, 2, 3))
apr24 = gregorian_to_jd(Date(2010, 4, 24))
apr19 = gregorian_to_jd(Date(2013, 4, 19))
apr20 = gregorian_to_jd(Date(2013, 4, 20))
apr21 = gregorian_to_jd(Date(2013, 4, 21))
print(tithi(date1, bangalore))
print(tithi(date2, bangalore))
print(tithi(date3, bangalore))
print(tithi(date2, helsinki))
print(tithi(apr24, bangalore))
print(tithi(feb3, bangalore))
print(tithi(apr19, helsinki))
print(tithi(apr20, helsinki))
print(tithi(apr21, helsinki))
return
def nakshatra_tests():
print(nakshatra(date1, bangalore))
print(nakshatra(date2, bangalore))
print(nakshatra(date3, bangalore))
print(nakshatra(date4, shillong))
return
def yoga_tests():
may22 = gregorian_to_jd(Date(2013, 5, 22))
print(yoga(date3, bangalore))
print(yoga(date2, bangalore))
print(yoga(may22, helsinki))
def masa_tests():
jd = gregorian_to_jd(Date(2013, 2, 10))
aug17 = gregorian_to_jd(Date(2012, 8, 17))
aug18 = gregorian_to_jd(Date(2012, 8, 18))
sep19 = gregorian_to_jd(Date(2012, 9, 18))
may20 = gregorian_to_jd(Date(2012, 5, 20))
may21 = gregorian_to_jd(Date(2012, 5, 21))
print(masa(jd, bangalore))
print(masa(aug17, bangalore))
print(masa(aug18, bangalore))
print(masa(sep19, bangalore))
print(masa(may20, helsinki))
print(masa(may21, helsinki))
if __name__ == "__main__":
bangalore = Place(12.972, 77.594, +5.5)
shillong = Place(25.569, 91.883, +5.5)
helsinki = Place(60.17, 24.935, +2.0)
date1 = gregorian_to_jd(Date(2009, 7, 15))
date2 = gregorian_to_jd(Date(2013, 1, 18))
date3 = gregorian_to_jd(Date(1985, 6, 9))
date4 = gregorian_to_jd(Date(2009, 6, 21))
apr_8 = gregorian_to_jd(Date(2010, 4, 8))
apr_10 = gregorian_to_jd(Date(2010, 4, 10))
masa_tests()