<?php
##
## this file name is 'class.solar.php'
##
## solar object -- get sun position or 24 solar terms
##
## [author]
##  - Chilbong Kim, <san2(at)linuxchannel.net>
##  - http://linuxchannel.net/
##
## [changes]
##  - 2003.09.08 : bug fixed
##  - 2003.09.06 : new build
##
## [근사식에 대한 신뢰]
##  - 표준편차 : 1289.7736 = 21.5 minutes (standard deviation)
##  - 평균오차 : 817.57409541246 = 13.6 minutes
##  - 최대오차 : +4102.7340(68.4 minutes), -4347.2395(72.5 minutes)
##
## [근사식으로 계산한 24절기 실제 오차] 1902 ~ 2037 년
##  - 표준편차 : 1122.1921 = 18.7 분
##  - 평균오차 : +686.08382175161 = +11.4 분
##  - 최대오차 : +4297.252300024(71.6 분), -4278.048699975(71.3 분)
##  - 최소오차 : +0.16999998688698(0초)
##
## [근사식 + 년도 보정으로 계산한 24절기 실제 오차] 1902 ~ 2037 년
##  - 표준편차 : 450.8534 = 7.5 분
##  - 평균오차 : +305.38638890903 = 5.0 분
##  - 최대오차 : +3028.2343000174 = 50.5 분, -1982.9391000271 = 33.1 분
##  - 최소오차 : +0.0085000991821289 = 0 초
##
## [valid date]
##  - 1902.01.01 00:00:00 <= utime <= 2037.12.31 23:59:59
##
## [download & online source view]
##  - http://ftp.linuxchannel.net/devel/php_solar/
##
## [demo]
##  - http://linuxchannel.net/gaggle/solar.php
##
## [references]
##  - http://cgi.chollian.net/~kohyc/
##  - http://user.chollian.net/~kimdbin/
##  - http://user.chollian.net/~kimdbin/re/calendar.html
##  - http://user.chollian.net/~kimdbin/re/suncoord.html
##  - http://user.chollian.net/~kimdbin/qna/al138.html
##  - http://ruby.kisti.re.kr/~manse/contents-3.html
##  - http://ruby.kisti.re.kr/~anastro/sub_index.htm
##  - http://www-ph.postech.ac.kr/~obs/lecture/lec1/elementary/nakedeyb.htm
##  - http://ruby.kisti.re.kr/~anastro/calendar/etime/ETime.html
##  - http://www.sundu.co.kr/5-information/5-3/5f3-3-5-04earth-1.htm
##  - http://www-ph.postech.ac.kr/~obs/lecture/lec1/elementary/nakedeya.htm
##  - http://upgradename.com/calm.php
##  - http://aa.usno.navy.mil/faq/docs/SunApprox.html
##  - http://aa.usno.navy.mil/data/docs/JulianDate.html
##
## [usage]
##
## [example]
##  $sun = array();
##  $terms = solar::terms(date('Y'),1,12,&$sun);
##  print_r($terms);
##  print_r($sun);
##  print_r(solar::sun(time()));
##

class solar
{
  ## check solar terms in today or tomorrow
  ##
  function &solar($utime=0, $GMT=FALSE)
  {
 return solar::today($utime,$GMT);
  }

  function &today($utime=0, $GMT=FALSE)
  {
 if(func_num_args() < 1) $utime = time();
 if($GMT) $utime -= 32400;

 list($year,$moon,$moonday) = explode(' ',date('Y n nd',$utime));

 $terms = solar::terms($year,$moon,0);

 if($term = $terms[$moonday])
 {
  $str = '오늘은 <B>'.$term.'</B>입니다.';
 }
 else if($term = $terms[date('nd',$utime+86400)])
 {
  $str = '내일은 <B>'.$term.'</B>입니다.';
 }

 return $str;
  }

