PageRenderTime 37ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/inc/moonphase.inc.php

https://github.com/chregu/isitfullmoon
PHP | 472 lines | 259 code | 89 blank | 124 comment | 25 complexity | f462c1d0c3d2b1e5e969bcdfb7bd5381 MD5 | raw file
  1. <?php
  2. //
  3. // 2008-10-27
  4. // File: moonphase.inc.php (http://www.sentry.net/~obsid/moonphase)
  5. // Calculate information about the phase of the moon at a given time.
  6. //
  7. // Based on the Perl module Astro::MoonPhase, version 0.60.
  8. // http://search.cpan.org/~brett/Astro-MoonPhase-0.60/MoonPhase.pm
  9. //
  10. //
  11. // License:
  12. //
  13. // Astro::MoonPhase module is distributed under the public domain,
  14. // and so is this PHP translation.
  15. //
  16. //
  17. // Credits:
  18. //
  19. // The moontool.c Release 2.0:
  20. // A Moon for the Sun
  21. // Designed and implemented by John Walker in December 1987,
  22. // revised and updated in February of 1988.
  23. //
  24. // Initial Perl transcription:
  25. // Raino Pikkarainen, 1998
  26. // raino.pikkarainen@saunalahti.fi
  27. //
  28. // The moontool.c Release 2.4:
  29. // Major enhancements by Ron Hitchens, 1989
  30. //
  31. // Revisions:
  32. // Brett Hamilton http://simple.be/
  33. // Bug fix, 2003
  34. // Second transcription and bugfixes, 2004
  35. //
  36. // Christopher J. Madsen http://www.cjmweb.net/
  37. // Added phaselist function, March 2007
  38. //
  39. // Translated to PHP by Stephen A. Zarkos <obsid@sentry.net>, 2007
  40. // Fixed broken phasehunt function, 2008-10-27
  41. // Added phaselist function, 2008-10-27
  42. //
  43. //
  44. // Documentation:
  45. //
  46. // http://search.cpan.org/~brett/Astro-MoonPhase-0.60/MoonPhase.pm
  47. // http://www.obsid.org/2008/05/calculate-moon-phase-data-with-php.html
  48. //
  49. // Error definitions.
  50. define( 'ERR_UNDEF', -1 );
  51. // Astronomical constants.
  52. define( 'EPOCH', 2444238.5 ); // 1980 January 0.0
  53. // Constants defining the Sun's apparent orbit.
  54. define( 'ELONGE', 278.833540 ); // ecliptic longitude of the Sun at epoch 1980.0
  55. define( 'ELONGP', 282.596403 ); // ecliptic longitude of the Sun at perigee
  56. define( 'ECCENT', 0.016718 ); // eccentricity of Earth's orbit
  57. define( 'SUNSMAX', 1.495985e8 ); // semi-major axis of Earth's orbit, km
  58. define( 'SUNANGSIZ', 0.533128 ); // sun's angular size, degrees, at semi-major axis distance
  59. // Elements of the Moon's orbit, epoch 1980.0.
  60. define( 'MMLONG', 64.975464 ); // moon's mean longitude at the epoch
  61. define( 'MMLONGP', 349.383063 ); // mean longitude of the perigee at the epoch
  62. define( 'MLNODE', 151.950429 ); // mean longitude of the node at the epoch
  63. define( 'MINC', 5.145396 ); // inclination of the Moon's orbit
  64. define( 'MECC', 0.054900 ); // eccentricity of the Moon's orbit
  65. define( 'MANGSIZ', 0.5181 ); // moon's angular size at distance a from Earth
  66. define( 'MSMAX', 384401.0 ); // semi-major axis of Moon's orbit in km
  67. define( 'MPARALLAX', 0.9507 ); // parallax at distance a from Earth
  68. define( 'SYNMONTH', 29.53058868 ); // synodic month (new Moon to new Moon)
  69. // Handy mathematical functions.
  70. function sgn ( $arg ) { return (($arg < 0) ? -1 : ($arg > 0 ? 1 : 0)); } // extract sign
  71. function fixangle ( $arg ) { return ($arg - 360.0 * (floor($arg / 360.0))); } // fix angle
  72. function torad ( $arg ) { return ($arg * (pi() / 180.0)); } // deg->rad
  73. function todeg ( $arg ) { return ($arg * (180.0 / pi())); } // rad->deg
  74. function dsin ( $arg ) { return (sin(torad($arg))); } // sin from deg
  75. function dcos ( $arg ) { return (cos(torad($arg))); } // cos from deg
  76. // jtime - convert internal date and time to astronomical Julian
  77. // time (i.e. Julian date plus day fraction)
  78. function jtime ( $timestamp ) {
  79. $julian = ( $timestamp / 86400 ) + 2440587.5; // (seconds / (seconds per day)) + julian date of epoch
  80. return $julian;
  81. }
  82. // jdaytosecs - convert Julian date to a UNIX epoch
  83. function jdaytosecs ( $jday=0 ) {
  84. $stamp = ( $jday - 2440587.5 ) * 86400; // (juliandate - jdate of unix epoch) * (seconds per julian day)
  85. return $stamp;
  86. }
  87. // jyear - convert Julian date to year, month, day, which are
  88. // returned via integer pointers to integers
  89. function jyear ( $td, &$yy, &$mm, &$dd ) {
  90. $td += 0.5; // astronomical to civil.
  91. $z = floor( $td );
  92. $f = $td - $z;
  93. if ( $z < 2299161.0 ) {
  94. $a = $z;
  95. }
  96. else {
  97. $alpha = floor( ($z - 1867216.25) / 36524.25 );
  98. $a = $z + 1 + $alpha - floor( $alpha / 4 );
  99. }
  100. $b = $a + 1524;
  101. $c = floor( ($b - 122.1) / 365.25 );
  102. $d = floor( 365.25 * $c );
  103. $e = floor( ($b - $d) / 30.6001 );
  104. $dd = $b - $d - floor( 30.6001 * $e ) + $f;
  105. $mm = $e < 14 ? $e - 1 : $e - 13;
  106. $yy = $mm > 2 ? $c - 4716 : $c - 4715;
  107. }
  108. // meanphase -- Calculates time of the mean new Moon for a given
  109. // base date. This argument K to this function is the
  110. // precomputed synodic month index, given by:
  111. //
  112. // K = (year - 1900) * 12.3685
  113. //
  114. // where year is expressed as a year and fractional year.
  115. function meanphase ( $sdate, $k ) {
  116. // Time in Julian centuries from 1900 January 0.5
  117. $t = ( $sdate - 2415020.0 ) / 36525;
  118. $t2 = $t * $t; // Square for frequent use
  119. $t3 = $t2 * $t; // Cube for frequent use
  120. $nt1 = 2415020.75933 + SYNMONTH * $k
  121. + 0.0001178 * $t2
  122. - 0.000000155 * $t3
  123. + 0.00033 * dsin( 166.56 + 132.87 * $t - 0.009173 * $t2 );
  124. return ( $nt1 );
  125. }
  126. // truephase - given a K value used to determine the mean phase of the
  127. // new moon, and a phase selector (0.0, 0.25, 0.5, 0.75),
  128. // obtain the true, corrected phase time.
  129. function truephase ( $k, $phase ) {
  130. $apcor = 0;
  131. $k += $phase; // add phase to new moon time
  132. $t = $k / 1236.85; // time in Julian centuries from 1900 January 0.5
  133. $t2 = $t * $t; // square for frequent use
  134. $t3 = $t2 * $t; // cube for frequent use
  135. // mean time of phase
  136. $pt = 2415020.75933
  137. + SYNMONTH * $k
  138. + 0.0001178 * $t2
  139. - 0.000000155 * $t3
  140. + 0.00033 * dsin( 166.56 + 132.87 * $t - 0.009173 * $t2 );
  141. // Sun's mean anomaly
  142. $m = 359.2242
  143. + 29.10535608 * $k
  144. - 0.0000333 * $t2
  145. - 0.00000347 * $t3;
  146. // Moon's mean anomaly
  147. $mprime = 306.0253
  148. + 385.81691806 * $k
  149. + 0.0107306 * $t2
  150. + 0.00001236 * $t3;
  151. // Moon's argument of latitude
  152. $f = 21.2964
  153. + 390.67050646 * $k
  154. - 0.0016528 * $t2
  155. - 0.00000239 * $t3;
  156. if ( ($phase < 0.01) || (abs($phase - 0.5) < 0.01) ) {
  157. // Corrections for New and Full Moon.
  158. $pt += ( 0.1734 - 0.000393 * $t ) * dsin( $m )
  159. + 0.0021 * dsin( 2 * $m )
  160. - 0.4068 * dsin( $mprime )
  161. + 0.0161 * dsin( 2 * $mprime )
  162. - 0.0004 * dsin( 3 * $mprime )
  163. + 0.0104 * dsin( 2 * $f )
  164. - 0.0051 * dsin( $m + $mprime )
  165. - 0.0074 * dsin( $m - $mprime )
  166. + 0.0004 * dsin( 2 * $f + $m )
  167. - 0.0004 * dsin( 2 * $f - $m )
  168. - 0.0006 * dsin( 2 * $f + $mprime )
  169. + 0.0010 * dsin( 2 * $f - $mprime )
  170. + 0.0005 * dsin( $m + 2 * $mprime );
  171. $apcor = 1;
  172. }
  173. elseif ( (abs($phase - 0.25) < 0.01 || (abs($phase - 0.75) < 0.01)) ) {
  174. $pt += ( 0.1721 - 0.0004 * $t ) * dsin( $m )
  175. + 0.0021 * dsin( 2 * $m )
  176. - 0.6280 * dsin( $mprime )
  177. + 0.0089 * dsin( 2 * $mprime )
  178. - 0.0004 * dsin( 3 * $mprime )
  179. + 0.0079 * dsin( 2 * $f )
  180. - 0.0119 * dsin( $m + $mprime )
  181. - 0.0047 * dsin( $m - $mprime )
  182. + 0.0003 * dsin( 2 * $f + $m )
  183. - 0.0004 * dsin( 2 * $f - $m )
  184. - 0.0006 * dsin( 2 * $f + $mprime )
  185. + 0.0021 * dsin( 2 * $f - $mprime )
  186. + 0.0003 * dsin( $m + 2 * $mprime )
  187. + 0.0004 * dsin( $m - 2 * $mprime )
  188. - 0.0003 * dsin( 2 * $m + $mprime );
  189. if ( $phase < 0.5 ) {
  190. // First quarter correction.
  191. $pt += 0.0028 - 0.0004 * dcos( $m ) + 0.0003 * dcos( $mprime );
  192. }
  193. else {
  194. // Last quarter correction.
  195. $pt += -0.0028 + 0.0004 * dcos( $m ) - 0.0003 * dcos( $mprime );
  196. }
  197. $apcor = 1;
  198. }
  199. if ( !$apcor ) {
  200. print "truephase() called with invalid phase selector ($phase).\n";
  201. exit( ERR_UNDEF );
  202. }
  203. return ( $pt );
  204. }
  205. // phasehunt - find time of phases of the moon which surround the current
  206. // date. Five phases are found, starting and ending with the
  207. // new moons which bound the current lunation
  208. function phasehunt ( $time=-1 ) {
  209. $k1k2 = time2k1k2($time);
  210. $k1 = $k1k2[0];
  211. $k2 = $k1k2[0];
  212. return array ( jdaytosecs( truephase($k1, 0.0) ),
  213. jdaytosecs( truephase($k1, 0.25) ),
  214. jdaytosecs( truephase($k1, 0.5) ),
  215. jdaytosecs( truephase($k1, 0.75) ),
  216. jdaytosecs( truephase($k2, 0.0) )
  217. );
  218. }
  219. function time2k1k2($time = -1) {
  220. if ( empty($time) || $time == -1 ) {
  221. $time = time();
  222. }
  223. $sdate = jtime( $time );
  224. $adate = $sdate - 45;
  225. jyear( $adate, $yy, $mm, $dd );
  226. $k1 = floor( ($yy + (($mm - 1) * (1.0 / 12.0)) - 1900) * 12.3685 );
  227. $adate = $nt1 = meanphase( $adate, $k1 );
  228. while (1) {
  229. $adate += SYNMONTH;
  230. $k2 = $k1 + 1;
  231. $nt2 = meanphase( $adate, $k2 );
  232. if (($nt1 <= $sdate) && ($nt2 > $sdate)) {
  233. break;
  234. }
  235. $nt1 = $nt2;
  236. $k1 = $k2;
  237. }
  238. return array($k1,$k2);
  239. }
  240. function phasemoon($time = -1,$phase = 0.5,$second = false) {
  241. $k1k2 = time2k1k2($time);
  242. $k1 = $k1k2[0];
  243. if ($second) {
  244. $k1 = $k1k2[1];
  245. }
  246. return jdaytosecs( truephase($k1, $phase) );
  247. }
  248. // phaselist() - Find time of phases of the moon between two dates.
  249. // Times (in & out) are seconds_since_1970
  250. function phaselist ( $sdate, $edate ) {
  251. if ( empty($sdate) || empty($edate) ) {
  252. return array();
  253. }
  254. $sdate = jtime( $sdate );
  255. $edate = jtime( $edate );
  256. $phases = array();
  257. $d = $k = $yy = $mm = 0;
  258. jyear( $sdate, $yy, $mm, $d );
  259. $k = floor(($yy + (($mm - 1) * (1.0 / 12.0)) - 1900) * 12.3685) - 2;
  260. while (1) {
  261. ++$k;
  262. foreach ( array(0.0, 0.25, 0.5, 0.75) as $phase ) {
  263. $d = truephase( $k, $phase );
  264. if ( $d >= $edate ) {
  265. return $phases;
  266. }
  267. if ( $d >= $sdate ) {
  268. if ( empty($phases) ) {
  269. array_push( $phases, floor(4 * $phase) );
  270. }
  271. array_push( $phases, jdaytosecs($d) );
  272. }
  273. }
  274. } // End while(1)
  275. }
  276. // kepler() - solve the equation of Kepler
  277. function kepler ( $m, $ecc ) {
  278. $EPSILON = 1e-6;
  279. $m = torad( $m );
  280. $e = $m;
  281. do {
  282. $delta = $e - $ecc * sin( $e ) - $m;
  283. $e -= $delta / ( 1 - $ecc * cos($e) );
  284. } while ( abs($delta) > $EPSILON );
  285. return ( $e );
  286. }
  287. // phase() - calculate phase of moon as a fraction:
  288. //
  289. // The argument is the time for which the phase is requested,
  290. // expressed as a Julian date and fraction. Returns the terminator
  291. // phase angle as a percentage of a full circle (i.e., 0 to 1),
  292. // and stores into pointer arguments the illuminated fraction of
  293. // the Moon's disc, the Moon's age in days and fraction, the
  294. // distance of the Moon from the centre of the Earth, and the
  295. // angular diameter subtended by the Moon as seen by an observer
  296. // at the centre of the Earth.
  297. function phase ( $time=0 ) {
  298. if ( empty($time) || $time == 0 ) {
  299. $time = time();
  300. }
  301. $pdate = jtime( $time );
  302. $pphase = 0; // illuminated fraction
  303. $mage = 0; // age of moon in days
  304. $dist = 0; // distance in kilometres
  305. $angdia = 0; // angular diameter in degrees
  306. $sudist = 0; // distance to Sun
  307. $suangdia = 0; // sun's angular diameter
  308. // my ($Day, $N, $M, $Ec, $Lambdasun, $ml, $MM, $MN, $Ev, $Ae, $A3, $MmP,
  309. // $mEc, $A4, $lP, $V, $lPP, $NP, $y, $x, $Lambdamoon, $BetaM,
  310. // $MoonAge, $MoonPhase,
  311. // $MoonDist, $MoonDFrac, $MoonAng, $MoonPar,
  312. // $F, $SunDist, $SunAng,
  313. // $mpfrac);
  314. // Calculation of the Sun's position.
  315. $Day = $pdate - EPOCH; // date within epoch
  316. $N = fixangle( (360 / 365.2422) * $Day ); // mean anomaly of the Sun
  317. $M = fixangle( $N + ELONGE - ELONGP ); // convert from perigee co-ordinates
  318. // to epoch 1980.0
  319. $Ec = kepler( $M, ECCENT ); // solve equation of Kepler
  320. $Ec = sqrt( (1 + ECCENT) / (1 - ECCENT) ) * tan( $Ec / 2 );
  321. $Ec = 2 * todeg( atan($Ec) ); // true anomaly
  322. $Lambdasun = fixangle( $Ec + ELONGP ); // Sun's geocentric ecliptic longitude
  323. # Orbital distance factor.
  324. $F = ( (1 + ECCENT * cos(torad($Ec))) / (1 - ECCENT * ECCENT) );
  325. $SunDist = SUNSMAX / $F; // distance to Sun in km
  326. $SunAng = $F * SUNANGSIZ; // Sun's angular size in degrees
  327. // Calculation of the Moon's position.
  328. // Moon's mean longitude.
  329. $ml = fixangle( 13.1763966 * $Day + MMLONG );
  330. // Moon's mean anomaly.
  331. $MM = fixangle( $ml - 0.1114041 * $Day - MMLONGP );
  332. // Moon's ascending node mean longitude.
  333. $MN = fixangle( MLNODE - 0.0529539 * $Day );
  334. // Evection.
  335. $Ev = 1.2739 * sin( torad(2 * ($ml - $Lambdasun) - $MM) );
  336. // Annual equation.
  337. $Ae = 0.1858 * sin( torad($M) );
  338. // Correction term.
  339. $A3 = 0.37 * sin( torad($M) );
  340. // Corrected anomaly.
  341. $MmP = $MM + $Ev - $Ae - $A3;
  342. // Correction for the equation of the centre.
  343. $mEc = 6.2886 * sin( torad($MmP) );
  344. // Another correction term.
  345. $A4 = 0.214 * sin( torad(2 * $MmP) );
  346. // Corrected longitude.
  347. $lP = $ml + $Ev + $mEc - $Ae + $A4;
  348. // Variation.
  349. $V = 0.6583 * sin( torad(2 * ($lP - $Lambdasun)) );
  350. // True longitude.
  351. $lPP = $lP + $V;
  352. // Corrected longitude of the node.
  353. $NP = $MN - 0.16 * sin( torad($M) );
  354. // Y inclination coordinate.
  355. $y = sin( torad($lPP - $NP) ) * cos( torad(MINC) );
  356. // X inclination coordinate.
  357. $x = cos(torad($lPP - $NP));
  358. // Ecliptic longitude.
  359. $Lambdamoon = todeg( atan2($y, $x) );
  360. $Lambdamoon += $NP;
  361. // Ecliptic latitude.
  362. $BetaM = todeg( asin(sin(torad($lPP - $NP)) * sin(torad(MINC))) );
  363. // Calculation of the phase of the Moon.
  364. // Age of the Moon in degrees.
  365. $MoonAge = $lPP - $Lambdasun;
  366. // Phase of the Moon.
  367. $MoonPhase = (1 - cos(torad($MoonAge))) / 2;
  368. // Calculate distance of moon from the centre of the Earth.
  369. $MoonDist = ( MSMAX * (1 - MECC * MECC)) / (1 + MECC * cos(torad($MmP + $mEc)) );
  370. // Calculate Moon's angular diameter.
  371. $MoonDFrac = $MoonDist / MSMAX;
  372. $MoonAng = MANGSIZ / $MoonDFrac;
  373. // Calculate Moon's parallax.
  374. $MoonPar = MPARALLAX / $MoonDFrac;
  375. $pphase = $MoonPhase;
  376. $mage = SYNMONTH * ( fixangle($MoonAge) / 360.0 );
  377. $dist = $MoonDist;
  378. $angdia = $MoonAng;
  379. $sudist = $SunDist;
  380. $suangdia = $SunAng;
  381. $mpfrac = fixangle($MoonAge) / 360.0;
  382. return array ( $mpfrac, $pphase, $mage, $dist, $angdia, $sudist, $suangdia );
  383. }