PHPで月齢を計算

(1/1)
月齢 (げつれい) とは、直前の朔(新月)の瞬間を "0" として、そこからの経過日数を表す数字だ。
月齢は、太陽と月の位置関係から計算できる。今回は、PHP で月の視黄経を算出するプログラムを作り、指定した月から 3 ヶ月分の月齢一覧を求めることを目標にする。

(2019 年 2 月 9 日)pahooCalendar::makeLunarCalendar の不具合を修正、getSolarTerm72 の漢字表記を変更した。

目次

サンプル・プログラムの実行例

PHPで月齢を計算

サンプル・プログラム

圧縮ファイルの内容
getMoonAge.phpサンプル・プログラム本体。
pahooCalendar.php暦計算クラス pahooCalendar。
暦計算クラスの使い方は「PHPで日出没・月出没・月齢・潮を計算」を参照。include_path が通ったディレクトリに配置すること。

準備:外部クラスなど

0030: //指定できる西暦年の範囲
0031: define('MIN_YEAR', 1948);
0032: define('MAX_YEAR', 2099);
0033: 
0034: //計算期間(月)
0035: define('CALC_TERM', 3);
0036: 
0037: //世界時からの時差
0038: define('UTCDIFF', 9);
0039: 
0040: //表示幅(単位:ピクセル)
0041: define('WIDTH', 500);
0042: 
0043: //暦計算クラス
0044: require_once('pahooCalendar.php');

あらかじめ、計算期間(月)と世界時からの時差を定数として定義しておく。

月齢、月の視半径、各種カレンダー計算は、ユーザークラス "pahooCalendar" に用意したメソッド群を利用する。
そこで、クラスファイル "pahooCalendar.php" を  require_once  し、オブジェクトを生成する。

解説:月の視黄経の算出

月の位置と月齢