  ## get sun position at unix timestamp
  ##
  ## [limit]
  ##  - mktime(0,0,0,1,1,1902) < $utime < mktime(23,59,59,12,31,2037)
  ##
  ## [study]
  ##  - w = 23.436
  ##  - tan RA = (sin L * cos w - tan e * sin w ) / cos L
  ##  - sin d = (sin e * cos w) + (cos e * sin w * sin L)
  ##
  ## [example]
  ##  - print_r(solar::sun(mktime(  10,0,0,3,21,2003)  ));
  ##  - print_r(solar::sun(mktime(10-9,0,0,3,21,2003),1)); // same as
  ##
  function &sun($utime, $GMT=FALSE)
  {
 static $L=0, $D = 0, $JD = 0;
 static $deg2rad = array();

 if($utime<-2145947400 || $utime>2145884399)
 {
  echo "\n".'error: invalid input '.$utime.
  ', 1902.01.01 00:00:00 <= utime <= 2037.12.31 23:59:59'."\n";
  return -1;
 }

 list($L,$atime) = solar::sunl($utime,$GMT,&$D,&$JD,&$deg2rad);

 ## Sun's ecliptic, in degress
 ##
 $e = sprintf('%.10f',23.439 - (0.00000036*$D)); // degress

 $cosg = cos($deg2rad['g']); // degress
 $cos2g = cos($deg2rad['2g']); // degress

 ## R == AU (sun ~ earth)
 ## The distance of the Sun from the Earth, R, in astronomical units (AU)
 ##
 $R = sprintf('%.10f',1.00014 - (0.01671*$cosg) - (0.00014*$cos2g));

 ## convert
 ##
 $deg2rad['e'] = deg2rad($e); // radian
 $deg2rad['L'] = deg2rad($L); // radian

 $cose = cos($deg2rad['e']); // degress
 $sinL = sin($deg2rad['L']); // degress
 $cosL = cos($deg2rad['L']); // degress
 $sine = sin($deg2rad['e']); // degress

 ## the Sun's right ascension, RA, and declination, d
  ##
 $tanRA = sprintf('%.10f',$cose * $sinL / $cosL); // degress
 $sind = sprintf('%.10f',$sine * $sinL); // degress

 //$RA = sprintf('%.10f',rad2deg(atan($tanRA))+180);
 $RA = sprintf('%.10f',rad2deg(atan($tanRA)));
 $RA = solar::deg2valid($RA);
 $d = sprintf('%.10f',rad2deg(asin($sind))); // Sun's declination, degress

 $solartime = solar::deg2solartime($L);
 $daytime = solar::deg2daytime($RA);

 //if(!($L1=round($L) % 15))
 //{
 // $idx = $L1 / 15;
 // list($hterms) = solar::gterms();
 //}

 ## all base degress or decimal
 ##
  return array
 (
 'JD' => $JD, /*** Julian Date ***/
 'L'  => $L, /*** Sun's geocentric apparent ecliptic longitude ***/
 'e'  => $e, /*** Sun's ecliptic ***/
 'R'  => $R, /*** Sun from the Earth, astronomical units (AU) ***/
 'RA' => $RA, /*** Sun's right ascension ***/
 'd'  => $d, /*** Sun's declination ***/
 'stime'  => $solartime,  /*** solar time ***/
 'dtime'  => $daytime,  /*** day time ***/
 'atime'  => $atime,  /*** append time for integer degress **/
 'utime'  => $utime,  /*** unix timestamp ***/
 'date'   => date('D, d M Y H:i:s T',$utime), /*** KST date ***/
 'gmdate' => gmdate('D, d M Y H:i:s T',$utime), /*** GMT date ***/
 '_L'  => solar::deg2angle($L),
 '_e'  => solar::deg2angle($e,1),
 '_RA' => solar::deg2angle($RA),
 '_d'  => solar::deg2angle($d,1),
 '_stime' => solar::time2htime($solartime),
 '_dtime' => solar::time2htime($daytime),
 '_atime' => solar::time2htime($atime,TRUE),
 );
  }

  function &sunl($utime, $GMT=FALSE, $D=0, $JD=0, $deg2rad=array())
  {
 if($GMT) $utime += 32400; // GMT to KST

 ## D -- get the number of days from base JD
 ## D = JD(Julian Date) - 2451545.0, base JD(J2000.0)
 ##
 ## base position (J2000.0), 2000-01-01 12:00:00, UT
 ## as   mktime(12,0,0-64,1,1,2000) == 946695536 unix timestamp at KST
 ## as gmmktime(12,0,0-64,1,1,2000) == 946727936 unix timestamp at GMT
 ##
 $D = $utime - 946727936; // number of time
 $D = sprintf('%.10f',$D/86400); // float, number of days
 $JD = sprintf('%.10f',$D+2451545.0); // float, Julian Date

 $g = sprintf('%.10f',357.529 + (0.98560028 * $D));
 $q = sprintf('%.10f',280.459 + (0.98564736 * $D));

 ## fixed
 ##
 $g = solar::deg2valid($g); // to valid degress
 $q = solar::deg2valid($q); // to valid degress

 ## convert
 ##
 $deg2rad = array();
 $deg2rad['g'] = deg2rad($g); // radian
 $deg2rad['2g'] = deg2rad($g*2); // radian

 $sing = sin($deg2rad['g']); // degress
 $sin2g = sin($deg2rad['2g']); // degress

 ## L is an approximation to the Sun's geocentric apparent ecliptic longitude
 ##
 $L = sprintf('%.10f',$q + (1.915 * $sing) + (0.020*$sin2g));
 $L = solar::deg2valid($L); // degress
 $atime = solar::deg2solartime(round($L)-$L); // float

 return array($L,$atime); // array, float degress, float seconds
  }

