このところLTspiceを使うことが多くなって勝手に不便に思っているのが、独立電圧源の波形の設定です。SPICE自体は細かく時間とか制御できるので機能的にはまったく不満はありません。しかし大枠だけ決めてあとはお任せ、みたいなズボラな設定が欲しいです。そこでズボラな設定用のスクリプトの作成に着手。着手しただけ。まだ機能的には全然。
※「SPICEの小瓶」投稿順インデックスはこちら
※今回作成着手したPythonスクリプト全文は末尾に。
実機で波形を観察するときに使っているのがDigilent社のAnalog Discovery2という装置です。低価格ながらオシロやら波形ジェネレータやらいろいろ入っている「万能」PCツールです。この波形ジェネレータの設定画面が以下に。
上記は、3角波を作るときの設定ですが、周波数、振幅、オフセットを入力すれば、私が必要とするような波形はできてしまいます。デフォルト値が入っているので、特に変更なければコマケー話は抜きにできるので、らくちん。
勿論三角波だけでなく、以下のように波形を選択可能です。それらについてもだいたい同じような感じで設定可能です。
一方、LTspiceの独立電圧源の波形の定義画面は以下のようです。例えば以下は、「三角波」を定義しているところですが、結構パラメータ多いです。時間などは秒単位で指定しないとなりません。計算ができない年寄りは、電卓使いますがケタ間違えることも度々。
上記の設定を冒頭のアイキャッチ画像のLTspice回路で観察したものが以下です。設定はメンドイですが、単純な三角波です。
私に変わって、波形を定義してくれるスクリプトということで、末尾にソースを添付したスクリプトを作り始めました。もしかすると「車輪の再発明」的な。
今のところ、正弦波、方形波、三角波だけですが、おいおい追加する予定です。
使いかたはこんな感じ。オプション引数で周波数、振幅、波形タイプなどを指定して走らせると、LTspiceに貼り付けられる定義を出力してくるので、これをコピペするっと。
一応、ヘルプ機能あり。以下のごとし。基本全てにデフォルト値があるので、デフォルトのままでよければ指定は不要。
なお、unittestにも対応。というより、unittestないとまともに動作しないです。あってもどうだか?
一応ね、今回外形を作れた感じなので、おいおい拡張していく予定であります。
SPICEの小瓶(5) 温度を変えてシミュレーション、ついでに物理定数 へ戻る
SPICEの小瓶(7) standard.bjt、書き換えちゃって大丈夫なの? へ進む
今回作成中のPythonスクリプト、途上です。バグなどあっても知りませんよ。
#!/usr/bin/python # coding: utf-8 ### @file waveGen.py ### @brief LTspice waveform Generator ### ### @author: Joseph Halfmoon ### @date: April 1, 2022 """ LTspice waveform generation script v0.1 input ===== -F frequency -A amplitude -O offset -D duty -T type. (sine square triangle) unittest ======== python -m unittest waveGen.uTestCase """ import argparse import sys import unittest versionSTR = "waveGen.py v0.1" def parse2float(st): """parse string to float Returns at Fail: None """ try: work = float(st) except: return None return work def chkVal(arg, dval): vl = parse2float(arg) if vl is None: nLen = len(arg) if nLen > 1: bdy = arg[0:nLen-1] lastChar = arg[nLen-1] vl = parse2float(bdy) if vl is None: vl = dval else: lastChar = "0" return (vl, lastChar) def freqIN(arg, dval): prefixes = 'mkMG' vl, lastChar = chkVal(arg, dval) pfx = prefixes.find(lastChar) if pfx == 0: ex = 1e-3 elif pfx == 1: ex = 1e3 elif pfx == 2: ex = 1e6 elif pfx == 3: ex = 1e9 else: ex = 1 return vl * ex def vIN(arg, dval): prefixes = 'm' vl, lastChar = chkVal(arg, dval) pfx = prefixes.find(lastChar) if pfx == 0: ex = 1e-3 else: ex = 1 return vl * ex class sinWave(object): """Wave Generation base class generate sine wave. """ def __init__(self, frequency, amplitude, offset, nCyc): self.freq = frequency self.amp = amplitude self.ofs = offset self.nCyc = nCyc def genWave(self): return "SINE({0} {1} {2} 0 0 0 {3})".format(self.ofs, self.amp, self.freq, self.nCyc) class squareWave(sinWave): """Square wave Generation class squareWave 0.0. """ def __init__(self, frequency, amplitude, offset, nCyc, duty): super(squareWave, self).__init__(frequency, amplitude, offset, nCyc) self.risTIM = 1e-9 self.falTIM = 1e-9 self.tperiod = 1.0 / self.freq self.ton = self.tperiod * duty self.duty = duty self.vinit = self.ofs - self.amp self.von = self.ofs + self.amp def genWave(self): return "PULSE({0} {1} 0 {2} {3} {4} {5} {6})".format(self.vinit, self.von, self.risTIM, self.falTIM, self.ton, self.tperiod, self.nCyc) class triangleWave(squareWave): """Triangle wave Generation class triangleWave 0.0. """ def __init__(self, frequency, amplitude, offset, nCyc, duty): super(triangleWave, self).__init__(frequency, amplitude, offset, nCyc, duty) self.risTIM = self.tperiod * self.duty self.falTIM = self.tperiod * (1.0 - self.duty) self.ton = 0.0 def genWave(self): return "PULSE({0} {1} 0 {2} {3} {4} {5} {6})".format(self.vinit, self.von, self.risTIM, self.falTIM, self.ton, self.tperiod, self.nCyc) class uTestCase(unittest.TestCase): def test_parse2float(self): self.assertIsNone(parse2float("1.0x")) self.assertAlmostEqual(1.25, parse2float("1.25")) def test_chkVal(self): vl, lc = chkVal("ABC", 1.11) self.assertAlmostEqual(1.11, vl) self.assertEqual("C", lc) vl, lc = chkVal("1.25", 0) self.assertAlmostEqual(1.25, vl) self.assertEqual("0", lc) vl, lc = chkVal("1.5m", 0) self.assertAlmostEqual(1.5, vl) self.assertEqual("m", lc) def test_freqIN(self): self.assertAlmostEqual(1000, freqIN("ABC", 1000)) self.assertAlmostEqual(100, freqIN("100X", 2000)) self.assertAlmostEqual(1.5, freqIN("1.5", 0)) self.assertAlmostEqual(1.23e3, freqIN("1.23k", 0)) self.assertAlmostEqual(1.23e-3, freqIN("1.23m", 0)) self.assertAlmostEqual(1.23e6, freqIN("1.23M", 0)) self.assertAlmostEqual(1.23e9, freqIN("1.23G", 0)) def test_vIN(self): self.assertAlmostEqual(1.1, vIN("XYZ", 1.1)) self.assertAlmostEqual(1.55, vIN("1.55X", 1.2)) self.assertAlmostEqual(1.5, vIN("1.5", 0)) self.assertAlmostEqual(100e-3, vIN("100m", 0)) def test_sinWave(self): wav = sinWave(1000, 1.0, 0.0, 10) self.assertEqual("SINE(0.0 1.0 1000 0 0 0 10)", wav.genWave()) def test_squareWave(self): wav = squareWave(1000, 1.0, 0.0, 10, 0.5) self.assertEqual("PULSE(-1.0 1.0 0 1e-09 1e-09 0.0005 0.001 10)", wav.genWave()) def test_triangleWave(self): wav = triangleWave(1000, 1.0, 0.0, 10, 0.5) self.assertEqual("PULSE(-1.0 1.0 0 0.0005 0.0005 0.0 0.001 10)", wav.genWave()) def main(): """main program. process options """ parser = argparse.ArgumentParser(description=versionSTR) parser.add_argument('--F', nargs=1, help='frequency (default=1k[Hz])') parser.add_argument('--A', nargs=1, help='amplitude (defalut=1[v])') parser.add_argument('--O', nargs=1, help='offset (defalut=0[v])') parser.add_argument('--D', nargs=1, help='duty (defalut=0.5)') parser.add_argument('--T', nargs=1, help='type (sine square triangle defalut=sine)') parser.add_argument('-V', dest='VERSION', help='Show Version, then exit', action='store_true', default=False) args = parser.parse_args() if args.VERSION: print( versionSTR ) sys.exit(0) freq = 1.0e3 if args.F is not None: freq = freqIN(args.F[0], freq) amp = 1.0 if args.A is not None: amp = vIN(args.A[0], amp) ofs = 0.0 if args.O is not None: ofs = vIN(args.O[0], ofs) dty = 0.5 if args.D is not None: dty = chkVal(args.D[0], dty) wav = sinWave(freq, amp, ofs, 10) if args.T is not None: if args.T[0].startswith("sq"): wav = squareWave(freq, amp, ofs, 10, dty) elif args.T[0].startswith("tr"): wav = triangleWave(freq, amp, ofs, 10, dty) print(wav.genWave()) sys.exit(0) if __name__ == "__main__": main()