PHPで弾道ミサイルの軌道を計算する

(1/1)
北朝鮮が、相次いで弾道ミサイルを発射している。弾道や着弾地点に関して様々な報道がなされているが、結果の数字ばかりで、それが正しいのか検証が難しい。
そこで今回は、マップ上で発射地点と着弾地点を指定し、弾道の最高点の高さを入力するだけで、軌道要素と弾道を計算するPHPプログラムをつくってみることにする。
ユーザーインターフェースは、「PHPで大圏航路を描く」を流用した。

なお、作者の力不足から、コリオリの力、多段ロケットによる加速、弾頭の形状・材質、地球大気による減速など、物理的な諸条件は含めていない。大まかな検証をすることが目的だと考えていただきたい。

(2025年9月19日)マーカードラッグ後に再描画するとマーカーの位置が戻ってしまう不具合を解消.jsDragMarker_Leaflet(), jsDrawLine_Leaflet 追加.
(2025年8月31日).pahooEnv導入
(2025年7月20日)GoogleMaps JavaScript APIのAdvancedMarkerに対応
(2025年6月14日)GoogleMaps JavaScript APIの変更に対応した.

目次

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

弾道ミサイルの軌道計算
弾道高を入力し、発射点と着弾点び住所を入力するか、地図上の地点をクリックする。計算ボタンをクリックすると、2地点を結ぶ射程(赤い曲線)が描かれ、ミサイルの射角、飛翔経路長、平均速度、飛翔時間などが表示される。

YOLPコンテンツジオコーダAPI(Yahoo!JAPAN)は、世界のキーワードから緯度・経度を求める機能が弱い。たとえば「サンティアゴ」でキーワード検索すると、Google Geocoding API チリの首都を第一候補として返すが、YOLPコンテンツジオコーダAPIはドミニカ共和国の県都しか返さない。
そこで、地図上のマーカーをドラッグし、計算ボタンを押すことで射程を再描画する機能を採り入れた。これは Googleマップ でも利用できる。

サンプル・プログラム

圧縮ファイルの内容
ballistic.phpサンプル・プログラム本体
.pahooEnvクラウドサービスを利用するためのアカウント情報などを記入する .env ファイル。
使い方は「各種クラウド連携サービス(WebAPI)の登録方法」を参照。include_path が通ったディレクトリに配置すること。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
pahooGeoCode.php住所・緯度・経度に関わるクラス pahooGeoCode。
使い方は「PHPで住所・ランドマークから最寄り駅を求める」などを参照。include_path が通ったディレクトリに配置すること。
ballistic.php 更新履歴
バージョン 更新日 内容
2.6.1 2025/09/19 マーカードラッグ後に再描画で位置ズレを解消
2.6.0 2025/09/19 jsDragMarker_Leaflet(), jsDrawLine_Leaflet() 追加
2.5.0 2025/08/31 .pahooEnv導入
2.4.0 2025/07/20 GoogleMapsAPIのAdvancedMarkerに対応
2.3 2021/03/28 PHP8対応,リファラ・チェック改良
pahooGeoCode.php 更新履歴
バージョン 更新日 内容
6.8.0 2025/08/10 アクセスキーなどを ".pahooEnd" に分離
6.7.1 2025/07/26 jsLine_Gmap() - bug-fix
6.7.0 2025/07/20 drawJSmap,drawGMap -- 引数 $markerLevel 追加
6.6.0 2025/07/19 drawJSmap,drawGMap,drawLeaflet -- マップ中心マーカー表示引数を追加
6.5.0 2025/06/14 GoogleMaps JavaScript APIの変更に対応
pahooInputData.php 更新履歴
バージョン 更新日 内容
2.0.1 2025/08/11 getParam() bug-fix
2.0.0 2025/08/11 pahooLoadEnv() 追加
1.9.0 2025/07/26 getParam() 引数に$trim追加
1.8.1 2025/03/15 validRegexPattern() debug
1.8.0 2024/11/12 validRegexPattern() 追加

弾道方程式

今回は、報道が流している数字が正しいのか検証することが目的であるので、まず、弾道方程式について、数式を含めた説明をしておく。各々の方程式の証明は、楕円について知っていれば、高校の数学レベルで解法可能である。ただし、動径上の速度については、物理学の知識が求められる。
末尾に、参考サイトや参考書籍を掲げた。ぜひご覧いただきたい。
弾道方程式
弾道ミサイルは人工衛星と異なり地球を周回しないが、地球の中心を焦点の一方とする楕円軌道を描く。これを弾道(ballistic)と呼ぶ。

