前回、Scilab上で生成したFM変調信号、サンプリング周波数一定の信号をLTspiceに輸入したらばLTspice上では「フツー」に定周期ではない(変曲点あたりで時間キザミが細かくなる)信号としてシミュレーションされていることを観察。今回は逆。LTspiceの非定周期の信号をScilabに輸出するけどどうするの?
※「SPICEの小瓶」投稿順インデックスはこちら
LTspiceの波形のFile書き出し
観察するに、LTspiceの「ソルバ」は、グラフで言えば「曲がり角」のような微分係数が大きく変化する付近では時間キザミが小さく、一方「直線的」になっている部分(勾配=微分係数の絶対値はデカかったりもするが)では時間キザミが広くなるようです。知らんけど。
一方、LTspiceで観察している波形は、波形を表示しているペインで右クリックから以下のように File >> Export data as text でファイルに落とすことができます。
書き出されるテキスト形式は以下のような TSV 形式です。
-
- 先頭行(1行目)にはV(Vout)のような電圧、電流などの種別とノード名を列挙
- 2行目以降には、時刻とそのときの電圧あるいは電流値など記載
- 区切り文字はタブ文字
大抵の表計算ソフトがタブ区切りの上記のような形式もCSV(ただし区切り文字はカンマでなくタブと指定必要な場合あるかも)として読み込めるような気がします。
今回は過去回で試用した、以下のFM変調波生成回路の出力Voutをテキストファイル出力し、Scilabへ輸出してみました。
LTspice出力ファイルのScilabでの読み込み
上記で出力したファイルには FMtest.csv (デリミタはタブだけれども)と名をつけて、Scilabの実況環境の「カレント・フォルダ」にセーブしました。
Scilabでの読み込みは csvRead関数で一発です。ただし、デリミタ文字はタブであるので、2個目の引数として \t を与えてます。こんな感じ。
waveLTspice=csvRead('FMtest.csv','\t'); plot(waveLTspice(2:$,1),waveLTspice(2:$,2))
なお、csvRead関数は1行目のヘッダを無視するようなオプションも持っていた筈なのですが、コマケー引数多数で分けわからんので、上記のようにファイル名とデリミタ文字指定のみで読み込んでおり、以降データアクセスする際に1ではなく2から取り出すようにして1行目のNaNが含まれないようにしてます。手抜きだよ。
Scilab自体は、時間間隔一定の信号でなくても扱うことはできるので、読み込んだデータ(1列目に時刻、2列目にVoutノードの電圧)をそのまま plot することが可能です。
上記のようにLTspice上と「多分変わらぬ」波形が見えとります。
念のため、上記データの時間キザミを以下のように確認してみました。
上記のように、時間キザミは一定ではないです。ここはLTspiceのままね。
Scilab上で「デジタル信号処理」するのに定周期化したいが
信号処理素人の老人は、こういう可変周期の「サンプリング」信号をどうやって処理するのか常識がありません。ただただ一定のサンプリング周波数Fsにてサンプリングされた信号が欲しいと希望するバカりです。そこで考えたのが、
-
- LTspiceから持ってきた信号は「アナログ信号」だと思いこむ
- そのアナログ信号をFs一定でサンプリングする
という方法です。さらに「アナログ信号」のサンプリング方法については以下2つがあり得るかと。
-
- ある時刻t0のデータv0は、次の時刻t1で新たなデータv1が到来するまで「ホールド」されておる(階段状の波形)としてFs側の時間キザミでサンプリングする。
- ある時刻t0のデータv0と次の時刻t1のデータv1は「折れ線グラフ」的に直線で連続しているとしてFs側の時間キザミでサンプリングする。
SPICE処理の実体からすると後者の方が近いような気がするけれども、前者の方はFsによるサンプリング時刻より未来の値を使っていないので、より現物的(ADコンバータでのサンプリング)な挙動?にも思える。まあ両方やってみれば良いかということで、今回は前者の処理をやってみることにしました。後者はまた次回ね。
「サンプリング」関数のお手製コードが以下に。第1引数にサンプリング周波数Fs、第2引数にLTspiceから読み込んだデータ(可変時間キザミ)を渡すと、定周期のサンプリング時刻のベクトル t と、定周期のサンプリング結果 y を返す関数です。
// Sampling LTWave // Fs: sampling frequency [Hz] // AVec: vector to be sampled // t: vector sampling time // y: vector sampling data function [t,y]=samplingLTwave(Fs, Avec) Tmin=min(Avec(2:$,1)); Tmax=max(Avec(2:$,1)); t=[Tmin:1/Fs:Tmax]; y=[]; widx=2; for idx = 1:length(t) while t(idx) > Avec(widx,1) widx = widx + 1; end y(idx) = Avec(widx,2); end endfunction
試用結果
上記の関数の適用は簡単。これでTとYに定周期のベクトルが得られます。なお、Fs=500000としてあります。
[T, Y]=samplingLTwave(Fs, waveLTspice);
GetしたTとYでプロットしてみます。
plot(T, Y)
過去回でも使用しているお手製のplotFFT2関数でFFTかけてみます。
plotFFT2(Fs, Y', 0)
FM変調波らしいピーク付近は良いのだけれども、周波数が高いところがガタガタいっているのが気にくわんといえばウソになります。やっぱりカクカクしているせい?
ローパスかけずに「リサンプリング」しているのがマズイのかも。ということでローパスフィルタをかけてみました。ひとたびFs一定の信号になってしまえばローパスフィルタの適用などお惚け老人にも簡単(といって計算するのはScilabですが。)ここではキャリア周波数Fc=20000と分かっているので、それを使って「不要な」高い周波数のところをIIRフィルタで一気に滅しまする。
[hz]=iir(7, 'lp', 'butt', [Fc*1.5/Fs, 0.5], [0.1, 0.1]); [YLPF, zf]=filter(hz.num, hz.den, Y);
上記のLPFによりGetした波形をプロットするのがこちら。
plot(T, YLPF)
そしてFFTもとってみます。
plotFFT2(Fs, YLPF', 0)
50kHz、サンプリング周波数の10分の1、までのFFTグラフが以下に。
それっぽいじゃないの?いいのかこういう処理で?でも、もう一つと比べてみないと。