[Pyrex] Bug in pyrex: difference in docstring contents prevents diagnosis of
undeclared name and produces bad C code
Parzival Herzog
parzp at shaw.ca
Fri Oct 15 19:40:37 CEST 2004
I was using pyrex
[parz at lobsang src]$ pyrexc --version
Pyrex version 0.9.3
to wrap a library in my first serious attempt to use pyrex, and I ran into
what seems to be a bug. I tried to compile my extension library, and I got
this result:
------------------------
[parz at lobsang src]$ pyrex pysweph.pyx
pysweph.c:116: error: redefinition of `struct __pyx_obj_7pysweph_body'
pysweph.c:134: error: redefinition of `struct __pyx_obj_7pysweph_houses'
pysweph.c:157: error: redefinition of `struct
__pyx_obj_7pysweph__houseiterator'
pysweph.c:163: error: redefinition of `__pyx_ptype_7pysweph_body'
pysweph.c:91: error: `__pyx_ptype_7pysweph_body' previously defined here
pysweph.c:164: error: redefinition of `__pyx_ptype_7pysweph_houses'
pysweph.c:92: error: `__pyx_ptype_7pysweph_houses' previously defined here
pysweph.c:165: error: redefinition of `__pyx_ptype_7pysweph__houseiterator'
pysweph.c:93: error: `__pyx_ptype_7pysweph__houseiterator' previously defined
here
--------------------------
Each extension class defined in pysweph.pyx generates these re-definition
errors.
"pyrex" is my script which bundles the pyrexc command with a gcc compilation
command:
------------------
[parz at lobsang src]$ cat ~/bin/pyrex
#!/bin/sh
# pyrex file
F=`basename $1 .pyx`
pyrexc $1 && gcc -shared -fpic -I/usr/include/python2.3 $F.c -o $F.so
-----------------
So, not seeing anything wrong, I tried to drastically cut down the code, and
then slowly re-built it, each time re-compiling, to see when the gcc error
messages would re-appear. I quickly found that a ctypedef name defined in an
imported module, pysweph.pyx, was not being qualified in my main module,
pysweph.pyx.
pysweph.pyx:
cimport pysweph as se
...
cdef int32 iflag
pysweph.pxd:
ctypedef int int32
...
Pyrex printed the error message:
----------------
[parz at lobsang src]$ pyrex pyswephX.pyx
/home/parz/x-src/swe-1.66.0/src/pyswephX.pyx:45:24: Syntax error in C variable
declaration
----------------
("pyswephX.pyx" is my cut-down version of the pysweph.pyx module.)
So, I replaced the references to "int32" with "se.int32", and these
diagnostics went away.
pyswephX.pyx:
cimport pysweph as se
...
cdef se.int32 iflag
Proceeding in this way, I re-built the entire module, and there were no more
diagnostics about re-declared identifiers coming out of gcc: pyswephX.pyx
compiled cleanly. When I used diff to compare the pysweph.pyx with
pyswephX.pyx, I noticed that some comment and string text had been altered.
This text contained non-ascii (codes > 128) characters, and it seems that
Kate, or the clipboard altered the encoding in the process of copying and
pasting that I had used to re-build pyswephX.pyx.
So to be sure that the problem was related only to the unqualifed references
to "int32", I replaced all "se.int32" in pyswephX.pyx with "int32" (same as
they were in my original file pysweph.pyx). I this in "pyswephY.pyx".
Compiling pyswephY.pyx, I got diagnostics for the unqualified "int32"
references:
-----------------
[parz at lobsang src]$ pyrex pyswephY.pyx
/home/parz/x-src/swe-1.66.0/src/pyswephY.pyx:102:15: Syntax error in C
variable declaration
-----------------
So now pysweph.pyx and pyswephY.pyx differ only in the content of two
docstrings:
--- pysweph.pyx 2004-10-15 02:31:12.000000000 -0500
+++ pyswephY.pyx 2004-10-15 11:46:34.000000000 -0500
@@ -108,7 +108,7 @@
self.iflag = 0
def __init__(self, bodyname):
- ''' bodyname ═══name of planet or body
+ ''' bodyname name of planet or body
'''
self.bodyname = ' '.join(map(str.capitalize, bodyname.split()))
ipl = Body[self.bodyname]
@@ -267,7 +267,7 @@
def __init__(self, double geolon, double geolat, systemname="Placidus"):
''' geolon, geolat location of observer
- systemname ═══ name of house system used
+ systemname name of house system used
'''
self.set_system(systemname)
self.selocation(geolon, geolat)
yet pysweph.pyx is erroneously accepted by pyrexc, and the generated code
produces gcc diagnostics:
------------------
[parz at lobsang src]$ pyrex pysweph.pyx
pysweph.c:116: error: redefinition of `struct __pyx_obj_7pysweph_body'
pysweph.c:134: error: redefinition of `struct __pyx_obj_7pysweph_houses'
pysweph.c:157: error: redefinition of `struct
__pyx_obj_7pysweph__houseiterator'
pysweph.c:163: error: redefinition of `__pyx_ptype_7pysweph_body'
pysweph.c:91: error: `__pyx_ptype_7pysweph_body' previously defined here
pysweph.c:164: error: redefinition of `__pyx_ptype_7pysweph_houses'
pysweph.c:92: error: `__pyx_ptype_7pysweph_houses' previously defined here
pysweph.c:165: error: redefinition of `__pyx_ptype_7pysweph__houseiterator'
pysweph.c:93: error: `__pyx_ptype_7pysweph__houseiterator' previously defined
here
----------------
while pyswephY.pyx is correctly rejected by pyrexc:
-------------------
[parz at lobsang src]$ pyrex pyswephY.pyx
/home/parz/x-src/swe-1.66.0/src/pyswephY.pyx:102:15: Syntax error in C
variable declaration
-------------------
--
Parzival Herzog
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pysweph.pxd
Type: text/x-csrc
Size: 12263 bytes
Desc: not available
Url : http://lists.copyleft.no/pipermail/pyrex/attachments/20041015/da393882/pysweph.bin
-------------- next part --------------
cimport pysweph as se
#-----------------------------------------------------------------------
# Python interface to swiss ephemeris
#-----------------------------------------------------------------------
Body = {
"Sun": se.SE_SUN,
"Moon": se.SE_MOON,
"Mercury": se.SE_MERCURY,
"Venus": se.SE_VENUS,
"Mars": se.SE_MARS,
"Jupiter": se.SE_JUPITER,
"Saturn": se.SE_SATURN,
"Uranus": se.SE_URANUS,
"Neptune": se.SE_NEPTUNE,
"Pluto": se.SE_PLUTO,
"Mean Node": se.SE_MEAN_NODE,
"True Node": se.SE_TRUE_NODE,
"Earth": se.SE_EARTH,
"Chiron": se.SE_CHIRON,
"Pholus": se.SE_PHOLUS,
"Ceres": se.SE_CERES,
"Pallas": se.SE_PALLAS,
"Juno": se.SE_JUNO,
"Vesta": se.SE_VESTA,
# Hamburger or Uranian "planets"
"Cupido": se.SE_CUPIDO,
"Hades": se.SE_HADES,
"Zeus": se.SE_ZEUS,
"Kronos": se.SE_KRONOS,
"Apollon": se.SE_APOLLON,
"Admetos": se.SE_ADMETOS,
"Vulkanus": se.SE_VULKANUS,
"Poseidon": se.SE_POSEIDON,
# other fictitious bodies
"Isis": se.SE_ISIS,
"Nibiru": se.SE_NIBIRU,
"Harrington": se.SE_HARRINGTON,
"Vulcan": se.SE_VULCAN,
"White Moon": se.SE_WHITE_MOON,
"Proserpina": se.SE_PROSERPINA,
"Waldemath": se.SE_WALDEMATH,
"Ascendant": se.SE_ASC,
"MC": se.SE_MC,
"Armc": se.SE_ARMC,
"Vertex": se.SE_VERTEX,
}
Ayanamasas = {
"Fagan-Bradley": se.SE_SIDM_FAGAN_BRADLEY,
"Lahiri": se.SE_SIDM_LAHIRI,
"Deluce": se.SE_SIDM_DELUCE,
"Raman": se.SE_SIDM_RAMAN,
"Ushashashi": se.SE_SIDM_USHASHASHI,
"Krishnamurti": se.SE_SIDM_KRISHNAMURTI,
"Djwhal Khul": se.SE_SIDM_DJWHAL_KHUL,
"Yukteshwar": se.SE_SIDM_YUKTESHWAR,
"Jn Bhasin": se.SE_SIDM_JN_BHASIN,
"Babylonian Kugler 1": se.SE_SIDM_BABYL_KUGLER1,
"Babylonian Kugler 2": se.SE_SIDM_BABYL_KUGLER2,
"Babylonian Kugler 3": se.SE_SIDM_BABYL_KUGLER3,
"Babylonian Huber": se.SE_SIDM_BABYL_HUBER,
"Babylonian Eta Piscium": se.SE_SIDM_BABYL_ETPSC,
"Aldebaran @ 15� Tau.": se.SE_SIDM_ALDEBARAN_15TAU,
"Hipparchos": se.SE_SIDM_HIPPARCHOS,
"Sassanian": se.SE_SIDM_SASSANIAN,
"Galactic Center @ 0� Sag.": se.SE_SIDM_GALCENT_0SAG,
"J2000": se.SE_SIDM_J2000,
"J1900": se.SE_SIDM_J1900,
"B1950": se.SE_SIDM_B1950,
}
class error(Exception):
pass
cdef double NoJD
NoJD = 1e200/1e-200
def set_ephe_path(path):
''' set directory path of ephemeris files '''
if path is None:
import os
path = os.path.dirname(__file__)
se.swe_set_ephe_path(path)
def close():
''' close Swiss Ephemeris '''
se.swe_close()
cdef class body:
''' An astronomical body '''
cdef readonly double tjd
cdef readonly double longitude, latitude, distance, long_speed, lat_speed, dist_speed
cdef readonly double geolon, geolat, geoalt
cdef readonly ayanamsa
cdef int32 iflag
def __new__(self, *args, **kwargs):
self.longitude = self.latitude = self.distance = self.long_speed = self.lat_speed = self.dist_speed = 0.0
self.geolon = self.geolat = self.geoalt = 0.0
self.tjd = NoJD
self.iflag = 0
def __init__(self, bodyname):
''' bodyname ���name of planet or body
'''
self.bodyname = ' '.join(map(str.capitalize, bodyname.split()))
ipl = Body[self.bodyname]
self.set_swiss()
self.set_apparent()
self.set_geocentric()
def set_swiss(self):
''' Use swiss ephemeris. '''
self.iflag = (self.iflag | se.SEFLG_SWIEPH) & ~(se.SEFLG_MOSEPH|se.SEFLG_JPLEPH)
self.calc(self.tjd)
def set_moshier(self):
''' Use Moshier (analytical) ephemeris. '''
self.iflag = (self.iflag | se.SEFLG_MOSEPH) & ~(se.SEFLG_SWIEPH|se.SEFLG_JPLEPH)
self.calc(self.tjd)
def set_apparent(self, int on=True):
''' Calculate apparent (on == True) or true (on == False) positions. '''
if on:
self.iflag = self.iflag | se.SEFLG_TRUEPOS
else:
self.iflag = self.iflag & ~se.SEFLG_TRUEPOS
self.calc(self.tjd)
def set_topo(self, double geolon, double geolat, double geoalt):
''' Calculate topocentric positions at geographic position of observer. '''
self.geolon = geolon
self.geolat = geolat
self.geoalt = geoalt
self.iflag = self.iflag | se.SEFLG_TOPOCTR
self.calc(self.tjd)
def set_geocentric(self):
''' Calculate geocentric positions. '''
self.iflag = self.iflag & ~se.SEFLG_TOPOCTR
self.calc(self.tjd)
def set_sid_mode(sidmode):
''' Calculate in named siderial mode '''
cdef int sid_mode
xx = Ayanamasas[sidmode] # to check
self.sid_mode = sidmode
self.iflag = self.iflag | se.SEFLG_SIDEREAL
self.calc(self.tjd)
def set_tropical_mode(mode):
''' Calculate in tropical mode. '''
self.iflag = self.iflag & ~se.SEFLG_SIDEREAL
self.calc(self.tjd)
def calc(self, double tjd):
''' Calculate position of body at time tjd.
tjd Julian day number, Universal Time
'''
if tjd == NoJD:
return
#cdef double xx[6]
cdef int32 res
cdef char serr[256]
# These mode settings need to be done at each calculation because
# the swiss ephemeris mode is global, while the modes of this
# body (self) may differ from the global setting.
if self.iflag & se.SEFLG_TOPOCTR:
se.swe_set_topo(self.geolon, self.geolat, self.geoalt)
if self.iflag & se.SEFLG_SIDERIAL:
se.swe_set_sid_mode(Ayanamasas[self.sid_mode], 0.0, 0.0)
self.ayanamsa = se.swe_get_ayanamsa_ut(tjd)
res = se.swe_calc_ut(tjd, ipl, iflag, &self.longitude, serr)
if res < 0:
raise error, serr
if res != self.iflag and \
(res ^ self.iflag) & ~(se.SEFLG_MOSEPH|se.SEFLG_MOSEPH|se.SEFLG_JPLEPH):
raise error, "cannot respect flag(s) %s in %s" % (hex(res ^ self.iflag), hex(self.iflag))
self.tjd = tjd
'''self.longitude = xx[0]
self.latitude = xx[1]
self.distance = xx[2]
self.long_speed = xx[3]
self.lat_speed = xx[4]
self.dist_speed = xx[5]'''
# fixed stars
#int32 se.swe_fixstar_ut(char *star, double tjd_ut, int32 iflag, double *xx, char *serr)
# exports from se.swehouse.c
HouseSystems = {
"Placidus": c'P',
"Koch": 'K',
"Porphyrius": 'O',
"Regiomontanus": 'R',
"Campanus": 'C',
"Equal": 'E',
"Vehlow Equal": 'V',
"Axial Rotation": 'X',
"Azimuthal": 'H',
"Topocentric": 'T',
# "Alcabitus": 'B',
# "Gauquelin Sectors": 'G',
"Morinus": 'M',
"Quadrant": '4',
"Bi-quadrant": '8',
}
cdef class houses:
''' House cusps, ascendant, MC, vertex '''
cdef readonly tjd
cdef double cusp[13]
cdef readonly double asc, mc, armc, vertex, equ_asc, co_asc1, co_asc2, polar_asc
cdef double _9, _10
cdef int ncusps
cdef readonly int32 iflag
cdef readonly double geolon, geolat
cdef int syscode
def __new__(self, *args, **kwargs):
self.ncusps = 0
self.geolon = self.geolat = 0.0
self.iflag = 0
self.tjd = NoJD
self.asc, self.mc, self.armc, self.vertex, self.equ_asc, self.co_asc1, self.co_asc2, self.polar_asc = NoJD
def __getitem__(self, int h):
if 0 <= h < ncusps:
return cusps[h+1]
else:
raise IndexError
def __getslice__(self, int h0, int h1):
if h0 > h1:
return []
if h0 < 0:
h0 = 0
if h1 > ncusps:
h1 = ncusps
res = [0.0]*(h1-h0)
for h from h0 <= h < h1:
res[h] = cusps[h+1]
return res
def __len__(self):
return ncusps
def __iter__(self):
return _houseiterator(self)
def __init__(self, double geolon, double geolat, systemname="Placidus"):
''' geolon, geolat location of observer
systemname ��� name of house system used
'''
self.set_system(systemname)
self.selocation(geolon, geolat)
def set_system(self, systemname):
systemname = ' '.join(map(str.capitalize, systemname.split()))
self.systemname = systemname
self.syscode = HouseSystem[self.systemname]
self.ncusps = 0
self.calc()
def set_location(self, double geolon, double geolat):
''' Calculate topocentric positions at geographic position of observer. '''
self.geolon = geolon
self.geolat = geolat
self.ncusps = 0
self.calc()
def set_sid_mode(self, sidmode):
''' Calculate using named siderial mode '''
cdef int sid_mode
xx = Ayanamasas[sidmode] # to check
self.sid_mode = sidmode
self.iflag = self.iflag | se.SEFLG_SIDEREAL
self.ncusps = 0
self.calc()
def set_tropical_mode(self):
''' Calculate in tropical mode. '''
self.iflag = self.iflag & ~se.SEFLG_SIDEREAL
self.ncusps = 0
self.calc()
def calc(self, double tjd=NoJD, armc=NoJD):
''' Calculate house cusp positions at UTC time tjd.
tjd Julian day number, Universal Time
'''
#cdef double xx[6]
cdef int32 res
if tjd == NoJD and armc == NoJD:
if self.tjd != NoJD:
tjd = self.tjd
elif self.armc != NoJD:
armc = self.armc
else:
return
if tjd != NoJD and armc != NoJD:
raise ValueError, "cannot specify both tjd and armc"
# These mode settings need to be done at each calculation because
# the swiss ephemeris mode is global, while the modes of this
# body (self) may differ from the global setting.
if self.iflag & se.SEFLG_SIDERIAL:
se.swe_set_sid_mode(Ayanamasas[self.sid_mode], 0.0, 0.0)
cdef char hsys
if self.syscode == c'4' or self.syscode == c'8':
hsys = c'P'
else:
hsys = self.syscode
if armc != NoJD:
res = se.swe_houses_armc(armc, self.geolat, eps, hsys,
self.cusp, &self.asc)
else:
res = se.swe_houses_ex(tjd, self.iflag, self.geolat, self.geolon, hsys,
self.cusp, &self.asc)
self.tjd = tjd
if res != se.OK:
if self.systemname != "Equal":
raise error, "cannot calculate %s houses: using Equal house system" % self.systemname
self.set_system("Equal")
else:
raise error, "cannot calculate %s houses" % self.systemname
if self.syscode == c'4' or self.syscode == c'G':
self.ncusps = 4
if self.syscode == c'4':
cusp[2] = cusp[4]
cusp[3] = cusp[7]
cusp[4] = cusp[10]
elif self.syscode == c'8':
self.ncusps = 8
cusp[1] = cusp[1]
cusp[2] = deg_midpoint(cusp[1], cusp[4])
cusp[3] = cusp[4]
cusp[4] = deg_midpoint(cusp[4], cusp[7])
cusp[5] = cusp[7]
cusp[6] = deg_midpoint(cusp[7], cusp[10])
cusp[7] = cusp[10]
cusp[8] = deg_midpoint(cusp[10], cusp[1])
else:
self.ncusps = 12
cdef class _houseiterator:
cdef double *h, *hlast
def __new__(self, houses Houses):
self.h = Houses.cusp + 1
self.hlast = self.h + Houses.ncusps
def __next__(self):
cdef double res
if h >= hlast:
raise StopIteration
res = h[0]
h = h + 1
return res
#double se.swe_house_pos(
# double armc, double geolat, double eps, int hsys, double *xpin, char *serr)
# exports from se.swedate.c
def julday(int y, int m, int d, double utime, int gregorian=True):
''' julian day from
y, m, d, year, month, day
utime, universal time in hours (decimal)
gregorian True = gregorian, False = julian calendar
'''
cdef double tjd
cdef int res
res = se.swe_date_conversion(y, m, d, utime, "jg"[gregorian], &tjd)
if res != se.OK:
raise ValueError, "illegal date"
return tjd
def revjul (double jd, int gregorian=True):
''' (year, month, day, ut) from julian day jd '''
cdef int jyear, jmon, jday
cdef double jut
se.swe_revjul (jd, gregorian, &jyear, &jmon, &jday, &jut)
return (jyear, jmon, jday, jut)
# exports from se.swephlib.c
def delta_t(double tjd):
''' delta t = Universal Time - Terrestrial Dynamical Time '''
return se.swe_deltat(tjd)
def equation_of_time(double lapt):
''' equation of time == Local Apparent Time - Local Mean Time '''
cdef double e
cdef int res
cdef char serr[256]
res = se.swe_time_equ(lapt, &e, serr)
if res != se.OK:
raise error, serr
return e
def siderial_time(double tjd_ut):
return se.swe_sidtime(tjd_ut)
def degnorm(double x):
''' x mod 360 '''
return se.swe_degnorm(x)
def radnorm(double x):
''' x mod (2*pi) '''
return se.swe_radnorm(x)
def rad_midpoint(double x1, double x0):
''' midpoint in radians of the smaller of the two intervals between x1 and x0 '''
return se.swe_rad_midp(x1, x0)
def deg_midpoint(double x1, double x0):
''' midpoint in degrees of the smaller of the two intervals between x1 and x0 '''
return se.swe_deg_midp(x1, x0)
def round2deg(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
# in effect, truncate to degree
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn
def round2deg_min(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign, integer minutes)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_MIN
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin
def round2deg_min_sec(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign, integer minutes, integer seconds)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'59"
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'59"
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_MIN
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin, isec
def deg2deg_min_fsec(double ddeg):
''' split ddeg to (integer degrees*sign, integer minutes, floating seconds)
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = 0
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin, isec + dsecfr
ZodiacNames = ("Aries","Taurus","Gemini","Cancer","Leo", "Virgo",
"Libra", "Scorpio", "Saggitarius", "Capricorn", "Aquarius", "Pisces")
def round2deg_sign(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, zodiac sign (0-11))
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�
keep is None -> unrestricted rounding (mod 360)
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
# in effect, truncate to degree
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12]
def round2deg_sign_min(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin
def round2deg_sign_min_sec(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes, integer seconds)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'59"
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'59"
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin, isec
def deg2deg_sign_min_fsec(double ddeg, names=ZodiacNames):
''' split ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes, floating seconds)
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = SE_SPLIT_DEG_ZODIACAL
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin, isec + dsecfr
-------------- next part --------------
cimport pysweph as se
#-----------------------------------------------------------------------
# Python interface to swiss ephemeris
#-----------------------------------------------------------------------
Body = {
"Sun": se.SE_SUN,
"Moon": se.SE_MOON,
"Mercury": se.SE_MERCURY,
"Venus": se.SE_VENUS,
"Mars": se.SE_MARS,
"Jupiter": se.SE_JUPITER,
"Saturn": se.SE_SATURN,
"Uranus": se.SE_URANUS,
"Neptune": se.SE_NEPTUNE,
"Pluto": se.SE_PLUTO,
"Mean Node": se.SE_MEAN_NODE,
"True Node": se.SE_TRUE_NODE,
"Earth": se.SE_EARTH,
"Chiron": se.SE_CHIRON,
"Pholus": se.SE_PHOLUS,
"Ceres": se.SE_CERES,
"Pallas": se.SE_PALLAS,
"Juno": se.SE_JUNO,
"Vesta": se.SE_VESTA,
# Hamburger or Uranian "planets"
"Cupido": se.SE_CUPIDO,
"Hades": se.SE_HADES,
"Zeus": se.SE_ZEUS,
"Kronos": se.SE_KRONOS,
"Apollon": se.SE_APOLLON,
"Admetos": se.SE_ADMETOS,
"Vulkanus": se.SE_VULKANUS,
"Poseidon": se.SE_POSEIDON,
# other fictitious bodies
"Isis": se.SE_ISIS,
"Nibiru": se.SE_NIBIRU,
"Harrington": se.SE_HARRINGTON,
"Vulcan": se.SE_VULCAN,
"White Moon": se.SE_WHITE_MOON,
"Proserpina": se.SE_PROSERPINA,
"Waldemath": se.SE_WALDEMATH,
"Ascendant": se.SE_ASC,
"MC": se.SE_MC,
"Armc": se.SE_ARMC,
"Vertex": se.SE_VERTEX,
}
Ayanamasas = {
"Fagan-Bradley": se.SE_SIDM_FAGAN_BRADLEY,
"Lahiri": se.SE_SIDM_LAHIRI,
"Deluce": se.SE_SIDM_DELUCE,
"Raman": se.SE_SIDM_RAMAN,
"Ushashashi": se.SE_SIDM_USHASHASHI,
"Krishnamurti": se.SE_SIDM_KRISHNAMURTI,
"Djwhal Khul": se.SE_SIDM_DJWHAL_KHUL,
"Yukteshwar": se.SE_SIDM_YUKTESHWAR,
"Jn Bhasin": se.SE_SIDM_JN_BHASIN,
"Babylonian Kugler 1": se.SE_SIDM_BABYL_KUGLER1,
"Babylonian Kugler 2": se.SE_SIDM_BABYL_KUGLER2,
"Babylonian Kugler 3": se.SE_SIDM_BABYL_KUGLER3,
"Babylonian Huber": se.SE_SIDM_BABYL_HUBER,
"Babylonian Eta Piscium": se.SE_SIDM_BABYL_ETPSC,
"Aldebaran @ 15� Tau.": se.SE_SIDM_ALDEBARAN_15TAU,
"Hipparchos": se.SE_SIDM_HIPPARCHOS,
"Sassanian": se.SE_SIDM_SASSANIAN,
"Galactic Center @ 0� Sag.": se.SE_SIDM_GALCENT_0SAG,
"J2000": se.SE_SIDM_J2000,
"J1900": se.SE_SIDM_J1900,
"B1950": se.SE_SIDM_B1950,
}
class error(Exception):
pass
cdef double NoJD
NoJD = 1e200/1e-200
def set_ephe_path(path):
''' set directory path of ephemeris files '''
if path is None:
import os
path = os.path.dirname(__file__)
se.swe_set_ephe_path(path)
def close():
''' close Swiss Ephemeris '''
se.swe_close()
cdef class body:
''' An astronomical body '''
cdef readonly double tjd
cdef readonly double longitude, latitude, distance, long_speed, lat_speed, dist_speed
cdef readonly double geolon, geolat, geoalt
cdef readonly ayanamsa
cdef se.int32 iflag
def __new__(self, *args, **kwargs):
self.longitude = self.latitude = self.distance = self.long_speed = self.lat_speed = self.dist_speed = 0.0
self.geolon = self.geolat = self.geoalt = 0.0
self.tjd = NoJD
self.iflag = 0
def __init__(self, bodyname):
''' bodyname ���name of planet or body
'''
self.bodyname = ' '.join(map(str.capitalize, bodyname.split()))
ipl = Body[self.bodyname]
self.set_swiss()
self.set_apparent()
self.set_geocentric()
def set_swiss(self):
''' Use swiss ephemeris. '''
self.iflag = (self.iflag | se.SEFLG_SWIEPH) & ~(se.SEFLG_MOSEPH|se.SEFLG_JPLEPH)
self.calc(self.tjd)
def set_moshier(self):
''' Use Moshier (analytical) ephemeris. '''
self.iflag = (self.iflag | se.SEFLG_MOSEPH) & ~(se.SEFLG_SWIEPH|se.SEFLG_JPLEPH)
self.calc(self.tjd)
def set_apparent(self, int on=True):
''' Calculate apparent (on == True) or true (on == False) positions. '''
if on:
self.iflag = self.iflag | se.SEFLG_TRUEPOS
else:
self.iflag = self.iflag & ~se.SEFLG_TRUEPOS
self.calc(self.tjd)
def set_topo(self, double geolon, double geolat, double geoalt):
''' Calculate topocentric positions at geographic position of observer. '''
self.geolon = geolon
self.geolat = geolat
self.geoalt = geoalt
self.iflag = self.iflag | se.SEFLG_TOPOCTR
self.calc(self.tjd)
def set_geocentric(self):
''' Calculate geocentric positions. '''
self.iflag = self.iflag & ~se.SEFLG_TOPOCTR
self.calc(self.tjd)
def set_sid_mode(sidmode):
''' Calculate in named siderial mode '''
cdef int sid_mode
xx = Ayanamasas[sidmode] # to check
self.sid_mode = sidmode
self.iflag = self.iflag | se.SEFLG_SIDEREAL
self.calc(self.tjd)
def set_tropical_mode(mode):
''' Calculate in tropical mode. '''
self.iflag = self.iflag & ~se.SEFLG_SIDEREAL
self.calc(self.tjd)
def calc(self, double tjd):
''' Calculate position of body at time tjd.
tjd Julian day number, Universal Time
'''
if tjd == NoJD:
return
#cdef double xx[6]
cdef se.int32 res
cdef char serr[256]
# These mode settings need to be done at each calculation because
# the swiss ephemeris mode is global, while the modes of this
# body (self) may differ from the global setting.
if self.iflag & se.SEFLG_TOPOCTR:
se.swe_set_topo(self.geolon, self.geolat, self.geoalt)
if self.iflag & se.SEFLG_SIDERIAL:
se.swe_set_sid_mode(Ayanamasas[self.sid_mode], 0.0, 0.0)
self.ayanamsa = se.swe_get_ayanamsa_ut(tjd)
res = se.swe_calc_ut(tjd, ipl, iflag, &self.longitude, serr)
if res < 0:
raise error, serr
if res != self.iflag and \
(res ^ self.iflag) & ~(se.SEFLG_MOSEPH|se.SEFLG_MOSEPH|se.SEFLG_JPLEPH):
raise error, "cannot respect flag(s) %s in %s" % (hex(res ^ self.iflag), hex(self.iflag))
self.tjd = tjd
'''self.longitude = xx[0]
self.latitude = xx[1]
self.distance = xx[2]
self.long_speed = xx[3]
self.lat_speed = xx[4]
self.dist_speed = xx[5]'''
# fixed stars
#se.int32 se.swe_fixstar_ut(char *star, double tjd_ut, se.int32 iflag, double *xx, char *serr)
# exports from se.swehouse.c
HouseSystems = {
"Placidus": c'P',
"Koch": 'K',
"Porphyrius": 'O',
"Regiomontanus": 'R',
"Campanus": 'C',
"Equal": 'E',
"Vehlow Equal": 'V',
"Axial Rotation": 'X',
"Azimuthal": 'H',
"Topocentric": 'T',
# "Alcabitus": 'B',
# "Gauquelin Sectors": 'G',
"Morinus": 'M',
"Quadrant": '4',
"Bi-quadrant": '8',
}
cdef class houses:
''' House cusps, ascendant, MC, vertex '''
cdef readonly tjd
cdef double cusp[13]
cdef readonly double asc, mc, armc, vertex, equ_asc, co_asc1, co_asc2, polar_asc
cdef double _9, _10
cdef int ncusps
cdef readonly se.int32 iflag
cdef readonly double geolon, geolat
cdef int syscode
def __new__(self, *args, **kwargs):
self.ncusps = 0
self.geolon = self.geolat = 0.0
self.iflag = 0
self.tjd = NoJD
self.asc, self.mc, self.armc, self.vertex, self.equ_asc, self.co_asc1, self.co_asc2, self.polar_asc = NoJD
def __getitem__(self, int h):
if 0 <= h < ncusps:
return cusps[h+1]
else:
raise IndexError
def __getslice__(self, int h0, int h1):
if h0 > h1:
return []
if h0 < 0:
h0 = 0
if h1 > ncusps:
h1 = ncusps
res = [0.0]*(h1-h0)
for h from h0 <= h < h1:
res[h] = cusps[h+1]
return res
def __len__(self):
return ncusps
def __iter__(self):
return _houseiterator(self)
def __init__(self, double geolon, double geolat, systemname="Placidus"):
''' geolon, geolat location of observer
systemname ��� name of house system used
'''
self.set_system(systemname)
self.selocation(geolon, geolat)
def set_system(self, systemname):
systemname = ' '.join(map(str.capitalize, systemname.split()))
self.systemname = systemname
self.syscode = HouseSystem[self.systemname]
self.ncusps = 0
self.calc()
def set_location(self, double geolon, double geolat):
''' Calculate topocentric positions at geographic position of observer. '''
self.geolon = geolon
self.geolat = geolat
self.ncusps = 0
self.calc()
def set_sid_mode(self, sidmode):
''' Calculate using named siderial mode '''
cdef int sid_mode
xx = Ayanamasas[sidmode] # to check
self.sid_mode = sidmode
self.iflag = self.iflag | se.SEFLG_SIDEREAL
self.ncusps = 0
self.calc()
def set_tropical_mode(self):
''' Calculate in tropical mode. '''
self.iflag = self.iflag & ~se.SEFLG_SIDEREAL
self.ncusps = 0
self.calc()
def calc(self, double tjd=NoJD, armc=NoJD):
''' Calculate house cusp positions at UTC time tjd.
tjd Julian day number, Universal Time
'''
#cdef double xx[6]
cdef se.int32 res
if tjd == NoJD and armc == NoJD:
if self.tjd != NoJD:
tjd = self.tjd
elif self.armc != NoJD:
armc = self.armc
else:
return
if tjd != NoJD and armc != NoJD:
raise ValueError, "cannot specify both tjd and armc"
# These mode settings need to be done at each calculation because
# the swiss ephemeris mode is global, while the modes of this
# body (self) may differ from the global setting.
if self.iflag & se.SEFLG_SIDERIAL:
se.swe_set_sid_mode(Ayanamasas[self.sid_mode], 0.0, 0.0)
cdef char hsys
if self.syscode == c'4' or self.syscode == c'8':
hsys = c'P'
else:
hsys = self.syscode
if armc != NoJD:
res = se.swe_houses_armc(armc, self.geolat, eps, hsys,
self.cusp, &self.asc)
else:
res = se.swe_houses_ex(tjd, self.iflag, self.geolat, self.geolon, hsys,
self.cusp, &self.asc)
self.tjd = tjd
if res != se.OK:
if self.systemname != "Equal":
raise error, "cannot calculate %s houses: using Equal house system" % self.systemname
self.set_system("Equal")
else:
raise error, "cannot calculate %s houses" % self.systemname
if self.syscode == c'4' or self.syscode == c'G':
self.ncusps = 4
if self.syscode == c'4':
cusp[2] = cusp[4]
cusp[3] = cusp[7]
cusp[4] = cusp[10]
elif self.syscode == c'8':
self.ncusps = 8
cusp[1] = cusp[1]
cusp[2] = deg_midpoint(cusp[1], cusp[4])
cusp[3] = cusp[4]
cusp[4] = deg_midpoint(cusp[4], cusp[7])
cusp[5] = cusp[7]
cusp[6] = deg_midpoint(cusp[7], cusp[10])
cusp[7] = cusp[10]
cusp[8] = deg_midpoint(cusp[10], cusp[1])
else:
self.ncusps = 12
cdef class _houseiterator:
cdef double *h, *hlast
def __new__(self, houses Houses):
self.h = Houses.cusp + 1
self.hlast = self.h + Houses.ncusps
def __next__(self):
cdef double res
if h >= hlast:
raise StopIteration
res = h[0]
h = h + 1
return res
#double se.swe_house_pos(
# double armc, double geolat, double eps, int hsys, double *xpin, char *serr)
# exports from se.swedate.c
def julday(int y, int m, int d, double utime, int gregorian=True):
''' julian day from
y, m, d, year, month, day
utime, universal time in hours (decimal)
gregorian True = gregorian, False = julian calendar
'''
cdef double tjd
cdef int res
res = se.swe_date_conversion(y, m, d, utime, "jg"[gregorian], &tjd)
if res != se.OK:
raise ValueError, "illegal date"
return tjd
def revjul (double jd, int gregorian=True):
''' (year, month, day, ut) from julian day jd '''
cdef int jyear, jmon, jday
cdef double jut
se.swe_revjul (jd, gregorian, &jyear, &jmon, &jday, &jut)
return (jyear, jmon, jday, jut)
# exports from se.swephlib.c
def delta_t(double tjd):
''' delta t = Universal Time - Terrestrial Dynamical Time '''
return se.swe_deltat(tjd)
def equation_of_time(double lapt):
''' equation of time == Local Apparent Time - Local Mean Time '''
cdef double e
cdef int res
cdef char serr[256]
res = se.swe_time_equ(lapt, &e, serr)
if res != se.OK:
raise error, serr
return e
def siderial_time(double tjd_ut):
return se.swe_sidtime(tjd_ut)
def degnorm(double x):
''' x mod 360 '''
return se.swe_degnorm(x)
def radnorm(double x):
''' x mod (2*pi) '''
return se.swe_radnorm(x)
def rad_midpoint(double x1, double x0):
''' midpoint in radians of the smaller of the two intervals between x1 and x0 '''
return se.swe_rad_midp(x1, x0)
def deg_midpoint(double x1, double x0):
''' midpoint in degrees of the smaller of the two intervals between x1 and x0 '''
return se.swe_deg_midp(x1, x0)
def round2deg(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�
keep is None -> unrestricted rounding
'''
cdef se.int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
# in effect, truncate to degree
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn
def round2deg_min(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign, integer minutes)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'
keep is None -> unrestricted rounding
'''
cdef se.int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_MIN
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin
def round2deg_min_sec(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign, integer minutes, integer seconds)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'59"
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'59"
keep is None -> unrestricted rounding
'''
cdef se.int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_MIN
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin, isec
def deg2deg_min_fsec(double ddeg):
''' split ddeg to (integer degrees*sign, integer minutes, floating seconds)
'''
cdef se.int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = 0
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin, isec + dsecfr
ZodiacNames = ("Aries","Taurus","Gemini","Cancer","Leo", "Virgo",
"Libra", "Scorpio", "Saggitarius", "Capricorn", "Aquarius", "Pisces")
def round2deg_sign(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, zodiac sign (0-11))
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�
keep is None -> unrestricted rounding (mod 360)
'''
cdef se.int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
# in effect, truncate to degree
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12]
def round2deg_sign_min(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'
keep is None -> unrestricted rounding
'''
cdef se.int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin
def round2deg_sign_min_sec(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes, integer seconds)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'59"
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'59"
keep is None -> unrestricted rounding
'''
cdef se.int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin, isec
def deg2deg_sign_min_fsec(double ddeg, names=ZodiacNames):
''' split ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes, floating seconds)
'''
cdef se.int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = SE_SPLIT_DEG_ZODIACAL
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin, isec + dsecfr
-------------- next part --------------
cimport pysweph as se
#-----------------------------------------------------------------------
# Python interface to swiss ephemeris
#-----------------------------------------------------------------------
Body = {
"Sun": se.SE_SUN,
"Moon": se.SE_MOON,
"Mercury": se.SE_MERCURY,
"Venus": se.SE_VENUS,
"Mars": se.SE_MARS,
"Jupiter": se.SE_JUPITER,
"Saturn": se.SE_SATURN,
"Uranus": se.SE_URANUS,
"Neptune": se.SE_NEPTUNE,
"Pluto": se.SE_PLUTO,
"Mean Node": se.SE_MEAN_NODE,
"True Node": se.SE_TRUE_NODE,
"Earth": se.SE_EARTH,
"Chiron": se.SE_CHIRON,
"Pholus": se.SE_PHOLUS,
"Ceres": se.SE_CERES,
"Pallas": se.SE_PALLAS,
"Juno": se.SE_JUNO,
"Vesta": se.SE_VESTA,
# Hamburger or Uranian "planets"
"Cupido": se.SE_CUPIDO,
"Hades": se.SE_HADES,
"Zeus": se.SE_ZEUS,
"Kronos": se.SE_KRONOS,
"Apollon": se.SE_APOLLON,
"Admetos": se.SE_ADMETOS,
"Vulkanus": se.SE_VULKANUS,
"Poseidon": se.SE_POSEIDON,
# other fictitious bodies
"Isis": se.SE_ISIS,
"Nibiru": se.SE_NIBIRU,
"Harrington": se.SE_HARRINGTON,
"Vulcan": se.SE_VULCAN,
"White Moon": se.SE_WHITE_MOON,
"Proserpina": se.SE_PROSERPINA,
"Waldemath": se.SE_WALDEMATH,
"Ascendant": se.SE_ASC,
"MC": se.SE_MC,
"Armc": se.SE_ARMC,
"Vertex": se.SE_VERTEX,
}
Ayanamasas = {
"Fagan-Bradley": se.SE_SIDM_FAGAN_BRADLEY,
"Lahiri": se.SE_SIDM_LAHIRI,
"Deluce": se.SE_SIDM_DELUCE,
"Raman": se.SE_SIDM_RAMAN,
"Ushashashi": se.SE_SIDM_USHASHASHI,
"Krishnamurti": se.SE_SIDM_KRISHNAMURTI,
"Djwhal Khul": se.SE_SIDM_DJWHAL_KHUL,
"Yukteshwar": se.SE_SIDM_YUKTESHWAR,
"Jn Bhasin": se.SE_SIDM_JN_BHASIN,
"Babylonian Kugler 1": se.SE_SIDM_BABYL_KUGLER1,
"Babylonian Kugler 2": se.SE_SIDM_BABYL_KUGLER2,
"Babylonian Kugler 3": se.SE_SIDM_BABYL_KUGLER3,
"Babylonian Huber": se.SE_SIDM_BABYL_HUBER,
"Babylonian Eta Piscium": se.SE_SIDM_BABYL_ETPSC,
"Aldebaran @ 15� Tau.": se.SE_SIDM_ALDEBARAN_15TAU,
"Hipparchos": se.SE_SIDM_HIPPARCHOS,
"Sassanian": se.SE_SIDM_SASSANIAN,
"Galactic Center @ 0� Sag.": se.SE_SIDM_GALCENT_0SAG,
"J2000": se.SE_SIDM_J2000,
"J1900": se.SE_SIDM_J1900,
"B1950": se.SE_SIDM_B1950,
}
class error(Exception):
pass
cdef double NoJD
NoJD = 1e200/1e-200
def set_ephe_path(path):
''' set directory path of ephemeris files '''
if path is None:
import os
path = os.path.dirname(__file__)
se.swe_set_ephe_path(path)
def close():
''' close Swiss Ephemeris '''
se.swe_close()
cdef class body:
''' An astronomical body '''
cdef readonly double tjd
cdef readonly double longitude, latitude, distance, long_speed, lat_speed, dist_speed
cdef readonly double geolon, geolat, geoalt
cdef readonly ayanamsa
cdef int32 iflag
def __new__(self, *args, **kwargs):
self.longitude = self.latitude = self.distance = self.long_speed = self.lat_speed = self.dist_speed = 0.0
self.geolon = self.geolat = self.geoalt = 0.0
self.tjd = NoJD
self.iflag = 0
def __init__(self, bodyname):
''' bodyname ���name of planet or body
'''
self.bodyname = ' '.join(map(str.capitalize, bodyname.split()))
ipl = Body[self.bodyname]
self.set_swiss()
self.set_apparent()
self.set_geocentric()
def set_swiss(self):
''' Use swiss ephemeris. '''
self.iflag = (self.iflag | se.SEFLG_SWIEPH) & ~(se.SEFLG_MOSEPH|se.SEFLG_JPLEPH)
self.calc(self.tjd)
def set_moshier(self):
''' Use Moshier (analytical) ephemeris. '''
self.iflag = (self.iflag | se.SEFLG_MOSEPH) & ~(se.SEFLG_SWIEPH|se.SEFLG_JPLEPH)
self.calc(self.tjd)
def set_apparent(self, int on=True):
''' Calculate apparent (on == True) or true (on == False) positions. '''
if on:
self.iflag = self.iflag | se.SEFLG_TRUEPOS
else:
self.iflag = self.iflag & ~se.SEFLG_TRUEPOS
self.calc(self.tjd)
def set_topo(self, double geolon, double geolat, double geoalt):
''' Calculate topocentric positions at geographic position of observer. '''
self.geolon = geolon
self.geolat = geolat
self.geoalt = geoalt
self.iflag = self.iflag | se.SEFLG_TOPOCTR
self.calc(self.tjd)
def set_geocentric(self):
''' Calculate geocentric positions. '''
self.iflag = self.iflag & ~se.SEFLG_TOPOCTR
self.calc(self.tjd)
def set_sid_mode(sidmode):
''' Calculate in named siderial mode '''
cdef int sid_mode
xx = Ayanamasas[sidmode] # to check
self.sid_mode = sidmode
self.iflag = self.iflag | se.SEFLG_SIDEREAL
self.calc(self.tjd)
def set_tropical_mode(mode):
''' Calculate in tropical mode. '''
self.iflag = self.iflag & ~se.SEFLG_SIDEREAL
self.calc(self.tjd)
def calc(self, double tjd):
''' Calculate position of body at time tjd.
tjd Julian day number, Universal Time
'''
if tjd == NoJD:
return
#cdef double xx[6]
cdef int32 res
cdef char serr[256]
# These mode settings need to be done at each calculation because
# the swiss ephemeris mode is global, while the modes of this
# body (self) may differ from the global setting.
if self.iflag & se.SEFLG_TOPOCTR:
se.swe_set_topo(self.geolon, self.geolat, self.geoalt)
if self.iflag & se.SEFLG_SIDERIAL:
se.swe_set_sid_mode(Ayanamasas[self.sid_mode], 0.0, 0.0)
self.ayanamsa = se.swe_get_ayanamsa_ut(tjd)
res = se.swe_calc_ut(tjd, ipl, iflag, &self.longitude, serr)
if res < 0:
raise error, serr
if res != self.iflag and \
(res ^ self.iflag) & ~(se.SEFLG_MOSEPH|se.SEFLG_MOSEPH|se.SEFLG_JPLEPH):
raise error, "cannot respect flag(s) %s in %s" % (hex(res ^ self.iflag), hex(self.iflag))
self.tjd = tjd
'''self.longitude = xx[0]
self.latitude = xx[1]
self.distance = xx[2]
self.long_speed = xx[3]
self.lat_speed = xx[4]
self.dist_speed = xx[5]'''
# fixed stars
#int32 se.swe_fixstar_ut(char *star, double tjd_ut, int32 iflag, double *xx, char *serr)
# exports from se.swehouse.c
HouseSystems = {
"Placidus": c'P',
"Koch": 'K',
"Porphyrius": 'O',
"Regiomontanus": 'R',
"Campanus": 'C',
"Equal": 'E',
"Vehlow Equal": 'V',
"Axial Rotation": 'X',
"Azimuthal": 'H',
"Topocentric": 'T',
# "Alcabitus": 'B',
# "Gauquelin Sectors": 'G',
"Morinus": 'M',
"Quadrant": '4',
"Bi-quadrant": '8',
}
cdef class houses:
''' House cusps, ascendant, MC, vertex '''
cdef readonly tjd
cdef double cusp[13]
cdef readonly double asc, mc, armc, vertex, equ_asc, co_asc1, co_asc2, polar_asc
cdef double _9, _10
cdef int ncusps
cdef readonly int32 iflag
cdef readonly double geolon, geolat
cdef int syscode
def __new__(self, *args, **kwargs):
self.ncusps = 0
self.geolon = self.geolat = 0.0
self.iflag = 0
self.tjd = NoJD
self.asc, self.mc, self.armc, self.vertex, self.equ_asc, self.co_asc1, self.co_asc2, self.polar_asc = NoJD
def __getitem__(self, int h):
if 0 <= h < ncusps:
return cusps[h+1]
else:
raise IndexError
def __getslice__(self, int h0, int h1):
if h0 > h1:
return []
if h0 < 0:
h0 = 0
if h1 > ncusps:
h1 = ncusps
res = [0.0]*(h1-h0)
for h from h0 <= h < h1:
res[h] = cusps[h+1]
return res
def __len__(self):
return ncusps
def __iter__(self):
return _houseiterator(self)
def __init__(self, double geolon, double geolat, systemname="Placidus"):
''' geolon, geolat location of observer
systemname ��� name of house system used
'''
self.set_system(systemname)
self.selocation(geolon, geolat)
def set_system(self, systemname):
systemname = ' '.join(map(str.capitalize, systemname.split()))
self.systemname = systemname
self.syscode = HouseSystem[self.systemname]
self.ncusps = 0
self.calc()
def set_location(self, double geolon, double geolat):
''' Calculate topocentric positions at geographic position of observer. '''
self.geolon = geolon
self.geolat = geolat
self.ncusps = 0
self.calc()
def set_sid_mode(self, sidmode):
''' Calculate using named siderial mode '''
cdef int sid_mode
xx = Ayanamasas[sidmode] # to check
self.sid_mode = sidmode
self.iflag = self.iflag | se.SEFLG_SIDEREAL
self.ncusps = 0
self.calc()
def set_tropical_mode(self):
''' Calculate in tropical mode. '''
self.iflag = self.iflag & ~se.SEFLG_SIDEREAL
self.ncusps = 0
self.calc()
def calc(self, double tjd=NoJD, armc=NoJD):
''' Calculate house cusp positions at UTC time tjd.
tjd Julian day number, Universal Time
'''
#cdef double xx[6]
cdef int32 res
if tjd == NoJD and armc == NoJD:
if self.tjd != NoJD:
tjd = self.tjd
elif self.armc != NoJD:
armc = self.armc
else:
return
if tjd != NoJD and armc != NoJD:
raise ValueError, "cannot specify both tjd and armc"
# These mode settings need to be done at each calculation because
# the swiss ephemeris mode is global, while the modes of this
# body (self) may differ from the global setting.
if self.iflag & se.SEFLG_SIDERIAL:
se.swe_set_sid_mode(Ayanamasas[self.sid_mode], 0.0, 0.0)
cdef char hsys
if self.syscode == c'4' or self.syscode == c'8':
hsys = c'P'
else:
hsys = self.syscode
if armc != NoJD:
res = se.swe_houses_armc(armc, self.geolat, eps, hsys,
self.cusp, &self.asc)
else:
res = se.swe_houses_ex(tjd, self.iflag, self.geolat, self.geolon, hsys,
self.cusp, &self.asc)
self.tjd = tjd
if res != se.OK:
if self.systemname != "Equal":
raise error, "cannot calculate %s houses: using Equal house system" % self.systemname
self.set_system("Equal")
else:
raise error, "cannot calculate %s houses" % self.systemname
if self.syscode == c'4' or self.syscode == c'G':
self.ncusps = 4
if self.syscode == c'4':
cusp[2] = cusp[4]
cusp[3] = cusp[7]
cusp[4] = cusp[10]
elif self.syscode == c'8':
self.ncusps = 8
cusp[1] = cusp[1]
cusp[2] = deg_midpoint(cusp[1], cusp[4])
cusp[3] = cusp[4]
cusp[4] = deg_midpoint(cusp[4], cusp[7])
cusp[5] = cusp[7]
cusp[6] = deg_midpoint(cusp[7], cusp[10])
cusp[7] = cusp[10]
cusp[8] = deg_midpoint(cusp[10], cusp[1])
else:
self.ncusps = 12
cdef class _houseiterator:
cdef double *h, *hlast
def __new__(self, houses Houses):
self.h = Houses.cusp + 1
self.hlast = self.h + Houses.ncusps
def __next__(self):
cdef double res
if h >= hlast:
raise StopIteration
res = h[0]
h = h + 1
return res
#double se.swe_house_pos(
# double armc, double geolat, double eps, int hsys, double *xpin, char *serr)
# exports from se.swedate.c
def julday(int y, int m, int d, double utime, int gregorian=True):
''' julian day from
y, m, d, year, month, day
utime, universal time in hours (decimal)
gregorian True = gregorian, False = julian calendar
'''
cdef double tjd
cdef int res
res = se.swe_date_conversion(y, m, d, utime, "jg"[gregorian], &tjd)
if res != se.OK:
raise ValueError, "illegal date"
return tjd
def revjul (double jd, int gregorian=True):
''' (year, month, day, ut) from julian day jd '''
cdef int jyear, jmon, jday
cdef double jut
se.swe_revjul (jd, gregorian, &jyear, &jmon, &jday, &jut)
return (jyear, jmon, jday, jut)
# exports from se.swephlib.c
def delta_t(double tjd):
''' delta t = Universal Time - Terrestrial Dynamical Time '''
return se.swe_deltat(tjd)
def equation_of_time(double lapt):
''' equation of time == Local Apparent Time - Local Mean Time '''
cdef double e
cdef int res
cdef char serr[256]
res = se.swe_time_equ(lapt, &e, serr)
if res != se.OK:
raise error, serr
return e
def siderial_time(double tjd_ut):
return se.swe_sidtime(tjd_ut)
def degnorm(double x):
''' x mod 360 '''
return se.swe_degnorm(x)
def radnorm(double x):
''' x mod (2*pi) '''
return se.swe_radnorm(x)
def rad_midpoint(double x1, double x0):
''' midpoint in radians of the smaller of the two intervals between x1 and x0 '''
return se.swe_rad_midp(x1, x0)
def deg_midpoint(double x1, double x0):
''' midpoint in degrees of the smaller of the two intervals between x1 and x0 '''
return se.swe_deg_midp(x1, x0)
def round2deg(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
# in effect, truncate to degree
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn
def round2deg_min(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign, integer minutes)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_MIN
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin
def round2deg_min_sec(double ddeg, keep=None):
''' round ddeg to (integer degrees*sign, integer minutes, integer seconds)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'59"
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'59"
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_MIN
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin, isec
def deg2deg_min_fsec(double ddeg):
''' split ddeg to (integer degrees*sign, integer minutes, floating seconds)
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = 0
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg*isgn, imin, isec + dsecfr
ZodiacNames = ("Aries","Taurus","Gemini","Cancer","Leo", "Virgo",
"Libra", "Scorpio", "Saggitarius", "Capricorn", "Aquarius", "Pisces")
def round2deg_sign(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, zodiac sign (0-11))
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�
keep is None -> unrestricted rounding (mod 360)
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
# in effect, truncate to degree
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12]
def round2deg_sign_min(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin
def round2deg_sign_min_sec(double ddeg, keep=None, names=ZodiacNames):
''' round ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes, integer seconds)
keep == "sign" -> don't round to next zodiac sign, e.g. 29.9999999 => 29�59'59"
keep == "degree" -> don't round to next degree, 13.9999999 => to 13�59'59"
keep is None -> unrestricted rounding
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = se.SE_SPLIT_DEG_ROUND_DEG | SE_SPLIT_DEG_ZODIACAL
if keep is not None:
if keep == "sign":
roundflag = roundflag | se.SE_SPLIT_KEEP_SIGN
elif keep == "degree":
roundflag = roundflag | se.SE_SPLIT_KEEP_DEG
else:
raise ValueError, "keep not in (None, 'sign', 'degree')"
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin, isec
def deg2deg_sign_min_fsec(double ddeg, names=ZodiacNames):
''' split ddeg to (integer degrees mod 30, names[zodiac sign (0-11)], integer minutes, floating seconds)
'''
cdef int32 roundflag, ideg, imin, isec, isgn
cdef double dsecfr
roundflag = SE_SPLIT_DEG_ZODIACAL
se.swe_split_deg(ddeg, roundflag, &ideg, &imin, &isec, &dsecfr, &isgn)
return ideg, names[isgn % 12], imin, isec + dsecfr
More information about the Pyrex
mailing list