sampa

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 24, 2025 License: MIT Imports: 4 Imported by: 0

README

Go SAMPA Go Reference Classic Go Reference

Go SAMPA is a package for calculating Sun and Moon position. SAMPA itself is acronym of Sun and Moon Position Algorithm. It uses algorithms from several sources:

  • Algorithm for calculating Sun position is taken from SPA paper by Ibrahim Reda and Afshin Andreas from NREL.
  • Algorithm for calculating Moon position is taken from SAMPA paper which also created by Ibrahim Reda and Afshin Andreas.
  • Algorithm for rise, set, transit time and phases of moon are taken from Astronomical Algorithms by Jean Meeus.

Table of Contents

Installation

To use this package, make sure your project use Go 1.20 or above, then run the following command via terminal:

go get -u -v github.com/Oybek-uzb/go-sampa

Features

  • Calculate Sun and Moon position for a given time and position.
  • Calculate rise, set and transit for Sun and Moon.
  • Calculate time when Sun or Moon reach a certain elevation.
  • Calculate moon phases around a specified date.
  • Thanks to Go, it will seamlessly handle DST times.
  • Fast and accurate enough for amateur use.

API

You can check the Go documentations to see the available APIs. However, the main interests in this package are:

  • Function GetSunPosition to calculate Sun location for the given time and location.
  • Function GetSunEvents to calculate Sunrise, Sunset, transit and any other additional events for the given time and location.
  • Function GetMoonPosition to calculate Moon location for the given time and location.
  • Function GetMoonEvents to calculate Moonrise, Moonset, transit and any other additional events for the given time and location.
  • Function GetMoonPhases to calculate the time for Moon phases around the specified date time.

Here is the example for all of those functions:

func main() {
	// Prepare location
	tz, _ := time.LoadLocation("Asia/Jakarta")
	jakarta := sampa.Location{Latitude: -6.14, Longitude: 106.81}

	// Fetch Sun position in Jakarta at 2023-05-20 18:15:00
	dt := time.Date(2023, 5, 20, 18, 15, 0, 0, tz)
	sunPosition, _ := sampa.GetSunPosition(dt, jakarta, nil)

	// Fetch Moon position in Jakarta at 2023-05-20 20:15:00
	dt = time.Date(2023, 5, 20, 20, 15, 0, 0, tz)
	moonPosition, _ := sampa.GetMoonPosition(dt, jakarta, nil)

	// Fetch Sun and Moon events in Jakarta at 2023-11-20
	dt = time.Date(2023, 11, 20, 0, 0, 0, 0, tz)
	sunEvents, _ := sampa.GetSunEvents(dt, jakarta, nil)
	moonEvents, _ := sampa.GetMoonEvents(dt, jakarta, nil)

	// Fetch Moon phases in Jakarta around 2023-03-05
	dt = time.Date(2023, 3, 5, 0, 0, 0, 0, tz)
	moonPhases := sampa.GetMoonPhases(dt, nil)

	// To handle DST, just load the timezone properly.
	// For example, here we calculate Sun in Oslo that use DST.
	cet, _ := time.LoadLocation("CET")
	oslo := sampa.Location{Latitude: 59.91, Longitude: 10.74}
	dt = time.Date(2023, 5, 20, 18, 15, 0, 0, cet)
	sunPositionInOslo, _ := sampa.GetSunPosition(dt, oslo, nil)
}

Accuracy

Disclaimer: I'm not an astronomer or physicist. I'm just an amateur with some interests toward celestial bodies.

This package has been extensively compared to Time and Date, which uses algorithm based on work by the U.S. Naval Observatory and NASA's Jet Propulsion Laboratory. I've compared the calculations for following locations:

Name Country Latitude Longitude Timezone Offset DST Offset
Lord Howe Island Australia 31°33'S 159°05'E Australia/Lord_Howe UTC+10:30 UTC+11:00
Maputo Mozambique 25°58'S 32°34'E Africa/Maputo UTC+02:00
Amsterdam Netherlands 52°22'N 4°54'E CET UTC+01:00 UTC+02:00
Oslo Norway 59°55'N 10°44'E CET UTC+01:00 UTC+02:00
Philipsburg Sint Maarten 18°02'N 63°03'W UTC-4 UTC-04:00
New York USA 40°43'N 74°01'W America/New_York UTC-05:00 UTC-04:00

