astro-0.4.2.0: src/Data/Astro/Moon.hs
{-|
Module: Data.Astro.Moon
Description: Calculation characteristics of the Moon
Copyright: Alexander Ignatyev, 2016
Calculation characteristics of the Moon.
= Example
@
import Data.Astro.Time.JulianDate
import Data.Astro.Coordinate
import Data.Astro.Types
import Data.Astro.Effects
import Data.Astro.CelestialObject.RiseSet
import Data.Astro.Moon
ro :: GeographicCoordinates
ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
dt :: LocalCivilTime
dt = lctFromYMDHMS (DH 1) 2017 6 25 10 29 0
today :: LocalCivilDate
today = lcdFromYMD (DH 1) 2017 6 25
jd :: JulianDate
jd = lctUniversalTime dt
-- distance from the Earth to the Moon in kilometres
mdu :: MoonDistanceUnits
mdu = moonDistance1 j2010MoonDetails jd
-- MDU 0.9550170577020396
distance :: Double
distance = mduToKm mdu
-- 367109.51199772174
-- Angular Size
angularSize :: DecimalDegrees
angularSize = moonAngularSize mdu
-- DD 0.5425033990980761
-- The Moon's coordinates
position :: JulianDate -> EquatorialCoordinates1
position = moonPosition1 j2010MoonDetails
ec1 :: EquatorialCoordinates1
ec1 = position jd
-- EC1 {e1Declination = DD 18.706180658927323, e1RightAscension = DH 7.56710547682055}
hc :: HorizonCoordinates
hc = ec1ToHC ro jd ec1
-- HC {hAltitude = DD 34.57694951316064, hAzimuth = DD 103.91119101451832}
-- Rise and Set
riseSet :: RiseSetMB
riseSet = riseAndSet2 0.000001 position ro verticalShift today
-- RiseSet
-- (Just (2017-06-25 06:22:51.4858 +1.0,DD 57.81458864497365))
-- (Just (2017-06-25 22:28:20.3023 +1.0,DD 300.4168238905249))
-- Phase
phase :: Double
phase = moonPhase j2010MoonDetails jd
-- 2.4716141948212922e-2
sunEC1 :: EquatorialCoordinates1
sunEC1 = sunPosition2 jd
-- EC1 {e1Declination = DD 23.37339098989099, e1RightAscension = DH 6.29262026252748}
limbAngle :: DecimalDegrees
limbAngle = moonBrightLimbPositionAngle ec1 sunEC1
-- DD 287.9869373767473
@
-}
module Data.Astro.Moon
(
moonPosition1
, moonPosition2
, moonDistance1
, moonAngularSize
, moonHorizontalParallax
, moonPhase
, moonBrightLimbPositionAngle
)
where
import qualified Data.Astro.Utils as U
import Data.Astro.Types (DecimalDegrees(..), GeographicCoordinates, toRadians, fromRadians, kmToAU)
import Data.Astro.Time.JulianDate (JulianDate(..), numberOfDays)
import Data.Astro.Coordinate (EquatorialCoordinates1(..), EclipticCoordinates(..), eclipticToEquatorial)
import Data.Astro.Planet (planetBrightLimbPositionAngle)
import Data.Astro.Sun (sunDetails, sunMeanAnomaly2, sunEclipticLongitude2)
import Data.Astro.Moon.MoonDetails (MoonDetails(..), MoonDistanceUnits(..), j2010MoonDetails, mduToKm)
import Data.Astro.Effects (parallax)
-- | Reduce the value to the range [0, 360)
reduceDegrees :: DecimalDegrees -> DecimalDegrees
reduceDegrees = U.reduceToZeroRange 360
-- | Calculate Equatorial Coordinates of the Moon with the given MoonDetails and at the given JulianDate.
-- It is recommended to use 'j2010MoonDetails' as a first parameter.
moonPosition1 :: MoonDetails -> JulianDate -> EquatorialCoordinates1
moonPosition1 md ut =
let sd = sunDetails ut
lambdaS = sunEclipticLongitude2 sd
ms = sunMeanAnomaly2 sd
mmq = meanMoonQuantities md ut
MQ lm'' _ nm' = correctedMoonQuantities lambdaS ms mmq
a = toRadians $ lm''-nm'
i = toRadians $ mdI md
y = (sin a) * (cos i)
x = cos a
at = reduceDegrees $ fromRadians $ atan2 y x
lambdaM = at + nm'
betaM = fromRadians $ asin $ (sin a) * (sin i)
in eclipticToEquatorial (EcC betaM lambdaM) ut
-- | Calculate Equatorial Coordinates of the Moon with the given MoonDetails,
-- distance to the Moon, geographic coordinates of the onserver,
-- height above sea-level of the observer measured in metres (20 is a good reasonable value for the height)
-- and at the given JulianDate.
-- It is recommended to use 'j2010MoonDetails' as a first parameter,
-- to obtain the distance to the Moon you can use `moonDistance1` function.
-- `moonPosition2` takes into account parallax effect.
moonPosition2 :: MoonDetails -> MoonDistanceUnits -> GeographicCoordinates -> Double -> JulianDate -> EquatorialCoordinates1
moonPosition2 md distance coords height jd =
let p = moonPosition1 md jd
in parallax coords height (kmToAU $ mduToKm distance) jd p
-- | Calculates the Moon's Distance at the given julian date.
-- Returns distance to the Moon
-- moonDistance1 :: JulianDate -> MoonDistanceUnits
-- you can use 'mduToKm' (defined in "Data.Astro.Moon.MoonDetails") to convert result to kilometers
moonDistance1 :: MoonDetails -> JulianDate -> MoonDistanceUnits
moonDistance1 md ut =
let sd = sunDetails ut
lambdaS = sunEclipticLongitude2 sd
ms = sunMeanAnomaly2 sd
mmq = meanMoonQuantities md ut
cmq = correctedMoonQuantities lambdaS ms mmq
mm' = toRadians $ mqAnomaly cmq
ec = toRadians $ centreEquation mm'
e = mdE md
in MDU $ (1 - e*e)/(1+e*(cos(mm'+ec)))
-- | Calculate the Moon's angular size at the given distance.
moonAngularSize :: MoonDistanceUnits -> DecimalDegrees
moonAngularSize (MDU p) = (mdBigTheta j2010MoonDetails) / (DD p)
-- | Calculates the Moon's horizontal parallax at the given distance.
moonHorizontalParallax :: MoonDistanceUnits -> DecimalDegrees
moonHorizontalParallax (MDU p) = (mdPi j2010MoonDetails) / (DD p)
-- | Calculates the Moon's phase (the area of the visible segment expressed as a fraction of the whole disk)
-- at the given universal time.
moonPhase :: MoonDetails -> JulianDate -> Double
moonPhase md ut =
let sd = sunDetails ut
lambdaS = sunEclipticLongitude2 sd
ms = sunMeanAnomaly2 sd
mmq = meanMoonQuantities md ut
MQ ml _ _ = correctedMoonQuantities lambdaS ms mmq
d = toRadians $ ml - lambdaS
f = 0.5 * (1 - cos d)
in f
-- | Calculate the Moon's position-angle of the bright limb.
-- It takes the Moon's coordinates and the Sun's coordinates.
-- Position-angle is the angle of the midpoint of the illuminated limb
-- measured eastwards from the north point of the disk.
moonBrightLimbPositionAngle :: EquatorialCoordinates1 -> EquatorialCoordinates1 -> DecimalDegrees
moonBrightLimbPositionAngle = planetBrightLimbPositionAngle
-- | The Moon's quantities
-- Used to store intermidiate results
data MoonQuantities = MQ {
mqLongitude :: DecimalDegrees -- ^ the Moon's longitude
, mqAnomaly :: DecimalDegrees -- ^ the Moon's anomaly
, mqAscendingNode :: DecimalDegrees -- ^ the Moon's ascending node's longitude
}
-- | Calculates the Moon's mean quantities on the given date.
-- It takes the Moon's orbita details and julian date
meanMoonQuantities :: MoonDetails -> JulianDate -> MoonQuantities
meanMoonQuantities md ut =
let d = DD $ numberOfDays (mdEpoch md) ut
lm = reduceDegrees $ (mdL md) + 13.1763966*d -- Moon's mean longitude
mm = reduceDegrees $ lm - 0.1114041*d - (mdP md) -- Moon's mean anomaly
nm = reduceDegrees $ (mdN md) - 0.0529539*d -- ascending node's mean longitude
in MQ lm mm nm
-- | Calculates correction for the equation of the centre
-- It takes the Moon's corrected anomaly in radians
centreEquation :: Double -> DecimalDegrees
centreEquation mm = DD $ 6.2886 * (sin mm)
-- | Calculates the Moon's corrected longitude, anomaly and asceding node's longitude
-- It takes the Sun's longitude, the Sun's mean anomaly and the Moon's mean quantities
correctedMoonQuantities :: DecimalDegrees -> DecimalDegrees -> MoonQuantities -> MoonQuantities
correctedMoonQuantities lambdaS ms (MQ lm mm nm) =
let ms' = toRadians ms
c = lm - lambdaS
ev = DD $ 1.2739 * (sin $ toRadians $ 2*c - mm) -- correction for evection
ae = DD $ 0.1858 * (sin ms') -- correction for annual equation
a3 = DD $ 0.37 * (sin ms') -- third correction
mm' = mm + (ev - ae - a3) -- Moon's corrected anomaly
mm'' = toRadians mm'
ec = centreEquation mm'' -- correction for the equation of the centre
a4 = DD $ 0.214 * (sin $ 2*mm'') -- fourth correction term
lm' = lm + (ev + ec -ae + a4) -- Moon's corrected longitude
v = DD $ 0.6583 * (sin $ toRadians $ 2*(lm' - lambdaS))-- correction for variation
lm'' = lm' + v -- Moon's true orbital longitude
nm' = nm - (DD $ 0.16 * (sin ms')) -- ascending node's corrected longitude
in MQ lm'' mm' nm'