  /***
  function &sinl($f, $v)
  {
 return sin(deg2rad($f+$v));
  }

  ## http://linux-sarang.net/board/?p=read&table=qa&no=198189
  ## -2 < sin x < + 2
  ## sin (77 + L) ==> (77 + L - 1.915 sin (77 + L))
  ##
  function &l2d($L)
  {
 $L = (int)$L; // 0 <= $L <= 345
 //$sinl  = sin(deg2rad(77+$L));
 $sinl = solar::sinl($L,77);
 //$sinl = solar::sinl($L-(1.915)*$sinl,77);

 //$sin2l = sin(deg2rad(154+($L*2)));
 $sin2l = solar::sinl($L*2,154);
 $sin2l = solar::sinl($L-(0.020*$L*2*$sin2l),154);
 $sin2l = solar::sinl($L-(0.020*$L*2*$sin2l),154);

 $D = sprintf('%.10f',
  ($L - 280.459 - (1.915 * $sinl) - (0.020 * $sin2l)) / 0.98564736);

 return $D; // float
  }

  function &l2jd($L)
  {
 $D  = solar::l2d($L);
 $JD = sprintf('%.10f',$D+2451545.0);
 //$JD = $JD + ($i * 360 / 0.98564736)

 return $JD; // float
  }

  function &l2utime($year, $L, $GMT=FLASE)
  {
 $i = (int)$year - 2000 + 1;
 $D = solar::l2d($L);

 $utime = ($D * 86400) + 946727936 + (31556925.216 * $i);
 $utime = round($utime);

 return $utime - ($GMT ? 32400 : 0); // integer
  }
  ***/

  ## 1 hour == 15 degress
  ## 1 degress == 4 minute == 240 seconds
  ##
  function &deg2daytime($deg)
  {
 return sprintf('%.4f',$deg*240); // seconds
  }

  ## 1 solar year == 365.242190 days == 31556925.216 seconds
  ## 1 degress == 31556925.216 seconds / 360 degress == 87658.1256 seconds
  ##
  function &deg2solartime($deg)
  {
 return sprintf('%.4f',$deg*87658.1256); // seconds
  }

  function &deg2angle($deg, $singed=FALSE)
  {
 if($singed) $singed = '+';
 if($deg <0) { $singed = '-'; $deg = abs($deg); }

 $time = sprintf('%.4f',$deg*3600);
 $degr = (int)$deg.chr(161).chr(198); //sprintf('%d',$deg);
 $time = sprintf('%.4f',$time-($degr*3600)); // fmod
 $mins = sprintf('%02d',$time/60).chr(161).chr(199);
 $secs = sprintf('%.4f',$time-($mins*60)).chr(161).chr(200); // fmod

 return $singed.$degr.$mins.$secs;
  }

  function &deg2valid($deg)
  {
 if($deg <= 360 && $deg >=0) return $deg;

 while($deg > 360) $deg = sprintf('%.10f',$deg - 360);
 while($deg <   0) $deg = sprintf('%.10f',$deg + 360);

 return (float)$deg; // float degress
  }

  function &moon2valid($moon)
  {
 //$moon = max($moon,1);
 //$moon = min($moon,12);

 if($moon < 1) $moon = 1;
 else if($moon > 12) $moon = 12;

 return (int)$moon;
  }

  function &time2htime($time, $singed=FALSE)
  {
 if($singed) $singed = '+';
 if($time<0) { $singed = '-'; $time = abs($time); }

 $days = (int)($time/86400); //sprintf('%03d',$time/86400);
 $time = sprintf('%.4f',$time-($days*86400)); // fmod
 $hour = sprintf('%02d',$time/3600);
 $time = sprintf('%.4f',$time-($hour*3600)); // fmod
 $mins = sprintf('%02d',$time/60);
 $secs = sprintf('%.4f',$time-($mins*60)); // fmod

 return $singed.$days.' '.$hour.' '.$mins.' '.$secs;
  }

