module SwissEphemeris
(
JulianTime (..),
SiderealTime (..),
HouseCusp,
SplitDegreesOption (..),
Planet (..),
HouseSystem (..),
ZodiacSignName (..),
NakshatraName (..),
EclipticPosition (..),
EquatorialPosition (..),
GeographicPosition (..),
HousePosition (..),
ObliquityInformation (..),
Angles (..),
CuspsCalculation (..),
LongitudeComponents (..),
setEphemeridesPath,
setNoEphemeridesPath,
closeEphemerides,
withEphemerides,
withoutEphemerides,
calculateEclipticPosition,
calculateEquatorialPosition,
calculateObliquity,
calculateCusps,
calculateCuspsLenient,
calculateCuspsStrict,
equatorialToEcliptic,
eclipticToEquatorial,
calculateSiderealTime,
calculateSiderealTimeSimple,
calculateHousePosition,
calculateHousePositionSimple,
julianDay,
deltaTime,
defaultSplitDegreesOptions,
splitDegrees,
splitDegreesZodiac,
)
where
import Control.Exception (bracket_)
import Data.Semigroup ((<>))
import Foreign
import Foreign.C.String
import Foreign.SwissEphemeris
import SwissEphemeris.Internal
import System.IO.Unsafe (unsafePerformIO)
setEphemeridesPath :: FilePath -> IO ()
setEphemeridesPath path =
withCString path $ \ephePath -> c_swe_set_ephe_path ephePath
setNoEphemeridesPath :: IO ()
setNoEphemeridesPath = c_swe_set_ephe_path nullPtr
closeEphemerides :: IO ()
closeEphemerides = c_swe_close
withEphemerides :: FilePath -> (IO a) -> IO a
withEphemerides ephemeridesPath =
bracket_
(setEphemeridesPath ephemeridesPath)
(closeEphemerides)
withoutEphemerides :: (IO a) -> IO a
withoutEphemerides =
bracket_
(setNoEphemeridesPath)
(closeEphemerides)
julianDay :: Int -> Int -> Int -> Double -> JulianTime
julianDay year month day hour = JulianTime $ realToFrac $ c_swe_julday y m d h gregorian
where
y = fromIntegral year
m = fromIntegral month
d = fromIntegral day
h = realToFrac hour
calculateEclipticPosition :: JulianTime -> Planet -> IO (Either String EclipticPosition)
calculateEclipticPosition time planet = do
let options = (mkCalculationOptions defaultCalculationOptions)
rawCoords <- calculateCoordinates' options time (planetNumber planet)
return $ fmap coordinatesFromList rawCoords
calculateEquatorialPosition :: JulianTime -> Planet -> IO (Either String EquatorialPosition)
calculateEquatorialPosition time planet = do
let options = (mkCalculationOptions $ defaultCalculationOptions ++ [equatorialPositions])
rawCoords <- calculateCoordinates' options time (planetNumber planet)
return $ fmap equatorialFromList rawCoords
calculateObliquity :: JulianTime -> IO (Either String ObliquityInformation)
calculateObliquity time = do
let options = CalcFlag 0
rawCoords <- calculateCoordinates' options time specialEclNut
return $ fmap obliquityNutationFromList rawCoords
calculateCoordinates' :: CalcFlag -> JulianTime -> PlanetNumber -> IO (Either String [Double])
calculateCoordinates' options time planet =
allocaArray 6 $ \coords -> allocaArray 256 $ \serr -> do
iflgret <-
c_swe_calc_ut
(realToFrac . unJulianTime $ time)
planet
options
coords
serr
if unCalcFlag iflgret < 0
then do
msg <- peekCAString serr
return $ Left msg
else do
result <- peekArray 6 coords
return $ Right $ map realToFrac result
eclipticToEquatorial :: ObliquityInformation -> EclipticPosition -> EquatorialPosition
eclipticToEquatorial oAndN ecliptic =
let obliquityLn = eclipticObliquity oAndN
eclipticPos = eclipticToList ecliptic
transformed = coordinateTransform' (negate obliquityLn) eclipticPos
in equatorialFromList transformed
equatorialToEcliptic :: ObliquityInformation -> EquatorialPosition -> EclipticPosition
equatorialToEcliptic oAndN equatorial =
let obliquityLn = eclipticObliquity oAndN
equatorialPos = equatorialToList equatorial
transformed = coordinateTransform' obliquityLn equatorialPos
in eclipticFromList transformed
coordinateTransform' :: Double -> [Double] -> [Double]
coordinateTransform' obliquity ins =
unsafePerformIO $ do
withArray (map realToFrac $ take 6 ins) $ \xpo -> allocaArray 6 $ \xpn -> do
_ <- c_swe_cotrans_sp xpo xpn (realToFrac obliquity)
result <- peekArray 6 xpn
return $ map realToFrac result
calculateCusps :: HouseSystem -> JulianTime -> GeographicPosition -> IO CuspsCalculation
calculateCusps = calculateCuspsLenient
calculateCuspsLenient :: HouseSystem -> JulianTime -> GeographicPosition -> IO CuspsCalculation
calculateCuspsLenient sys time loc =
allocaArray 13 $ \cusps -> allocaArray 10 $ \ascmc -> do
rval <-
c_swe_houses
(realToFrac . unJulianTime $ time)
(realToFrac $ geoLat loc)
(realToFrac $ geoLng loc)
(fromIntegral $ toHouseSystemFlag sys)
cusps
ascmc
(_ : cuspsL) <- peekArray 13 cusps
anglesL <- peekArray 10 ascmc
return $
CuspsCalculation
(map realToFrac $ cuspsL)
(anglesFromList $ map realToFrac $ anglesL)
(if rval < 0 then Porphyrius else sys)
calculateCuspsStrict :: HouseSystem -> JulianTime -> GeographicPosition -> IO (Either String CuspsCalculation)
calculateCuspsStrict sys time loc = do
calcs@(CuspsCalculation _ _ sys') <- calculateCuspsLenient sys time loc
if sys' /= sys
then pure $ Left $ "Unable to calculate cusps in the requested house system (used " ++ (show sys') ++ " instead.)"
else pure $ Right calcs
calculateHousePositionSimple :: HouseSystem -> JulianTime -> GeographicPosition -> EclipticPosition -> IO (Either String HousePosition)
calculateHousePositionSimple sys time loc pos = do
obliquityAndNutation <- calculateObliquity time
case obliquityAndNutation of
Left e -> return $ Left e
Right on -> do
SiderealTime siderealTime <- calculateSiderealTime time on
let armc' = siderealTime * 15 + geoLng loc
calculateHousePosition sys armc' loc on pos
calculateHousePosition :: HouseSystem -> Double -> GeographicPosition -> ObliquityInformation -> EclipticPosition -> IO (Either String HousePosition)
calculateHousePosition sys armc' geoCoords obliq eclipticCoords =
withArray [realToFrac $ lng eclipticCoords, realToFrac $ lat eclipticCoords] $ \xpin -> allocaArray 256 $ \serr -> do
housePos <-
c_swe_house_pos
(realToFrac armc')
(realToFrac $ geoLat geoCoords)
(realToFrac $ eclipticObliquity obliq)
(fromIntegral $ toHouseSystemFlag sys)
xpin
serr
if housePos <= 0
then do
msg <- peekCAString serr
return $ Left msg
else do
let houseN = truncate housePos
cuspD = housePos - (fromIntegral houseN)
return $ Right $ HousePosition houseN (realToFrac cuspD)
calculateSiderealTimeSimple :: JulianTime -> IO SiderealTime
calculateSiderealTimeSimple jt = do
sidTime <- c_swe_sidtime (realToFrac . unJulianTime $ jt)
return $ SiderealTime $ realToFrac sidTime
calculateSiderealTime :: JulianTime -> ObliquityInformation -> IO SiderealTime
calculateSiderealTime jt on = do
let obliq = realToFrac $ eclipticObliquity on
nut = realToFrac $ nutationLongitude on
sidTime <- c_swe_sidtime0 (realToFrac . unJulianTime $ jt) obliq nut
return $ SiderealTime $ realToFrac sidTime
deltaTime :: JulianTime -> IO Double
deltaTime jt = do
deltaT <- c_swe_deltat . realToFrac . unJulianTime $ jt
return $ realToFrac deltaT
splitDegreesZodiac :: Double -> LongitudeComponents
splitDegreesZodiac = splitDegrees $ defaultSplitDegreesOptions <> [SplitZodiacal, RoundSeconds]
splitDegrees :: [SplitDegreesOption] -> Double -> LongitudeComponents
splitDegrees options d =
LongitudeComponents sign deg m s sf signum' nak
where
(z, deg, m, s, sf) = splitDegrees' flags d
flags = foldSplitDegOptions $ map splitOptionToFlag options
isZodiacSplit = SplitZodiacal `elem` options
isNakshatraSplit = SplitNakshatra `elem` options
sign = if isZodiacSplit then (Just . toEnum $ z) else Nothing
nak = if isNakshatraSplit then (Just . toEnum $ z) else Nothing
signum' = if (not isZodiacSplit && not isNakshatraSplit) then Just z else Nothing
splitDegrees' :: SplitDegFlag -> Double -> (Int, Integer, Integer, Integer, Double)
splitDegrees' options deg =
unsafePerformIO $ do
alloca $ \ideg -> alloca $ \imin -> alloca $ \isec -> alloca $ \dsecfr -> alloca $ \isign -> do
poke dsecfr 0
_ <-
c_swe_split_deg
(realToFrac deg)
options
ideg
imin
isec
dsecfr
isign
sign' <- peek isign
deg' <- peek ideg
min' <- peek imin
sec' <- peek isec
secfr <- peek dsecfr
return $ ((fromIntegral sign'), (fromIntegral deg'), (fromIntegral min'), (fromIntegral sec'), (realToFrac secfr))