Skip to content

Commit 25806a0

Browse files
jonseymourJon Seymour
authored andcommitted
Modification to allow dawn and dusk calculations.
1 parent 9c7d514 commit 25806a0

File tree

1 file changed

+83
-21
lines changed

1 file changed

+83
-21
lines changed

astrotime.go

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
// NAA - NOAA's Astronomical Algorithms
22
package astrotime
33

4-
// (JavaScript web page
4+
// (JavaScript web page
55
// http://www.srrb.noaa.gov/highlights/sunrise/sunrise.html by
66
// Chris Cornwall, Aaron Horiuchi and Chris Lehman)
77
// Ported to C++ by Pete Gray ([email protected]), July 2006
8-
// Released as Open Source and can be used in any way, as long as the
8+
// Released as Open Source and can be used in any way, as long as the
99
// above description remains in place.
10-
10+
//
11+
// Modified by Jon Seymour ([email protected]) to allow dawn
12+
// and dusk calculations.
13+
//
1114
import (
1215
"math"
1316
"time"
@@ -26,6 +29,17 @@ const (
2629
OneDay = time.Hour * 24
2730
)
2831

32+
const (
33+
ASTRONOMICAL_DAWN = -18.0
34+
ASTRONOMICAL_DUSK = -18.0
35+
NAUTICAL_DAWN = -12.0
36+
NAUTICAL_DUSK = -12.0
37+
CIVIL_DAWN = -6.0
38+
CIVIL_DUSK = -6.0
39+
SUNRISE = 0
40+
SUNSET = 0
41+
)
42+
2943
// CalcJD converts a time.Time object to a julian date
3044
func CalcJD(t time.Time) float64 {
3145
y, m, d, hh, mm, ss, ms := t.Year(), int(t.Month()), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond()/1e6
@@ -235,10 +249,11 @@ func calcSunDeclination(t float64) float64 {
235249
//Return value:
236250
// hour angle of sunrise in radians
237251

238-
func calcHourAngleSunrise(lat float64, solarDec float64) float64 {
252+
func calcHourAngleSunrise(lat float64, solarDec float64, solarElevation float64) float64 {
239253
latRad := DegToRad * lat
240254
sdRad := DegToRad * solarDec
241-
return (math.Acos(math.Cos(DegToRad*90.833)/(math.Cos(latRad)*math.Cos(sdRad)) - math.Tan(latRad)*math.Tan(sdRad)))
255+
correction := calcRefractiveCorrection(solarElevation)
256+
return (math.Acos(math.Cos(DegToRad*(90+correction))/(math.Cos(latRad)*math.Cos(sdRad)) - math.Tan(latRad)*math.Tan(sdRad)))
242257
}
243258

244259
// Name: calcSolNoonUTC
@@ -273,7 +288,7 @@ func calcSolNoonUTC(t float64, longitude float64) float64 {
273288
// time in minutes from zero Z
274289

275290
// Calculate the UTC sunrise for the given day at the given location
276-
func calcSunriseUTC(jd float64, latitude float64, longitude float64) float64 {
291+
func calcSunriseUTC(jd float64, latitude float64, longitude float64, solarElevation float64) float64 {
277292

278293
t := calcTimeJulianCent(jd)
279294

@@ -288,7 +303,7 @@ func calcSunriseUTC(jd float64, latitude float64, longitude float64) float64 {
288303

289304
eqTime := calcEquationOfTime(tnoon)
290305
solarDec := calcSunDeclination(tnoon)
291-
hourAngle := calcHourAngleSunrise(latitude, solarDec)
306+
hourAngle := calcHourAngleSunrise(latitude, solarDec, solarElevation)
292307

293308
delta := longitude - RadToDeg*hourAngle
294309
timeDiff := 4 * delta
@@ -299,37 +314,59 @@ func calcSunriseUTC(jd float64, latitude float64, longitude float64) float64 {
299314
newt := calcTimeJulianCent(calcJDFromJulianCent(t) + timeUTC/1440.0)
300315
eqTime = calcEquationOfTime(newt)
301316
solarDec = calcSunDeclination(newt)
302-
hourAngle = calcHourAngleSunrise(latitude, solarDec)
317+
hourAngle = calcHourAngleSunrise(latitude, solarDec, solarElevation)
303318
delta = longitude - RadToDeg*hourAngle
304319
timeDiff = 4 * delta
305320
timeUTC = 720 + timeDiff - eqTime
306321
return timeUTC
307322
}
308323

309-
// CalcSunrise calculates the sunrise, in local time, on the day t at the
310-
// location specified in longitude and latitude.
311-
func CalcSunrise(t time.Time, latitude float64, longitude float64) time.Time {
324+
// CalcDawn calculates the dawn, in local time, on the day t at the
325+
// location specified in longitude and latitude. If solarElevation is 0, then dawn is identical
326+
// to sunrise. A negative solarElevation specifies that the time calculated is for when the sun
327+
// is -solarElevation degrees below the horizon.
328+
func CalcDawn(t time.Time, latitude float64, longitude float64, solarElevation float64) time.Time {
312329
jd := CalcJD(t)
313-
sunriseUTC := time.Duration(math.Floor(calcSunriseUTC(jd, latitude, longitude)*60) * 1e9)
330+
sunriseUTC := time.Duration(math.Floor(calcSunriseUTC(jd, latitude, longitude, solarElevation)*60) * 1e9)
314331
loc, _ := time.LoadLocation("UTC")
315332
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc).Add(sunriseUTC).In(t.Location())
316333
}
317334

335+
// CalcSunrise calculates the sunrise, in local time, on the day t at the
336+
// location specified in longitude and latitude.
337+
func CalcSunrise(t time.Time, latitude float64, longitude float64) time.Time {
338+
return CalcDawn(t, latitude, longitude, SUNRISE)
339+
}
340+
341+
// Calculates the refractive correction for the specified solar elevation.
342+
// The refractive correction only applies at sunrise and sunset (solarElevation == 0).
343+
// Otherwise the correction is simply the negative of the solarElevation
344+
func calcRefractiveCorrection(solarElevation float64) float64 {
345+
if solarElevation == 0 {
346+
return 0.833
347+
} else {
348+
return -solarElevation
349+
}
350+
}
351+
318352
// Name: calcHourAngleSunset
319353
// Type: Function
320354
// Purpose: calculate the hour angle of the sun at sunset for the
321355
// latitude
322356
// Arguments:
323357
// lat : latitude of observer in degrees
324358
// solarDec : declination angle of sun in degrees
359+
// solarElevation : solarElevation of the sun w.r.t. horizon - 0 for sunset, -6 for civil dusk, etc...
325360
// Return value:
326361
// hour angle of sunset in radians
327362

328-
func calcHourAngleSunset(lat float64, solarDec float64) float64 {
363+
func calcHourAngleSunset(lat float64, solarDec float64, solarElevation float64) float64 {
329364
latRad := DegToRad * lat
330365
sdRad := DegToRad * solarDec
331366

332-
HA := (math.Acos(math.Cos(DegToRad*90.833)/(math.Cos(latRad)*math.Cos(sdRad)) - math.Tan(latRad)*math.Tan(sdRad)))
367+
correction := calcRefractiveCorrection(solarElevation)
368+
369+
HA := (math.Acos(math.Cos(DegToRad*(90+correction))/(math.Cos(latRad)*math.Cos(sdRad)) - math.Tan(latRad)*math.Tan(sdRad)))
333370

334371
return -HA // in radians
335372
}
@@ -345,7 +382,7 @@ func calcHourAngleSunset(lat float64, solarDec float64) float64 {
345382
// Return value:
346383
// time in minutes from zero Z
347384

348-
func calcSunsetUTC(jd float64, latitude float64, longitude float64) float64 {
385+
func calcSunsetUTC(jd float64, latitude float64, longitude float64, solarElevation float64) float64 {
349386
t := calcTimeJulianCent(jd)
350387

351388
// *** Find the time of solar noon at the location, and use
@@ -359,7 +396,7 @@ func calcSunsetUTC(jd float64, latitude float64, longitude float64) float64 {
359396

360397
eqTime := calcEquationOfTime(tnoon)
361398
solarDec := calcSunDeclination(tnoon)
362-
hourAngle := calcHourAngleSunset(latitude, solarDec)
399+
hourAngle := calcHourAngleSunset(latitude, solarDec, solarElevation)
363400

364401
delta := longitude - RadToDeg*hourAngle
365402
timeDiff := 4 * delta
@@ -370,22 +407,29 @@ func calcSunsetUTC(jd float64, latitude float64, longitude float64) float64 {
370407
newt := calcTimeJulianCent(calcJDFromJulianCent(t) + timeUTC/1440.0)
371408
eqTime = calcEquationOfTime(newt)
372409
solarDec = calcSunDeclination(newt)
373-
hourAngle = calcHourAngleSunset(latitude, solarDec)
410+
hourAngle = calcHourAngleSunset(latitude, solarDec, solarElevation)
374411

375412
delta = longitude - RadToDeg*hourAngle
376413
timeDiff = 4 * delta
377414
return 720 + timeDiff - eqTime
378415
}
379416

380-
// CalcSunset calculates the sunset, in local time, on the day t at the
381-
// location specified in longitude and latitude.
382-
func CalcSunset(t time.Time, latitude float64, longitude float64) time.Time {
417+
// CalcDusk calculates the dusk, in local time, on the day t at the
418+
// location specified in longitude and latitude. The solarElevation specifies that the time that the sun will
419+
// be -solarElevation degrees below the horizon.
420+
func CalcDusk(t time.Time, latitude float64, longitude float64, solarElevation float64) time.Time {
383421
jd := CalcJD(t)
384-
sunsetUTC := time.Duration(math.Floor(calcSunsetUTC(jd, latitude, longitude)*60) * 1e9)
422+
sunsetUTC := time.Duration(math.Floor(calcSunsetUTC(jd, latitude, longitude, solarElevation)*60) * 1e9)
385423
loc, _ := time.LoadLocation("UTC")
386424
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc).Add(sunsetUTC).In(t.Location())
387425
}
388426

427+
// CalcSunset calculates the sunset, in local time, on the day t at the
428+
// location specified in longitude and latitude.
429+
func CalcSunset(t time.Time, latitude float64, longitude float64) time.Time {
430+
return CalcDusk(t, latitude, longitude, SUNSET)
431+
}
432+
389433
// NextSunrise returns date/time of the next sunrise after tAfter
390434
func NextSunrise(tAfter time.Time, latitude float64, longitude float64) (tSunrise time.Time) {
391435
tSunrise = CalcSunrise(tAfter, latitude, longitude)
@@ -395,6 +439,15 @@ func NextSunrise(tAfter time.Time, latitude float64, longitude float64) (tSunris
395439
return
396440
}
397441

442+
// NextDawn returns date/time of the next dawn at the specified solar solarElevation, after the specified time.
443+
func NextDawn(tAfter time.Time, latitude float64, longitude float64, solarElevation float64) (tSunrise time.Time) {
444+
tSunrise = CalcDawn(tAfter, latitude, longitude, solarElevation)
445+
if tAfter.After(tSunrise) {
446+
tSunrise = CalcDawn(tAfter.Add(OneDay), latitude, longitude, solarElevation)
447+
}
448+
return
449+
}
450+
398451
// NextSunset returns date/time of the next sunset after tAfter
399452
func NextSunset(tAfter time.Time, latitude float64, longitude float64) (tSunset time.Time) {
400453
tSunset = CalcSunset(tAfter, latitude, longitude)
@@ -403,3 +456,12 @@ func NextSunset(tAfter time.Time, latitude float64, longitude float64) (tSunset
403456
}
404457
return
405458
}
459+
460+
// NextDusk returns date/time of the next dusk at the specified solar solarElevation, after the specified tine.
461+
func NextDusk(tAfter time.Time, latitude float64, longitude float64, solarElevation float64) (tSunset time.Time) {
462+
tSunset = CalcDusk(tAfter, latitude, longitude, solarElevation)
463+
if tAfter.After(tSunset) {
464+
tSunset = CalcDusk(tAfter.Add(OneDay), latitude, longitude, solarElevation)
465+
}
466+
return
467+
}

0 commit comments

Comments
 (0)