初めてgem作ってみた - 軌道で遊びたい -

この記事は「[ソニックガーデン 若手プログラマ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/sonicgarden-junior)」の17日目の記事です。

https://qiita.com/advent-calendar/2024/sonicgarden-junior

---

## はじめに
みなさん宇宙はお好きですか？100km上空からは宇宙らしいので割と身近だったりしますよね。人工衛星のニュースなどを見聞きすると、どんな軌道なんだろうかと気になることも多いかと思います。
地球の周りを回っている人工衛星はほとんど、[Celestrak](https://celestrak.org/)というサイトで[TLE](https://earth.jaxa.jp/gpr/ProductsOperations/docs/GCOM-W/W1TLE_ja.pdf)（Two Line Elements）という形式の軌道要素が公開されています。このTLEを使って自分で色々と遊べるgemがあったら面白そうだなということで作ってみました。

gemの作り方は、以下の記事を参考にさせていただきました。

https://qiita.com/harashoo/items/1284fd5362c1a4f6c8a8

gemの作り方自体は参考記事からの差分がないので、この記事ではどんなgemを作ったかにフォーカスします。

## どんなgem?
TLEをインプットに別の軌道要素を計算するようなgemです。

https://rubygems.org/gems/korba

具体的には以下の機能を作ってみました。
1. TLEのテキストデータ[^1]を読み込んで軌道要素にパース
1. TLEのJSONデータ[^2]を読み込んで軌道要素にパース
1. TLEから作った軌道のKeplerianのオブジェクトを作成
1. TLEの軌道要素から高度を算出
1. Cartesianのオブジェクトを作成

なんか物足りないですが、今後に期待です。。

[^1]: こういうデータ https://celestrak.org/NORAD/elements/gp.php?CATNR=25544&FORMAT=tle
[^2]: こういうデータ https://celestrak.org/NORAD/elements/gp.php?CATNR=25544&FORMAT=json
（昔はこんな使いやすいのはなかったので、便利になっている）

### TLEのテキストデータを読み込んで軌道要素にパース
ISS（国際宇宙ステーション）のTLEを例にします。
https://celestrak.org/NORAD/elements/table.php?GROUP=stations&FORMAT=tle
このページの一番上の`Latest Data`を使ってみます。
```ruby
irb(main):001> require 'korba'
irb(main):002> tle = Korba::Tle.new(<<~TLE)
                        ISS (ZARYA)             
                        1 25544U 98067A   24347.95246197  .00018784  00000+0  32977-3 0  9991
                        2 25544  51.6388 149.7053 0007501 335.8963 160.2029 15.50536434486275
                     TLE
# コピペしたい人がいるかもしれないので、ヒアドキュメント部分のirbプロンプトは消してます。
=> 
#<Korba::Tle:0x0000000129b75230
...
irb(main):003> tle
=> 
#<Korba::Tle:0x0000000129b75230
 @arg_of_pericenter=335.8963,          # 近地点引数
 @eccentricity=0.0007501,              # 離心率
 @epoch="2024-12-12T22:51:32.714207",
 @inclination=51.6388,                 # 傾斜角
 @mean_anomaly=160.2029,               # 平均近点角
 @mean_motion=15.50536434486275,       # 平均運動
 @object_name="ISS (ZARYA)             ",
 @ra_of_asc_node=149.7053,             # 昇交点赤経
 @tle_string="ISS (ZARYA)             \n1 25544U 98067A   24347.95246197  .00018784  00000+0  32977-3 0  9991\n2 25544  51.6388 149.7053 0007501 335.8963 160.2029 15.50536434486275">
```
TLEの、なに書いてあるかわからない状態から各要素がどんな値なのか一目瞭然になりました！便利！
（単位は距離は`m`、角度は`degrees`です）

### TLEのJSONデータを読み込んで軌道要素にパース
```ruby
irb(main):001> require 'korba'
irb(main):002> tle_json = {"OBJECT_NAME":"ISS (ZARYA)","OBJECT_ID":"1998-067A","EPOCH":"2024-12-12T22:51:32.714208","MEAN_MOTION":15.50536434,"ECCENTRICITY":0.0007501,"INCLINATION":51.6388,"RA_OF_ASC_NODE":149.7053,"ARG_OF_PERICENTER":335.8963,"MEAN_ANOMALY":160.2029,"EPHEMERIS_TYPE":0,"CLASSIFICATION_TYPE":"U","NORAD_CAT_ID":25544,"ELEMENT_SET_NO":999,"REV_AT_EPOCH":48627,"BSTAR":0.00032977,"MEAN_MOTION_DOT":0.00018784,"MEAN_MOTION_DDOT":0}
=> 
{:OBJECT_NAME=>"ISS (ZARYA)",
...
irb(main):003> tle = Korba::Tle.new(tle_json, type: :json)
=> 
#<Korba::Tle:0x000000011fe7b7e0
...
irb(main):004> tle
=> 
#<Korba::Tle:0x000000011fe7b7e0
 @arg_of_pericenter=335.8963,
 @eccentricity=0.0007501,
 @epoch="2024-12-12T22:51:32.714208",
 @inclination=51.6388,
 @mean_anomaly=160.2029,
 @mean_motion=15.50536434,
 @object_id="1998-067A",
 @object_name="ISS (ZARYA)",
 @ra_of_asc_node=149.7053,
 @tle_json=
  {:OBJECT_NAME=>"ISS (ZARYA)",
   :OBJECT_ID=>"1998-067A",
   :EPOCH=>"2024-12-12T22:51:32.714208",
   :MEAN_MOTION=>15.50536434,
   :ECCENTRICITY=>0.0007501,
   :INCLINATION=>51.6388,
   :RA_OF_ASC_NODE=>149.7053,
   :ARG_OF_PERICENTER=>335.8963,
   :MEAN_ANOMALY=>160.2029,
   :EPHEMERIS_TYPE=>0,
   :CLASSIFICATION_TYPE=>"U",
   :NORAD_CAT_ID=>25544,
   :ELEMENT_SET_NO=>999,
   :REV_AT_EPOCH=>48627,
   :BSTAR=>0.00032977,
   :MEAN_MOTION_DOT=>0.00018784,
   :MEAN_MOTION_DDOT=>0}>
```
JSONあるんかい、わかりやすいの落ちてるやんと思った人は鋭いですね。その通りなんですが一旦横に置いときます。TLEをわかりやすくしたいんじゃなく、軌道で遊ぶのが目的なので。

### TLEから作った軌道のKeplerianのオブジェクトを作成
さっきのやつをこう
```ruby
irb(main):005> tle.to_kep
=> 
#<Korba::Kep:0x0000000129d17a48
 @arg_of_pericenter=335.8963,
 @eccentricity=0.0007501,
 @epoch="2024-12-12T22:51:32.714208",
 @inclination=51.6388,
 @mean_anomaly=160.2029,
 @object_name="ISS (ZARYA)",
 @ra_of_asc_node=149.7053,
 @semi_major_axis=6793295.781644506> # 軌道長半径
```
軌道を特定するために必要な6要素のうち、1個変わっただけなんですけど、やっぱり軌道長半径がわかると軌道のイメージがよりつきやすいですね。
地球の半径が頭にあれば、なんとなくの高度もわかってISSって近いんだなーとか思うかもですが、地球の半径を覚えてる必要もないので高度の計算機能も作りました。

### TLEの軌道要素から高度を算出
```ruby
irb(main):006> tle.height_at_perigee # 近地点高度
=> 410063.13047869503
irb(main):007> tle.height_at_apogee  # 遠地点高度
=> 420254.43281031866
```
人工衛星の軌道は楕円なので近地点（地球に最も近くなるところ）と遠地点（地球から最も遠くなるところ）の2つの高度があるとわかりやすいですね。

### Cartesian（地球中心慣性系）のオブジェクトを作成
TLEからでもKeplerianからでも
```ruby
irb(main):008> tle.to_car
=> 
#<Korba::Car:0x000000012bb540b0
 @epoch="2024-12-12T22:51:32.714207",
 @object_name="ISS (ZARYA)             ",
 @vx=6308.89485150973,
 @vy=279.4748703287188,
 @vz=-4325.798068923932,
 @x=2756480.692923069,
 @y=-4996823.877996226,
 @z=3694266.1741632815>
```
いまいち、ピンと来ない軌道要素ではあるんですが、これを元に座標系変えてとかすると役に立つやつなので算出できるようにしました。（このピンと来ないやつが一番手がかかってます。。）
Newton法で方程式の解を数値解析してくれる[`Newton.nlsolve`](https://docs.ruby-lang.org/ja/latest/class/Newton.html)と[`Vector`](https://docs.ruby-lang.org/ja/latest/class/Vector.html)を初めて使いました。

:::note warn
注意
計算の確からしさはCelestrakや本などの例題と照らし合わせて、大体合ってるから計算方法は間違ってないだろうなというのを確かめたくらいで精緻には検証していません
:::
## ついでにgemを使ったRailsアプリも作った
gem作ったら使いたいなと思い、簡素なRailsアプリも作りました。

https://korbas-misty-sun-4798.fly.dev

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/868893/45665393-e705-fc54-dc20-07de72f08b65.png)
*[fly.io](https://fly.io/)で動いてるRailsアプリです。
初めて[mvp.css](https://andybrewer.github.io/mvp/)を使ってみました。読み込むだけ、ただHTML書くだけでよしなにスタイルをつけてくれます。（逆にいうと調整はほぼ効かない）*

機能はシンプルです。TLE入力、`read tle`押す。高度、TLE、Keplerian、Cartesianが表示される。
ちなみに画像はひまわり9号の軌道です。今日も元気に3万6千km先で日本のみんなのために観測してくれてます。ありがたや

## まとめ
軌道で遊びたい人向けのgemを作って、それを使ったRailsアプリを作ってみた、という紹介記事でした。
ただ、まだまだ遊ぶには機能不足なので、今後成長させていきたいなと思っています。
やっぱり、伝播したいし、軌道座標とかでの速度とか気になりますよね。あとはRailsアプリも可視化とか、、乞うご期待。

明日の「[ソニックガーデン 若手プログラマ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/sonicgarden-junior)」は、@yappu00 です。お楽しみに！

https://qiita.com/advent-calendar/2024/sonicgarden-junior

---
## リンク集
- https://qiita.com/advent-calendar/2024/sonicgarden-junior
- https://qiita.com/harashoo/items/1284fd5362c1a4f6c8a8
- https://earth.jaxa.jp/gpr/ProductsOperations/docs/GCOM-W/W1TLE_ja.pdf
- https://celestrak.org/NORAD/elements/gp.php?CATNR=25544&FORMAT=tle
- https://rubygems.org/gems/korba
- https://docs.ruby-lang.org/ja/latest/class/Newton.html
- https://docs.ruby-lang.org/ja/latest/class/Vector.html
- https://korbas-misty-sun-4798.fly.dev
- https://fly.io/
- https://andybrewer.github.io/mvp/