0724: /**
0725:  * 月の黄経計算(視黄経)
0726:  * @param double $jy 2000.0からの経過年数
0727:  * @return double 月の黄経(視黄経)
0728: */
0729: function __longitude_moon($jy) {
0730:     $am  = 0.0006 * sin(deg2rad($this->__angle( 54.0 + 19.3  * $jy)));
0731:     $am += 0.0006 * sin(deg2rad($this->__angle( 71.0 +  0.2  * $jy)));
0732:     $am += 0.0020 * sin(deg2rad($this->__angle( 55.0 + 19.34 * $jy)));
0733:     $am += 0.0040 * sin(deg2rad($this->__angle(119.5 +  1.33 * $jy)));
0734:     $rm_moon  = 0.0003 * sin(deg2rad($this->__angle(280.0   + 23221.3    * $jy)));
0735:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle(161.0   +    40.7    * $jy)));
0736:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle(311.0   +  5492.0    * $jy)));
0737:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle(147.0   + 18089.3    * $jy)));
0738:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle( 66.0   +  3494.7    * $jy)));
0739:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle( 83.0   +  3814.0    * $jy)));
0740:     $rm_moon += 0.0004 * sin(deg2rad($this->__angle( 20.0   +   720.0    * $jy)));
0741:     $rm_moon += 0.0004 * sin(deg2rad($this->__angle( 71.0   +  9584.7    * $jy)));
0742:     $rm_moon += 0.0004 * sin(deg2rad($this->__angle(278.0   +   120.1    * $jy)));
0743:     $rm_moon += 0.0004 * sin(deg2rad($this->__angle(313.0   +   398.7    * $jy)));
0744:     $rm_moon += 0.0005 * sin(deg2rad($this->__angle(332.0   +  5091.3    * $jy)));
0745:     $rm_moon += 0.0005 * sin(deg2rad($this->__angle(114.0   + 17450.7    * $jy)));
0746:     $rm_moon += 0.0005 * sin(deg2rad($this->__angle(181.0   + 19088.0    * $jy)));
0747:     $rm_moon += 0.0005 * sin(deg2rad($this->__angle(247.0   + 22582.7    * $jy)));
0748:     $rm_moon += 0.0006 * sin(deg2rad($this->__angle(128.0   +  1118.7    * $jy)));
0749:     $rm_moon += 0.0007 * sin(deg2rad($this->__angle(216.0   +   278.6    * $jy)));
0750:     $rm_moon += 0.0007 * sin(deg2rad($this->__angle(275.0   +  4853.3    * $jy)));
0751:     $rm_moon += 0.0007 * sin(deg2rad($this->__angle(140.0   +  4052.0    * $jy)));
0752:     $rm_moon += 0.0008 * sin(deg2rad($this->__angle(204.0   +  7906.7    * $jy)));
0753:     $rm_moon += 0.0008 * sin(deg2rad($this->__angle(188.0   + 14037.3    * $jy)));
0754:     $rm_moon += 0.0009 * sin(deg2rad($this->__angle(218.0   +  8586.0    * $jy)));
0755:     $rm_moon += 0.0011 * sin(deg2rad($this->__angle(276.5   + 19208.02   * $jy)));
0756:     $rm_moon += 0.0012 * sin(deg2rad($this->__angle(339.0   + 12678.71   * $jy)));
0757:     $rm_moon += 0.0016 * sin(deg2rad($this->__angle(242.2   + 18569.38   * $jy)));
0758:     $rm_moon += 0.0018 * sin(deg2rad($this->__angle(  4.1   +  4013.29   * $jy)));
0759:     $rm_moon += 0.0020 * sin(deg2rad($this->__angle( 55.0   +    19.34   * $jy)));
0760:     $rm_moon += 0.0021 * sin(deg2rad($this->__angle(105.6   +  3413.37   * $jy)));
0761:     $rm_moon += 0.0021 * sin(deg2rad($this->__angle(175.1   +   719.98   * $jy)));
0762:     $rm_moon += 0.0021 * sin(deg2rad($this->__angle( 87.5   +  9903.97   * $jy)));
0763:     $rm_moon += 0.0022 * sin(deg2rad($this->__angle(240.6   +  8185.36   * $jy)));
0764:     $rm_moon += 0.0024 * sin(deg2rad($this->__angle(252.8   +  9224.66   * $jy)));
0765:     $rm_moon += 0.0024 * sin(deg2rad($this->__angle(211.9   +   988.63   * $jy)));
0766:     $rm_moon += 0.0026 * sin(deg2rad($this->__angle(107.2   + 13797.39   * $jy)));
0767:     $rm_moon += 0.0027 * sin(deg2rad($this->__angle(272.5   +  9183.99   * $jy)));
0768:     $rm_moon += 0.0037 * sin(deg2rad($this->__angle(349.1   +  5410.62   * $jy)));
0769:     $rm_moon += 0.0039 * sin(deg2rad($this->__angle(111.3   + 17810.68   * $jy)));
0770:     $rm_moon += 0.0040 * sin(deg2rad($this->__angle(119.5   +     1.33   * $jy)));
0771:     $rm_moon += 0.0040 * sin(deg2rad($this->__angle(145.6   + 18449.32   * $jy)));
0772:     $rm_moon += 0.0040 * sin(deg2rad($this->__angle( 13.2   + 13317.34   * $jy)));
0773:     $rm_moon += 0.0048 * sin(deg2rad($this->__angle(235.0   +    19.34   * $jy)));
0774:     $rm_moon += 0.0050 * sin(deg2rad($this->__angle(295.4   +  4812.66   * $jy)));
0775:     $rm_moon += 0.0052 * sin(deg2rad($this->__angle(197.2   +   319.32   * $jy)));
0776:     $rm_moon += 0.0068 * sin(deg2rad($this->__angle( 53.2   +  9265.33   * $jy)));
0777:     $rm_moon += 0.0079 * sin(deg2rad($this->__angle(278.2   +  4493.34   * $jy)));
0778:     $rm_moon += 0.0085 * sin(deg2rad($this->__angle(201.5   +  8266.71   * $jy)));
0779:     $rm_moon += 0.0100 * sin(deg2rad($this->__angle( 44.89  + 14315.966  * $jy)));
0780:     $rm_moon += 0.0107 * sin(deg2rad($this->__angle(336.44  + 13038.696  * $jy)));
0781:     $rm_moon += 0.0110 * sin(deg2rad($this->__angle(231.59  +  4892.052  * $jy)));
0782:     $rm_moon += 0.0125 * sin(deg2rad($this->__angle(141.51  + 14436.029  * $jy)));
0783:     $rm_moon += 0.0153 * sin(deg2rad($this->__angle(130.84  +   758.698  * $jy)));
0784:     $rm_moon += 0.0305 * sin(deg2rad($this->__angle(312.49  +  5131.979  * $jy)));
0785:     $rm_moon += 0.0348 * sin(deg2rad($this->__angle(117.84  +  4452.671  * $jy)));
0786:     $rm_moon += 0.0410 * sin(deg2rad($this->__angle(137.43  +  4411.998  * $jy)));
0787:     $rm_moon += 0.0459 * sin(deg2rad($this->__angle(238.18  +  8545.352  * $jy)));
0788:     $rm_moon += 0.0533 * sin(deg2rad($this->__angle( 10.66  + 13677.331  * $jy)));
0789:     $rm_moon += 0.0572 * sin(deg2rad($this->__angle(103.21  +  3773.363  * $jy)));
0790:     $rm_moon += 0.0588 * sin(deg2rad($this->__angle(214.22  +   638.635  * $jy)));
0791:     $rm_moon += 0.1143 * sin(deg2rad($this->__angle(  6.546 +  9664.0404 * $jy)));
0792:     $rm_moon += 0.1856 * sin(deg2rad($this->__angle(177.525 +   359.9905 * $jy)));
0793:     $rm_moon += 0.2136 * sin(deg2rad($this->__angle(269.926 +  9543.9773 * $jy)));
0794:     $rm_moon += 0.6583 * sin(deg2rad($this->__angle(235.700 +  8905.3422 * $jy)));
0795:     $rm_moon += 1.2740 * sin(deg2rad($this->__angle(100.738 +  4133.3536 * $jy)));
0796:     $rm_moon += 6.2887 * sin(deg2rad($this->__angle(134.961 +  4771.9886 * $jy + $am)));
0797: 
0798:     return $rm_moon + $this->__angle(218.3161 + 4812.67881 * $jy);
0799: }

