ビアネタリウム

ある日、基板を編集していました。

![スクリーンショット 2023-10-24 22.13.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/96ad7c0c-9d84-ca94-4044-9ed5ecf64e8a.png)


うーん...

![スクリーンショット 2023-10-24 22.16.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/e8628dc7-c0c9-59a2-a4b9-070036382c58.png)

あれ

![スクリーンショット 2023-10-24 22.17.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/d5184cfa-2e93-c6a2-ea28-8b9232e8fdb2.png)

素敵じゃん。

ということで、ビアで星空を作ります。

# 方針

星の数ほどのビアを手動で配置するのは大変なので、KiCadのPython Scriptingを使って自動化してみようと思います。

流れはこんな感じ

1. データベースから星の座標を取得 
1. Python Scriptingでビアを配置

# Python Scriptingを触ってみる

Python Scriptingでは、フットプリントのオブジェクトにアクセスして配置やプロパティの変更ができます。スクリプトはKiCadのスクリプトコンソールから実行します。

下記のサイトを参考にさせていただきました。

[KiCadのPython scripting機能を使ってみる : 東京工業大学 ロボット技術研究会](http://titech-ssr.blog.jp/archives/1047505890.html)
[KiCad PCBNew Python Scripting Cheat Sheet | atomic14](https://www.atomic14.com/2022/10/24/kicad-python-scripting-cheat-sheet-copy.html)
[KiCad Pcbnew Python Scripting: pcbnew Namespace Reference](https://docs.kicad.org/doxygen-python-7.0/namespacepcbnew.html)


## スクリプトを作る

まずは、スクリプトコンソールを立ち上げます。

PCBエディターの`ツール > スクリプトコンソール`またはツールバーの![スクリーンショット 2023-10-24 234548.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/dc54bbfc-ce03-1207-c557-22cba2d04e02.png)ボタンから起動できます。

こんな画面が出てくるので、適当にprintでもしてみます。

```python:Shell
>>> print("Hello, World!")
Hello, World!
```

![スクリーンショット 2023-10-24 235416.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/6ca06e5e-9447-b676-807c-15345b91910f.png)


よくある対話型シェルですね。

ただ、このままでは使い勝手がよくないので、pyファイルを作って実行してみます。

KiCadプロジェクトのルート（kicad_proファイルと同階層）にpyファイルを作り、適当なプログラムを書き込みます。

```python:main.py
print("Hello, py file!")
```

実行するには下のように`import <ファイル名>`をスクリプトコンソールで実行します。

```python:Shell
>>> import main
Hello, py file!
```

以降はこのファイルを編集していきます。

## ビアを作成する

pythonから基板を編集するにはpcbnewを使います。KiCadのPCBエディタはpcbnewをラップしているようです。

[Pcbnew | 5.1 | 日本語 | Documentation | KiCad](https://docs.kicad.org/5.1/ja/pcbnew/pcbnew.html)

まずは、pcbnewインポートして`BOARD`オブジェクトを取得します。

```python:main.py
import pcbnew


board = pcbnew.GetBoard()
```

取得した`BOARD`オブジェクトを介してパーツの追加や移動ができるようになります。注意としては、PCB図面全体をボードとして扱うため、`Edge.Cuts`で囲んでいなくても図面全体がボードとなります。

次に、新しいビアを作成してみます。APIを参考にして`PCB_VIA`オブジェクトをインスタンス化します。

[KiCad Pcbnew Python Scripting: pcbnew.PCB_VIA Class Reference](https://docs.kicad.org/doxygen-python-7.0/classpcbnew_1_1PCB__VIA.html)

```diff_python:main.py
import pcbnew


board = pcbnew.GetBoard()

+ via = pcbnew.PCB_VIA(board)
+ via.SetX(int(0 * 1_000_000))
+ via.SetY(int(0 * 1_000_000))
+ via.SetWidth(int(1.5 * 1_000_000))
+ via.SetDrill(int(1.0 * 1_000_000))
```

コンストラクタの引数には親要素を渡します。今回は先ほど取得した`BOARD`オブジェクトです。

> `__init__(PCB_VIA self, BOARD_ITEM aParent) -> PCB_VIA`

ついでに位置と穴径、ランド系も指定します。

位置指定には`PCB_VIA`クラスが継承している`EDA_ITEM`クラスに`SetPosition()`関数があるほか、`BOARD_ITEM`クラスに`SetX()` `SetY()`関数があります。`SetX()` `SetY()`は引数がint型で使い勝手がいいため、今回はこちらで指定しています。

> ![classpcbnew_1_1PCB__VIA__inherit__graph.png (261×411)](https://docs.kicad.org/doxygen-python-7.0/classpcbnew_1_1PCB__VIA__inherit__graph.png)

また、pcbnewでは位置や径などの長さをnmで管理しているため、関数の引数もnmに換算してから渡す必要があります。

最後に、作成したビアをボードに追加します。

```diff_python:main.py
import pcbnew


board = pcbnew.GetBoard()

via = pcbnew.PCB_VIA(board)
via.SetX(int(0 * 1_000_000))
via.SetY(int(0 * 1_000_000))
via.SetWidth(int(1.5 * 1_000_000))
via.SetDrill(int(1.0 * 1_000_000))

+ board.Add(via)
+ pcbnew.Refresh()
```

実行してみましょう。

```python:Shell
>>> import main
```

![スクリーンショット 2023-10-25 141243.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/7d725916-0dbc-69f3-2197-1999b8ee3860.png)

位置(0, 0)に穴径1mm、ランド系1.5mmのビアが追加されました。これで任意の位置にビアを配置できるようになったので、次は配置する座標を決めていきます。

# 星の座標を取得する

星の座標として[Astro Commons](http://astro.starfree.jp/commons/help/)さんのヒッパルコス星座線恒星データを使わせていただきます。このデータセットには赤道座標を含む690個の恒星が収録されています。

pythonからダウンロードしたcsvファイルを読み込み、赤経と赤緯を抽出してみます。

```diff_python:main.py
import csv


with open('hip_constellation_line_star.csv') as f:
    reader = csv.reader(f)
    for row in reader:
        ra = (float(row[1]) * 15) + (float(row[2]) * (15 / 60)) + (float(row[3]) * (15 / 3600))
        dec = float(row[4]) + (float(row[5]) * (1 / 60)) + (float(row[6]) * (1 / 3600))
```

`ra`が赤経、`dec`が赤緯です。赤緯は° " 'で表すのに対し、赤経はh m sで表し、1 hは15°なので注意が必要です。

# 完成

`ra`をx座標に`dec`をy座標に指定して等級によって径を変えれば完成です。

<details><summary>全てのコード</summary>

```diff_python:main.py
import pcbnew
import csv
import math


board = pcbnew.GetBoard()

with open('hip_constellation_line_star.csv') as f:
    reader = csv.reader(f)
    for row in reader:
        # 行から必要なデータを抽出
        ra = (float(row[1]) * 15) + (float(row[2]) * (15 / 60)) + (float(row[3]) * (15 / 3600))
        dec = float(row[4]) + (float(row[5]) * (1 / 60)) + (float(row[6]) * (1 / 3600))
        magnutude = float(row[7])
        number = row[0]

        # 平面座標に変換
        x = -ra * 1_000_000
        y = -dec * 1_000_000

        # 等級を適当な曲線でサイズに変換
        size = (2 * math.e ** (-0.2 * magnutude)) * 1_000_000

        via = pcbnew.PCB_VIA(board)
        via.SetX(int(x))
        via.SetY(int(y))
        via.SetWidth(int(size))
        via.SetDrill(int(size * (1 / 1.5)))
        board.Add(via)

        text = pcbnew.PCB_TEXT(board)
        text.SetX(int(x + (size / 2 + 1)))
        text.SetY(int(y))
        text.SetHorizJustify(pcbnew.GR_TEXT_H_ALIGN_LEFT)
        text.SetLayer(pcbnew.F_Fab)
        text.SetText(number)
        board.Add(text)


        pcbnew.Refresh()
```
</details>

いでよ！

![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/36c884f5-b9a9-1011-c98e-637cfaaa6ab7.gif)

それでは観察していきましょう。

## オリオン座

![スクリーンショット 2023-10-25 171203.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/d89d7c56-1963-7b3b-395e-da1e595d9acd.png)

いいですねぇ。若干ゼータ星がずれてますがオリオン座になってます。左上の大きなビアがベテルギウス、右下がリゲルですね。

## おおいぬ座

![スクリーンショット 2023-10-25 171525.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/2faae5f9-fffb-a7c3-9f78-cbbb97ed9716.png)

これは完全におおいぬ座ですね。ひときわ大きなシリウスが目立ちます。

## こいぬ座

![スクリーンショット 2023-10-25 171725.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/96879352-d3ac-3ae1-4594-65b3c524d6b2.png)

これは...~~線にしか見えない~~

## 冬の大三角形

![スクリーンショット 2023-10-25 171921.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673727/10f5d162-14c8-2561-514a-e1355afd29b8.png)

最後に冬の大三角形です。大電流にも対応できそうです。

# おしまい

__いかがでしたか？__

私はとても癒されました。Python Scriptingは情報が少なく、ここまで難儀する部分もありましたが、今回のビア配置を応用すれば様々な作業をPythonで自動化することができます。

今回のプロジェクトはGithubで公開しています。

[waiwai2525/vianetarium](https://github.com/waiwai2525/vianetarium)