Annotation of embedaddon/php/ext/date/lib/astro.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | PHP Version 5                                                        |
                      4:    +----------------------------------------------------------------------+
1.1.1.3 ! misho       5:    | Copyright (c) 1997-2013 The PHP Group                                |
1.1       misho       6:    +----------------------------------------------------------------------+
                      7:    | This source file is subject to version 3.01 of the PHP license,      |
                      8:    | that is bundled with this package in the file LICENSE, and is        |
                      9:    | available through the world-wide-web at the following url:           |
                     10:    | http://www.php.net/license/3_01.txt                                  |
                     11:    | If you did not receive a copy of the PHP license and are unable to   |
                     12:    | obtain it through the world-wide-web, please send a note to          |
                     13:    | license@php.net so we can mail you a copy immediately.               |
                     14:    +----------------------------------------------------------------------+
                     15:    | Algorithms are taken from a public domain source by Paul             |
                     16:    | Schlyter, who wrote this in December 1992                            |
                     17:    +----------------------------------------------------------------------+
                     18:    | Authors: Derick Rethans <derick@derickrethans.nl>                    |
                     19:    +----------------------------------------------------------------------+
                     20:  */
                     21: 
1.1.1.2   misho      22: /* $Id$ */
1.1       misho      23: 
                     24: #include <stdio.h>
                     25: #include <math.h>
                     26: #include "timelib.h"
                     27: 
                     28: #define days_since_2000_Jan_0(y,m,d) \
                     29:        (367L*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530L)
                     30: 
                     31: #ifndef PI
                     32:  #define PI        3.1415926535897932384
                     33: #endif
                     34: 
                     35: #define RADEG     ( 180.0 / PI )
                     36: #define DEGRAD    ( PI / 180.0 )
                     37: 
                     38: /* The trigonometric functions in degrees */
                     39: 
                     40: #define sind(x)  sin((x)*DEGRAD)
                     41: #define cosd(x)  cos((x)*DEGRAD)
                     42: #define tand(x)  tan((x)*DEGRAD)
                     43: 
                     44: #define atand(x)    (RADEG*atan(x))
                     45: #define asind(x)    (RADEG*asin(x))
                     46: #define acosd(x)    (RADEG*acos(x))
                     47: #define atan2d(y,x) (RADEG*atan2(y,x))
                     48: 
                     49: 
                     50: /* Following are some macros around the "workhorse" function __daylen__ */
                     51: /* They mainly fill in the desired values for the reference altitude    */
                     52: /* below the horizon, and also selects whether this altitude should     */
                     53: /* refer to the Sun's center or its upper limb.                         */
                     54: 
                     55: 
                     56: #include "astro.h"
                     57: 
                     58: /******************************************************************/
                     59: /* This function reduces any angle to within the first revolution */
                     60: /* by subtracting or adding even multiples of 360.0 until the     */
                     61: /* result is >= 0.0 and < 360.0                                   */
                     62: /******************************************************************/
                     63: 
                     64: #define INV360    (1.0 / 360.0)
                     65: 
                     66: /*****************************************/
                     67: /* Reduce angle to within 0..360 degrees */
                     68: /*****************************************/
                     69: static double astro_revolution(double x)
                     70: {
                     71:        return (x - 360.0 * floor(x * INV360));
                     72: }
                     73: 
                     74: /*********************************************/
                     75: /* Reduce angle to within +180..+180 degrees */
                     76: /*********************************************/
                     77: static double astro_rev180( double x )
                     78: {
                     79:        return (x - 360.0 * floor(x * INV360 + 0.5));
                     80: }
                     81: 
                     82: /*******************************************************************/
                     83: /* This function computes GMST0, the Greenwich Mean Sidereal Time  */
                     84: /* at 0h UT (i.e. the sidereal time at the Greenwhich meridian at  */
                     85: /* 0h UT).  GMST is then the sidereal time at Greenwich at any     */
                     86: /* time of the day.  I've generalized GMST0 as well, and define it */
                     87: /* as:  GMST0 = GMST - UT  --  this allows GMST0 to be computed at */
                     88: /* other times than 0h UT as well.  While this sounds somewhat     */
                     89: /* contradictory, it is very practical:  instead of computing      */
                     90: /* GMST like:                                                      */
                     91: /*                                                                 */
                     92: /*  GMST = (GMST0) + UT * (366.2422/365.2422)                      */
                     93: /*                                                                 */
                     94: /* where (GMST0) is the GMST last time UT was 0 hours, one simply  */
                     95: /* computes:                                                       */
                     96: /*                                                                 */
                     97: /*  GMST = GMST0 + UT                                              */
                     98: /*                                                                 */
                     99: /* where GMST0 is the GMST "at 0h UT" but at the current moment!   */
                    100: /* Defined in this way, GMST0 will increase with about 4 min a     */
                    101: /* day.  It also happens that GMST0 (in degrees, 1 hr = 15 degr)   */
                    102: /* is equal to the Sun's mean longitude plus/minus 180 degrees!    */
                    103: /* (if we neglect aberration, which amounts to 20 seconds of arc   */
                    104: /* or 1.33 seconds of time)                                        */
                    105: /*                                                                 */
                    106: /*******************************************************************/
                    107: 
                    108: static double astro_GMST0(double d)
                    109: {
                    110:        double sidtim0;
                    111:        /* Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr  */
                    112:        /* L = M + w, as defined in sunpos().  Since I'm too lazy to */
                    113:        /* add these numbers, I'll let the C compiler do it for me.  */
                    114:        /* Any decent C compiler will add the constants at compile   */
                    115:        /* time, imposing no runtime or code overhead.               */
                    116:        sidtim0 = astro_revolution((180.0 + 356.0470 + 282.9404) + (0.9856002585 + 4.70935E-5) * d);
                    117:        return sidtim0;
                    118: } 
                    119: 
                    120: /* This function computes the Sun's position at any instant */
                    121: 
                    122: /******************************************************/
                    123: /* Computes the Sun's ecliptic longitude and distance */
                    124: /* at an instant given in d, number of days since     */
                    125: /* 2000 Jan 0.0.  The Sun's ecliptic latitude is not  */
                    126: /* computed, since it's always very near 0.           */
                    127: /******************************************************/
                    128: static void astro_sunpos(double d, double *lon, double *r)
                    129: {
                    130:        double M,         /* Mean anomaly of the Sun */
                    131:               w,         /* Mean longitude of perihelion */
                    132:                          /* Note: Sun's mean longitude = M + w */
                    133:               e,         /* Eccentricity of Earth's orbit */
                    134:               E,         /* Eccentric anomaly */
                    135:               x, y,      /* x, y coordinates in orbit */
                    136:               v;         /* True anomaly */
                    137: 
                    138:        /* Compute mean elements */
                    139:        M = astro_revolution(356.0470 + 0.9856002585 * d);
                    140:        w = 282.9404 + 4.70935E-5 * d;
                    141:        e = 0.016709 - 1.151E-9 * d;
                    142: 
                    143:        /* Compute true longitude and radius vector */
                    144:        E = M + e * RADEG * sind(M) * (1.0 + e * cosd(M));
                    145:        x = cosd(E) - e;
                    146:        y = sqrt(1.0 - e*e) * sind(E);
                    147:        *r = sqrt(x*x + y*y);              /* Solar distance */
                    148:        v = atan2d(y, x);                  /* True anomaly */
                    149:        *lon = v + w;                        /* True solar longitude */
                    150:        if (*lon >= 360.0) {
                    151:                *lon -= 360.0;                   /* Make it 0..360 degrees */
                    152:        }
                    153: }
                    154: 
                    155: static void astro_sun_RA_dec(double d, double *RA, double *dec, double *r)
                    156: {
                    157:        double lon, obl_ecl, x, y, z;
                    158: 
                    159:        /* Compute Sun's ecliptical coordinates */
                    160:        astro_sunpos(d, &lon, r);
                    161: 
                    162:        /* Compute ecliptic rectangular coordinates (z=0) */
                    163:        x = *r * cosd(lon);
                    164:        y = *r * sind(lon);
                    165: 
                    166:        /* Compute obliquity of ecliptic (inclination of Earth's axis) */
                    167:        obl_ecl = 23.4393 - 3.563E-7 * d;
                    168: 
                    169:        /* Convert to equatorial rectangular coordinates - x is unchanged */
                    170:        z = y * sind(obl_ecl);
                    171:        y = y * cosd(obl_ecl);
                    172: 
                    173:        /* Convert to spherical coordinates */
                    174:        *RA = atan2d(y, x);
                    175:        *dec = atan2d(z, sqrt(x*x + y*y));
                    176: }
                    177: 
                    178: /**
                    179:  * Note: timestamp = unixtimestamp (NEEDS to be 00:00:00 UT)
                    180:  *       Eastern longitude positive, Western longitude negative       
                    181:  *       Northern latitude positive, Southern latitude negative       
                    182:  *       The longitude value IS critical in this function!            
                    183:  *       altit = the altitude which the Sun should cross              
                    184:  *               Set to -35/60 degrees for rise/set, -6 degrees       
                    185:  *               for civil, -12 degrees for nautical and -18          
                    186:  *               degrees for astronomical twilight.                   
                    187:  *         upper_limb: non-zero -> upper limb, zero -> center         
                    188:  *               Set to non-zero (e.g. 1) when computing rise/set     
                    189:  *               times, and to zero when computing start/end of       
                    190:  *               twilight.                                            
                    191:  *        *rise = where to store the rise time                        
                    192:  *        *set  = where to store the set  time                        
                    193:  *                Both times are relative to the specified altitude,  
                    194:  *                and thus this function can be used to compute       
                    195:  *                various twilight times, as well as rise/set times   
                    196:  * Return value:  0 = sun rises/sets this day, times stored at        
                    197:  *                    *trise and *tset.                               
                    198:  *               +1 = sun above the specified "horizon" 24 hours.     
                    199:  *                    *trise set to time when the sun is at south,    
                    200:  *                    minus 12 hours while *tset is set to the south  
                    201:  *                    time plus 12 hours. "Day" length = 24 hours     
                    202:  *               -1 = sun is below the specified "horizon" 24 hours   
                    203:  *                    "Day" length = 0 hours, *trise and *tset are    
                    204:  *                    both set to the time when the sun is at south.  
                    205:  *                                                                    
                    206:  */
                    207: int timelib_astro_rise_set_altitude(timelib_time *t_loc, double lon, double lat, double altit, int upper_limb, double *h_rise, double *h_set, timelib_sll *ts_rise, timelib_sll *ts_set, timelib_sll *ts_transit)
                    208: {
                    209:        double  d,  /* Days since 2000 Jan 0.0 (negative before) */
                    210:        sr,         /* Solar distance, astronomical units */
                    211:        sRA,        /* Sun's Right Ascension */
                    212:        sdec,       /* Sun's declination */
                    213:        sradius,    /* Sun's apparent radius */
                    214:        t,          /* Diurnal arc */
                    215:        tsouth,     /* Time when Sun is at south */
                    216:        sidtime;    /* Local sidereal time */
                    217:        timelib_time *t_utc;
                    218:        timelib_sll   timestamp, old_sse;
                    219: 
                    220:        int rc = 0; /* Return cde from function - usually 0 */
                    221: 
                    222:        /* Normalize time */
                    223:        old_sse = t_loc->sse;
                    224:        t_loc->h = 12;
                    225:        t_loc->i = t_loc->s = 0;
                    226:        timelib_update_ts(t_loc, NULL);
                    227: 
                    228:        /* Calculate TS belonging to UTC 00:00 of the current day */
                    229:        t_utc = timelib_time_ctor();
                    230:        t_utc->y = t_loc->y;
                    231:        t_utc->m = t_loc->m;
                    232:        t_utc->d = t_loc->d;
                    233:        t_utc->h = t_utc->i = t_utc->s = 0;
                    234:        timelib_update_ts(t_utc, NULL);
                    235: 
                    236:        /* Compute d of 12h local mean solar time */
                    237:        timestamp = t_loc->sse;
                    238:        d = timelib_ts_to_juliandate(timestamp) - lon/360.0;
                    239: 
                    240:        /* Compute local sidereal time of this moment */
                    241:        sidtime = astro_revolution(astro_GMST0(d) + 180.0 + lon);
                    242: 
                    243:        /* Compute Sun's RA + Decl at this moment */
                    244:        astro_sun_RA_dec( d, &sRA, &sdec, &sr );
                    245: 
                    246:        /* Compute time when Sun is at south - in hours UT */
                    247:        tsouth = 12.0 - astro_rev180(sidtime - sRA) / 15.0;
                    248: 
                    249:        /* Compute the Sun's apparent radius, degrees */
                    250:        sradius = 0.2666 / sr;
                    251: 
                    252:        /* Do correction to upper limb, if necessary */
                    253:        if (upper_limb) {
                    254:                altit -= sradius;
                    255:        }
                    256: 
                    257:        /* Compute the diurnal arc that the Sun traverses to reach */
                    258:        /* the specified altitude altit: */
                    259:        {
                    260:                double cost;
                    261:                cost = (sind(altit) - sind(lat) * sind(sdec)) / (cosd(lat) * cosd(sdec));
                    262:                *ts_transit = t_utc->sse + (tsouth * 3600);
                    263:                if (cost >= 1.0) {
                    264:                        rc = -1;
                    265:                        t = 0.0;       /* Sun always below altit */
                    266: 
                    267:                        *ts_rise = *ts_set = t_utc->sse + (tsouth * 3600);
                    268:                } else if (cost <= -1.0) {
                    269:                        rc = +1;
                    270:                        t = 12.0;      /* Sun always above altit */
                    271: 
                    272:                        *ts_rise = t_loc->sse - (12 * 3600);
                    273:                        *ts_set  = t_loc->sse + (12 * 3600);
                    274:                } else {
                    275:                        t = acosd(cost) / 15.0;   /* The diurnal arc, hours */
                    276: 
                    277:                        /* Store rise and set times - as Unix Timestamp */
                    278:                        *ts_rise = ((tsouth - t) * 3600) + t_utc->sse;
                    279:                        *ts_set  = ((tsouth + t) * 3600) + t_utc->sse;
                    280: 
                    281:                        *h_rise = (tsouth - t);
                    282:                        *h_set  = (tsouth + t);
                    283:                }
                    284:        }
                    285: 
                    286:        /* Kill temporary time and restore original sse */
                    287:        timelib_time_dtor(t_utc);
                    288:        t_loc->sse = old_sse;
                    289: 
                    290:        return rc;
                    291: }
                    292: 
                    293: double timelib_ts_to_juliandate(timelib_sll ts)
                    294: {
                    295:        double tmp;
                    296: 
                    297:        tmp = ts;
                    298:        tmp /= 86400;
                    299:        tmp += 2440587.5;
                    300:        tmp -= 2451543;
                    301: 
                    302:        return tmp;
                    303: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>