├── SolarPosition.h ├── examples ├── solarTimeFunctionDemo │ └── solarTimeFunctionDemo.ino ├── solarTimeRTC │ └── solarTimeRTC.ino ├── solarTimeNoClockDemo │ └── solarTimeNoClockDemo.ino └── solarTimeDemo │ └── solarTimeDemo.ino ├── README.md └── SolarPosition.cpp /SolarPosition.h: -------------------------------------------------------------------------------- 1 | // SolarPosition.h 2 | 3 | // 2019 Ken Willmott 4 | // Arduino library based on the program "Arduino Uno and Solar Position Calculations" 5 | // (c) David R. Brooks, which can be found at http://www.instesre.org/ArduinoDocuments.htm 6 | // and issued under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License: 7 | // https://creativecommons.org/licenses/by-nc-nd/4.0/ 8 | 9 | #include //https://github.com/PaulStoffregen/Time 10 | #include 11 | 12 | const float KM_PER_AU = 149597870.7; //kilometers per astronomical unit 13 | 14 | typedef time_t(*getExternalTime)(); 15 | 16 | // data structure to store solar position results 17 | struct SolarPosition_t 18 | { 19 | float elevation = 0; 20 | float azimuth = 0; 21 | float distance = 0; 22 | time_t time = 0; 23 | }; 24 | 25 | // utility functions 26 | long JulianDate(int year, int month, int day); 27 | SolarPosition_t calculateSolarPosition(time_t tParam, float Latitude, float Longitude); 28 | 29 | // class interface 30 | class SolarPosition 31 | { 32 | private: 33 | 34 | static getExternalTime getExtTimePtr; // shared pointer to external sync function 35 | 36 | // current values: 37 | float Latitude; 38 | float Longitude; 39 | 40 | // results: 41 | SolarPosition_t result; 42 | 43 | public: 44 | 45 | SolarPosition(float Latitude, float Longitude); 46 | 47 | static void setTimeProvider(getExternalTime getTimeFunction); 48 | 49 | SolarPosition_t getSolarPosition(); 50 | SolarPosition_t getSolarPosition(time_t t); 51 | 52 | float getSolarElevation(); 53 | float getSolarElevation(time_t t); 54 | 55 | float getSolarAzimuth(); 56 | float getSolarAzimuth(time_t t); 57 | 58 | float getSolarDistance(); 59 | float getSolarDistance(time_t t); 60 | }; 61 | -------------------------------------------------------------------------------- /examples/solarTimeFunctionDemo/solarTimeFunctionDemo.ino: -------------------------------------------------------------------------------- 1 | // solarTimeFunctionDemo 2 | 3 | // Arduino example sketch for SolarPosition library 4 | // 5 | // This sketch demonstrates how to directly access the utility function that the object oriented interface uses 6 | // to calculate solar positions. Note that the function units are radians and AU (astronomical units), 7 | // unlike the SolarPosition class which automatically converts those to degrees and kilometers. 8 | 9 | // 2019 Ken Willmott 10 | // Arduino library based on the program "Arduino Uno and Solar Position Calculations" 11 | // (c) David R. Brooks, which can be found at http://www.instesre.org/ArduinoDocuments.htm 12 | // and issued under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License: 13 | // https://creativecommons.org/licenses/by-nc-nd/4.0/ 14 | 15 | #include 16 | 17 | // number of decimal digits to print 18 | const uint8_t digits = 3; 19 | 20 | // A solar position structure to demonstrate storing complete positions 21 | SolarPosition_t savedPosition; 22 | 23 | // a test position: 24 | // (43.6777, -79.6248) Toronto, Canada airport (YYZ) 25 | 26 | const float Latitude = 43.6777; 27 | const float Longitude = -79.6248; 28 | 29 | // program begins 30 | 31 | void setup() 32 | { 33 | Serial.begin(9600); 34 | Serial.println(F("\tSolar Position Utility Function Demo")); 35 | 36 | // obtain a complete current position 37 | savedPosition = calculateSolarPosition(SECS_YR_2000, Latitude * DEG_TO_RAD, Longitude * DEG_TO_RAD); 38 | 39 | Serial.print(F("The sun was at an elevation of ")); 40 | Serial.print(savedPosition.elevation * RAD_TO_DEG, 4); 41 | Serial.print(F(" and an azimuth of ")); 42 | Serial.println(savedPosition.azimuth * RAD_TO_DEG, 4); 43 | Serial.print(F("in YYZ airport at ")); 44 | printTime(savedPosition.time); 45 | 46 | Serial.print(F("The earth was ")); 47 | Serial.print(savedPosition.distance * KM_PER_AU, 0); 48 | Serial.println(F(" km from the Sun.")); 49 | Serial.println(); 50 | 51 | Serial.println(F("Finished...")); 52 | Serial.println(); 53 | } 54 | 55 | void loop() 56 | { 57 | } 58 | 59 | // Print a time to serial 60 | // 61 | void printTime(time_t t) 62 | { 63 | tmElements_t someTime; 64 | breakTime(t, someTime); 65 | 66 | Serial.print(someTime.Hour); 67 | Serial.print(F(":")); 68 | Serial.print(someTime.Minute); 69 | Serial.print(F(":")); 70 | Serial.print(someTime.Second); 71 | Serial.print(F(" UTC on ")); 72 | Serial.print(dayStr(someTime.Wday)); 73 | Serial.print(F(", ")); 74 | Serial.print(monthStr(someTime.Month)); 75 | Serial.print(F(" ")); 76 | Serial.print(someTime.Day); 77 | Serial.print(F(", ")); 78 | Serial.println(tmYearToCalendar(someTime.Year)); 79 | } 80 | -------------------------------------------------------------------------------- /examples/solarTimeRTC/solarTimeRTC.ino: -------------------------------------------------------------------------------- 1 | // solarTimeRTC 2 | 3 | // Arduino example sketch for SolarPosition library 4 | // 5 | // Calculate solar position from time and location information 6 | // using an RTC as a time source. 7 | 8 | // 2017 Ken Willmott 9 | // Arduino library based on the program "Arduino Uno and Solar Position Calculations" 10 | // (c) David R. Brooks, which can be found at http://www.instesre.org/ArduinoDocuments.htm 11 | // and issued under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License: 12 | // https://creativecommons.org/licenses/by-nc-nd/4.0/ 13 | 14 | #include 15 | 16 | // choose your RTC library (DS1307 lib can also read from the DS3231 IC): 17 | 18 | //#include 19 | #include 20 | 21 | // number of decimal digits to print 22 | const uint8_t digits = 3; 23 | 24 | // some test positions: 25 | SolarPosition Toronto(43.653109, -79.386304); // Toronto, Canada 26 | SolarPosition Timbuktu(16.775214, -3.007455); // Timbuktu, Mali, Africa 27 | SolarPosition Melbourne(-37.668987, 144.841006); //Melbourne Airport (MEL) 28 | SolarPosition Ulaanbaatar(47.847410, 106.769004); //Ulaanbaatar Airport (ULN) 29 | 30 | // program begins 31 | 32 | void setup() 33 | { 34 | Serial.begin(9600); 35 | Serial.println(F("\tSolar Position Demo")); 36 | 37 | // set the Time service as the time provider 38 | SolarPosition::setTimeProvider(RTC.get); 39 | } 40 | 41 | void loop() 42 | { 43 | // now test the real time methods: 44 | // 45 | printTime(RTC.get()); 46 | 47 | Serial.print(F("Toronto:\t")); 48 | printSolarPosition(Toronto.getSolarPosition(), digits); 49 | Serial.print(F("Melbourne:\t")); 50 | printSolarPosition(Melbourne.getSolarPosition(), digits); 51 | Serial.print(F("Timbuktu:\t")); 52 | printSolarPosition(Timbuktu.getSolarPosition(), digits); 53 | Serial.print(F("Ulaanbaatar:\t")); 54 | printSolarPosition(Ulaanbaatar.getSolarPosition(), digits); 55 | Serial.println(); 56 | 57 | delay(15000); 58 | } 59 | 60 | // Print a solar position to serial 61 | // 62 | void printSolarPosition(SolarPosition_t pos, int numDigits) 63 | { 64 | Serial.print(F("el: ")); 65 | Serial.print(pos.elevation, numDigits); 66 | Serial.print(F(" deg\t")); 67 | 68 | Serial.print(F("az: ")); 69 | Serial.print(pos.azimuth, numDigits); 70 | Serial.println(F(" deg")); 71 | } 72 | 73 | // Print a time to serial 74 | // 75 | void printTime(time_t t) 76 | { 77 | tmElements_t someTime; 78 | breakTime(t, someTime); 79 | 80 | Serial.print(someTime.Hour); 81 | Serial.print(F(":")); 82 | Serial.print(someTime.Minute); 83 | Serial.print(F(":")); 84 | Serial.print(someTime.Second); 85 | Serial.print(F(" UTC on ")); 86 | Serial.print(dayStr(someTime.Wday)); 87 | Serial.print(F(", ")); 88 | Serial.print(monthStr(someTime.Month)); 89 | Serial.print(F(" ")); 90 | Serial.print(someTime.Day); 91 | Serial.print(F(", ")); 92 | Serial.println(tmYearToCalendar(someTime.Year)); 93 | } 94 | 95 | -------------------------------------------------------------------------------- /examples/solarTimeNoClockDemo/solarTimeNoClockDemo.ino: -------------------------------------------------------------------------------- 1 | // solarTimeNoClockDemo 2 | 3 | // Arduino example sketch for SolarPosition library 4 | // 5 | // Calculate solar position from time and location information 6 | // not using any clock to keep time. Just do calculations. 7 | 8 | // 2017 Ken Willmott 9 | // Arduino library based on the program "Arduino Uno and Solar Position Calculations" 10 | // (c) David R. Brooks, which can be found at http://www.instesre.org/ArduinoDocuments.htm 11 | // and issued under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License: 12 | // https://creativecommons.org/licenses/by-nc-nd/4.0/ 13 | 14 | #include 15 | 16 | // number of decimal digits to print 17 | const uint8_t digits = 3; 18 | 19 | // some test positions: 20 | SolarPosition Timbuktu(16.775214, -3.007455); // Timbuktu, Mali, Africa 21 | 22 | // create a fixed UNIX time to test fixed time method 23 | int someS = 0; //second 24 | int someM = 0; //minute 25 | int someH = 12; //hour 26 | int someD = 15; //day 27 | int someMo = 4; //month 28 | int someY = 1985; //year 29 | tmElements_t someTime = {someS, someM, someH, 0, someD, someMo, CalendarYrToTm(someY) }; 30 | time_t someEpochTime = makeTime(someTime); 31 | 32 | // program begins 33 | 34 | void setup() 35 | { 36 | Serial.begin(9600); 37 | Serial.println(F("\tSolar Position Demo")); 38 | 39 | // First test the fixed time methods: 40 | // 41 | Serial.print(F("The sun was at an elevation of ")); 42 | Serial.print(Timbuktu.getSolarElevation(someEpochTime), 4); 43 | Serial.print(F(" and an azimuth of ")); 44 | Serial.println(Timbuktu.getSolarAzimuth(someEpochTime), 4); 45 | Serial.print(F("in Timbuktu at ")); 46 | printTime(someEpochTime); 47 | 48 | Serial.print(F("The earth was ")); 49 | Serial.print(Timbuktu.getSolarDistance(someEpochTime), 0); 50 | Serial.println(F(" km from the Sun.")); 51 | Serial.println(); 52 | 53 | Serial.println(F("Done...")); 54 | Serial.println(); 55 | } 56 | 57 | void loop() 58 | { 59 | } 60 | 61 | // Print a solar position to serial 62 | // 63 | void printSolarPosition(SolarPosition_t pos, int numDigits) 64 | { 65 | Serial.print(F("el: ")); 66 | Serial.print(pos.elevation, numDigits); 67 | Serial.print(F(" deg\t")); 68 | 69 | Serial.print(F("az: ")); 70 | Serial.print(pos.azimuth, numDigits); 71 | Serial.println(F(" deg")); 72 | } 73 | 74 | // Print a time to serial 75 | // 76 | void printTime(time_t t) 77 | { 78 | tmElements_t someTime; 79 | breakTime(t, someTime); 80 | 81 | Serial.print(someTime.Hour); 82 | Serial.print(F(":")); 83 | Serial.print(someTime.Minute); 84 | Serial.print(F(":")); 85 | Serial.print(someTime.Second); 86 | Serial.print(F(" UTC on ")); 87 | Serial.print(dayStr(someTime.Wday)); 88 | Serial.print(F(", ")); 89 | Serial.print(monthStr(someTime.Month)); 90 | Serial.print(F(" ")); 91 | Serial.print(someTime.Day); 92 | Serial.print(F(", ")); 93 | Serial.println(tmYearToCalendar(someTime.Year)); 94 | } 95 | 96 | -------------------------------------------------------------------------------- /examples/solarTimeDemo/solarTimeDemo.ino: -------------------------------------------------------------------------------- 1 | // solarTimeDemo 2 | 3 | // Arduino example sketch for SolarPosition library 4 | // 5 | // Calculate solar position from time and location information 6 | // using Time library functions (relying on CPU clock based timer) to keep time. 7 | 8 | // 2017 Ken Willmott 9 | // Arduino library based on the program "Arduino Uno and Solar Position Calculations" 10 | // (c) David R. Brooks, which can be found at http://www.instesre.org/ArduinoDocuments.htm 11 | // and issued under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License: 12 | // https://creativecommons.org/licenses/by-nc-nd/4.0/ 13 | 14 | #include 15 | 16 | // number of decimal digits to print 17 | const uint8_t digits = 3; 18 | 19 | // some test positions: 20 | SolarPosition Toronto(43.6777, -79.6248); // Toronto, Canada airport (YYZ) 21 | SolarPosition Timbuktu(16.775214, -3.007455); // Timbuktu, Mali, Africa 22 | SolarPosition Melbourne(-37.668987, 144.841006); //Melbourne Airport (MEL) 23 | SolarPosition Ulaanbaatar(47.847410, 106.769004); //Ulaanbaatar Airport (ULN) 24 | 25 | // A solar position structure to demonstrate storing complete positions 26 | SolarPosition_t savedPosition; 27 | 28 | // create a fixed UNIX time to test fixed time method 29 | int someS = 0; //second 30 | int someM = 0; //minute 31 | int someH = 12; //hour 32 | int someD = 15; //day 33 | int someMo = 4; //month 34 | int someY = 1985; //year 35 | tmElements_t someTime = {someS, someM, someH, 0, someD, someMo, CalendarYrToTm(someY) }; 36 | time_t someEpochTime = makeTime(someTime); 37 | 38 | // program begins 39 | 40 | void setup() 41 | { 42 | Serial.begin(9600); 43 | Serial.println(F("\tSolar Position Demo")); 44 | 45 | // set Time clock to Jan. 1, 2000 46 | setTime(SECS_YR_2000); 47 | Serial.print(F("Setting clock to ")); 48 | printTime(SECS_YR_2000); 49 | 50 | // set the Time library time service as the time provider 51 | SolarPosition::setTimeProvider(now); 52 | 53 | // save a complete current position 54 | savedPosition = Ulaanbaatar.getSolarPosition(); 55 | 56 | // First test the fixed time methods: 57 | // 58 | Serial.print(F("The sun was at an elevation of ")); 59 | Serial.print(Timbuktu.getSolarPosition(someEpochTime).elevation, 4); 60 | Serial.print(F(" and an azimuth of ")); 61 | Serial.println(Timbuktu.getSolarPosition(someEpochTime).azimuth, 4); 62 | Serial.print(F("in Timbuktu at ")); 63 | printTime(someEpochTime); 64 | 65 | Serial.print(F("The earth was ")); 66 | Serial.print(Timbuktu.getSolarDistance(someEpochTime), 0); 67 | Serial.println(F(" km from the Sun.")); 68 | Serial.println(); 69 | 70 | Serial.println(F("Real time sun position reports follow...")); 71 | Serial.println(); 72 | } 73 | 74 | void loop() 75 | { 76 | // now test the real (synchronized) time methods: 77 | // 78 | printTime(Toronto.getSolarPosition().time); 79 | Serial.print(F("Toronto:\t")); 80 | printSolarPosition(Toronto.getSolarPosition(), digits); 81 | Serial.print(F("Melbourne:\t")); 82 | printSolarPosition(Melbourne.getSolarPosition(), digits); 83 | Serial.print(F("Timbuktu:\t")); 84 | printSolarPosition(Timbuktu.getSolarPosition(), digits); 85 | Serial.print(F("Ulaanbaatar:\t")); 86 | printSolarPosition(Ulaanbaatar.getSolarPosition(), digits); 87 | Serial.println(); 88 | Serial.print(F("Ulaanb. Saved:\t")); 89 | printSolarPosition(savedPosition, digits); 90 | Serial.println(); 91 | 92 | delay(15000); 93 | } 94 | 95 | // Print a solar position to serial 96 | // 97 | void printSolarPosition(SolarPosition_t pos, int numDigits) 98 | { 99 | Serial.print(F("el: ")); 100 | Serial.print(pos.elevation, numDigits); 101 | Serial.print(F(" deg\t")); 102 | 103 | Serial.print(F("az: ")); 104 | Serial.print(pos.azimuth, numDigits); 105 | Serial.println(F(" deg")); 106 | } 107 | 108 | // Print a time to serial 109 | // 110 | void printTime(time_t t) 111 | { 112 | tmElements_t someTime; 113 | breakTime(t, someTime); 114 | 115 | Serial.print(someTime.Hour); 116 | Serial.print(F(":")); 117 | Serial.print(someTime.Minute); 118 | Serial.print(F(":")); 119 | Serial.print(someTime.Second); 120 | Serial.print(F(" UTC on ")); 121 | Serial.print(dayStr(someTime.Wday)); 122 | Serial.print(F(", ")); 123 | Serial.print(monthStr(someTime.Month)); 124 | Serial.print(F(" ")); 125 | Serial.print(someTime.Day); 126 | Serial.print(F(", ")); 127 | Serial.println(tmYearToCalendar(someTime.Year)); 128 | } 129 | 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SolarPosition 2 | Arduino Library to calculate the position of the sun relative to geographic coordinates 3 | 4 | based on the program "Arduino Uno and Solar Position Calculations" 5 | (c) David R. Brooks, which can be found at http://www.instesre.org/ArduinoDocuments.htm 6 | and issued under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License: 7 | https://creativecommons.org/licenses/by-nc-nd/4.0/ 8 | 9 | Dr. Brooks program uses equations are from the book, 10 | "Jean Meeus, Astronomical Algorithms, Willmann-Bell, Inc., Richmond, VA". 11 | A very important aspect is that straightforward floating point calculations for those equations 12 | are likely to introduce significant errors with the 32 bit float type used on the Arduino. 13 | So a special method of splitting Julian days into an integer and fractional part is used 14 | to overcome that problem. 15 | 16 | This adaptation introduces an object oriented interface, and a method of synchronizing the time 17 | to a time source in a standard UNIX epoch format. Thus it is easily usable with any Arduino user 18 | program. 19 | 20 | It relies on the Time library, https://github.com/PaulStoffregen/Time 21 | which provides convenient constants definitions and functions 22 | which are not only useful in simplifying the library code, but can be used in the user program 23 | to manipulate and display the time values associated with the solar locations. 24 | 25 | # Usage 26 | 27 | First ensure that the Time library is correctly installed. 28 | 29 | Include this library: 30 | 31 | #include 32 | 33 | In the setup() function, set the time provider if you are using the real time methods: 34 | 35 | SolarPosition::setTimeProvider(); 36 | 37 | For example: 38 | 39 | SolarPosition::setTimeProvider(RTC.get); 40 | 41 | # Data Storage 42 | 43 | If you want to store solar positions as aggregate data (i.e. as a whole), you should use 44 | the struct datatype that is provided for that purpose: 45 | 46 | ``` 47 | struct SolarPosition_t 48 | { 49 | float elevation = 0; 50 | float azimuth = 0; 51 | float distance = 0; 52 | time_t time = 0; 53 | }; 54 | ``` 55 | 56 | Once you declare a SolarPosition_t object, like this: 57 | 58 | SolarPosition_t somePosition; 59 | 60 | you can assign sun position data to it, including the time associated with the position, 61 | by using the method that returns an aggregate position like this: 62 | 63 | somePosition = someLocation.getSolarPosition(); 64 | 65 | Subsequently, you can access a member variable like this: 66 | 67 | azimuthVariable = somePosition.azimuth; 68 | 69 | This is a convenient way to ensure that all the variables were created at the same time, if that 70 | is important for the application. 71 | 72 | # Accessing Data Directly 73 | 74 | The above data structure is only necessary if you wish to store the results - if not, you can access members 75 | directly, like this: 76 | 77 | azimuthVariable = someLocation.getSolarPosition().azimuth; 78 | 79 | However, direct methods are provided for those members so the above can be stated more simply: 80 | 81 | azimuthVariable = someLocation.getSolarAzimuth(); 82 | 83 | All parameters of a position object are calculated at the same time. 84 | Before any solar calculation is performed internally, a check is made to see if the time has changed from the 85 | previous calculation. If the results are available from the previous calculation, they will be used instead 86 | of performing the calculations again. This avoids wasting time in redundant calculations. 87 | 88 | # Utility Function 89 | 90 | In case the geographical locations will be assigned dynamically or radians are preferred as an angular unit, or for any other reason the SolarPosition class is not preferred, direct access to the utility function calculateSolarPosition() is permitted. See the example sketch solarTimeFunctionDemo for details. 91 | 92 | # Time parameter 93 | 94 | The mathematical formulas in the library base all calculations on UTC time (otherwise known as GMT). 95 | Therefore, all times must be in UTC, and any time provider sources such as RTC ICs must be configured 96 | to provide UTC time. If local time is needed, it is recommended to perform a conversion separately from 97 | the workings of this library. 98 | 99 | All methods in this library use a time_t type time for calculations. If the parameter is not passed, then 100 | the time source that has been specified by the setTimeProvider() method will be used. This makes it 101 | easy to work with either real time, or arbitrary past or future times. 102 | 103 | If the real time methods are called, a time provider function must first be selected once with the 104 | setTimeProvider() method. The time provider is shared between all objects. Failing to do so will not 105 | crash the program, but invalid results will be returned. Once it is set, methods that take no 106 | time value as a parameter, will automatically get the time from the time provider. In this case, 107 | the methods return real time values. It is not necessary to use a time provider if the fixed 108 | time methods are called, since the time parameter is passed to them explicitly. 109 | 110 | # Constructor 111 | 112 |