発射点をa、着弾点をbとすると、地表面に沿った円弧acbを射程(range)と呼ぶ。acbの左側は地中に潜っていると考えてほしい。
射程を、地球中心から見た角度φを半射程角(semi-range angle)と呼ぶ。射程をS、地球の半径をR(6,378,136m)とすると、φは次の式によって求められる。
$$ \phi = \frac{S}{2R} $$
ミサイルの最終段のロケットモーターが燃え切ったときの速度比をvoとすると、次の式によって求められる。 $$ \displaystyle \begin{align} v_o &= \sqrt{\frac{-B+\sqrt{B^2-AC}}{A}} \\ A &= \sin^2\gamma \ (\cos^2\phi - \sin^2\gamma) \\ B &= \sin^2\gamma \ (1 - \cos^2\phi) \\ B &= \cos^2\phi - 1 \end{align} $$
楕円の離心率e、長径a、短径bは、次の式によって求められる。 $$ \displaystyle \begin{align} e &= \sqrt{1-{v_o}^2 (2 -{v_o}^2) \sin^2\gamma} \\ a &= \frac{R}{2 - {v_o}^2} \\ b &= a \sqrt{1 - e^2} \end{align} $$
地上から測った軌道の最高点の高さ(軌道高)hmは、次の式によって求められる。
$$ hm = a (1 - e) - R $$

軌道上の速度は変化する。地球中心に対する軌道上の角度をθ、地上の引力係数をμとすると、θにおける動径rと、動径上の速度vは、次の式によって求められる。 $$ \displaystyle \begin{align} \mu &= 3.986044\times10^{14}m^3s^{-2} \\ r &= \frac{a (1 - e^2)}{1 - e \cdot \cos \theta} \\ v &= \sqrt{\mu (\frac{2}{r} - \frac{1}{a}}) \end{align} $$

準備:PHP の https対応

クラウド連携や相手先サイトのデータを読み込むのに https通信を使うため、PHPに OpenSSLモジュールが組み込まれている必要がある。関数  phpinfo  を使って、下図のように表示されればOKだ。
OpenSSL - PHP
そうでない場合は、次の手順に従ってOpenSSLを有効化し、PHPを再起動させる必要がある。

Windowsでは、"php.ini" の下記の行を有効化する。
extension=php_openssl.dll
Linuxでは --with-openssl=/usr オプションを付けて再ビルドする。→OpenSSLインストール手順

これで準備は完了だ。

準備:pahooInputData 関数群

PHPのバージョンや入力データのバリデーションなど、汎用的に使う関数群を収めたファイル "pahooInputData.php" が同梱されているが、include_path が通ったディレクトリに配置してほしい。他のプログラムでも "pahooInputData.php" を利用するが、常に最新のファイルを1つ配置すればよい。

また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。

準備:pahooGeoCode クラス