0801: /**
0802:  * 月の黄経計算(視黄経)
0803:  * @param int $year, $month, $day  グレゴリオ暦による年月日
0804:  * @param double $hour, $min, $sec 時分秒(世界時)
0805:  * @return double 月の黄経(視黄経)
0806: */
0807: function longitude_moon($year$month$day$hour$min$sec) {
0808:     $jy = $this->Gregorian2JY($year$month$day$hour$min$sec);
0809: 
0810:     return $this->__longitude_moon($jy);
0811: }

計算式は『日の出・日の入りの計算』(長沢工=著)による。

解説:月齢の算出

1042: /**
1043:  * 月齢を求める(視黄経)
1044:  * @param int $year, $month, $day  グレゴリオ暦による年月日
1045:  * @param double $hour, $min, $sec 時分秒(世界時)
1046:  * @return double 月齢(視黄経)
1047: */
1048: function moon_age($year$month$day$hour$min$sec) {
1049:     $jd0 = $this->Gregorian2JD($year$month$day$hour$min$sec) + ($this->TDIFF / 24);
1050:     $tm1 = floor($jd0);
1051:     $tm2 = $jd0 - $tm1;
1052: 
1053:     //繰り返し計算によって朔の時刻を計算
1054:     //誤差が±1.0 sec以内になったら打ち切る
1055:     $lc = 1;
1056:     $delta_t1 = 0;
1057:     $delta_t2 = 1;
1058:     while (($delta_t1 + abs($delta_t2)) > (1.0 / 86400.0)) {
1059:         $jd = $tm1 + $tm2;
1060:         list($year$month$day$hour$min$sec) = $this->JD2Gregorian($jd);
1061:         $longitude_sun  = $this->longitude_sun($year$month$day$hour$min$sec);
1062:         $longitude_moon = $this->longitude_moon($year$month$day$hour$min$sec);
1063: 
1064:         //Δλ=λmoon-λsun
1065:         $delta_rm = $longitude_moon - $longitude_sun;
1066: 
1067:         //ループ1回目 で $delta_rm < 0.0 の場合には引き込み範囲に入るよう補正
1068:         if ($lc == 1 && $delta_rm < 0) {
1069:             $delta_rm = $this->__angle($delta_rm);
1070:         //春分の近くで朔がある場合 ( 0 ≦λsun≦ 20 ) で、月の黄経λmoon≧300 の
1071:         //場合には、Δλ= 360.0 - Δλ と計算して補正
1072:         } else if ($longitude_sun >= 0 && $longitude_sun <= 20 && $longitude_moon >= 300) {
1073:             $delta_rm = $this->__angle($delta_rm);
1074:             $delta_rm = 360 - $delta_rm;
1075:         //Δλの引き込み範囲 ( ±40°) を逸脱した場合には補正
1076:         } else if (abs($delta_rm) > 40.0) {
1077:             $delta_rm = $this->__angle($delta_rm);
1078:         }
1079: 
1080:         //時刻引数の補正値 Δt
1081:         $delta_t1  = floor($delta_rm * 29.530589 / 360.0);
1082:         $delta_t2  = $delta_rm * 29.530589 / 360.0;
1083:         $delta_t2 -= $delta_t1;
1084: 
1085:         //時刻引数の補正
1086:         $tm1 = $tm1 - $delta_t1;
1087:         $tm2 = $tm2 - $delta_t2;
1088:         if ($tm2 < 0) {
1089:             $tm2++;
1090:             $tm1--;
1091:         }
1092: 
1093:         //ループ回数が15回になったら、初期値 tm を tm-26
1094:         if ($lc == 15 && abs($delta_t1 + $delta_t2) > (1.0 / 86400.0)) {
1095:             $tm1 = floor($jd0 - 26);
1096:             $tm2 = 0;
1097:             //初期値を補正したにも関わらず振動を続ける場合は、
1098:             //初期値を答えとして返して強制的にループを抜け出して異常終了
1099:         } else if ($lc > 30 && abs($delta_t1 + $delta_t2) > (1.0 / 86400.0)) {
1100:             $tm1 = $jd0;
1101:             $tm2 = 0;
1102:             break;
1103:         }
1104:         $lc++;
1105:     }
1106: 
1107:     //時刻引数を合成
1108:     $ma = $jd0 - ($tm2 + $tm1);
1109:     if ($ma > 30)   $ma -= 30;
1110:     return $ma;
1111: }

太陽と月の黄経が一致した瞬間が「(新月)」、月が太陽より 90 度東にきた瞬間を「上弦」、月と太陽が 180 度離れた瞬間を「(満月)」、月が太陽より 90 度西にきた瞬間を「下弦」と定義されている。
ここから、月齢を求めるためには、「PHP で二十四節気一覧を作成」で紹介した太陽の視黄経と、今回作成した 月の視黄経の差分を計算することにする。

Wikipedia には月齢を求める簡易式が掲載されているが、それより今回のプログラムの方が正確である。

活用例

月齢=今日の月・現在の月=」(みんなの知識 ちょっと便利帳)では、このサンプル・プログラムを活用し、月の写真を使って月齢を表示するようにしている。ありがとうございます。

参考書籍

表紙 日の出・日の入りの計算
著者 長沢工
出版社 地人書館
サイズ 単行本
発売日 1999年12月
価格 1,650円(税込)
rakuten
ISBN 9784805206348
 

参考サイト

(この項おわり)
header