The calculations can be seen in comparison directory.

From the comparison, I've found that by average the results are accurate to within a minute for both Sun and Moon events. The difference are pretty small for area around equators, and become quite large for area with higher latitude (>45°) where the Sun might not rise or set for the entire day. However, I argue this package is still correct and accurate enough to use.

For example, the largest time difference for Sun event in our comparison data is for astronomical dusk in Oslo at 22 April 2023:

  • TimeAndDate said the astronomical dusk will occur at "2023-04-22 01:00:16"
  • Go SAMPA calculation result is "2023-04-22 01:01:04" (48 seconds difference)

However, if we compare the Sun elevation angle for both times using the official SPA calculator, we'll get the following result:

  • At "2023-04-22 01:00:16" (TimeAndDate result) the Sun elevation is -17.995° (calc).
  • At "2023-04-22 01:01:04" (Go SAMPA result) the Sun elevation is -18.002° (calc).

Since astronomical dusk occured when Sun is 18° below horizons, both calculations are correct despite the huge time difference between them.

FAQ

  1. Does the elevation affects calculation result?

    Yes, it will affect the result for rise and set time. If the elevation is very high, it can affect the times by a couple of minutes thanks to atmospheric refraction. However, most apps that I know prefer to set elevation to zero, which means every locations will be treated as located in sea level.

  2. The rise and set times are different compared to reality!

    Apparently there are no algorithms that could exactly calculate when the Sun will rise and set, and I assume it's the same for the Moon.

  3. Are the calculation results are accurate up to seconds?

    While the results of this package are in seconds, as mentioned above there are several seconds different between this package and other famous calculators like TimeAndDate. So, it's better to not expect it to be exactly accurate to seconds, and instead treat it as minute rounding suggestions.

  4. Why are the sunrise and sunset times occured in different day?

    In this package, the sunrise and sunset are connected to transit time (the time when Sun reach meridian). However, in area with higher latitude sometime the Sun will never rise nor set for the entire day. In this case, sunrise might occur yesterday and the sunset might occur tomorrow.

  5. Why are the moonrise and moonset times not calculated?

    There are some days where Moon never reach the meridian. In those case the moonrise and moonset will not be calculated since in this package rise and set are chained to transit time.

Resources

  1. Reda, I.; Andreas, A. (2003). Solar Position Algorithm for Solar Radiation Applications. 55 pp.; NREL Report No. TP-560-34302, Revised January 2008. (web)
  2. Reda, I. (2010). Solar Eclipse Monitoring for Solar Energy Applications Using the Solar and Moon Position Algorithms. 35 pp.; NREL Report No. TP-3B0-47681. (web)
  3. Meeus, J. (1998). Astronomical Algorithms, Second Edition.

License

Go-Sampa is distributed using MIT license.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CustomMoonEvent

type CustomMoonEvent struct {
	Name          string
	BeforeTransit bool
	Elevation     func(todayData MoonPosition) float64
}

CustomMoonEvent is the custom event when Moon reach the specified elevation angle.

type CustomSunEvent

type CustomSunEvent struct {
	Name          string
	BeforeTransit bool
	Elevation     func(todayData SunPosition) float64
}

CustomSunEvent is the custom event when Sun reach the specified elevation angle.

type Location

type Location struct {
	Latitude    float64
	Longitude   float64
	Elevation   float64
	Temperature float64
	Pressure    float64
}

Location is coordinate and data for the location where Sun and Moon position will be calculated.

type MoonEvents

type MoonEvents struct {
	Transit  MoonPosition
	Moonrise MoonPosition
	Moonset  MoonPosition
	Others   map[string]MoonPosition
}

MoonEvents is the positions of Moon when rise, set, transit, and reached custom elevation angles.

func GetMoonEvents

func GetMoonEvents(date time.Time, loc Location, opts *Options, customEvents ...CustomMoonEvent) (MoonEvents, error)

GetMoonEvents calculates the time when Moon rise, set, transit, and reached elevation angles that defined by `CustomMoonEvent`.

type MoonPhase