SolarPosition(Latitude, Longitude)

113 | 114 | SolarPosition(float Latitude, float Longitude); 115 | create a location given Latitude and Longitude in decimal degrees 116 | 117 | # Public Methods 118 | 119 |

SolarPosition::setTimeProvider(

120 | 121 | static void setTimeProvider(getExternalTime getTimeFunction); 122 | 123 |

getSolarPosition()

124 | 125 | SolarPosition_t getSolarPosition(); 126 | SolarPosition_t getSolarPosition(time_t t); 127 | return a complete solar position structure 128 | See the description of struct SolarPosition_t for information about the data members 129 | 130 |

getSolarElevation()

131 | 132 | float getSolarElevation(); 133 | float getSolarElevation(time_t t); 134 | return solar elevation in decimal degrees 135 | 136 |

getSolarAzimuth()

137 | 138 | float getSolarAzimuth(); 139 | float getSolarAzimuth(time_t t); 140 | return solar azimuth in decimal degrees 141 | 142 |

getSolarDistance()

143 | 144 | float getSolarDistance(); 145 | float getSolarDistance(time_t t); 146 | return solar distance (from center of the earth to the sun) in kilometers 147 | -------------------------------------------------------------------------------- /SolarPosition.cpp: -------------------------------------------------------------------------------- 1 | // SolarPosition.cpp 2 | 3 | // 2019 Ken Willmott 4 | // Arduino library based on the program "Arduino Uno and Solar Position Calculations" 5 | // (c) David R. Brooks, which can be found at http://www.instesre.org/ArduinoDocuments.htm 6 | // and issued under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License: 7 | // https://creativecommons.org/licenses/by-nc-nd/4.0/ 8 | 9 | #include "SolarPosition.h" 10 | 11 | getExternalTime SolarPosition::getExtTimePtr = NULL; // pointer to external sync function 12 | 13 | // constructor 14 | // 15 | SolarPosition::SolarPosition(const float Lat, const float Lon) { 16 | Latitude = Lat * DEG_TO_RAD; 17 | Longitude = Lon * DEG_TO_RAD; 18 | } 19 | 20 | // *** public methods: *** 21 | 22 | // assign a Time provider function 23 | // 24 | void SolarPosition::setTimeProvider(getExternalTime getTimeFunction) { 25 | getExtTimePtr = getTimeFunction; 26 | } 27 | 28 | // Get current Position 29 | // 30 | SolarPosition_t SolarPosition::getSolarPosition() { 31 | const SolarPosition_t nullPos; 32 | 33 | if (getExtTimePtr != NULL) { 34 | time_t timeNow = getExtTimePtr(); 35 | SolarPosition_t pos; 36 | result = calculateSolarPosition(timeNow, Latitude, Longitude); 37 | pos.elevation = result.elevation * RAD_TO_DEG; 38 | pos.azimuth = result.azimuth * RAD_TO_DEG; 39 | pos.distance = result.distance * KM_PER_AU; 40 | pos.time = timeNow; 41 | return pos; 42 | } else { 43 | return nullPos; 44 | } 45 | } 46 | 47 | // Get Position for specified time 48 | // 49 | SolarPosition_t SolarPosition::getSolarPosition(time_t t) { 50 | SolarPosition_t pos; 51 | result = calculateSolarPosition(t, Latitude, Longitude); 52 | pos.elevation = result.elevation * RAD_TO_DEG; 53 | pos.azimuth = result.azimuth * RAD_TO_DEG; 54 | pos.distance = result.distance * KM_PER_AU; 55 | pos.time = t; 56 | return pos; 57 | } 58 | 59 | // Get current Elevation 60 | // 61 | float SolarPosition::getSolarElevation() { 62 | if (getExtTimePtr != NULL) { 63 | result = calculateSolarPosition(getExtTimePtr(), Latitude, Longitude); 64 | return result.elevation * RAD_TO_DEG; 65 | } else { 66 | return 0; 67 | } 68 | } 69 | 70 | // Get Elevation for specified time 71 | // 72 | float SolarPosition::getSolarElevation(time_t t) { 73 | result = calculateSolarPosition(t, Latitude, Longitude); 74 | return result.elevation * RAD_TO_DEG; 75 | } 76 | 77 | // Get current Azimuth 78 | // 79 | float SolarPosition::getSolarAzimuth() { 80 | if (getExtTimePtr != NULL) { 81 | result = calculateSolarPosition(getExtTimePtr(), Latitude, Longitude); 82 | return result.azimuth * RAD_TO_DEG; 83 | } else { 84 | return 0; 85 | } 86 | } 87 | 88 | // Get Azimuth for specified time 89 | // 90 | float SolarPosition::getSolarAzimuth(time_t t) { 91 | result = calculateSolarPosition(t, Latitude, Longitude); 92 | return result.azimuth * RAD_TO_DEG; 93 | } 94 | 95 | // Get current Solar distance in AU 96 | // 97 | float SolarPosition::getSolarDistance() { 98 | if (getExtTimePtr != NULL) { 99 | result = calculateSolarPosition(getExtTimePtr(), Latitude, Longitude); 100 | return result.distance * KM_PER_AU; 101 | } else { 102 | return 0; 103 | } 104 | } 105 | 106 | // Get Solar distance in AU for specified time 107 | // 108 | float SolarPosition::getSolarDistance(time_t t) { 109 | result = calculateSolarPosition(t, Latitude, Longitude); 110 | return result.distance * KM_PER_AU; 111 | } 112 | 113 | // 114 | // *** end of public methods: *** 115 | 116 | // *** beginning of utility functions *** 117 | // 118 | 119 | long JulianDate(int year, int month, int day) { 120 | long JD_whole; 121 | int A, B; 122 | if (month <= 2) { 123 | year--; 124 | month += 12; 125 | } 126 | A = year / 100; 127 | B = 2 - A + A / 4; 128 | JD_whole = (long) (365.25 * (year + 4716)) + (int) (30.6001 * (month + 1)) 129 | + day + B - 1524; 130 | return JD_whole; 131 | } 132 | 133 | SolarPosition_t calculateSolarPosition(time_t tParam, float Latitude, 134 | float Longitude) { 135 | 136 | const float DAYS_PER_JULIAN_CENTURY = 36525.0; 137 | const long Y2K_JULIAN_DAY = 2451545; 138 | 139 | tmElements_t timeCandidate; 140 | static time_t timePrevious = 0; 141 | static float latPrevious; 142 | static float lonPrevious; 143 | static SolarPosition_t result; 144 | 145 | long JD_whole; 146 | long JDx; 147 | float JD_frac; 148 | float rightAscension; 149 | float Declination; 150 | float hourAngle; 151 | float GreenwichHourAngle; 152 | float elapsedT; 153 | float solarLongitude; 154 | float solarMeanAnomaly; 155 | float earthOrbitEccentricity; 156 | float sunCenter; 157 | float solarTrueLongitude; 158 | float solarTrueAnomaly; 159 | float equatorObliquity; 160 | 161 | if (tParam != timePrevious or Latitude != latPrevious 162 | or Longitude != lonPrevious) // only calculate if time or location has changed 163 | { 164 | breakTime(tParam, timeCandidate); 165 | JD_whole = JulianDate(tmYearToCalendar(timeCandidate.Year), 166 | timeCandidate.Month, timeCandidate.Day); 167 | JD_frac = (timeCandidate.Hour + timeCandidate.Minute / 60.0 168 | + timeCandidate.Second / 3600.0) / 24.0 - 0.5; 169 | 170 | elapsedT = JD_whole - Y2K_JULIAN_DAY; 171 | elapsedT = (elapsedT + JD_frac) / DAYS_PER_JULIAN_CENTURY; 172 | 173 | solarLongitude = DEG_TO_RAD 174 | * fmod(280.46645 + 36000.76983 * elapsedT, 360); 175 | solarMeanAnomaly = DEG_TO_RAD 176 | * fmod(357.5291 + 35999.0503 * elapsedT, 360); 177 | earthOrbitEccentricity = 0.016708617 - 0.000042037 * elapsedT; 178 | 179 | sunCenter = DEG_TO_RAD 180 | * ((1.9146 - 0.004847 * elapsedT) * sin(solarMeanAnomaly) 181 | + (0.019993 - 0.000101 * elapsedT) 182 | * sin(2 * solarMeanAnomaly) 183 | + 0.00029 * sin(3 * solarMeanAnomaly)); 184 | 185 | solarTrueAnomaly = solarMeanAnomaly + sunCenter; 186 | equatorObliquity = DEG_TO_RAD 187 | * (23 + 26 / 60. + 21.448 / 3600. - 46.815 / 3600 * elapsedT); 188 | 189 | JDx = JD_whole - Y2K_JULIAN_DAY; 190 | 191 | GreenwichHourAngle = 280.46061837 + (360 * JDx) % 360 192 | + .98564736629 * JDx + 360.98564736629 * JD_frac; 193 | GreenwichHourAngle = fmod(GreenwichHourAngle, 360.0); 194 | 195 | solarTrueLongitude = fmod(sunCenter + solarLongitude, TWO_PI); 196 | 197 | rightAscension = atan2(sin(solarTrueLongitude) * cos(equatorObliquity), 198 | cos(solarTrueLongitude)); 199 | 200 | Declination = asin(sin(equatorObliquity) * sin(solarTrueLongitude)); 201 | hourAngle = DEG_TO_RAD * GreenwichHourAngle + Longitude 202 | - rightAscension; 203 | 204 | // results: 205 | result.distance = 1.000001018 206 | * (1 - earthOrbitEccentricity * earthOrbitEccentricity) 207 | / (1 + earthOrbitEccentricity * cos(solarTrueAnomaly)); 208 | 209 | // elevation from the horizon 210 | result.elevation = asin( 211 | sin(Latitude) * sin(Declination) 212 | + cos(Latitude) * (cos(Declination) * cos(hourAngle))); 213 | 214 | // Azimuth measured eastward from north. 215 | result.azimuth = PI 216 | + atan2(sin(hourAngle), 217 | cos(hourAngle) * sin(Latitude) 218 | - tan(Declination) * cos(Latitude)); 219 | 220 | // copy the time 221 | result.time = tParam; 222 | 223 | // remember the parameters 224 | timePrevious = tParam; 225 | latPrevious = Latitude; 226 | lonPrevious = Longitude; 227 | } 228 | return result; 229 | } 230 | 231 | // *** end of utility functions *** 232 | --------------------------------------------------------------------------------