项目作者: aligusnet

项目描述 :
Amateur astronomical computations
高级语言: Haskell
项目地址: git://github.com/aligusnet/astro.git
创建时间: 2016-08-26T05:31:26Z
项目社区:https://github.com/aligusnet/astro

开源协议:Other

下载


Amateur astronomical computations

Build Status
Coverage Status
Documentation
Hackage

Usage

Install stack

It will take care of everything.

Get started with Stack

Build the project

  1. stack build

Run unit tests

  1. stack test

Documentation

Useful types

Decimal hours and Decimal degrees

Types to represent hours (used in celestial coordinate systems and as time zone) and degrees (used in coordinate systems).

  1. import Data.Astro.Types
  2. -- 10h 15m 19.7s
  3. dh :: DecimalHours
  4. dh = fromHMS 10 15 19.7
  5. -- DH 10.255472222222222
  6. (h, m, s) = toHMS dh
  7. -- (10,15,19.699999999999562)
  8. -- 51°2840
  9. dd :: DecimalDegrees
  10. dd = fromDMS 51 28 40
  11. -- DD 51.477777777777774
  12. (d, m, s) = toDMS dd
  13. -- (51,28,39.999999999987494)

Geographic Coordinates

  1. import Data.Astro.Types
  2. -- the Royal Observatory, Greenwich
  3. ro :: GeographicCoordinates
  4. ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
  5. -- GeoC {geoLatitude = DD 51.4778, geoLongitude = DD (-0.0014)}

Time

The main time datetime type used in the library is JulianDate defined in Data.Astro.Time.JulianDate. JulianDate is just a number of days since noon of 1 January 4713 BC:

  1. import Data.Astro.Time.JulianDate
  2. -- 2017-06-25 9:29:00 (GMT)
  3. jd :: JulianDate
  4. jd = fromYMDHMS 2017 6 25 9 29 0
  5. -- JD 2457929.895138889

LocalCiviTime and LocalCivilDate are Julian dates with time zones:

  1. import Data.Astro.Time.JulianDate
  2. import Data.Astro.Types
  3. -- 2017-06-25 10:29:00 +0100 (BST)
  4. lct :: LocalCivilTime
  5. lct = lctFromYMDHMS (DH 1) 2017 6 25 10 29 0
  6. -- 2017-06-25 10:29:00.0000 +1.0
  7. lctJD :: JulianDate
  8. lctJD = lctUniversalTime lct
  9. -- JD 2457929.895138889
  10. lctTZ :: DecimalHours
  11. lctTZ = lctTimeZone lct
  12. -- DH 1.0
  13. lcd :: LocalCivilDate
  14. lcd = lcdFromYMD (DH 1) 2017 6 25
  15. lcdJD :: JulianDate
  16. lcdJD = lcdDate lcd
  17. -- JD 2457929.5
  18. lcdTZ :: DecimalHours
  19. lcdTZ = lcdTimeZone lcd
  20. -- DH 1.0

Celestial coordinate systems

The celestical coordinate systems are defined in Data.Astro.Coordinate.

If you would like to locate Sirius in the sky you need to know the altitude or ‘how far up’ angle in the sky and azimuth - ‘how far round’ angle from the north direction to the east. this describes the Horizontal coordinate system:

alt Horizontal coordinate system

  1. import Data.Astro.Coordinate
  2. import Data.Astro.Types
  3. hc :: HorizonCoordinates
  4. hc = HC (DD 30.5) (DD 180)
  5. -- HC {hAltitude = DD 30.0, hAzimuth = DD 180.0}

Unfortunately the Horizontal coordinate system values depend on the position of the observer. And it’s not handy when you need to share coordinates of some celestial object with your friend in Japan.

The second coordinate system is the Equatorial coordinate system. This coordinate system uses the location of the centre of the Earth as the zero point so it does not depend on the observer’s location.