type MoonPhase uint8
const (
	NewMoon MoonPhase = iota + 1
	WaxingCrescent
	FirstQuarter
	WaxingGibbous
	FullMoon
	WaningGibbous
	LastQuarter
	WaningCrescent
)

func (MoonPhase) String

func (mp MoonPhase) String() string

type MoonPhases

type MoonPhases struct {
	NewMoon      time.Time
	FirstQuarter time.Time
	FullMoon     time.Time
	LastQuarter  time.Time
	NextNewMoon  time.Time
	MonthLength  float64
}

MoonPhases is times when Moon reach its phases.

func GetMoonPhases

func GetMoonPhases(dt time.Time, opts *Options) MoonPhases

GetMoonPhases calculate the time for Moon phases around the specified date time. It uses timezone in `dt` for the location, so adjust it properly.

type MoonPosition

type MoonPosition struct {
	DateTime                     time.Time
	JulianDay                    float64
	GeocentricLongitude          float64
	GeocentricLatitude           float64
	GeocentricDistance           float64
	HorizontalParallax           float64
	NutationLongitude            float64
	NutationObliquity            float64
	EclipticTrueObliquity        float64
	ApparentLongitude            float64
	MeanSiderealTime             float64
	ApparentSiderealTime         float64
	GeocentricRightAscension     float64
	GeocentricDeclination        float64
	ObserverLocalHourAngle       float64
	RightAscensionParallax       float64
	TopocentricRightAscension    float64
	TopocentricDeclination       float64
	TopocentricLocalHourAngle    float64
	TopocentricElevationAngle    float64
	TopocentricZenithAngle       float64
	TopocentricAstroAzimuthAngle float64
	TopocentricAzimuthAngle      float64
	Elongation                   float64
	PercentIlluminated           float64
	MoonSunAngle                 float64
	Phase                        MoonPhase
}

MoonPosition is the result from calculating Moon position.

func GetMoonPosition

func GetMoonPosition(dt time.Time, loc Location, opts *Options) (MoonPosition, error)

GetMoonPosition calculates the Moon position for the specified location and date time.

func (MoonPosition) IsZero

func (mp MoonPosition) IsZero() bool

IsZero reports whether Moon position is empty or not.

type Options

type Options struct {
	SurfaceSlope           float64
	SurfaceAzimuthRotation float64
	DeltaT                 float64
}

Options is the advanced configuration for calculating the Sun and Moon position.

type SunEvents

type SunEvents struct {
	Transit SunPosition
	Sunrise SunPosition
	Sunset  SunPosition
	Others  map[string]SunPosition
}

SunEvents is the positions of Sun when rise, set, transit, and reached custom elevation angles.

func GetSunEvents

func GetSunEvents(date time.Time, loc Location, opts *Options, customEvents ...CustomSunEvent) (SunEvents, error)

GetSunEvents calculates the time when Sun rise, set, transit, and reached elevation angles that defined by `CustomSunEvent`.

type SunPosition

type SunPosition struct {
	DateTime                     time.Time
	JulianDay                    float64
	EarthHeliocentricLongitude   float64
	EarthHeliocentricLatitude    float64
	EarthRadiusVector            float64
	GeocentricLongitude          float64
	GeocentricLatitude           float64
	NutationLongitude            float64
	NutationObliquity            float64
	EclipticTrueObliquity        float64
	AbberationCorrection         float64
	ApparentLongitude            float64
	MeanSiderealTime             float64
	ApparentSiderealTime         float64
	GeocentricRightAscension     float64
	GeocentricDeclination        float64
	ObserverLocalHourAngle       float64
	RightAscensionParallax       float64
	TopocentricRightAscension    float64
	TopocentricDeclination       float64
	TopocentricLocalHourAngle    float64
	TopocentricElevationAngle    float64
	TopocentricZenithAngle       float64
	TopocentricAstroAzimuthAngle float64
	TopocentricAzimuthAngle      float64
	SurfaceIncidenceAngle        float64
}

SunPosition is the result from calculating Sun position.

func GetSunPosition

func GetSunPosition(dt time.Time, loc Location, opts *Options) (SunPosition, error)

GetSunPosition calculates the Sun position for the specified location and date time.

func (SunPosition) IsZero

func (sp SunPosition) IsZero() bool

IsZero reports whether Sun position is empty or not.

Directories

Path Synopsis
scripts

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL