RSS Twitter Facebook


« 2018年08月 | 2018年09月のアーカイブ | 2018年10月 »

2018/09/30

KiCad : Pythonスクリプトでガーバーをまとめる


今年 7 月に基板 CAD の KiCAD が 1 年ぶりくらいに 5.0 にバージョンアップして以来、はじめてちゃんと使ってみたのですが、なかなか良いです。レンダリングを OpenGL にすると動かない機能があるとかいう中途半端な感じがなくなってるし、ライブラリもかなり整備されました。個人的にはワイヤーを削除した時にジャンクションだけが取り残されたりしなくなったのでフラストレーションが激減しました。

KiCad

それでもうひとつ気になる機能があって Action Plugins という奴なんですが、これは Python で書いたプラグインでメニューからマクロ操作的な事ができるというもので 5.0 から使える予定だったのですが、これの動かし方がわからない。

という事で色々調べた結果、今公開されている 5.0.0 では何かの手違いで無効にしたまま公開してしまったようです。そのうちアップデートされると思いますが、今すぐ使ってみるには安定版の 5.0.0 ではなくて Nightly Build の方を入れれば試せます。

以前のバージョンでも Python スクリプト自体は使えたのですが、呼び出し方にいまいち問題があって、Python コンソールを開いてコマンド名を打ち込む必要がありました。これだと、ちょっとした便利コマンドみたいなものをせっかく作っても、いざ使おうとした時にはもう使い方を覚えていないとか、存在そのものを覚えていないという事になりがちです。 で、これをメニューに登録できるようにした、とまあそれだけの事ではあるんですけど、使い勝手はかなり違うと思います。



ただこの Python スクリプト周りはまだドキュメンテーションが行き届いていないので、どうやればやりたい事ができるのか調べるのには結構手探りの試行錯誤が必要そうです。とりあえず、ガーバーファイルを FusionPCB とか Elecrow スタイルにリネームして ZIP にまとめるスクリプトを書いてみました。

動かすには Linux の場合、http://docs.kicad-pcb.org/doxygen/md_Documentation_development_pcbnew-plugins.html にあるように、このファイル ( action_menu_gerber_zip.py ) を /usr/share/kicad/scripting/plugins/ あたりに置けば動くようです。ただし Windows の場合はこの説明が怪しくて色々やった所どうやら、

C:\Users\ユーザー名\AppData\Roaming\kicad\scripting

に置けば動作するようです(このあたりの挙動はまだ確定かどうかわからない)。

スクリプトの中身ですが、FusionPCB や Elecrow を使っている人なら何度もやる事になるガーバーをリネームしてZIPで固めるという作業を自動的に行います。出力はプロジェクトのあるフォルダ下に 'Gerber' というサブフォルダを作成してそこに納めます。

FusionPCB と Elecrow は必要とするファイルの拡張子は同じなのですが推奨する条件が多少違っているので、一応切り替えやすいように先頭の方にまとめています。ダイアログを出して設定したりできるようにするべきかも知れませんが、まあそのうち KiCad 安定版で動作するようになったら...

設定説明FusionPCBElecrow
merge_npthドリルファイルのメッキなしとありを1ファイルにまとめるかまとめるのが推奨 (多分別ファイル xxx-NPTH.TXT を添付でもやってくれるとは思う)別ファイル推奨。まとめた時はメッキありになっちゃうかも
use_aux_origin補助座標 AuxOrigin を使うかどうかどちらかと言うと KiCad 側のドリルファイルの補助座標を Python から扱うやり方が正しいかどうか多少曖昧な所があるけど多分どちらでも大丈夫
excellon_formatドリルファイルの数値のフォーマットサプレスリーディング推奨推奨は不明だけど、DECIMAL_FORMAT で発注している人が多そう


動作は Windows 10 + KiCad (6.0.0-rc1-dev-536-g40a3b4a53) 環境でしか確認していません。

# file : action_menu_gerber_zip.py
#
# (gerber_zip)
#
# Copyright (C) 2018 g200kg
#   Released under MIT License
#

import pcbnew
from pcbnew import *
import wx
import os
import zipfile

#
# Setup params
#

gerber_subdir = "Gerber"
merge_npth = False
use_aux_origin = True
excellon_format = EXCELLON_WRITER.DECIMAL_FORMAT
#excellon_format = EXCELLON_WRITER.SUPPRESS_LEADING

#
#
#

class GerberZip( pcbnew.ActionPlugin ):
    '''
    Make Gerber Zip-file for Elecrow / FusionPCB
    '''
    layers = [
        [ F_Cu,     'GTL', None ],
        [ B_Cu,     'GBL', None ],
        [ F_SilkS,  'GTO', None ],
        [ B_SilkS,  'GBO', None ],
        [ F_Mask,   'GTS', None ],
        [ B_Mask,   'GBS', None ],
        [ Edge_Cuts,'GML', None ],
        [ In1_Cu,   'GL2', None ],
        [ In2_Cu,   'GL3', None ],
        [ In3_Cu,   'GL4', None ],
        [ In4_Cu,   'GL5', None ],
    ]
    max_layer = 7

    def defaults( self ):
        self.name = "Make Gerber-Zip (Elecrow/FusionPCB style)"
        self.category = "Plot"
        self.description = "Make Gerber-Zip-file for Elecrow / FusionPCB"

    def forcedel( self, fname ):
        if os.path.exists(fname):
            os.remove(fname)

    def forceren( self, src, dst ):
        self.forcedel(dst)
        os.rename(src, dst)

    def Run( self ):
        board = pcbnew.GetBoard()
        board_fname = board.GetFileName()
        board_dir = os.path.dirname(board_fname)
        board_basename = (os.path.splitext(os.path.basename(board_fname)))[0]
        gerber_dir = '%s/%s' % (board_dir, gerber_subdir)
        drill_fname = '%s/%s.TXT' % (gerber_dir, board_basename)
        npth_fname = '%s/%s-NPTH.TXT' % (gerber_dir, board_basename)
        zip_fname = '%s/%s.zip' % (gerber_dir, board_basename)
        if not os.path.exists(gerber_dir):
            os.mkdir(gerber_dir)

        max_layer = board.GetCopperLayerCount() + 5
# PLOT
        pc = pcbnew.PLOT_CONTROLLER(board)
        po = pc.GetPlotOptions()

        po.SetOutputDirectory(gerber_dir)
        po.SetPlotValue(True)
        po.SetPlotReference(True)
        po.SetExcludeEdgeLayer(False)
        po.SetLineWidth(FromMM(0.1))
        po.SetSubtractMaskFromSilk(True)
        po.SetUseAuxOrigin(use_aux_origin)

        for layer in self.layers:
            targetname = '%s/%s.%s' % (gerber_dir, board_basename, layer[1])
            self.forcedel(targetname)
        self.forcedel(drill_fname)
        self.forcedel(npth_fname)
        self.forcedel(zip_fname)

        for i in range(max_layer):
            layer = self.layers[i]
            pc.SetLayer(layer[0])
            pc.OpenPlotfile(layer[1],PLOT_FORMAT_GERBER,layer[1])
            pc.PlotLayer()
            layer[2] = pc.GetPlotFileName()
        pc.ClosePlot()

        for i in range(max_layer):
            layer = self.layers[i]
            targetname = '%s/%s.%s' % (gerber_dir, board_basename, layer[1])
            self.forceren(layer[2],targetname)
# DRILL
        ew = EXCELLON_WRITER(board)
        ew.SetFormat(True, excellon_format, 3, 3)
        offset = wxPoint(0,0)
        if(use_aux_origin):
            offset = board.GetAuxOrigin()
        ew.SetOptions(False, False, offset, merge_npth)
        ew.CreateDrillandMapFilesSet(gerber_dir,True,False)
        if merge_npth:
            self.forceren('%s/%s.drl' % (gerber_dir, board_basename), drill_fname)
        else:
            self.forceren('%s/%s-PTH.drl' % (gerber_dir, board_basename), drill_fname)
            self.forceren('%s/%s-NPTH.drl' % (gerber_dir, board_basename), npth_fname)
# ZIP
        with zipfile.ZipFile(zip_fname,'w') as f:
            for i in range(max_layer):
                layer = self.layers[i]
                targetname = '%s/%s.%s' % (gerber_dir, board_basename, layer[1])
                f.write(targetname, os.path.basename(targetname))
            f.write(drill_fname, os.path.basename(drill_fname))
            if not merge_npth:
                f.write(npth_fname, os.path.basename(npth_fname))

        wx.MessageBox('GerberZip Complete.\n\n%s' % zip_fname, 'Gerber Zip', wx.OK|wx.ICON_INFORMATION)

GerberZip().register()

posted by g200kg : 1:45 PM : PermaLink

2018/09/19

Web Audio API が CR になりました



ついこの間 6月版の WebAudio API WD の日本語訳を作ったばかりなんですが、9月18日付けで CR が公開されました。WD の更新は結構ゆっくりだったのに急に動きが速まったんでしょうか?

https://www.w3.org/TR/2018/CR-webaudio-20180918/

W3C 勧告のプロセスとしては、次のような手順を踏みます。
  1. WD ( Working Draft ) : ワーキンググループが作成する草案
  2. CR ( Candidate Recommendation ) : WD の内容が固まったら勧告候補となり、この後も、レビューやら仕様に従った実装やらが進められる
  3. PR ( Proposed Recommendation ) : 勧告候補に沿った実装が(できれば二つ以上)存在するなどの条件を満たしたら、勧告案として最終レビューが行なわれる
  4. Recommendation : 勧告案のレビューをパスする事で最終的にこの「W3C勧告」となる
いわゆる「W3C勧告」にはまだステップがありますけど、CR になった事で、明らかにまだやらなきゃいけない事が残っているというような状態ではなく、必要な機能が揃った安定した仕様案という位置付けになったという所ですかね。

という事で、日本語訳の方もまた何とかしないといけないと思っているのですが、ざっと見た感じでは、6月版 WD から機能的に増えたものがあるわけではなくて説明がおかしかった所を修正したり、動作が不明瞭だった部分を加筆したりという内容になっています。基本的な内容については 6月版 WD と違いはありません。

CR での具体的な変更点は ChangeLog の Since Working Draft of 19 June 2018 で確認できます。

CR の日本語訳はそのうちまたこちらで公開するつもりです。 Web Audio API (日本語訳)

posted by g200kg : 3:26 PM : PermaLink

2018/09/18

整数の平方根(切り捨て)



FPU を使えない環境でたま~に使いたくなって、どうやってたっけ? と毎回昔のコードを掘り返したりしているので、メモ。

バビロニアンメソッドと呼ばれる奴で、16bit値くらいなら割り算10回分程度。
真の値は戻り値と戻り値 +1 の間になるように多少気を使っている。

unsigned int sqrint(unsigned int x){
  unsigned int y1 = x, y2 = (x + 1) >> 1;
  while(y2 < y1)
    y1 = y2, y2 = (y1 + x / y1) >> 1;
  return y1;
}

9.21 追記-----
これだと初期値が適当すぎて収束が遅いので、@xevixeviさんが、もうちょっと改善する案をツイートしてくれましたので、追記しておきます。

有効ビット数まで切り詰めて初期値とするので必要な反復計算がかなり減ります。速度重視ならこっち。また最初の例だと、UINT_MAX を入力した時に例外が起こるのを回避できてます。

unsigned int sqrint(unsigned int x){
  unsigned int y1 = x, y2 = 1, x2 = x;
  while(x2 >>= 2)
    y2 = (y2 << 1) + 1;
  while(y2 < y1)
    y1 = y2, y2 = (y1 + x / y1) >> 1;
  return y1;
}

ついでに...
処理時間に占める除算の割合が高そうなので、ループの終了条件の確認で1回余計に回るのを避けるために毎回乗算するパターン。これが割に合うかどうかは CPU 次第だけど、除算に比べて乗算が充分に速ければこっちの方が速くなる可能性があります。

unsigned int sqrint(unsigned int x){
  unsigned int y = 1, x2 = x;
  while(x2 >>= 2)
    y = (y << 1) + 1;
  while(y * y > x)
    y = (y + x / y) >> 1;
  return y;
}

posted by g200kg : 12:20 PM : PermaLink

2018/09/02

WebAudio API 2018 日本語訳 最新版公開


6月19日に公開されていた W3C の「Web Audio API」のワーキングドラフトの日本語化が完了したので公開します。前回の版(2015年版) と比べて大きな所では

 ・ScriptProcessor の置き換えとなる AudioWorklet の仕様
 ・名前が何度かふらふら変わっていた Panner 関係の整理
 ・新しいノード constantSourceNode、mediaStreamTrackSourceNode の定義
 ・ノードのコンストラクタの仕様

あたりが最新仕様に沿ったものになっています。

なかなか翻訳に手こずったなと思ったら、ブラウザを作る人向けの内部アルゴリズムの手順等も大幅に加筆されているので2015年版に比べてサイズが 3 倍近くになってます。

Web Audio API を軽く触ってみたいという時には少し重いドキュメントですが、これが原典ですし、ソフトウェアによる音響処理の実装の解説として見れば、実際的な音響処理について大勢が寄ってたかって編集/レビューしたとてもレアな文書だと思います。興味がある人には読み物としてじっくり読んでもとても有用な内容です。これが普通に公開されているのだから読まないともったいないですよ。

Web Audio API, W3C Working Draft, 19 June 2018 (日本語訳)

posted by g200kg : 11:32 AM : PermaLink

« 2018年08月 | 2018年09月のアーカイブ | 2018年10月 »


g200kg