V2.1
This commit is contained in:
1
lib/SolarCalculator/.piopm
Normal file
1
lib/SolarCalculator/.piopm
Normal file
@ -0,0 +1 @@
|
||||
{"type": "library", "name": "SolarCalculator", "version": "2.0.1", "spec": {"owner": "jpb10", "id": 13243, "name": "SolarCalculator", "requirements": null, "uri": null}}
|
||||
21
lib/SolarCalculator/LICENSE
Normal file
21
lib/SolarCalculator/LICENSE
Normal 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.
|
||||
121
lib/SolarCalculator/README.md
Normal file
121
lib/SolarCalculator/README.md
Normal 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.
|
||||
|
||||
@ -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()
|
||||
{
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
58
lib/SolarCalculator/examples/SunriseSunset/SunriseSunset.ino
Normal file
58
lib/SolarCalculator/examples/SunriseSunset/SunriseSunset.ino
Normal 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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
41
lib/SolarCalculator/keywords.txt
Normal file
41
lib/SolarCalculator/keywords.txt
Normal 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
|
||||
10
lib/SolarCalculator/library.properties
Normal file
10
lib/SolarCalculator/library.properties
Normal 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
|
||||
368
lib/SolarCalculator/src/SolarCalculator.cpp
Normal file
368
lib/SolarCalculator/src/SolarCalculator.cpp
Normal 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
|
||||
127
lib/SolarCalculator/src/SolarCalculator.h
Normal file
127
lib/SolarCalculator/src/SolarCalculator.h
Normal 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
|
||||
Reference in New Issue
Block a user