This commit is contained in:
2026-01-15 15:00:21 +08:00
parent e67749e2e4
commit 419f6cc78c
705 changed files with 9981 additions and 35723 deletions

View File

@ -0,0 +1 @@
{"type": "library", "name": "SolarCalculator", "version": "2.0.1", "spec": {"owner": "jpb10", "id": 13243, "name": "SolarCalculator", "requirements": null, "uri": null}}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 jpb10
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,121 @@
# SolarCalculator Library for Arduino
SolarCalculator is inspired by the [NOAA Solar Calculator](https://gml.noaa.gov/grad/solcalc/).
This library provides functions to calculate the times of sunrise, sunset, solar noon, twilight (dawn and dusk), Sun's
apparent position in the sky, equation of time, etc.
Most formulae are taken from Astronomical Algorithms by Jean Meeus and optimized for 8-bit AVR platform.
## Installation
Download and copy SolarCalculator to your local Arduino/libraries directory.
### Time
Date and time inputs are assumed to be in **Universal Coordinated Time** (UTC).
Although not required, it is recommended to use SolarCalculator along with the
[Time](https://github.com/PaulStoffregen/Time) library or similar.
## Usage
Include SolarCalculator.h in your sketch:
```cpp
#include <SolarCalculator.h>
```
Calculate the times of sunrise, transit (solar noon), and sunset, in hours:
```cpp
double latitude = 45.55; // Observer's latitude
double longitude = -73.633; // Observer's longitude
int time_zone = -5; // UTC offset
int year = 2022; // Calendar year (1901-2099)
int month = 1; // Calendar month (1-12)
int day = 1; // Calendar day (1-31)
double transit, sunrise, sunset;
calcSunriseSunset(year, month, day, latitude, longitude, transit, sunrise, sunset);
```
Or, using the Time library (Unix time):
```cpp
time_t utc = now();
calcSunriseSunset(utc, latitude, longitude, transit, sunrise, sunset);
```
Convert to local standard time:
```cpp
double sunrise_local = sunrise + time_zone;
```
* Refer to the example sketches for more on rounding and printing the results.
Similarly, calculate the times of dawn and dusk, in hours:
```cpp
calcCivilDawnDusk(utc, latitude, longitude, transit, c_dawn, c_dusk);
calcNauticalDawnDusk(utc, latitude, longitude, transit, n_dawn, n_dusk);
calcAstronomicalDawnDusk(utc, latitude, longitude, transit, a_dawn, a_dusk);
```
Sun's equatorial coordinates, in degrees and AUs:
```cpp
calcEquatorialCoordinates(utc, rt_ascension, declination, radius_vector);
```
Sun's horizontal coordinates, corrected for atmospheric refraction, in degrees:
```cpp
calcHorizontalCoordinates(utc, latitude, longitude, azimuth, elevation);
```
Equation of time, in minutes of time:
```cpp
calcEquationOfTime(utc, eq);
```
where the results are passed by reference.
## Examples
The following example sketches are included in this library:
* `SunriseSunset`: Calculate the times of sunrise, solar noon, and sunset for a given date and location.
* `SunriseSunsetAltitude`: Calculate the rise and set times at a height above the level of the horizon.
* `SolarCalculatorTimeLib`: Calculate the rise and set times, equation of time, and current solar coordinates.
* `SolarTrackingTimeLib`: Monitor the Sun's position in the sky for any location on Earth.
* `EquationOfTime`: Plot the equation of time for a given year.
## Notes
### Accuracy
Various things to consider:
* The amount of atmospheric refraction changes with air temperature, pressure, and the elevation of the observer.
Therefore, sunrise and sunset times can only be accurate to the nearest minute (Meeus, 1998).
* Assuming a purely elliptical motion of the Earth, solar coordinates have a "low accuracy" of 0.01° (Meeus, 1998). To
this precision, we ignore nutation, delta T, and higher-order terms in the relevant expressions.
* Arduino's single precision floating numbers have the equivalent of `24 * log10(2)` ≈ 7.22 significant digits.
Although this is generally not sufficient for mathematical astronomy (Meeus, 1998), it is good enough for our purposes.
### Sunrise and sunset
The algorithm for finding the times of sunrise and sunset implemented in this library is valid for all latitudes between
the Arctic and Antarctic circles (about ± 66.5°). Outside this range, a more general algorithm should be used but is not
provided at this time.
## References
ESRL Global Monitoring Laboratory (n.d.). *NOAA Solar Calculator*. https://gml.noaa.gov/grad/solcalc/
Meeus, J. (1998). *Astronomical algorithms* (2nd ed.). Willmann-Bell.

View File

@ -0,0 +1,35 @@
//======================================================================================================================
// SolarCalculator Library for Arduino example sketch: EquationOfTime.ino
//
// Plot the equation of time for a given year.
//
// Tested with Arduino IDE 1.8.19 and Arduino Uno
//======================================================================================================================
#include <SolarCalculator.h>
int year = 2022;
void setup()
{
Serial.begin(9600);
// Starting day (January 1)
JulianDay day(year, 1, 1);
for (int i = 0; i < 365; i++)
{
double eq;
calcEquationOfTime(day, eq);
// Print and view with serial plotter (Ctrl+Shift+L)
Serial.println(eq);
// Next day
++day.JD;
}
}
void loop()
{
}

View File

@ -0,0 +1,116 @@
//======================================================================================================================
// SolarCalculator Library for Arduino example sketch: SolarCalculatorTimeLib.ino
//
// Calculate the rise and set times, equation of time, and current solar coordinates.
//
// Tested with Arduino IDE 1.8.19 and Arduino Uno
//======================================================================================================================
#include <SolarCalculator.h>
#include <TimeLib.h>
// Location
double latitude = 45.55;
double longitude = -73.633;
int utc_offset = -5;
void setup()
{
Serial.begin(9600);
double transit, sunrise, sunset; // Event times, in hours (UTC)
double eq; // Equation of time, in minutes
double ra, dec, r; // Equatorial coordinates, in degrees and AUs
double az, el; // Horizontal coordinates, in degrees
// Set system time to compile time
setTime(toUtc(compileTime()));
// Set time manually (hr, min, sec, day, mo, yr)
//setTime(0, 0, 0, 1, 1, 2022);
// Get current time
time_t utc = now();
calcEquationOfTime(utc, eq);
calcEquatorialCoordinates(utc, ra, dec, r);
calcHorizontalCoordinates(utc, latitude, longitude, az, el);
calcSunriseSunset(utc, latitude, longitude, transit, sunrise, sunset);
// Print results
Serial.print(F("Sunrise: "));
printSunTime24h(sunrise + utc_offset);
Serial.print(F("Transit: "));
printSunTime24h(transit + utc_offset);
Serial.print(F("Sunset: "));
printSunTime24h(sunset + utc_offset);
Serial.print(F("Eq of time: "));
Serial.print(eq);
Serial.println(F(" min"));
Serial.print(F("RA: "));
Serial.print(degreesToHours(ra), 3);
Serial.print(F("h Dec: "));
Serial.print(dec);
Serial.print(F("° R: "));
Serial.print(r, 6);
Serial.println(F(" AU"));
Serial.print(F("Az: "));
Serial.print(az);
Serial.print(F("° El: "));
Serial.print(el);
Serial.println(F("°"));
}
void loop()
{
}
time_t toUtc(time_t local)
{
return local - utc_offset * 3600L;
}
double degreesToHours(double deg)
{
return deg / 15;
}
// Code from JChristensen/Timezone Clock example
time_t compileTime()
{
const uint8_t COMPILE_TIME_DELAY = 8;
const char *compDate = __DATE__, *compTime = __TIME__, *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
char chMon[4], *m;
tmElements_t tm;
strncpy(chMon, compDate, 3);
chMon[3] = '\0';
m = strstr(months, chMon);
tm.Month = ((m - months) / 3 + 1);
tm.Day = atoi(compDate + 4);
tm.Year = atoi(compDate + 7) - 1970;
tm.Hour = atoi(compTime);
tm.Minute = atoi(compTime + 3);
tm.Second = atoi(compTime + 6);
time_t t = makeTime(tm);
return t + COMPILE_TIME_DELAY;
}
void printSunTime24h(double hours)
{
int m = int(round(hours * 60));
int hr = (m / 60) % 24;
int mn = m % 60;
printDigits(hr);
Serial.print(':');
printDigits(mn);
Serial.println();
}
void printDigits(int digits)
{
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}

View File

@ -0,0 +1,80 @@
//======================================================================================================================
// SolarCalculator Library for Arduino example sketch: SolarTrackingTimeLib.ino
//
// Monitor the Sun's position in the sky for any location on Earth.
//
// Tested with Arduino IDE 1.8.19 and Arduino Uno
//======================================================================================================================
#include <SolarCalculator.h>
#include <TimeLib.h>
// Location
double latitude = 45.55;
double longitude = -73.633;
int utc_offset = -5;
// Refresh interval, in seconds
int interval = 10;
void setup()
{
Serial.begin(9600);
// Set system time to compile time
setTime(toUtc(compileTime()));
// Set time manually (hr, min, sec, day, mo, yr)
//setTime(0, 0, 0, 1, 1, 2022);
}
void loop()
{
static unsigned long next_millis = 0;
// At every interval
if (millis() > next_millis)
{
time_t utc = now();
double az, el;
// Calculate the solar position, in degrees
calcHorizontalCoordinates(utc, latitude, longitude, az, el);
// Print results
Serial.print(F("Az: "));
Serial.print(az);
Serial.print(F("° El: "));
Serial.print(el);
Serial.println(F("°"));
next_millis = millis() + interval * 1000L;
}
}
time_t toUtc(time_t local)
{
return local - utc_offset * 3600L;
}
// Code from JChristensen/Timezone Clock example
time_t compileTime()
{
const uint8_t COMPILE_TIME_DELAY = 8;
const char *compDate = __DATE__, *compTime = __TIME__, *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
char chMon[4], *m;
tmElements_t tm;
strncpy(chMon, compDate, 3);
chMon[3] = '\0';
m = strstr(months, chMon);
tm.Month = ((m - months) / 3 + 1);
tm.Day = atoi(compDate + 4);
tm.Year = atoi(compDate + 7) - 1970;
tm.Hour = atoi(compTime);
tm.Minute = atoi(compTime + 3);
tm.Second = atoi(compTime + 6);
time_t t = makeTime(tm);
return t + COMPILE_TIME_DELAY;
}

View File

@ -0,0 +1,58 @@
//======================================================================================================================
// SolarCalculator Library for Arduino example sketch: SunriseSunset.ino
//
// Calculate the times of sunrise, solar noon, and sunset for a given date and location.
//
// Tested with Arduino IDE 1.8.19 and Arduino Uno
//======================================================================================================================
#include <SolarCalculator.h>
void setup()
{
Serial.begin(9600);
// Date
int year = 2022;
int month = 1;
int day = 1;
// Location
double latitude = 45.55;
double longitude = -73.633;
int utc_offset = -5;
double transit, sunrise, sunset;
// Calculate the times of sunrise, transit, and sunset, in hours (UTC)
calcSunriseSunset(year, month, day, latitude, longitude, transit, sunrise, sunset);
// Get the approximate times (minimum program size) (iterations = 0)
//calcSunriseSunset(year, month, day, latitude, longitude, transit, sunrise, sunset, SUNRISESET_STD_ALTITUDE, 0);
// Print results
char str[6];
Serial.println(hoursToString(sunrise + utc_offset, str));
Serial.println(hoursToString(transit + utc_offset, str));
Serial.println(hoursToString(sunset + utc_offset, str));
}
void loop()
{
}
// Rounded HH:mm format
char * hoursToString(double h, char *str)
{
int m = int(round(h * 60));
int hr = (m / 60) % 24;
int mn = m % 60;
str[0] = (hr / 10) % 10 + '0';
str[1] = (hr % 10) + '0';
str[2] = ':';
str[3] = (mn / 10) % 10 + '0';
str[4] = (mn % 10) + '0';
str[5] = '\0';
return str;
}

View File

@ -0,0 +1,61 @@
//======================================================================================================================
// SolarCalculator Library for Arduino example sketch: SunriseSunsetAltitude.ino
//
// Calculate the rise and set times at a height above the level of the horizon.
//
// Tested with Arduino IDE 1.8.19 and Arduino Uno
//======================================================================================================================
#include <SolarCalculator.h>
void setup()
{
Serial.begin(9600);
// Date
int year = 2022;
int month = 1;
int day = 1;
// Location
double latitude = 45.5034;
double longitude = -73.5869;
int utc_offset = -5;
double transit, sunrise, sunset;
// From the Explanatory Supplement to the Astronomical Almanac (1992), p. 484
// Sunrise or sunset at a height above the level of the horizon occurs when the Sun's altitude is approximately:
int height = 200; // in meters
double sun_altitude = SUNRISESET_STD_ALTITUDE - 0.0353 * sqrt(height);
// Calculate the times of sunrise, transit, and sunset, in hours (UTC)
calcSunriseSunset(year, month, day, latitude, longitude, transit, sunrise, sunset, sun_altitude);
// Print results
char str[6];
Serial.println(hoursToString(sunrise + utc_offset, str));
Serial.println(hoursToString(transit + utc_offset, str));
Serial.println(hoursToString(sunset + utc_offset, str));
}
void loop()
{
}
// Rounded HH:mm format
char * hoursToString(double h, char *str)
{
int m = int(round(h * 60));
int hr = (m / 60) % 24;
int mn = m % 60;
str[0] = (hr / 10) % 10 + '0';
str[1] = (hr % 10) + '0';
str[2] = ':';
str[3] = (mn / 10) % 10 + '0';
str[4] = (mn % 10) + '0';
str[5] = '\0';
return str;
}

View File

@ -0,0 +1,41 @@
# Syntax coloring map for SolarCalculator
# Datatypes (KEYWORD1)
JulianDay KEYWORD1
# Methods and functions (KEYWORD2)
wrapTo360 KEYWORD2
wrapTo180 KEYWORD2
interpolateCoordinates KEYWORD2
fractionalDay KEYWORD2
calcJulianDay KEYWORD2
calcJulianCent KEYWORD2
calcGeomMeanLongSun KEYWORD2
calcGeomMeanAnomalySun KEYWORD2
calcSunEqOfCenter KEYWORD2
calcSunRadVector KEYWORD2
calcMeanObliquityOfEcliptic KEYWORD2
calcSolarCoordinates KEYWORD2
calcGrMeanSiderealTime KEYWORD2
equationOfTimeSmart KEYWORD2
calcDeltaT KEYWORD2
equatorial2horizontal KEYWORD2
calcRefraction KEYWORD2
calcEquationOfTime KEYWORD2
calcEquatorialCoordinates KEYWORD2
calcHorizontalCoordinates KEYWORD2
calcRiseSetTimes KEYWORD2
calcSunriseSunset KEYWORD2
calcCivilDawnDusk KEYWORD2
calcNauticalDawnDusk KEYWORD2
calcAstronomicalDawnDusk KEYWORD2
# Instances (KEYWORD2)
SolarCalculator KEYWORD2
solarcalculator KEYWORD2
# Constants (LITERAL1)
SUNRISESET_STD_ALTITUDE LITERAL1
CIVIL_DAWNDUSK_STD_ALTITUDE LITERAL1
NAUTICAL_DAWNDUSK_STD_ALTITUDE LITERAL1
ASTRONOMICAL_DAWNDUSK_STD_ALTITUDE LITERAL1

View File

@ -0,0 +1,10 @@
; name=SolarCalculator
; version=2.0.1
; author=jpb10
; maintainer=jpb10
; sentence=A library inspired by the NOAA Solar Calculator.
; paragraph=It provides functions to calculate the times of sunrise, sunset, solar noon, twilight (dawn and dusk), solar coordinates, equation of time, etc.
; category=Other
; url=https://github.com/jpb10/SolarCalculator
; architectures=*
; includes=SolarCalculator.h

View File

@ -0,0 +1,368 @@
//======================================================================================================================
// SolarCalculator Library for Arduino
//
// This library provides functions to calculate the times of sunrise, sunset, solar noon, twilight (dawn and dusk),
// Sun's apparent position in the sky, equation of time, etc.
//
// Most formulae are taken from Astronomical Algorithms by Jean Meeus and optimized for 8-bit AVR platform.
//======================================================================================================================
#ifndef ARDUINO
#include <cmath>
#endif
#include "SolarCalculator.h"
//namespace solarcalculator {
JulianDay::JulianDay(unsigned long utc)
{
JD = static_cast<unsigned long>(utc / 86400) + 2440587.5;
m = (utc % 86400) / 86400.0;
}
JulianDay::JulianDay(int year, int month, int day, int hour, int minute, int second)
{
JD = calcJulianDay(year, month, day);
m = fractionalDay(hour, minute, second);
}
//======================================================================================================================
// Intermediate calculations
//
// Time T is measured in Julian centuries (36525 ephemeris days from the epoch J2000.0)
//======================================================================================================================
#ifndef ARDUINO
double radians(double deg)
{
return deg * M_PI / 180;
}
double degrees(double rad)
{
return rad * 180 / M_PI;
}
#endif
double wrapTo360(double angle)
{
angle = fmod(angle, 360);
if (angle < 0) angle += 360;
return angle; // [0, 360)
}
double wrapTo180(double angle)
{
angle = wrapTo360(angle + 180);
return angle - 180; // [-180, 180)
}
// Interpolation of three tabular values, valid for n between -1 and +1
double interpolateCoordinates(double n, double y1, double y2, double y3)
{
if (fabs(y2 - y1) > 180) // if coordinate is discontinuous
{ // add or subtract 360 degrees
if (y1 < 0) y1 += 360;
else if (y2 < 0) y1 -= 360;
}
else if (fabs(y3 - y2) > 180)
{
if (y3 < 0) y3 += 360;
else if (y2 < 0) y3 -= 360;
}
double a = y2 - y1;
double b = y3 - y2;
double c = b - a;
return y2 + n * (a + b + n * c) / 2;
}
double fractionalDay(int hour, int minute, int second)
{
return (hour + minute / 60.0 + second / 3600.0) / 24;
}
// Valid from 1901 to 2099, Van Flandern & Pulkkinen (1979)
double calcJulianDay(int year, int month, int day)
{
return 367.0 * year - static_cast<int>(7 * (year + (month + 9) / 12) / 4) + static_cast<int>(275 * month / 9) +
day + 1721013.5;
}
double calcJulianCent(JulianDay jd)
{
return (jd.JD - 2451545 + jd.m) / 36525;
}
double calcGeomMeanLongSun(double T)
{
return wrapTo360(280.46646 + T * 36000.76983); // in degrees
}
double calcGeomMeanAnomalySun(double T)
{
return wrapTo360(357.52911 + T * 35999.05029); // in degrees
}
double calcSunEqOfCenter(double T)
{
double M = calcGeomMeanAnomalySun(T);
return sin(radians(M)) * 1.914602 + sin(2 * radians(M)) * 0.019993; // in degrees
}
double calcSunRadVector(double T)
{
double M = calcGeomMeanAnomalySun(T);
return 1.00014 - 0.01671 * cos(radians(M)) - 0.00014 * cos(2 * radians(M)); // in AUs
}
double calcMeanObliquityOfEcliptic(double T)
{
return 23.4392911 - T * 0.0130042; // in degrees
}
// Mean geocentric equatorial coordinates, accurate to ~0.01 degree
void calcSolarCoordinates(double T, double &ra, double &dec)
{
double L0 = calcGeomMeanLongSun(T);
double C = calcSunEqOfCenter(T);
double L = L0 + C - 0.00569; // corrected for aberration
double eps = calcMeanObliquityOfEcliptic(T);
ra = degrees(atan2(cos(radians(eps)) * sin(radians(L)), cos(radians(L)))); // [-180, 180)
dec = degrees(asin(sin(radians(eps)) * sin(radians(L))));
}
double calcGrMeanSiderealTime(JulianDay jd)
{
double GMST = wrapTo360(100.46061837 + 0.98564736629 * (jd.JD - 2451545));
return wrapTo360(GMST + 360.985647 * jd.m); // in degrees
}
void equatorial2horizontal(double H, double dec, double lat, double &az, double &el)
{
az = degrees(atan2(sin(radians(H)), cos(radians(H)) * sin(radians(lat)) -
tan(radians(dec)) * cos(radians(lat))));
el = degrees(asin(sin(radians(lat)) * sin(radians(dec)) +
cos(radians(lat)) * cos(radians(dec)) * cos(radians(H))));
}
// Approximate atmospheric refraction correction, in degrees
double calcRefraction(double elev)
{
if (elev < -0.575)
return -20.774 / tan(radians(elev)) / 3600; // Zimmerman (1981)
else
return 1.02 / tan(radians(elev + 10.3 / (elev + 5.11))) / 60; // Sæmundsson (1986)
}
// Equation of ephemeris time by Smart (1978)
double equationOfTimeSmart(double T)
{
double L0 = calcGeomMeanLongSun(T);
double M = calcGeomMeanAnomalySun(T);
// Using numerical values for the eccentricity and obliquity in 2025
return 2.465 * sin(2 * radians(L0)) - 1.913 * sin(radians(M)) +
0.165 * sin(radians(M)) * cos(2 * radians(L0)) -
0.053 * sin(4 * radians(L0)) - 0.02 * sin(2 * radians(M)); // in degrees
}
// Simple polynomial expressions for delta T (ΔT), in seconds of time
double calcDeltaT(double year)
{
if (year > 1997)
{
double t = year - 2015;
return 67.62 + t * (0.3645 + 0.0039755 * t); // Fred Espenak (2014)
}
else // y > 948
{
double u = (year - 2000) / 100;
return 64.69 + u * (80.59 + 23.604 * u); // fitted to historical data, very approximate before 1900
}
}
//======================================================================================================================
// Solar calculator
//
// All calculations assume time inputs in Coordinated Universal Time (UTC)
//======================================================================================================================
// Equation of time, in minutes of time
void calcEquationOfTime(JulianDay jd, double &E)
{
double T = calcJulianCent(jd);
E = 4 * equationOfTimeSmart(T);
}
// Sun's geocentric (as seen from the center of the Earth) equatorial coordinates, in degrees and AUs
void calcEquatorialCoordinates(JulianDay jd, double &rt_ascension, double &declination, double &radius_vector)
{
double T = calcJulianCent(jd);
calcSolarCoordinates(T, rt_ascension, declination);
rt_ascension = wrapTo360(rt_ascension);
radius_vector = calcSunRadVector(T);
}
// Sun's topocentric (as seen from the observer's place on the Earth's surface) horizontal coordinates, in degrees
void calcHorizontalCoordinates(JulianDay jd, double latitude, double longitude, double &azimuth, double &elevation)
{
double T = calcJulianCent(jd);
double GMST = calcGrMeanSiderealTime(jd);
double ra, dec;
calcSolarCoordinates(T, ra, dec);
double H = GMST + longitude - ra;
equatorial2horizontal(H, dec, latitude, azimuth, elevation);
azimuth += 180; // measured from the North
// elevation -= 8.794 * cos(radians(elevation)) / 3600; // parallax in altitude, always < 0.0025 degrees
elevation += calcRefraction(elevation);
}
// Helper function
void calcRiseSetTimes(double (&m)[3], JulianDay jd, double latitude, double longitude, double h0)
{
double T = calcJulianCent(jd);
double GMST = calcGrMeanSiderealTime(jd);
double ra, dec;
calcSolarCoordinates(T, ra, dec);
// Local hour angle at sunrise or sunset (±NaN if body is circumpolar)
double H0 = degrees(acos((sin(radians(h0)) - sin(radians(latitude)) * sin(radians(dec))) /
(cos(radians(latitude)) * cos(radians(dec)))));
m[0] = wrapTo360(ra - longitude - GMST + jd.m * 360) / 360;
m[1] = m[0] - H0 / 360;
m[2] = m[0] + H0 / 360;
}
// Find the times of sunrise, transit, and sunset, in hours
void calcSunriseSunset(JulianDay jd, double latitude, double longitude,
double &transit, double &sunrise, double &sunset, double altitude, int iterations)
{
double m[3], times[3];
m[0] = 0.5 - longitude / 360;
for (int i = 0; i <= iterations; i++)
{
for (int j = 0; j < 3; j++)
{
jd.m = m[j];
calcRiseSetTimes(times, jd, latitude, longitude, altitude);
m[j] = times[j];
if (i == 0) // first iteration
{
m[1] = times[1]; m[2] = times[2]; // approximate rise and set times
break;
}
}
}
transit = m[0] * 24;
sunrise = m[1] * 24;
sunset = m[2] * 24;
}
//======================================================================================================================
// Wrapper functions
//
// All calculations assume time inputs in Coordinated Universal Time (UTC)
//======================================================================================================================
void calcEquationOfTime(unsigned long utc, double &E)
{
JulianDay jd(utc);
calcEquationOfTime(jd, E);
}
void calcEquationOfTime(int year, int month, int day, int hour, int minute, int second, double &E)
{
JulianDay jd(year, month, day, hour, minute, second);
calcEquationOfTime(jd, E);
}
void calcEquatorialCoordinates(unsigned long utc, double &rt_ascension, double &declination, double &radius_vector)
{
JulianDay jd(utc);
calcEquatorialCoordinates(jd, rt_ascension, declination, radius_vector);
}
void calcEquatorialCoordinates(int year, int month, int day, int hour, int minute, int second,
double &rt_ascension, double &declination, double &radius_vector)
{
JulianDay jd(year, month, day, hour, minute, second);
calcEquatorialCoordinates(jd, rt_ascension, declination, radius_vector);
}
void calcHorizontalCoordinates(unsigned long utc, double latitude, double longitude,
double &azimuth, double &elevation)
{
JulianDay jd(utc);
calcHorizontalCoordinates(jd, latitude, longitude, azimuth, elevation);
}
void calcHorizontalCoordinates(int year, int month, int day, int hour, int minute, int second,
double latitude, double longitude, double &azimuth, double &elevation)
{
JulianDay jd(year, month, day, hour, minute, second);
calcHorizontalCoordinates(jd, latitude, longitude, azimuth, elevation);
}
void calcSunriseSunset(unsigned long utc, double latitude, double longitude,
double &transit, double &sunrise, double &sunset, double altitude, int iterations)
{
JulianDay jd(utc);
calcSunriseSunset(jd, latitude, longitude, transit, sunrise, sunset, altitude, iterations);
}
void calcSunriseSunset(int year, int month, int day, double latitude, double longitude,
double &transit, double &sunrise, double &sunset, double altitude, int iterations)
{
JulianDay jd(year, month, day);
calcSunriseSunset(jd, latitude, longitude, transit, sunrise, sunset, altitude, iterations);
}
void calcCivilDawnDusk(unsigned long utc, double latitude, double longitude,
double &transit, double &dawn, double &dusk)
{
calcSunriseSunset(utc, latitude, longitude, transit, dawn, dusk, CIVIL_DAWNDUSK_STD_ALTITUDE);
}
void calcCivilDawnDusk(int year, int month, int day, double latitude, double longitude,
double &transit, double &dawn, double &dusk)
{
calcSunriseSunset(year, month, day, latitude, longitude, transit, dawn, dusk, CIVIL_DAWNDUSK_STD_ALTITUDE);
}
void calcNauticalDawnDusk(unsigned long utc, double latitude, double longitude,
double &transit, double &dawn, double &dusk)
{
calcSunriseSunset(utc, latitude, longitude, transit, dawn, dusk, NAUTICAL_DAWNDUSK_STD_ALTITUDE);
}
void calcNauticalDawnDusk(int year, int month, int day, double latitude, double longitude,
double &transit, double &dawn, double &dusk)
{
calcSunriseSunset(year, month, day, latitude, longitude, transit, dawn, dusk, NAUTICAL_DAWNDUSK_STD_ALTITUDE);
}
void calcAstronomicalDawnDusk(unsigned long utc, double latitude, double longitude,
double &transit, double &dawn, double &dusk)
{
calcSunriseSunset(utc, latitude, longitude, transit, dawn, dusk, ASTRONOMICAL_DAWNDUSK_STD_ALTITUDE);
}
void calcAstronomicalDawnDusk(int year, int month, int day, double latitude, double longitude,
double &transit, double &dawn, double &dusk)
{
calcSunriseSunset(year, month, day, latitude, longitude, transit, dawn, dusk, ASTRONOMICAL_DAWNDUSK_STD_ALTITUDE);
}
//} // namespace

View File

@ -0,0 +1,127 @@
//======================================================================================================================
// SolarCalculator Library for Arduino
//
// This library provides functions to calculate the times of sunrise, sunset, solar noon, twilight (dawn and dusk),
// Sun's apparent position in the sky, equation of time, etc.
//
// Most formulae are taken from Astronomical Algorithms by Jean Meeus and optimized for 8-bit AVR platform.
//======================================================================================================================
#ifndef SOLARCALCULATOR_H
#define SOLARCALCULATOR_H
#ifdef ARDUINO
#include <Arduino.h>
#endif
//namespace solarcalculator {
constexpr double SUNRISESET_STD_ALTITUDE = -0.8333;
constexpr double CIVIL_DAWNDUSK_STD_ALTITUDE = -6.0;
constexpr double NAUTICAL_DAWNDUSK_STD_ALTITUDE = -12.0;
constexpr double ASTRONOMICAL_DAWNDUSK_STD_ALTITUDE = -18.0;
struct JulianDay
{
double JD; // Julian day at 0h UT (JD ending in .5)
double m; // Fractional day, 0h to 24h (decimal number between 0 and 1)
explicit JulianDay(unsigned long utc); // Unix time, i.e. seconds since 0h UT 1 January 1970
JulianDay(int year, int month, int day, int hour = 0, int minute = 0, int second = 0); // Calendar date (UTC)
};
//======================================================================================================================
// Intermediate calculations
//
// Time T is measured in Julian centuries (36525 ephemeris days from the epoch J2000.0)
//======================================================================================================================
// Utilities
double wrapTo360(double angle);
double wrapTo180(double angle);
double interpolateCoordinates(double n, double y1, double y2, double y3);
// Julian day
double fractionalDay(int hour, int minute, int second);
double calcJulianDay(int year, int month, int day);
double calcJulianCent(JulianDay jd);
// Solar coordinates
double calcGeomMeanLongSun(double T);
double calcGeomMeanAnomalySun(double T);
double calcSunEqOfCenter(double T);
double calcSunRadVector(double T);
double calcMeanObliquityOfEcliptic(double T);
void calcSolarCoordinates(double T, double &ra, double &dec);
// Sidereal time at Greenwich, solar time, and ΔT
double calcGrMeanSiderealTime(JulianDay jd);
double equationOfTimeSmart(double T);
double calcDeltaT(double year);
// Sun's position in the sky
void equatorial2horizontal(double H, double dec, double lat, double &az, double &el);
double calcRefraction(double elev);
//======================================================================================================================
// Solar calculator
//
// All calculations assume time inputs in Coordinated Universal Time (UTC)
//======================================================================================================================
// Equation of time, in minutes of time
void calcEquationOfTime(JulianDay jd, double &E);
// Sun's geocentric (as seen from the center of the Earth) equatorial coordinates, in degrees and AUs
void calcEquatorialCoordinates(JulianDay jd, double &rt_ascension, double &declination, double &radius_vector);
// Sun's topocentric (as seen from the observer's place on the Earth's surface) horizontal coordinates, in degrees
void calcHorizontalCoordinates(JulianDay jd, double latitude, double longitude, double &azimuth, double &elevation);
// Find the times of sunrise, transit, and sunset, in hours
void calcSunriseSunset(JulianDay jd, double latitude, double longitude,
double &transit, double &sunrise, double &sunset,
double altitude = SUNRISESET_STD_ALTITUDE, int iterations = 1);
//======================================================================================================================
// Wrapper functions
//
// All calculations assume time inputs in Coordinated Universal Time (UTC)
//======================================================================================================================
void calcEquationOfTime(unsigned long utc, double &E);
void calcEquationOfTime(int year, int month, int day, int hour, int minute, int second, double &E);
void calcEquatorialCoordinates(unsigned long utc, double &rt_ascension, double &declination, double &radius_vector);
void calcEquatorialCoordinates(int year, int month, int day, int hour, int minute, int second,
double &rt_ascension, double &declination, double &radius_vector);
void calcHorizontalCoordinates(unsigned long utc, double latitude, double longitude,
double &azimuth, double &elevation);
void calcHorizontalCoordinates(int year, int month, int day, int hour, int minute, int second,
double latitude, double longitude, double &azimuth, double &elevation);
void calcSunriseSunset(unsigned long utc, double latitude, double longitude,
double &transit, double &sunrise, double &sunset,
double altitude = SUNRISESET_STD_ALTITUDE, int iterations = 1);
void calcSunriseSunset(int year, int month, int day, double latitude, double longitude,
double &transit, double &sunrise, double &sunset,
double altitude = SUNRISESET_STD_ALTITUDE, int iterations = 1);
void calcCivilDawnDusk(unsigned long utc, double latitude, double longitude,
double &transit, double &dawn, double &dusk);
void calcCivilDawnDusk(int year, int month, int day, double latitude, double longitude,
double &transit, double &dawn, double &dusk);
void calcNauticalDawnDusk(unsigned long utc, double latitude, double longitude,
double &transit, double &dawn, double &dusk);
void calcNauticalDawnDusk(int year, int month, int day, double latitude, double longitude,
double &transit, double &dawn, double &dusk);
void calcAstronomicalDawnDusk(unsigned long utc, double latitude, double longitude,
double &transit, double &dawn, double &dusk);
void calcAstronomicalDawnDusk(int year, int month, int day, double latitude, double longitude,
double &transit, double &dawn, double &dusk);
//} // namespace
#endif //SOLARCALCULATOR_H