pahooGeoCode.php

  40: class pahooGeoCode {
  41:     public $items;      // 検索結果格納用
  42:     public $error;      // エラー・フラグ
  43:     public $errmsg;     // エラー・メッセージ
  44:     public $hits;       // 検索ヒット件数
  45:     public $webapi// 直前に呼び出したWebAPI URL
  46: 
  47:     // -- 以下のデータは .env ファイルに記述可能
  48:     // Google Cloud Platform APIキー
  49:     // https://cloud.google.com/maps-platform/
  50:     // ※Google Maps APIを利用しないのなら登録不要
  51:     public $GOOGLE_API_KEY_1 = '';      // HTTPリファラ用
  52:     public $GOOGLE_API_KEY_2 = '';      // IP制限用
  53:     public $GOOGLE_MAP_ID    = '';      // GoogleMaps ID
  54: 
  55:     // Yahoo! JAPAN Webサービス アプリケーションID
  56:     // https://e.developer.yahoo.co.jp/register
  57:     // ※Yahoo! JAPAN Webサービスを利用しないのなら登録不要
  58:     public $YAHOO_APPLICATION_ID = '';
  59: 
  60:     // OSM Nominatim Search API利用時に知らせるメールアドレス
  61:     // https://wiki.openstreetmap.org/wiki/JA:Nominatim#.E6.A4.9C.E7.B4.A2
  62:     // ※OSM Nominatim Search APIを利用しないのなら登録不要
  63:     public $NOMINATIM_EMAIL = '';
  64: 
  65:     // IP2Location.io APIキー
  66:     // https://www.ip2location.io/
  67:     // ※IP2Location.ioを利用しないのなら登録不要
  68:     public $IP2LOCATION_API_KEY = '';

GoogleマップやLeafletなどによる地図描画や住所検索を行うためのクラスが pahooGeoCode である。同梱のクラス・ファイル "pahooGeoCode.php" は include_path が通ったディレクトリに配置してほしい。他のプログラムでも pahooGeoCodeクラス を利用するが、常に最新のクラス・ファイルを1つ配置すればよい。

地図や住所検索として Google を利用するのであれば Google Cloud Platform APIキーマップID が必要で、その入手方法は「Google Cloud Platform - WebAPIの登録方法」を、Yahoo!JAPAN を利用するのであれば Yahoo! JAPAN Webサービス アプリケーションIDが必要で、その入手方法は「Yahoo!JAPAN デベロッパーネットワーク - WebAPIの登録方法」を、IP2Location.ioを利用するのであれば「PHPでIPアドレスやホスト名から住所を求める」を、それぞれ参照されたい。

PHPのクラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」を参照されたい。

準備:地図サービスの選択

ballistic.php

  53: // 地図描画サービスの選択
  54: //    0:Google
  55: //    2:地理院地図・OSM
  56: define('MAPSERVICE', 2);
  57: 
  58: // 住所検索サービスの選択
  59: //    0:Google
  60: //    1:Yahoo!JAPAN
  61: //   11:HeartRails Geo API
  62: //   12:OSM Nominatim Search API
  63: define('GEOSERVICE', 1);
  64: 
  65: // 逆ジオコーディングサービスの選択
  66: //    0:Google
  67: //    1:Yahoo!JAPAN
  68: //   11:HeartRails Geo API
  69: //   21:簡易ジオコーディングサービス
  70: define('REVGEOSERVICE', 1);

表示する地図は、Googleマップ地理院地図・オープンストリートマップ(OSM)から選べる。あらかじめ、定数 MAPSERVIC に値を設定すること。
住所検索サービスは、GoogleYahoo!JAPANHeartRails Geo APIOSM Nominatim Search API から選べる。あらかじめ、定数 GEOSERVICE に値を設定すること。
逆ジオコーディングサービスは、GoogleYahoo!JAPANHeartRails Geo API簡易ジオコーディングサービスから選べる。あらかじめ、定数 REVGEOSERVICE に値を設定すること。

解説:初期値

ballistic.php

  72: // マップの表示サイズ(単位:ピクセル)
  73: define('MAP_WIDTH',  600);
  74: define('MAP_HEIGHT', 400);
  75: // マップID
  76: define('MAPID', 'map_id');
  77: 
  78: // 初期値
  79: define('DEF_LONGITUDE0', 165.0);        // 中心座標(経度)
  80: define('DEF_LATITUDE0',  30.5);     //         (緯度)
  81: define('DEF_TYPE',      'roadmap');     // マップタイプ
  82: define('DEF_ZOOM',      3);             // ズーム
  83: define('DEF_CATEGORY', 'world');        // カテゴリ
  84: 
  85: // 発射点
  86: define('DEF_FROM',      '東倉里ミサイル発射場');        // 地名
  87: define('DEF_CAT_FROM',  'world');                       // カテゴリ
  88: define('DEF_LATITUDE1',  39.660075);                    // 緯度
  89: define('DEF_LONGITUDE1', 124.705294);                   // 経度
  90: 
  91: // 着弾点
  92: define('DEF_TO',        'ホノルル');                    // 地名
  93: define('DEF_CAT_TO',    'world');                       // カテゴリ
  94: define('DEF_LATITUDE2',  21.308889);                    // 経度
  95: define('DEF_LONGITUDE2', -157.826111);                  // 経度
  96: 
  97: define('DEF_HM', 1500);             // 弾道高(km)

軌道計算プログラムの本体 "ballistic.php" の冒頭に、初期値を定義している。
初回起動時に表示する発射点は、北朝鮮の東倉里 (トンチャンリ) ミサイル発射場、着弾地点はハワイ・ホノルルとしてある。弾道高は1500kmである。

解説:マーカー設定など

ユーザー関数 jsDragMarker_YOLPmapjsDrawLine_YOLPmapjsDragMarker_GmapjsDrawLine_Gmap は、マーカー設定などを行うためのKavaScriptで、「PHPで大圏航路を描く」で作成したものと同じである。

解説:射程,弾道高から軌道要素を求める

ballistic.php

 479: /**
 480:  * 射程,射角から軌道要素を求める
 481:  * @param   double $dt   射程(メートル)
 482:  * @param   double $aof  射角(度)
 483:  * @param   array  $elements  軌道要素を格納する配列
 484:  * @return  なし
 485: */
 486: function getOrbitalElementSub($dt, $aof, &$elements) {
 487:     static $r = 6378136;        // 地球半径(メートル)
 488: 
 489:     // ラジアン変換
 490:     $aof = deg2rad(90 - $aof);
 491: 
 492:     $sra = $dt / (2 * $r);      // 半射程角
 493: 
 494:     $ax = pow(sin($aof), 2* (pow(cos($sra), 2- pow(sin($aof), 2));
 495:     $bx = pow(sin($aof), 2* (1 - pow(cos($sra), 2));
 496:     $cx = pow(cos($sra), 2- 1;
 497: 
 498:     $vo = sqrt((-$bx + sqrt(pow($bx, 2- $ax * $cx)) / $ax);   // 燃え切り速度比
 499:     $e = sqrt(1 - pow($vo, 2* (2 - pow($vo, 2)) * pow(sin($aof), 2)); // 離心率
 500:     $a = $r / (2 - pow($vo, 2));        // 長径
 501:     $b = $a * sqrt(1 - pow($e, 2));     // 短径
 502:     $hmm = $a * (1 + $e- $r;          // 弾道高
 503: 
 504:     $elements['sra'] = $sra;
 505:     $elements['vo']  = $vo;
 506:     $elements['e']   = $e;
 507:     $elements['a']   = $a;
 508:     $elements['b']   = $b;
 509:     $elements['hmm'] = $hmm;
 510: }

ballistic.php

 512: /**
 513:  * 射程,弾道高から軌道要素を求める
 514:  * @param   double $dt  射程(メートル)
 515:  * @param   double $hmm 弾道高(メートル)
 516:  * @param   array  $elements  軌道要素を格納する配列
 517:  * @return  なし
 518: */
 519: function getOrbitalElement($dt, $hmm, &$elements) {
 520:     $flag = TRUE;
 521:     $aof = 0.0;     // 射角(度)
 522: 
 523:     // 誤差0.1度で射角を求める
 524:     while ($flag && ($aof < 90.0)) {
 525:         $aof +1.0;
 526:         getOrbitalElementSub($dt, $aof, $elements);
 527:         if ($elements['hmm'] == $hmm) {
 528:             $flag = FALSE;
 529:             break;
 530:         } else if ($elements['hmm'> $hmm) {
 531:             $aof -1.0;
 532:             while ($flag && ($aof < 90.0)) {
 533:                 $aof +0.1;
 534:                 getOrbitalElementSub($dt, $aof, $elements);
 535:                 if ($elements['hmm'>$hmm) {
 536:                     $flag = FALSE;
 537:                     break;
 538:                 }
 539:             }
 540:         }
 541:     }
 542:     $elements['dt'] = $dt;
 543:     $elements['aof'] = $aof;
 544: }

北朝鮮の弾道ミサイルは、迎撃を回避するため超高高度に達するロフテッド軌道をとるという報道もある。
そこで本プログラムでは、射角の代わりに弾道高を入力して、軌道要素を求める関数 getOrbitalElement を用意した。

弾道高から射角を逆算することは難しいので、射角を0.1度ずつ増やし、ユーザー関数 getOrbitalElementSub を呼び出し、与えられた弾道高にほぼ近づいたら計算を打ち切るという方法で軌道要素を求めている。

計算回数を減らすため、初段は1度ずつ射角を増やし、近づいてきたら0.1度ずつ増やすようにしている。

解説:軌道要素から、飛翔経路長、速度を求める

ballistic.php

 447: /**
 448:  * 軌道要素から、飛翔経路長、速度を求める
 449:  * @param   array  $elements  軌道要素
 450:  * @return  なし
 451: */
 452: function getBallistic(&$elements) {
 453:     static $u = 3.98602e+14;        // 地心引力係数
 454:     $n = 100;                       // 分割数
 455: 
 456:     $dw = $elements['sra'] / $n;
 457:     $ds = 0.0;                      // 飛翔経路長
 458:     $vs = 0.0;
 459:     $vmax = 0.0;                    // 最大速度
 460:     for ($i = 0$i <100$i++) {
 461:         $r = $elements['a'* (1.0 - pow($elements['e'], 2)) / (1.0 - $elements['e'* cos($dw * $i));
 462:         $v = sqrt($u * ((2.0 / $r- (1.0 / $elements['a'])));
 463:         if ($v > $vmax$vmax = $v;
 464:         $vs +$v;
 465:         if ($i == 0)    $tmp1 = $r;
 466:         else            $tmp2 = $r;
 467:         if ($i > 1) {
 468:             $dx = $tmp2 * sin($dw);
 469:             $dy = $tmp1 - $tmp2 * cos($dw);
 470:             $ds +sqrt(pow($dx, 2+ pow($dy, 2));
 471:             $tmp1 = $tmp2;
 472:         }
 473:     }
 474:     $elements['ds'] = $ds * 2;
 475:     $elements['vm'] = $vs / ($n + 1);
 476:     $elements['vmax'] = $vmax;
 477: }

ミサイルの飛翔経路長は、楕円の弧の一部なのだが、これを計算するには無限級数を扱わなければならず、計算時間がかかる。
そこで、「弾道方程式」で紹介した動径を使って、動径角を100分割して、それを足していくことで飛翔経路長を近似している。
同時に速度を100回求め、平均速度と最高速度を計算している。

解説:軌道要素の計算

ballistic.php

 847:     // 軌道要素の計算
 848:     if (($hm > 0&& ($items['longitude1'!''&& ($items['latitude1'!''&& ($items['longitude2'!''&& ($items['latitude2'!'')) {
 849:         $dt = $pgc1->distance($items['longitude1'], $items['latitude1'], $items['longitude2'], $items['latitude2']);
 850:         $items['hmm'] = $hm * 1000;
 851:         getOrbitalElement($dt, $hm * 1000, $items);
 852:         getBallistic($items);
 853:     }
 854: 
 855:     // 地図描画サービス
 856:     switch (MAPSERVICE) {
 857:         // Google
 858:         case 0:
 859:             $call  = jsDragMarker_Gmap($items2[$i]['latitude'], $items2[$i]['longitude']);
 860:             $call .jsDrawLine_Gmap(1, $items1, 'rgb(255, 0, 0)', 1);
 861:             $call .jsDrawLine_Gmap(2, $items2, 'rgb(0, 0, 0)', 1);
 862:             $zd = 1;
 863:             break;
 864:         // Yahoo!JAPAN
 865:         case 1:
 866:             $call  = jsDragMarker_YOLPmap($items2[$i]['latitude'], $items2[$i]['longitude']);
 867:             $call .jsDrawLine_YOLPmap(1, $items1, 'FF0000', 1);
 868:             $call .jsDrawLine_YOLPmap(2, $items2, '000000', 1);
 869:             $zd = 0;
 870:             break;
 871:         // 地理院地図・OSM
 872:         case 2:
 873:             $call  = jsDragMarker_Leaflet($items2[$i]['latitude'], $items2[$i]['longitude']);
 874:             $call .jsDrawLine_Leaflet(1, $items1, 'FF0000', 1);
 875:             $call .jsDrawLine_Leaflet(2, $items2, '000000', 1);
 876:             $zd = 0;
 877:             break;
 878:         default:
 879:             $call = '';
 880:             $zd = 0;
 881:             break;
 882:     }
 883:     $lat = $items2[$i]['latitude'];
 884:     $lng = $items2[$i]['longitude'];
 885: 
 886: // エラー発生時
 887: else {

マップから得た2地点の緯度・経度を射程に変換するには、メソッド distance を使った。このメソッドは、「PHPで2地点間の直線距離を求める」で紹介している。

結語

東倉里から東京を狙う場合、弾道高100kmなら、発射から5分半で着弾する。
一方、弾道高1000kmのロフテッド軌道だと、着弾まで16分半かかる。Jアラートが出てから避難するには十分な時間だ。

さて、東倉里からホノルルを狙う弾道高1500kmの軌道では、ミサイルの平均速度はマッハ16、最高速度はマッハ20に達する。これは最終段のロケットモーターが燃え尽きた後の速度であるから、打ち上げ直後の速度はもっと遅いし、着弾時には空気抵抗の影響で減速するだろう。
ただ、着弾前に攻撃するミサイル迎撃システム「PAC3」が演習で撃ち落とした飛翔体はマッハ4である。

参考サイト

参考書籍

表紙 惑星探査機の軌道計算入門
著者 半揚稔雄
出版社 日本評論社
サイズ 単行本
発売日 2017年09月19日頃
価格 2,420円(税込)
ISBN 9784535788459
序章 宇宙飛翔力学とは 第1章 円錐曲線の幾何学 第2章 ケプラー運動 第3章 軌道遷移 第4章 軌道決定 第5章 ロケットの性能 第6章 惑星間飛行 付録
 
表紙 天文計算入門
著者 長谷川一郎
出版社 恒星社厚生閣
サイズ 単行本
発売日 1996年01月25日頃
価格 2,750円(税込)
ISBN 9784769908180
 
(この項おわり)
header