PHPで月齢を計算

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

(2021年5月8日)PHP8対応,リファラ・チェック改良

目次

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

PHPで月齢を計算

サンプル・プログラム

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

準備:外部クラスなど

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

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

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

解説:月の視黄経の算出

月の位置と月齢

0763: /**
0764:  * 月の黄経計算(視黄経)
0765:  * @param double $jy 2000.0からの経過年数
0766:  * @return double月の黄経(視黄経)
0767: */
0768: function __longitude_moon($jy) {
0769:     $am  = 0.0006 * sin(deg2rad($this->__angle( 54.0 + 19.3  * $jy)));
0770:     $am += 0.0006 * sin(deg2rad($this->__angle( 71.0 +  0.2  * $jy)));
0771:     $am += 0.0020 * sin(deg2rad($this->__angle( 55.0 + 19.34 * $jy)));
0772:     $am += 0.0040 * sin(deg2rad($this->__angle(119.5 +  1.33 * $jy)));
0773:     $rm_moon  = 0.0003 * sin(deg2rad($this->__angle(280.0   + 23221.3    * $jy)));
0774:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle(161.0   +    40.7    * $jy)));
0775:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle(311.0   +  5492.0    * $jy)));
0776:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle(147.0   + 18089.3    * $jy)));
0777:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle( 66.0   +  3494.7    * $jy)));
0778:     $rm_moon += 0.0003 * sin(deg2rad($this->__angle( 83.0   +  3814.0    * $jy)));
0779:     $rm_moon += 0.0004 * sin(deg2rad($this->__angle( 20.0   +   720.0    * $jy)));
0780:     $rm_moon += 0.0004 * sin(deg2rad($this->__angle( 71.0   +  9584.7    * $jy)));
0781:     $rm_moon += 0.0004 * sin(deg2rad($this->__angle(278.0   +   120.1    * $jy)));
0782:     $rm_moon += 0.0004 * sin(deg2rad($this->__angle(313.0   +   398.7    * $jy)));
0783:     $rm_moon += 0.0005 * sin(deg2rad($this->__angle(332.0   +  5091.3    * $jy)));
0784:     $rm_moon += 0.0005 * sin(deg2rad($this->__angle(114.0   + 17450.7    * $jy)));
0785:     $rm_moon += 0.0005 * sin(deg2rad($this->__angle(181.0   + 19088.0    * $jy)));
0786:     $rm_moon += 0.0005 * sin(deg2rad($this->__angle(247.0   + 22582.7    * $jy)));
0787:     $rm_moon += 0.0006 * sin(deg2rad($this->__angle(128.0   +  1118.7    * $jy)));
0788:     $rm_moon += 0.0007 * sin(deg2rad($this->__angle(216.0   +   278.6    * $jy)));
0789:     $rm_moon += 0.0007 * sin(deg2rad($this->__angle(275.0   +  4853.3    * $jy)));
0790:     $rm_moon += 0.0007 * sin(deg2rad($this->__angle(140.0   +  4052.0    * $jy)));
0791:     $rm_moon += 0.0008 * sin(deg2rad($this->__angle(204.0   +  7906.7    * $jy)));
0792:     $rm_moon += 0.0008 * sin(deg2rad($this->__angle(188.0   + 14037.3    * $jy)));
0793:     $rm_moon += 0.0009 * sin(deg2rad($this->__angle(218.0   +  8586.0    * $jy)));
0794:     $rm_moon += 0.0011 * sin(deg2rad($this->__angle(276.5   + 19208.02   * $jy)));
0795:     $rm_moon += 0.0012 * sin(deg2rad($this->__angle(339.0   + 12678.71   * $jy)));
0796:     $rm_moon += 0.0016 * sin(deg2rad($this->__angle(242.2   + 18569.38   * $jy)));
0797:     $rm_moon += 0.0018 * sin(deg2rad($this->__angle(  4.1   +  4013.29   * $jy)));
0798:     $rm_moon += 0.0020 * sin(deg2rad($this->__angle( 55.0   +    19.34   * $jy)));
0799:     $rm_moon += 0.0021 * sin(deg2rad($this->__angle(105.6   +  3413.37   * $jy)));
0800:     $rm_moon += 0.0021 * sin(deg2rad($this->__angle(175.1   +   719.98   * $jy)));
0801:     $rm_moon += 0.0021 * sin(deg2rad($this->__angle( 87.5   +  9903.97   * $jy)));
0802:     $rm_moon += 0.0022 * sin(deg2rad($this->__angle(240.6   +  8185.36   * $jy)));
0803:     $rm_moon += 0.0024 * sin(deg2rad($this->__angle(252.8   +  9224.66   * $jy)));
0804:     $rm_moon += 0.0024 * sin(deg2rad($this->__angle(211.9   +   988.63   * $jy)));
0805:     $rm_moon += 0.0026 * sin(deg2rad($this->__angle(107.2   + 13797.39   * $jy)));
0806:     $rm_moon += 0.0027 * sin(deg2rad($this->__angle(272.5   +  9183.99   * $jy)));
0807:     $rm_moon += 0.0037 * sin(deg2rad($this->__angle(349.1   +  5410.62   * $jy)));
0808:     $rm_moon += 0.0039 * sin(deg2rad($this->__angle(111.3   + 17810.68   * $jy)));
0809:     $rm_moon += 0.0040 * sin(deg2rad($this->__angle(119.5   +     1.33   * $jy)));
0810:     $rm_moon += 0.0040 * sin(deg2rad($this->__angle(145.6   + 18449.32   * $jy)));
0811:     $rm_moon += 0.0040 * sin(deg2rad($this->__angle( 13.2   + 13317.34   * $jy)));
0812:     $rm_moon += 0.0048 * sin(deg2rad($this->__angle(235.0   +    19.34   * $jy)));
0813:     $rm_moon += 0.0050 * sin(deg2rad($this->__angle(295.4   +  4812.66   * $jy)));
0814:     $rm_moon += 0.0052 * sin(deg2rad($this->__angle(197.2   +   319.32   * $jy)));
0815:     $rm_moon += 0.0068 * sin(deg2rad($this->__angle( 53.2   +  9265.33   * $jy)));
0816:     $rm_moon += 0.0079 * sin(deg2rad($this->__angle(278.2   +  4493.34   * $jy)));
0817:     $rm_moon += 0.0085 * sin(deg2rad($this->__angle(201.5   +  8266.71   * $jy)));
0818:     $rm_moon += 0.0100 * sin(deg2rad($this->__angle( 44.89  + 14315.966  * $jy)));
0819:     $rm_moon += 0.0107 * sin(deg2rad($this->__angle(336.44  + 13038.696  * $jy)));
0820:     $rm_moon += 0.0110 * sin(deg2rad($this->__angle(231.59  +  4892.052  * $jy)));
0821:     $rm_moon += 0.0125 * sin(deg2rad($this->__angle(141.51  + 14436.029  * $jy)));
0822:     $rm_moon += 0.0153 * sin(deg2rad($this->__angle(130.84  +   758.698  * $jy)));
0823:     $rm_moon += 0.0305 * sin(deg2rad($this->__angle(312.49  +  5131.979  * $jy)));
0824:     $rm_moon += 0.0348 * sin(deg2rad($this->__angle(117.84  +  4452.671  * $jy)));
0825:     $rm_moon += 0.0410 * sin(deg2rad($this->__angle(137.43  +  4411.998  * $jy)));
0826:     $rm_moon += 0.0459 * sin(deg2rad($this->__angle(238.18  +  8545.352  * $jy)));
0827:     $rm_moon += 0.0533 * sin(deg2rad($this->__angle( 10.66  + 13677.331  * $jy)));
0828:     $rm_moon += 0.0572 * sin(deg2rad($this->__angle(103.21  +  3773.363  * $jy)));
0829:     $rm_moon += 0.0588 * sin(deg2rad($this->__angle(214.22  +   638.635  * $jy)));
0830:     $rm_moon += 0.1143 * sin(deg2rad($this->__angle(  6.546 +  9664.0404 * $jy)));
0831:     $rm_moon += 0.1856 * sin(deg2rad($this->__angle(177.525 +   359.9905 * $jy)));
0832:     $rm_moon += 0.2136 * sin(deg2rad($this->__angle(269.926 +  9543.9773 * $jy)));
0833:     $rm_moon += 0.6583 * sin(deg2rad($this->__angle(235.700 +  8905.3422 * $jy)));
0834:     $rm_moon += 1.2740 * sin(deg2rad($this->__angle(100.738 +  4133.3536 * $jy)));
0835:     $rm_moon += 6.2887 * sin(deg2rad($this->__angle(134.961 +  4771.9886 * $jy + $am)));
0836: 
0837:     return $rm_moon + $this->__angle(218.3161 + 4812.67881 * $jy);
0838: }