  function &gterms()
  {
 ## mktime(7+9,36,19-64,3,20,2000), 2000-03-20 16:35:15(KST)
 ##
 if(!defined('__SOLAR_START__'))
 {
  define('__SOLAR_START__',953537715); // start base unix timestamp
  define('__SOLAR_TYEAR__',31556940); // tropicalyear to seconds
  define('__SOLAR_BYEAR__',2000); // start base year
 }

 $hterms = array
 (
  '소한','대한','입춘','우수','경칩','춘분','청명','곡우',
  '입하','소만','망종','하지','소서','대서','입추','처서',
  '백로','추분','한로','상강','입동','소설','대설','동지'
 );

 $tterms = array
 (
  -6418939, -5146737, -3871136, -2589569, -1299777,        0,
   1310827,  2633103,  3966413,  5309605,  6660762,  8017383,
   9376511, 10735018, 12089855, 13438199, 14777792, 16107008,
  17424841, 18731368, 20027093, 21313452, 22592403, 23866369
 );

 return array($hterms,$tterms);
  }

  function &tterms($year)
  {
 $addstime = array
 (
  1902=> 1545, 1903=> 1734, 1904=> 1740, 1906=>  475, 1907=>  432,
  1908=>  480, 1909=>  462, 1915=> -370, 1916=> -332, 1918=> -335,
  1919=> -263, 1925=>  340, 1927=>  344, 1928=> 2133, 1929=> 2112,
  1930=> 2100, 1931=> 1858, 1936=> -400, 1937=> -400, 1938=> -342,
  1939=> -300, 1944=>  365, 1945=>  380, 1946=>  400, 1947=>  200,
  1948=>  244, 1953=> -266, 1954=> 2600, 1955=> 3168, 1956=> 3218,
  1957=> 3366, 1958=> 3300, 1959=> 3483, 1960=> 2386, 1961=> 3015,
  1962=> 2090, 1963=> 2090, 1964=> 2264, 1965=> 2370, 1966=> 2185,
  1967=> 2144, 1968=> 1526, 1971=> -393, 1972=> -430, 1973=> -445,
  1974=> -543, 1975=> -393, 1980=>  300, 1981=>  490, 1982=>  400,
  1983=>  445, 1984=>  393, 1987=>-1530, 1988=>-1600, 1990=> -362,
  1991=> -366, 1992=> -400, 1993=> -449, 1994=> -321, 1995=> -344,
  1999=>  356, 2000=>  480, 2001=>  483, 2002=>  504, 2003=>  294,
  2007=> -206, 2008=> -314, 2009=> -466, 2010=> -416, 2011=> -457,
  2012=> -313, 2018=>  347, 2020=>  257, 2021=>  351, 2022=>  159,
  2023=>  177, 2026=> -134, 2027=> -340, 2028=> -382, 2029=> -320,
  2030=> -470, 2031=> -370, 2032=> -373, 2036=>  349, 2037=>  523,
 );

 $addttime = array
 (
  1919=> array(14=>-160), 1939=> array(10=> -508),
  1953=> array( 0=> 220), 1954=> array( 1=>-2973),
  1982=> array(18=> 241), 1988=> array(13=>-2455),
  2013=> array( 6=> 356), 2031=> array(20=>  411),
  2023=> array( 0=>  399, 11=>-571),
 );

 return array($addstime[$year],$addttime[$year]);
  }

