[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