From eba2b50eb4fdf724da899734f4b8d5f206c49d10 Mon Sep 17 00:00:00 2001 From: Don Cross Date: Wed, 1 Nov 2023 16:43:48 -0400 Subject: [PATCH] Go: added function HorizonFromVector. --- generate/template/astronomy.go | 28 ++++++++ source/golang/README.md | 113 +++++++++++++++++++-------------- source/golang/astronomy.go | 28 ++++++++ 3 files changed, 123 insertions(+), 46 deletions(-) diff --git a/generate/template/astronomy.go b/generate/template/astronomy.go index beefee64..90c423a5 100644 --- a/generate/template/astronomy.go +++ b/generate/template/astronomy.go @@ -513,6 +513,34 @@ func EquatorFromVector(vector AstroVector) Equatorial { } } +// Converts cartesian coordinates to horizontal coordinates. +// Given a horizontal Cartesian vector, returns horizontal azimuth and altitude. +// +// IMPORTANT: This function differs from SphereFromVector in two ways: +// - SphereFromVector returns a `Lon` value that represents azimuth defined counterclockwise +// from north (e.g., west = +90), but this function represents a clockwise rotation +// (e.g., east = +90). The difference is because `SphereFromVector` is intended +// to preserve the vector "right-hand rule", while this function defines azimuth in a more +// traditional way as used in navigation and cartography. +// - This function optionally corrects for atmospheric refraction, while `SphereFromVector` +// does not. +// +// The returned structure contains the azimuth in `Lon`. +// It is measured in degrees clockwise from north: east = +90 degrees, west = +270 degrees. +// +// The altitude is stored in `Lat`. +// +// The distance to the observed object is stored in `Dist`, +// and is expressed in astronomical units (AU). +func HorizonFromVector(vector AstroVector, refraction Refraction) Spherical { + sphere := SphereFromVector(vector) + return Spherical{ + sphere.Lat + RefractionAngle(refraction, sphere.Lat), + toggleAzimuthDirection(sphere.Lon), + sphere.Dist, + } +} + // Converts spherical coordinates to Cartesian coordinates. func VectorFromSphere(sphere Spherical, time AstroTime) AstroVector { radlat := RadiansFromDegrees(sphere.Lat) diff --git a/source/golang/README.md b/source/golang/README.md index bb367a09..aebe6724 100644 --- a/source/golang/README.md +++ b/source/golang/README.md @@ -69,6 +69,7 @@ It provides a suite of well\-tested functions for calculating positions of the S - [type SearchContext](<#SearchContext>) - [type SeasonsInfo](<#SeasonsInfo>) - [type Spherical](<#Spherical>) + - [func HorizonFromVector\(vector AstroVector, refraction Refraction\) Spherical](<#HorizonFromVector>) - [func SphereFromVector\(vector AstroVector\) Spherical](<#SphereFromVector>) - [type StateVector](<#StateVector>) - [func RotateState\(rotation RotationMatrix, state StateVector\) StateVector](<#RotateState>) @@ -173,7 +174,7 @@ const ( ``` -## func [AngleBetween]() +## func [AngleBetween]() ```go func AngleBetween(avec AstroVector, bvec AstroVector) float64 @@ -191,7 +192,7 @@ func DaysFromCalendar(year, month, day, hour, minute int, second float64) float6 -## func [DefineStar]() +## func [DefineStar]() ```go func DefineStar(body Body, ra, dec, distanceLightYears float64) error @@ -200,7 +201,7 @@ func DefineStar(body Body, ra, dec, distanceLightYears float64) error -## func [DegreesFromRadians]() +## func [DegreesFromRadians]() ```go func DegreesFromRadians(radians float64) float64 @@ -218,7 +219,7 @@ func Dot(a, b AstroVector) float64 Returns the scalar dot product of two vectors. -## func [MassProduct]() +## func [MassProduct]() ```go func MassProduct(body Body) float64 @@ -227,7 +228,7 @@ func MassProduct(body Body) float64 Returns the product of mass and universal gravitational constant of a Solar System body. For problems involving the gravitational interactions of Solar System bodies, it is helpful to know the product GM, where G = the universal gravitational constant and M = the mass of the body. In practice, GM is known to a higher precision than either G or M alone, and thus using the product results in the most accurate results. This function returns the product GM in the units au^3/day^2. The values come from page 10 of a JPL memorandum regarding the DE405/LE405 ephemeris: https://web.archive.org/web/20120220062549/http://iau-comm4.jpl.nasa.gov/de405iom/de405iom.pdf -## func [ObserverGravity]() +## func [ObserverGravity]() ```go func ObserverGravity(latitude, height float64) float64 @@ -236,7 +237,7 @@ func ObserverGravity(latitude, height float64) float64 Calculates the gravitational acceleration experienced by an observer on the Earth. This function implements the WGS 84 Ellipsoidal Gravity Formula. The result is a combination of inward gravitational acceleration with outward centrifugal acceleration, as experienced by an observer in the Earth's rotating frame of reference. The resulting value increases toward the Earth's poles and decreases toward the equator, consistent with changes of the weight measured by a spring scale of a fixed mass moved to different latitudes and heights on the Earth. The latitude is of the observer in degrees north or south of the equator. By formula symmetry, positive latitudes give the same answer as negative latitudes, so the sign does not matter. The height is specified above the sea level geoid in meters. No range checking is done; however, accuracy is only valid in the range 0 to 100000 meters. The return value is the gravitational acceleration expressed in meters per second squared. -## func [PlanetOrbitalPeriod]() +## func [PlanetOrbitalPeriod]() ```go func PlanetOrbitalPeriod(body Body) float64 @@ -245,7 +246,7 @@ func PlanetOrbitalPeriod(body Body) float64 PlanetOrbitalPeriod returns the average number of days it takes for a planet to orbit the Sun. -## func [RadiansFromDegrees]() +## func [RadiansFromDegrees]() ```go func RadiansFromDegrees(degrees float64) float64 @@ -254,7 +255,7 @@ func RadiansFromDegrees(degrees float64) float64 RadiansFromDegrees converts an angle expressed in degrees to an angle expressed in radians. -## func [RefractionAngle]() +## func [RefractionAngle]() ```go func RefractionAngle(refraction Refraction, altitude float64) float64 @@ -263,7 +264,7 @@ func RefractionAngle(refraction Refraction, altitude float64) float64 RefractionAngle calculates the amount of "lift" to an altitude angle caused by atmospheric refraction. Given an altitude angle and a refraction option, calculates the amount of "lift" caused by atmospheric refraction. This is the number of degrees higher in the sky an object appears due to the lensing of the Earth's atmosphere. This function works best near sea level. To correct for higher elevations, call Atmosphere for that elevation and multiply the refraction angle by the resulting relative density. The refraction parameter specifies which refraction correction to use. If set to NormalRefraction, uses a well\-behaved refraction model that works well for all valid values \(\-90 to \+90\) of altitude. If set to JplHorizonsRefraction, this function returns a value compatible with the JPL Horizons tool. This is provided for internal unit tests that compare against JPL Horizons data. Any other value, including NoRefraction, causes this function to return 0.0. The return value is a non\-negative value expressed in degrees of refraction above the horizontal. -## func [SiderealTime]() +## func [SiderealTime]() ```go func SiderealTime(time *AstroTime) float64 @@ -272,7 +273,7 @@ func SiderealTime(time *AstroTime) float64 Given a date and time, SiderealTime calculates the rotation of the Earth, represented by the equatorial angle of the Greenwich prime meridian with respect to distant stars \(not the Sun, which moves relative to background stars by almost one degree per day\). This angle is called Greenwich Apparent Sidereal Time \(GAST\). GAST is measured in sidereal hours in the half\-open range \[0, 24\). When GAST = 0, it means the prime meridian is aligned with the of\-date equinox, corrected at that time for precession and nutation of the Earth's axis. In this context, the "equinox" is the direction in space where the Earth's orbital plane \(the ecliptic\) intersects with the plane of the Earth's equator, at the location on the Earth's orbit of the \(seasonal\) March equinox. As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, then starts over at 0. To convert to degrees, multiply the return value by 15. As an optimization, this function caches the sidereal time value in the time parameter. The value is reused later as needed, to avoid redundant calculations. -## type [AstroMoonQuarter]() +## type [AstroMoonQuarter]() @@ -284,7 +285,7 @@ type AstroMoonQuarter struct { ``` -## type [AstroSearchFunc]() +## type [AstroSearchFunc]() @@ -391,7 +392,7 @@ type AstroVector struct { ``` -### func [CorrectLightTravel]() +### func [CorrectLightTravel]() ```go func CorrectLightTravel(posFunc PositionFunction, time AstroTime) (*AstroVector, error) @@ -408,7 +409,7 @@ For common use cases, it is simpler to use BackdatePosition for calculating the For geocentric calculations, GeoVector also backdates the returned position vector for light travel time, only it returns the observation time in the returned vector's \`t\` field rather than the backdated time. -### func [GeoMoon]() +### func [GeoMoon]() ```go func GeoMoon(time AstroTime) AstroVector @@ -417,7 +418,7 @@ func GeoMoon(time AstroTime) AstroVector GeoMoon calculates the equatorial geocentric position of the Moon at a given time. The returned vector indicates the Moon's center relative to the Earth's center. The vector components are expressed in AU \(astronomical units\). The coordinates are oriented with respect to the Earth's equator at the J2000 epoch. In Astronomy Engine, this orientation is called EQJ. -### func [RotateVector]() +### func [RotateVector]() ```go func RotateVector(rotation RotationMatrix, vector AstroVector) AstroVector @@ -426,7 +427,7 @@ func RotateVector(rotation RotationMatrix, vector AstroVector) AstroVector RotateVector applies a rotation to a vector, yielding a vector in another orientation system. -### func [VectorFromSphere]() +### func [VectorFromSphere]() ```go func VectorFromSphere(sphere Spherical, time AstroTime) AstroVector @@ -444,7 +445,7 @@ func (vec AstroVector) Length() float64 Returns the length of vec expressed in the same distance units as vec's components. -## type [AtmosphereInfo]() +## type [AtmosphereInfo]() AtmosphereInfo contains information about idealized atmospheric variables at a given elevation. @@ -466,7 +467,7 @@ func Atmosphere(elevationMeters float64) (AtmosphereInfo, error) Atmosphere calculates U.S. Standard Atmosphere \(1976\) variables as a function of elevation. elevationMeters is the elevation above sea level at which to calculate atmospheric variables. It must be in the range \-500 to \+100000 or an error will occur. 1. COESA, U.S. Standard Atmosphere, 1976, U.S. Government Printing Office, Washington, DC, 1976. 2. Jursa, A. S., Ed., Handbook of Geophysics and the Space Environment, Air Force Geophysics Laboratory, 1985. See: https://hbcp.chemnetbase.com/faces/documents/14_12/14_12_0001.xhtml https://ntrs.nasa.gov/api/citations/19770009539/downloads/19770009539.pdf https://www.ngdc.noaa.gov/stp/space-weather/online-publications/miscellaneous/us-standard-atmosphere-1976/us-standard-atmosphere_st76-1562_noaa.pdf -## type [AxisInfo]() +## type [AxisInfo]() @@ -480,7 +481,7 @@ type AxisInfo struct { ``` -## type [Body]() +## type [Body]() @@ -514,7 +515,7 @@ func CalendarFromDays(ut float64) (*CalendarDateTime, error) CalendarFromDays converts a J2000 day value to a Gregorian calendar date and time. -## type [DeltaTimeFunc]() +## type [DeltaTimeFunc]() @@ -523,7 +524,7 @@ type DeltaTimeFunc func(ut float64) float64 ``` -## type [EclipseKind]() +## type [EclipseKind]() @@ -532,7 +533,7 @@ type EclipseKind int ``` -## type [Ecliptic]() +## type [Ecliptic]() A location of a body expressed in angular coordinates relative to the plane of the Earth's orbit around the Sun @@ -545,7 +546,7 @@ type Ecliptic struct { ``` -## type [Equatorial]() +## type [Equatorial]() A location of a body expressed in angular coordinates relative to the Earth's equator @@ -568,7 +569,7 @@ func EquatorFromVector(vector AstroVector) Equatorial Given an equatorial vector, calculates equatorial angular coordinates. -## type [JupiterMoonsInfo]() +## type [JupiterMoonsInfo]() @@ -582,7 +583,7 @@ type JupiterMoonsInfo struct { ``` -### func [JupiterMoons]() +### func [JupiterMoons]() ```go func JupiterMoons(time AstroTime) JupiterMoonsInfo @@ -591,7 +592,7 @@ func JupiterMoons(time AstroTime) JupiterMoonsInfo Calculates Jovicentric positoins and velocities of Jupiter's largest 4 moons. Calculates position and velocity vectors for Jupiter's moons Io, Europa, Ganymede, and Callisto, at the given date and time. The vectors are jovicentric \(relative to the center of Jupiter\). Their orientation is the Earth's equatorial system at the J2000 epoch \(EQJ\). The position components are expressed in astronomical units \(AU\), and the velocity components are in AU/day. To convert to heliocentric position vectors, call HelioVector with Jupiter as the body to get Jupiter's heliocentric position, then add the jovicentric moon positions. Likewise, you can call \#Astronomy.GeoVector to convert to geocentric positions; however, you will have to manually correct for light travel time from the Jupiter system to Earth to figure out what time to pass to \`JupiterMoons\` to get an accurate picture of how Jupiter and its moons look from Earth. -## type [LibrationInfo]() +## type [LibrationInfo]() @@ -607,7 +608,7 @@ type LibrationInfo struct { ``` -## type [NodeEventInfo]() +## type [NodeEventInfo]() @@ -619,7 +620,7 @@ type NodeEventInfo struct { ``` -## type [NodeEventKind]() +## type [NodeEventKind]() @@ -628,7 +629,7 @@ type NodeEventKind int ``` -## type [Observer]() +## type [Observer]() The location of a point on or near the surface of the Earth @@ -641,7 +642,7 @@ type Observer struct { ``` -## type [PositionFunction]() +## type [PositionFunction]() The function CorrectLightTravel solves a generalized problem of deducing how far in the past light must have left a target object to be seen by an observer at a specified time. This interface expresses an arbitrary position function as a function of time that is passed to CorrectLightTravel. @@ -652,7 +653,7 @@ type PositionFunction interface { ``` -## type [Refraction]() +## type [Refraction]() @@ -671,7 +672,7 @@ const ( ``` -## type [RotationMatrix]() +## type [RotationMatrix]() RotationMatrix is a 3x3 matrix used to convert a vector from one orientation system to another. @@ -682,7 +683,7 @@ type RotationMatrix struct { ``` -### func [CombineRotation]() +### func [CombineRotation]() ```go func CombineRotation(a, b RotationMatrix) RotationMatrix @@ -691,7 +692,7 @@ func CombineRotation(a, b RotationMatrix) RotationMatrix CombineRotation combines the effects of two consecutive rotation matrices into a single rotation matrix. -### func [IdentityMatrix]() +### func [IdentityMatrix]() ```go func IdentityMatrix() RotationMatrix @@ -700,7 +701,7 @@ func IdentityMatrix() RotationMatrix Creates a rotation matrix that represents no rotation at all. -### func [InverseRotation]() +### func [InverseRotation]() ```go func InverseRotation(rotation RotationMatrix) RotationMatrix @@ -709,7 +710,7 @@ func InverseRotation(rotation RotationMatrix) RotationMatrix Calculates the inverse of a rotation matrix. Given a rotation matrix that performs some coordinate transform, this function returns the matrix that reverses that transform. -### func [Pivot]() +### func [Pivot]() ```go func Pivot(rotation RotationMatrix, axis int, angle float64) (*RotationMatrix, error) @@ -718,7 +719,7 @@ func Pivot(rotation RotationMatrix, axis int, angle float64) (*RotationMatrix, e Pivot re\-orients a rotation matrix by pivoting it by an angle around one of its axes. Given a rotation matrix, a selected coordinate axis, and an angle in degrees, this function pivots the rotation matrix by that angle around that coordinate axis. For example, if you have rotation matrix that converts ecliptic coordinates \(ECL\) to horizontal coordinates \(HOR\), but you really want to convert ECL to the orientation of a telescope camera pointed at a given body, you can use Pivot twice: \(1\) pivot around the zenith axis by the body's azimuth, then \(2\) pivot around the western axis by the body's altitude angle. The resulting rotation matrix will then reorient ECL coordinates to the orientation of your telescope camera. The axis parameter is an integer that selects which axis to pivot about: 0=x, 1=y, 2=z. The angle parameter is an angle in degrees indicating the amount of rotation around the specified axis. Positive angles indicate rotation counterclockwise as seen from the positive direction along that axis, looking towards the origin point of the orientation system. Any finite number of degrees is allowed, but best precision will result from keeping angle in the range \[\-360, \+360\]. -### func [RotationEclEqj]() +### func [RotationEclEqj]() ```go func RotationEclEqj() RotationMatrix @@ -727,7 +728,7 @@ func RotationEclEqj() RotationMatrix Calculates a rotation matrix from J2000 mean ecliptic \(ECL\) to J2000 mean equator \(EQJ\). -### func [RotationEqdEqj]() +### func [RotationEqdEqj]() ```go func RotationEqdEqj(time *AstroTime) RotationMatrix @@ -736,7 +737,7 @@ func RotationEqdEqj(time *AstroTime) RotationMatrix Calculates a rotation matrix that converts equator\-of\-date \(EQD\) to J2000 mean equator \(EQJ\). -### func [RotationEqjEcl]() +### func [RotationEqjEcl]() ```go func RotationEqjEcl() RotationMatrix @@ -745,7 +746,7 @@ func RotationEqjEcl() RotationMatrix Calculates a rotation matrix from J2000 mean equator \(EQJ\) to J2000 mean ecliptic \(ECL\). -### func [RotationEqjGal]() +### func [RotationEqjGal]() ```go func RotationEqjGal() RotationMatrix @@ -754,7 +755,7 @@ func RotationEqjGal() RotationMatrix Calculates a rotation matrix from J2000 mean equator \(EQJ\) to galactic \(GAL\). -### func [RotationGalEqj]() +### func [RotationGalEqj]() ```go func RotationGalEqj() RotationMatrix @@ -763,7 +764,7 @@ func RotationGalEqj() RotationMatrix -## type [SearchContext]() +## type [SearchContext]() @@ -774,7 +775,7 @@ type SearchContext interface { ``` -## type [SeasonsInfo]() +## type [SeasonsInfo]() @@ -800,6 +801,26 @@ type Spherical struct { } ``` + +### func [HorizonFromVector]() + +```go +func HorizonFromVector(vector AstroVector, refraction Refraction) Spherical +``` + +Converts cartesian coordinates to horizontal coordinates. Given a horizontal Cartesian vector, returns horizontal azimuth and altitude. + +IMPORTANT: This function differs from SphereFromVector in two ways: + +- SphereFromVector returns a \`Lon\` value that represents azimuth defined counterclockwise from north \(e.g., west = \+90\), but this function represents a clockwise rotation \(e.g., east = \+90\). The difference is because \`SphereFromVector\` is intended to preserve the vector "right\-hand rule", while this function defines azimuth in a more traditional way as used in navigation and cartography. +- This function optionally corrects for atmospheric refraction, while \`SphereFromVector\` does not. + +The returned structure contains the azimuth in \`Lon\`. It is measured in degrees clockwise from north: east = \+90 degrees, west = \+270 degrees. + +The altitude is stored in \`Lat\`. + +The distance to the observed object is stored in \`Dist\`, and is expressed in astronomical units \(AU\). + ### func [SphereFromVector]() @@ -827,7 +848,7 @@ type StateVector struct { ``` -### func [RotateState]() +### func [RotateState]() ```go func RotateState(rotation RotationMatrix, state StateVector) StateVector @@ -854,7 +875,7 @@ func (state StateVector) Velocity() AstroVector Position returns the velocity vector inside a state vector. -## type [TimeFormat]() +## type [TimeFormat]() @@ -874,7 +895,7 @@ const ( ``` -## type [Topocentric]() +## type [Topocentric]() A location of a body as seen from an observer's point of view on or near the surface of the Earth. The topocentric position can optionally be corrected for atmospheric refraction. @@ -888,7 +909,7 @@ type Topocentric struct { ``` -### func [Horizon]() +### func [Horizon]() ```go func Horizon(time AstroTime, observer Observer, ra, dec float64, refraction Refraction) (*Topocentric, error) diff --git a/source/golang/astronomy.go b/source/golang/astronomy.go index 88be6633..fc84433c 100644 --- a/source/golang/astronomy.go +++ b/source/golang/astronomy.go @@ -513,6 +513,34 @@ func EquatorFromVector(vector AstroVector) Equatorial { } } +// Converts cartesian coordinates to horizontal coordinates. +// Given a horizontal Cartesian vector, returns horizontal azimuth and altitude. +// +// IMPORTANT: This function differs from SphereFromVector in two ways: +// - SphereFromVector returns a `Lon` value that represents azimuth defined counterclockwise +// from north (e.g., west = +90), but this function represents a clockwise rotation +// (e.g., east = +90). The difference is because `SphereFromVector` is intended +// to preserve the vector "right-hand rule", while this function defines azimuth in a more +// traditional way as used in navigation and cartography. +// - This function optionally corrects for atmospheric refraction, while `SphereFromVector` +// does not. +// +// The returned structure contains the azimuth in `Lon`. +// It is measured in degrees clockwise from north: east = +90 degrees, west = +270 degrees. +// +// The altitude is stored in `Lat`. +// +// The distance to the observed object is stored in `Dist`, +// and is expressed in astronomical units (AU). +func HorizonFromVector(vector AstroVector, refraction Refraction) Spherical { + sphere := SphereFromVector(vector) + return Spherical{ + sphere.Lat + RefractionAngle(refraction, sphere.Lat), + toggleAzimuthDirection(sphere.Lon), + sphere.Dist, + } +} + // Converts spherical coordinates to Cartesian coordinates. func VectorFromSphere(sphere Spherical, time AstroTime) AstroVector { radlat := RadiansFromDegrees(sphere.Lat)