  ## get the 24 solar terms, 1902 ~ 2037
  ##
  ## [usage]
  ##  - array solar::terms(int year [, int smoon [, int length [, array &sun]]] )
  ##
  function &terms($year=0, $smoon=1, $length=12, $sun=array())
  {
 $year  = (int)$year;
 $sun = array();
 $smoon = (int)$smoon;
 $length = (int)$length;
 $times = array();

 if(!$year) $year = date('Y');
 if($year<1902 || $year>2037)
 {
  echo "\n".'error: invalid input '.$year.
  ', 1902 <= year <= 2037'."\n";
  return -1;
 }

 list($hterms,$tterms) = solar::gterms();
 list($addstime,$addttime) = solar::tterms($year);

 ## mktime(7+9,36,19-64,3,20,2000), 2000-03-20 16:35:15(KST)
 ##
 $start = __SOLAR_START__; // start base unix timestamp
 $tyear = __SOLAR_TYEAR__; // tropicalyear to seconds
 $byear = __SOLAR_BYEAR__; // start base year

 $start += ($year - $byear) * $tyear;

 if($length < -12) $length = -12;
 else if($length > 12) $length = 12;

 $smoon = solar::moon2valid($smoon);
 $emoon = solar::moon2valid($smoon+$length);

 $sidx =  (min($smoon,$emoon) - 1) * 2;
 $eidx = ((max($smoon,$emoon) - 1) * 2) + 1;

 for($i=$sidx; $i<=$eidx; $i++)
 {
  $time = $start + $tterms[$i];
  list(,$atime) = solar::sunl($time,FALSE);
  $time += $atime + $addstime + $addttime[$i]; // re-fixed
  $terms[date('nd',$time)] = &$hterms[$i];
  $times[] = $time; // fixed utime
 }

 ## for detail information
 ##
 if(func_num_args() > 3)
 {
  $i = $sidx;
  foreach($times AS $time)
  {
   $sun[$i] = solar::sun($time,FALSE);
   $sun[$i]['_avgdate'] =
    gmdate('D, d M Y H:i:s T',$start+$tterms[$i]);
   $sun[$i]['_name'] = &$hterms[$i];
   $i++;
  }
 }

 unset($times);

 return $terms; // array
  }
} // end of class

/*** example ***
$sun = array();
$terms = solar::terms(date('Y'),1,12,&$sun);
print_r($terms);
print_r($sun);
print_r(solar::sun(time()));
echo solar::today()."\n";
echo solar::solar(mktime(0,0,0,3,20))."\n";
echo solar::solar(mktime(0,0,0,3,21))."\n";
echo solar::solar(mktime(0,0,0,3,22))."\n";
echo "\n\n";
print_r(solar::terms(2023));
***/
?>


<?php
##
## array sort test

##########################################

require_once '_lib/class.solar.ph';

echo "<P><H2>24 절기 - 24 solar terms</H2>\n";

if($_GET['view'])
{
echo "<A HREF='$_SERVER[PHP_SELF]'>소스닫기</A><HR>\n";
highlight_file(basename($_SERVER[PHP_SELF]));
return;
}

echo "<A HREF='$_SERVER[PHP_SELF]?view=1'>소스보기</A><HR>\n";

echo '<PRE>'."\n";
?>

solar object -- get sun position or 24 solar terms

[author]
- Chilbong Kim, < san2(at)linuxchannel.net >
- http://linuxchannel.net/

[근사식에 대한 신뢰] 1902 ~ 2000 년
- 표준편차 : 1289.7736 = 21.5 분
- 평균오차 : +817.57409541246 = +13.6 분
- 최대오차 : +4102.7340(68.4 분), -4347.2395(72.5 분)

[근사식으로 계산한 24절기 실제 오차] 1902 ~ 2037 년
- 표준편차 : 1122.1921 = 18.7 분
- 평균오차 : +686.08382175161 = +11.4 분
- 최대오차 : +4297.252300024(71.6 분), -4278.048699975(71.3 분)
- 최소오차 : +0.16999998688698(0초)

[근사식 + 년도 보정으로 계산한 24절기 실제 오차] 1902 ~ 2037 년
- 표준편차 : 450.8534 = 7.5 분
- 평균오차 : +305.38638890903 = 5.0 분
- 최대오차 : +3028.2343000174 = 50.5 분, -1982.9391000271 = 33.1 분
- 최소오차 : +0.0085000991821289 = 0 초

<?php
list($year,$moon) = preg_split('/\s+/',date('Y n'));
$today  = solar::today();
$tmoon  = solar::terms($year,$moon,0);
$sun    = solar::sun(time());
$suns   = array();
$tterms = solar::terms($year,1,12,&$suns);

echo '<H3>년도 : '.$year.' 년</H3><P>'."\n";
echo '오늘 : '.$today.'<P>'."\n";
echo '이번달 : '.$year.' 년 '.$moon.' 월'."\n";
print_r($tmoon);
echo "\n\n";
echo '현재시각의 태양의 위치: '."\n";
print_r($sun);
echo "\n\n";
print_r($tterms);
echo "\n\n";
print_r($suns);
echo '</PRE>'."\n";

?>

Posted by 무중 이승수 지지닷컴

댓글을 달아 주세요