We have two flavours of equatorial coordinates:

  • the first one uses the vernal equinox as a starting direction for the ‘how far round’ coordinate (right ascension, α),

  • the second one uses the meridian instead of the vernal equinox (hour angle).

We can consider the second one as a transition coordinate system between the horizontal one and the ‘true’ equatorial one.

  1. import Data.Astro.Coordinate
  2. import Data.Astro.Types
  3. ec1 :: EquatorialCoordinates1
  4. ec1 = EC1 (DD 71.7) (DH 8)
  5. -- EC1 {e1Declination = DD 71.7, e1RightAscension = DH 8.0}
  6. ec2 :: EquatorialCoordinates2
  7. ec2 = EC1 (DD 77.7) (DH 11)
  8. -- EC2 {e2Declination = DD 77.7, e2HoursAngle = DH 11.0}

Transformations

Say, now is 2017-06-25 10:29 BST and we are somewhere near the Royal Observatory, Greenwich.

Let convert the current location of the Sun in horizon coordinates (altitude: 49°18′21.77″, azimuth: 118°55′19.53″) to equatorial coordinates and back to horizon ones:

  1. import Data.Astro.Time.JulianDate
  2. import Data.Astro.Coordinate
  3. import Data.Astro.Types
  4. ro :: GeographicCoordinates
  5. ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
  6. dt :: LocalCivilTime
  7. dt = lctFromYMDHMS (DH 1) 2017 6 25 10 29 0
  8. sunHC :: HorizonCoordinates
  9. sunHC = HC (fromDMS 49 18 21.77) (fromDMS 118 55 19.53)
  10. -- HC {hAltitude = DD 49.30604722222222, hAzimuth = DD 118.92209166666666}
  11. sunEC2 :: EquatorialCoordinates2
  12. sunEC2 = horizonToEquatorial (geoLatitude ro) sunHC
  13. -- EC2 {e2Declination = DD 23.378295912623855, e2HoursAngle = DH 21.437117068873537}
  14. sunEC1 :: EquatorialCoordinates1
  15. sunEC1 = EC1 (e2Declination sunEC2) (haToRA (e2HoursAngle sunEC2) (geoLongitude ro) (lctUniversalTime dt))
  16. -- EC1 {e1Declination = DD 23.378295912623855, e1RightAscension = DH 6.29383725890224}
  17. sunEC2' :: EquatorialCoordinates2
  18. sunEC2' = EC2 (e1Declination sunEC1) (raToHA (e1RightAscension sunEC1) (geoLongitude ro) (lctUniversalTime dt))
  19. -- EC2 {e2Declination = DD 23.378295912623855, e2HoursAngle = DH 21.437117068873537}
  20. sunHC' :: HorizonCoordinates
  21. sunHC' = equatorialToHorizon (geoLatitude ro) sunEC2'
  22. -- HC {hAltitude = DD 49.30604722222222, hAzimuth = DD 118.92209166666666}

You can use function-shortcuts to simplify transformation EquatorialCoordinates1 <-> HorizonCoordinates: ec1ToHC and hcToEC1:

  1. import Data.Astro.Time.JulianDate
  2. import Data.Astro.Coordinate
  3. import Data.Astro.Types
  4. ro :: GeographicCoordinates
  5. ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
  6. dt :: LocalCivilTime
  7. dt = lctFromYMDHMS (DH 1) 2017 6 25 10 29 0
  8. sunHC :: HorizonCoordinates
  9. sunHC = HC (fromDMS 49 18 21.77) (fromDMS 118 55 19.53)
  10. -- HC {hAltitude = DD 49.30604722222222, hAzimuth = DD 118.92209166666666}
  11. sunEC1 :: EquatorialCoordinates1
  12. sunEC1 = hcToEC1 ro (lctUniversalTime dt) sunHC
  13. -- EC1 {e1Declination = DD 23.378295912623855, e1RightAscension = DH 6.29383725890224}
  14. sunHC' :: HorizonCoordinates
  15. sunHC' = ec1ToHC ro (lctUniversalTime dt) sunEC1
  16. -- HC {hAltitude = DD 49.30604722222222, hAzimuth = DD 118.92209166666666}

Stars

The ancient astronomers noted that there were 2 types of stars: some of them were fixed, travelling the same way across the sky every sidereal day and another were wanderers (planetai in ancient Greek).

Of course, stars are not fixed, they are travelling with high speeds but distances to them are so high that their movement is very difficult to note. So we can assume that they are fixed for our purposes.

Given the “fixed” equatorial coordinates of the star we only need to transform them to the horizon coordinates to find out where the star in the sky.

In the example below we will use Data.Astro.Star module which defines equatorial coordinates of some stars:

  1. import Data.Astro.Time.JulianDate
  2. import Data.Astro.Coordinate
  3. import Data.Astro.Types
  4. import Data.Astro.Star
  5. ro :: GeographicCoordinates
  6. ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
  7. dt :: LocalCivilTime
  8. dt = lctFromYMDHMS (DH 1) 2017 6 25 10 29 0
  9. -- Calculate location of Betelgeuse
  10. betelgeuseEC1 :: EquatorialCoordinates1
  11. betelgeuseEC1 = starCoordinates Betelgeuse
  12. -- EC1 {e1Declination = DD 7.407064, e1RightAscension = DH 5.919529}
  13. betelgeuseHC :: HorizonCoordinates
  14. betelgeuseHC = ec1ToHC ro (lctUniversalTime dt) betelgeuseEC1
  15. -- HC {hAltitude = DD 38.30483892505852, hAzimuth = DD 136.75755644642248}

Rise and Set

Data.Astro.CelestialObject.RiseSet module defines RiseSet type to represent time and azimuth of rise and set.

Let calculate rise and set time of Rigel:

  1. import Data.Astro.Time.JulianDate
  2. import Data.Astro.Coordinate
  3. import Data.Astro.Types
  4. import Data.Astro.Effects
  5. import Data.Astro.CelestialObject.RiseSet
  6. import Data.Astro.Star
  7. ro :: GeographicCoordinates
  8. ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
  9. today :: LocalCivilDate
  10. today = lcdFromYMD (DH 1) 2017 6 25
  11. -- Calculate location of Betelgeuse
  12. rigelEC1 :: EquatorialCoordinates1
  13. rigelEC1 = starCoordinates Rigel
  14. verticalShift :: DecimalDegrees
  15. verticalShift = refract (DD 0) 12 1012
  16. -- DD 0.5660098245614035
  17. rigelRiseSet :: RiseSetLCT
  18. rigelRiseSet = riseAndSetLCT ro today verticalShift rigelEC1
  19. -- RiseSet (2017-06-25 06:38:18.4713 +1.0,DD 102.51249855335433) (2017-06-25 17:20:33.4902 +1.0,DD 257.48750144664564)

As we can see Rigel rose today at 06:38:18 and will set at 17:20:33, azimuths of rise and set 102.51° and 257.49° correspondingly.

We used refract function of Data.Astro.Effects module with reasonable default parameters to calculate vertical shift.

Planets

The planets is completely different story. We cannot assume that the planets have “fixed” location in equatorial coordinates like stars.

What we can do is to describe details of the planets’ orbit and calculate their positions at any given moment.

Planets and planet details are defined in Data.Astro.Planet module. j2010PlanetDetails returns details for the given planet.
This module also defines planetPosition, planetDistance1 and planetAngularDiameter to calculate position of the given planet, distance to the planet and angular size of the planet correspondingly.

1 at the end of the planetDistance1 means that this function uses not very precise method to do calculations. Sometimes there are 2-methods available in the library, but not always.

Let us do some planets-related calculations.

Do some initialisation:

  1. import Data.Astro.Time.JulianDate
  2. import Data.Astro.Coordinate
  3. import Data.Astro.Types
  4. import Data.Astro.Effects
  5. import Data.Astro.CelestialObject.RiseSet
  6. import Data.Astro.Planet
  7. ro :: GeographicCoordinates
  8. ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
  9. dt :: LocalCivilTime
  10. dt = lctFromYMDHMS (DH 1) 2017 6 25 10 29 0
  11. today :: LocalCivilDate
  12. today = lcdFromYMD (DH 1) 2017 6 25
  13. jupiterDetails :: PlanetDetails
  14. jupiterDetails = j2010PlanetDetails Jupiter
  15. earthDetails :: PlanetDetails
  16. earthDetails = j2010PlanetDetails Earth
  17. jupiterPosition :: JulianDate -> EquatorialCoordinates1
  18. jupiterPosition = planetPosition planetTrueAnomaly1 jupiterDetails earthDetails

Calculate Jupiter’s coordinates:

  1. jupiterEC1 :: EquatorialCoordinates1
  2. jupiterEC1 = jupiterPosition (lctUniversalTime dt)
  3. -- EC1 {e1Declination = DD (-4.104626810672402), e1RightAscension = DH 12.863365504382228}
  4. jupiterHC :: HorizonCoordinates
  5. jupiterHC = ec1ToHC ro (lctUniversalTime dt) jupiterEC1
  6. -- HC {hAltitude = DD (-30.67914598469227), hAzimuth = DD 52.29376845044007}

As be can see Jupiter is below the horizon now (the altitude is negative), that’s unfortunate.

Now let us calculate distance to Jupiter:

  1. jupiterDistance :: AstronomicalUnits
  2. jupiterDistance = planetDistance1 jupiterDetails earthDetails (lctUniversalTime dt)
  3. -- AU 5.193435872521039

1 Astronomical Unit is an average distance from the Earth to the Sun.

and calculate an angular size now:

  1. jupiterAngularSize :: DecimalDegrees
  2. jupiterAngularSize = planetAngularDiameter jupiterDetails jupiterDistance
  3. -- DD 1.052289877865987e-2
  4. toDMS jupiterAngularSize
  5. -- (0,0,37.88243560317554)

Rise and Set

Calculate rise and set times of planets are not easy task, because planets change their equatorial coordinates during the day.

riseAndSet2 function of Data.Astro.CelestialObject.RiseSet module applies iterative approach: calculates rise and set date for midday coordinates and then recalculates rise time for rise coordinates and set for set coordinates obtained from the previous step:

  1. verticalShift :: DecimalDegrees
  2. verticalShift = refract (DD 0) 12 1012
  3. -- DD 0.5660098245614035
  4. jupiterRiseSet :: RiseSetMB
  5. jupiterRiseSet = riseAndSet2 0.000001 jupiterPosition ro verticalShift today
  6. -- RiseSet
  7. -- (Just (2017-06-25 13:53:27.3109 +1.0,DD 95.88943953535569))
  8. -- (Just (2017-06-25 01:21:23.5835 +1.0,DD 264.1289033612776))

We can see now why at 10 am Jupiter is below horizon because it will rise only at 1:53 pm.

Sun

Some examples of doing the Sun’s related calculations:

  1. import Data.Astro.Time.JulianDate
  2. import Data.Astro.Coordinate
  3. import Data.Astro.Types
  4. import Data.Astro.Sun
  5. ro :: GeographicCoordinates
  6. ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
  7. dt :: LocalCivilTime
  8. dt = lctFromYMDHMS (DH 1) 2017 6 25 10 29 0
  9. today :: LocalCivilDate
  10. today = lcdFromYMD (DH 1) 2017 6 25
  11. jd :: JulianDate
  12. jd = lctUniversalTime dt
  13. verticalShift :: DecimalDegrees
  14. verticalShift = refract (DD 0) 12 1012
  15. -- distance from the Earth to the Sun in kilometres
  16. distance :: Double
  17. distance = sunDistance jd
  18. -- 1.5206375976421073e8
  19. -- Angular Size
  20. angularSize :: DecimalDegrees
  21. angularSize = sunAngularSize jd
  22. -- DD 0.5244849215333616
  23. -- The Sun's coordinates
  24. ec1 :: EquatorialCoordinates1
  25. ec1 = sunPosition2 jd
  26. -- EC1 {e1Declination = DD 23.37339098989099, e1RightAscension = DH 6.29262026252748}
  27. hc :: HorizonCoordinates
  28. hc = ec1ToHC ro jd ec1
  29. -- HC {hAltitude = DD 49.312050979507404, hAzimuth = DD 118.94723825710143}
  30. -- Rise and Set
  31. riseSet :: RiseSetMB
  32. riseSet = sunRiseAndSet ro 0.833333 today
  33. -- RiseSet
  34. -- (Just (2017-06-25 04:44:04.3304 +1.0,DD 49.043237261724215))
  35. -- (Just (2017-06-25 21:21:14.4565 +1.0,DD 310.91655607595595))

Moon

The Moon’s related calculations. Data.Astro.Moon module defines 2 new types of functions we haven’t seen before: moonPhase and moonBrightLimbPositionAngle which calculate the phase (the area of the visible segment expressed as a fraction of the whole disk) and the position-angle which is the angle of the midpoint of the illuminated limb measured eastwards from the north point of the disk.

  1. import Data.Astro.Time.JulianDate
  2. import Data.Astro.Coordinate
  3. import Data.Astro.Types
  4. import Data.Astro.Effects
  5. import Data.Astro.CelestialObject.RiseSet
  6. import Data.Astro.Moon
  7. ro :: GeographicCoordinates
  8. ro = GeoC (fromDMS 51 28 40) (-(fromDMS 0 0 5))
  9. dt :: LocalCivilTime
  10. dt = lctFromYMDHMS (DH 1) 2017 6 25 10 29 0
  11. today :: LocalCivilDate
  12. today = lcdFromYMD (DH 1) 2017 6 25
  13. jd :: JulianDate
  14. jd = lctUniversalTime dt
  15. -- distance from the Earth to the Moon in kilometres
  16. mdu :: MoonDistanceUnits
  17. mdu = moonDistance1 j2010MoonDetails jd
  18. -- MDU 0.9550170577020396
  19. distance :: Double
  20. distance = mduToKm mdu
  21. -- 367109.51199772174
  22. -- Angular Size
  23. angularSize :: DecimalDegrees
  24. angularSize = moonAngularSize mdu
  25. -- DD 0.5425033990980761
  26. -- The Moon's coordinates
  27. position :: JulianDate -> EquatorialCoordinates1
  28. position = moonPosition1 j2010MoonDetails
  29. ec1 :: EquatorialCoordinates1
  30. ec1 = position jd
  31. -- EC1 {e1Declination = DD 18.706180658927323, e1RightAscension = DH 7.56710547682055}
  32. hc :: HorizonCoordinates
  33. hc = ec1ToHC ro jd ec1
  34. -- HC {hAltitude = DD 34.57694951316064, hAzimuth = DD 103.91119101451832}
  35. -- Rise and Set
  36. riseSet :: RiseSetMB
  37. riseSet = riseAndSet2 0.000001 position ro verticalShift today
  38. -- RiseSet
  39. -- (Just (2017-06-25 06:22:51.4858 +1.0,DD 57.81458864497365))
  40. -- (Just (2017-06-25 22:28:20.3023 +1.0,DD 300.4168238905249))
  41. -- Phase
  42. phase :: Double
  43. phase = moonPhase j2010MoonDetails jd
  44. -- 2.4716141948212922e-2
  45. sunEC1 :: EquatorialCoordinates1
  46. sunEC1 = sunPosition2 jd
  47. -- EC1 {e1Declination = DD 23.37339098989099, e1RightAscension = DH 6.29262026252748}
  48. limbAngle :: DecimalDegrees
  49. limbAngle = moonBrightLimbPositionAngle ec1 sunEC1
  50. -- DD 287.9869373767473