Sun position calculation library. Adaptation of Hannes Hassler's Helios class.

Dependents:   sunTracker weather_station_proj weather_station_project weather_station_proj_v1_2

Committer:
acracan
Date:
Sun Jun 24 12:10:33 2018 +0000
Revision:
0:ad31da30ae64
Adaptation of Hannes Hassler's Helios class

Who changed what in which revision?

UserRevisionLine numberNew contents of line
acracan 0:ad31da30ae64 1 /*
acracan 0:ad31da30ae64 2 Helios.cpp-
acracan 0:ad31da30ae64 3 Copyright (c) 2011 Hannes Hassler. All rights reserved.
acracan 0:ad31da30ae64 4
acracan 0:ad31da30ae64 5 This library is free software; you can redistribute it and/or
acracan 0:ad31da30ae64 6 modify it under the terms of the GNU Lesser General Public
acracan 0:ad31da30ae64 7 License as published by the Free Software Foundation; either
acracan 0:ad31da30ae64 8 version 2.1 of the License, or (at your option) any later version.
acracan 0:ad31da30ae64 9
acracan 0:ad31da30ae64 10 This library is distributed in the hope that it will be useful,
acracan 0:ad31da30ae64 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
acracan 0:ad31da30ae64 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
acracan 0:ad31da30ae64 13 Lesser General Public License for more details.
acracan 0:ad31da30ae64 14
acracan 0:ad31da30ae64 15 You should have received a copy of the GNU Lesser General Public
acracan 0:ad31da30ae64 16 License along with this library; if not, write to the Free Software
acracan 0:ad31da30ae64 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
acracan 0:ad31da30ae64 18
acracan 0:ad31da30ae64 19 This library can be used for calculating the solar position on Arduino.
acracan 0:ad31da30ae64 20
acracan 0:ad31da30ae64 21 The algorithm is an adaption from
acracan 0:ad31da30ae64 22 the "PSA" solar positioning algorithm, as documented in:
acracan 0:ad31da30ae64 23
acracan 0:ad31da30ae64 24 Blanco-Muriel et al.: Computing the Solar Vector. Solar Energy Vol 70 No 5 pp 431-441.
acracan 0:ad31da30ae64 25 http://dx.doi.org/10.1016/S0038-092X(00)00156-0
acracan 0:ad31da30ae64 26
acracan 0:ad31da30ae64 27 According to the paper, "The algorithm allows .. the true solar vector
acracan 0:ad31da30ae64 28 to be determined with an accuracy of 0.5
acracan 0:ad31da30ae64 29 minutes of arc for the period 1999–2015.
acracan 0:ad31da30ae64 30 The original code has been downloaded from
acracan 0:ad31da30ae64 31 http://www.psa.es/sdg/sunpos.htm
acracan 0:ad31da30ae64 32
acracan 0:ad31da30ae64 33 Adaptions:
acracan 0:ad31da30ae64 34 Modified calculation of number of Days since 1.Jan 2000 (dJulianDate-2451545.0)
acracan 0:ad31da30ae64 35 Neccessary because of the limited double precision on Arduino
acracan 0:ad31da30ae64 36 (double has the same precision as float on the current Arduino (2011))
acracan 0:ad31da30ae64 37 It should be used only for dates between 1.1.2000 and 31.12.2100
acracan 0:ad31da30ae64 38 (PSA itself has garantueed accuracy only until 2015)
acracan 0:ad31da30ae64 39
acracan 0:ad31da30ae64 40 */
acracan 0:ad31da30ae64 41
acracan 0:ad31da30ae64 42 #include "Helios.h"
acracan 0:ad31da30ae64 43 #include <math.h>
acracan 0:ad31da30ae64 44 #include <mbed.h>
acracan 0:ad31da30ae64 45
acracan 0:ad31da30ae64 46
acracan 0:ad31da30ae64 47 Helios::Helios(double latitude, double longitude, int tzOffset)
acracan 0:ad31da30ae64 48 {
acracan 0:ad31da30ae64 49 setLocalLatitude(latitude);
acracan 0:ad31da30ae64 50 setLocalLongitude(longitude);
acracan 0:ad31da30ae64 51 setLocalTimeZoneOffset(tzOffset);
acracan 0:ad31da30ae64 52 }
acracan 0:ad31da30ae64 53
acracan 0:ad31da30ae64 54
acracan 0:ad31da30ae64 55 void Helios::updatePosition()
acracan 0:ad31da30ae64 56 {
acracan 0:ad31da30ae64 57 // rtc time
acracan 0:ad31da30ae64 58 time_t rtcTime;
acracan 0:ad31da30ae64 59 struct tm *rtcTimeInfo;
acracan 0:ad31da30ae64 60
acracan 0:ad31da30ae64 61 double dElapsedJulianDays;
acracan 0:ad31da30ae64 62 double dDecimalHours;
acracan 0:ad31da30ae64 63 double dEclipticLongitude;
acracan 0:ad31da30ae64 64 double dEclipticObliquity;
acracan 0:ad31da30ae64 65 double dRightAscension;
acracan 0:ad31da30ae64 66 double dDeclination;
acracan 0:ad31da30ae64 67
acracan 0:ad31da30ae64 68 double dSin_EclipticLongitude;
acracan 0:ad31da30ae64 69 double dMeanLongitude;
acracan 0:ad31da30ae64 70 double dMeanAnomaly;
acracan 0:ad31da30ae64 71 double dOmega;
acracan 0:ad31da30ae64 72
acracan 0:ad31da30ae64 73 double dGreenwichMeanSiderealTime;
acracan 0:ad31da30ae64 74 double dLocalMeanSiderealTime;
acracan 0:ad31da30ae64 75
acracan 0:ad31da30ae64 76 double dHourAngle;
acracan 0:ad31da30ae64 77 double dCos_HourAngle;
acracan 0:ad31da30ae64 78 double dParallax;
acracan 0:ad31da30ae64 79 double dZenithAngle;
acracan 0:ad31da30ae64 80
acracan 0:ad31da30ae64 81 // get current time from RTC
acracan 0:ad31da30ae64 82 time(&rtcTime);
acracan 0:ad31da30ae64 83 rtcTimeInfo = localtime(&rtcTime);
acracan 0:ad31da30ae64 84
acracan 0:ad31da30ae64 85 // Calculate difference in days between the current Julian Day
acracan 0:ad31da30ae64 86 // and JD 2451545.0, which is noon 1 January 2000 Universal Time
acracan 0:ad31da30ae64 87
acracan 0:ad31da30ae64 88 // Calculate time of the day in UT decimal hours
acracan 0:ad31da30ae64 89 dDecimalHours = (rtcTimeInfo->tm_hour - iTzOffset) + (rtcTimeInfo->tm_min
acracan 0:ad31da30ae64 90 + rtcTimeInfo->tm_sec / 60.0 ) / 60.0;
acracan 0:ad31da30ae64 91
acracan 0:ad31da30ae64 92 // Calculate current Julian Day
acracan 0:ad31da30ae64 93 long int iYfrom2000=rtcTimeInfo->tm_year-100;
acracan 0:ad31da30ae64 94 long int iA=(14-(rtcTimeInfo->tm_mon+1))/12;
acracan 0:ad31da30ae64 95 long int iM=(rtcTimeInfo->tm_mon+1)+12*iA-3;
acracan 0:ad31da30ae64 96
acracan 0:ad31da30ae64 97 long int liAux3=(153*iM+2)/5;
acracan 0:ad31da30ae64 98 long int liAux4=365*(iYfrom2000-iA);
acracan 0:ad31da30ae64 99 long int liAux5=(iYfrom2000-iA)/4;
acracan 0:ad31da30ae64 100
acracan 0:ad31da30ae64 101
acracan 0:ad31da30ae64 102 dElapsedJulianDays=(double)(rtcTimeInfo->tm_mday+liAux3+liAux4+liAux5+59)+
acracan 0:ad31da30ae64 103 -0.5+dDecimalHours/24.0;
acracan 0:ad31da30ae64 104
acracan 0:ad31da30ae64 105
acracan 0:ad31da30ae64 106 // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the
acracan 0:ad31da30ae64 107 // ecliptic in radians but without limiting the angle to be less than 2*Pi
acracan 0:ad31da30ae64 108 // (i.e., the result may be greater than 2*Pi)
acracan 0:ad31da30ae64 109 dOmega=2.1429-0.0010394594*dElapsedJulianDays;
acracan 0:ad31da30ae64 110 dMeanLongitude = 4.8950630+ 0.017202791698*dElapsedJulianDays; // Radians
acracan 0:ad31da30ae64 111 dMeanAnomaly = 6.2400600+ 0.0172019699*dElapsedJulianDays;
acracan 0:ad31da30ae64 112 dEclipticLongitude = dMeanLongitude + 0.03341607*sin( dMeanAnomaly )
acracan 0:ad31da30ae64 113 + 0.00034894*sin( 2*dMeanAnomaly )-0.0001134
acracan 0:ad31da30ae64 114 -0.0000203*sin(dOmega);
acracan 0:ad31da30ae64 115 dEclipticObliquity = 0.4090928 - 6.2140e-9*dElapsedJulianDays
acracan 0:ad31da30ae64 116 +0.0000396*cos(dOmega);
acracan 0:ad31da30ae64 117
acracan 0:ad31da30ae64 118 // Calculate celestial coordinates ( right ascension and declination ) in radians
acracan 0:ad31da30ae64 119 // but without limiting the angle to be less than 2*Pi (i.e., the result may be
acracan 0:ad31da30ae64 120 // greater than 2*Pi)
acracan 0:ad31da30ae64 121 dSin_EclipticLongitude= sin( dEclipticLongitude );
acracan 0:ad31da30ae64 122 double dY1 = cos( dEclipticObliquity ) * dSin_EclipticLongitude;
acracan 0:ad31da30ae64 123 double dX1 = cos( dEclipticLongitude );
acracan 0:ad31da30ae64 124 dRightAscension = atan2( dY1,dX1 );
acracan 0:ad31da30ae64 125 if( dRightAscension < 0.0 ) dRightAscension = dRightAscension + twopi;
acracan 0:ad31da30ae64 126 dDeclination = asin( sin( dEclipticObliquity )*dSin_EclipticLongitude );
acracan 0:ad31da30ae64 127
acracan 0:ad31da30ae64 128 // Calculate local coordinates ( azimuth and zenith angle ) in degrees
acracan 0:ad31da30ae64 129 dGreenwichMeanSiderealTime = 6.6974243242 +
acracan 0:ad31da30ae64 130 0.0657098283*dElapsedJulianDays
acracan 0:ad31da30ae64 131 + dDecimalHours;
acracan 0:ad31da30ae64 132
acracan 0:ad31da30ae64 133 dLocalMeanSiderealTime = (dGreenwichMeanSiderealTime*15
acracan 0:ad31da30ae64 134 + dLongitude)*rad;
acracan 0:ad31da30ae64 135 dHourAngle = dLocalMeanSiderealTime - dRightAscension;
acracan 0:ad31da30ae64 136
acracan 0:ad31da30ae64 137 dCos_HourAngle= cos( dHourAngle );
acracan 0:ad31da30ae64 138 dZenithAngle = (acos( dCos_Latitude*dCos_HourAngle
acracan 0:ad31da30ae64 139 *cos(dDeclination) + sin( dDeclination )*dSin_Latitude));
acracan 0:ad31da30ae64 140 double dY = -sin( dHourAngle );
acracan 0:ad31da30ae64 141 double dX = tan( dDeclination )*dCos_Latitude - dSin_Latitude*dCos_HourAngle;
acracan 0:ad31da30ae64 142 dAzimuth=atan2( dY, dX );
acracan 0:ad31da30ae64 143 if ( dAzimuth < 0.0 )
acracan 0:ad31da30ae64 144 dAzimuth = dAzimuth + twopi;
acracan 0:ad31da30ae64 145 dAzimuth = dAzimuth/rad;
acracan 0:ad31da30ae64 146 // Parallax Correction
acracan 0:ad31da30ae64 147 dParallax=(dEarthMeanRadius/dAstronomicalUnit)
acracan 0:ad31da30ae64 148 *sin(dZenithAngle);
acracan 0:ad31da30ae64 149 dZenithAngle=(dZenithAngle
acracan 0:ad31da30ae64 150 + dParallax)/rad;
acracan 0:ad31da30ae64 151 dElevation=90-dZenithAngle;
acracan 0:ad31da30ae64 152 }
acracan 0:ad31da30ae64 153
acracan 0:ad31da30ae64 154 void Helios::setLocalLatitude(double latitude)
acracan 0:ad31da30ae64 155 {
acracan 0:ad31da30ae64 156 double dLatitudeInRadians;
acracan 0:ad31da30ae64 157
acracan 0:ad31da30ae64 158 dLatitude = latitude;
acracan 0:ad31da30ae64 159 dLatitudeInRadians = dLatitude*rad;
acracan 0:ad31da30ae64 160 dCos_Latitude = cos( dLatitudeInRadians );
acracan 0:ad31da30ae64 161 dSin_Latitude = sin( dLatitudeInRadians );
acracan 0:ad31da30ae64 162 }
acracan 0:ad31da30ae64 163
acracan 0:ad31da30ae64 164 void Helios::setLocalLongitude(double longitude)
acracan 0:ad31da30ae64 165 {
acracan 0:ad31da30ae64 166 dLongitude = longitude;
acracan 0:ad31da30ae64 167 }
acracan 0:ad31da30ae64 168
acracan 0:ad31da30ae64 169 void Helios::setLocalTimeZoneOffset(int tzOffset)
acracan 0:ad31da30ae64 170 {
acracan 0:ad31da30ae64 171 iTzOffset = tzOffset;
acracan 0:ad31da30ae64 172 }
acracan 0:ad31da30ae64 173
acracan 0:ad31da30ae64 174 double Helios::azimuth()
acracan 0:ad31da30ae64 175 {
acracan 0:ad31da30ae64 176 return dAzimuth;
acracan 0:ad31da30ae64 177 }
acracan 0:ad31da30ae64 178
acracan 0:ad31da30ae64 179 double Helios::elevation()
acracan 0:ad31da30ae64 180 {
acracan 0:ad31da30ae64 181 return dElevation;
acracan 0:ad31da30ae64 182 }