0840: /**
0841:  * 月の黄経計算(視黄経)
0842:  * @param int $year, $month, $day  グレゴリオ暦による年月日
0843:  * @param double $hour, $min, $sec時分秒(世界時)
0844:  * @return double月の黄経(視黄経)
0845: */
0846: function longitude_moon($year$month$day$hour$min$sec) {
0847:     $jy = $this->Gregorian2JY($year$month$day$hour$min$sec);
0848: 
0849:     return $this->__longitude_moon($jy);
0850: }

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

解説:月齢の算出

1081: /**
1082:  * 月齢を求める(視黄経)
1083:  * @param int $year, $month, $day  グレゴリオ暦による年月日
1084:  * @param double $hour, $min, $sec時分秒(世界時)
1085:  * @return double月齢(視黄経)
1086: */
1087: function moon_age($year$month$day$hour$min$sec) {
1088:     $jd0 = $this->Gregorian2JD($year$month$day$hour$min$sec) + ($this->TDIFF / 24);
1089:     $tm1 = floor($jd0);
1090:     $tm2 = $jd0 - $tm1;
1091: 
1092:     //繰り返し計算によって朔の時刻を計算
1093:     //誤差が±1.0 sec以内になったら打ち切る
1094:     $lc = 1;
1095:     $delta_t1 = 0;
1096:     $delta_t2 = 1;
1097:     while (($delta_t1 + abs($delta_t2)) > (1.0 / 86400.0)) {
1098:         $jd = $tm1 + $tm2;
1099:         list($year$month$day$hour$min$sec) = $this->JD2Gregorian($jd);
1100:         $longitude_sun  = $this->longitude_sun($year$month$day$hour$min$sec);
1101:         $longitude_moon = $this->longitude_moon($year$month$day$hour$min$sec);
1102: 
1103:         //Δλ=λmoon-λsun
1104:         $delta_rm = $longitude_moon - $longitude_sun;
1105: 
1106:         //ループ1回目 で $delta_rm < 0.0の場合には引き込み範囲に入るよう補正
1107:         if ($lc == 1 && $delta_rm < 0) {
1108:             $delta_rm = $this->__angle($delta_rm);
1109:         //春分の近くで朔がある場合 ( 0 ≦λsun≦ 20 ) で、月の黄経λmoon≧300の
1110:         //場合には、Δλ= 360.0 - Δλ と計算して補正
1111:         } else if ($longitude_sun >= 0 && $longitude_sun <= 20 && $longitude_moon >= 300) {
1112:             $delta_rm = $this->__angle($delta_rm);
1113:             $delta_rm = 360 - $delta_rm;
1114:         //Δλの引き込み範囲 ( ±40°) を逸脱した場合には補正
1115:         } else if (abs($delta_rm) > 40.0) {
1116:             $delta_rm = $this->__angle($delta_rm);
1117:         }
1118: 
1119:         //時刻引数の補正値 Δt
1120:         $delta_t1  = floor($delta_rm * 29.530589 / 360.0);
1121:         $delta_t2  = $delta_rm * 29.530589 / 360.0;
1122:         $delta_t2 -= $delta_t1;
1123: 
1124:         //時刻引数の補正
1125:         $tm1 = $tm1 - $delta_t1;
1126:         $tm2 = $tm2 - $delta_t2;
1127:         if ($tm2 < 0) {
1128:             $tm2++;
1129:             $tm1--;
1130:         }
1131: 
1132:         //ループ回数が15回になったら、初期値tmをtm-26
1133:         if ($lc == 15 && abs($delta_t1 + $delta_t2) > (1.0 / 86400.0)) {
1134:             $tm1 = floor($jd0 - 26);
1135:             $tm2 = 0;
1136:             //初期値を補正したにも関わらず振動を続ける場合は、
1137:             //初期値を答えとして返して強制的にループを抜け出して異常終了
1138:         } else if ($lc > 30 && abs($delta_t1 + $delta_t2) > (1.0 / 86400.0)) {
1139:             $tm1 = $jd0;
1140:             $tm2 = 0;
1141:             break;
1142:         }
1143:         $lc++;
1144:     }
1145: 
1146:     //時刻引数を合成
1147:     $ma = $jd0 - ($tm2 + $tm1);
1148:     if ($ma > 30)   $ma -= 30;
1149:     return $ma;
1150: }

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

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

活用例

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

参考書籍

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

参考サイト

(この項おわり)
header