はじめに
PyVISA を使うことで、オシロスコープからデータを取得することができます。インストール方法は、PythonでVISA - Qiita を参照してください。また、私の場合 PyVISA を使う際にエラーが出てしまったので、【備忘録】pyvisa.errors.VisaIOError: VI_ERROR_INV_OBJECT (-1073807346): The given session or object reference is invalid.が出たとき - Qiita を参考にして NI-VISA をインストールしました。
[追記]
この記事は現在工事中です。
[工事ノート:
ascii でデータを取得するより、binary で取得する方がかなり早いです。
その際、オシロスコープのバイナリのエンコード指定については How is binary data represented on Tektronix oscilloscopes? | テクトロニクス などを参照してください。query_binary_values() 関数に渡す is_big_endian 引数の値が分かります。また、引数 datatype の値は、DATA:WIDTH に指定したバイト数で、エンコードで指定したデータ型を用いてください。型の指定に使う文字は、 struct — Interpret bytes as packed binary data — Python 3.8.6 documentation を参照してください。例えば、DATA:WIDTH 2
で DATA:ENC SRIBINARY
を指定した場合、2 byte の符号付き整数が、little endian で転送されてくるため、query_binary_values('CURV?', datatype='h', is_big_endian=False, container=np.array)
のように指定します。ここで、datatype='h'
が、2 byte の符号付き整数型(short) を意味しています。
pyvisa が頻繁にタイムアウトのエラーを返すときは、query_binary_values('CURV?', datatype='h', is_big_endian=False, container=np.array, delay=0.1)
のように、delay
引数を指定すると良いかもしれません。この例ではオシロスコープに query を write してから、0.1 秒待った後に read します。
オシロスコープへの詳細な設定は、各オシロスコープのプログラマーマニュアルを参照してください。例:オシロスコープ(MSO2000B, DPO2000B, MSO2000 and DPO2000Series Oscilloscopes) のプログラマーマニュアル プログラマーマニュアルには、オシロスコープからのデータをもとのデータに直す方法が書かれていると思います。また、pyvisa のリファレンスは User guide — PyVISA documentation です。
また、以下のコードは冗長になっているため、適宜書き直していただければ幸いです。
-- ノート終わり]
Python のコード
下記に、コードと説明のコメントを書きました。
import numpy as np
import visa
rm = visa.ResourceManager()
# この場所では、rm.list_resources() の中から
# 目的の機器を選択して rm.open_resource() に渡してください
inst = rm.open_resource(rm.list_resources()[0])
# timeout の設定
scope.timeout = 30000 # 30 秒
# Setting source as Channel 1
scope.write('DATA:SOU CH1')
scope.write('DATA:WIDTH 1')
scope.write('DATA:ENC ASCI')
# オシロスコープから、FULL にデータを取得する設定です
# 私の環境では、この場所をコメントアウトするとデータ数が削減されるようです
scope.write('DATA:RESOLUTION FULL')
# Getting axis info
# データを取得する処理は、
# How to save Data from Oscilloscope using Python in Linux - Tech For Curious
# <https://techforcurious.website/how-to-save-data-from-oscilloscope-using-python-in-linux/#more-348> や、
# How do I get a waveform using the Instrument Control Toolbox in Matlab? | Tektronix
# <https://techforcurious.website/how-to-save-data-from-oscilloscope-using-python-in-linux/#more-348>
# を参考にしました
ymult = float(scope.query('WFMPRE:YMULT?')) # y-axis least count
yzero = float(scope.query('WFMPRE:YZERO?')) # y-axis zero error
yoff = float(scope.query('WFMPRE:YOFF?')) # y-axis offset
xincr = float(scope.query('WFMPRE:XINCR?')) # x-axis least count
xoff = float(scope.query('WFMP:PT_OFF?')) # x-axis offset
xzero = float(scope.query('WFMPRE:XZERO?')) # x-axis least count
# データを取得します。私の環境では、約 6.5 秒かかりました
ADC_wave = scope.query_ascii_values('CURV?', container=np.array)
# データ
volts = (ADC_wave - yoff) * ymult + yzero
time = np.arange(xzero, xincr * (len(Volts) - xoff) + xzero, xincr)
上のコードで、データをリアルタイムに取得・表示し、セーブできるようにしたものをここに載せます。
import numpy as np
import matplotlib.pyplot as plt
import visa
from matplotlib import _pylab_helpers
import tkinter
from tkinter import filedialog
import threading
import csv
# セーブするかどうかを伝えるフラグ
# この変数を変更する際は、スレッドをロックしてください
is_save_requested = False
def gui(lock):
global is_save_requested
def change_title():
global is_save_requested
lock.acquire()
is_save_requested = True
lock.release()
root = tkinter.Tk()
#ボタン
button1 = tkinter.Button(
master=root,
text="Save", #初期値
width=30, #幅
bg="lightblue", #色
command=change_title #クリックに実行する関数
)
button1.pack()
root.mainloop()
def get_data_from_inst(rm , scope):
scope.timeout = 30000
# Setting source as Channel 1
scope.write('DATA:SOU CH1')
scope.write('DATA:WIDTH 1')
scope.write('DATA:ENC ASCI')
scope.write('DATA:RESOLUTION FULL')
# Getting axis info
ymult = float(scope.query('WFMPRE:YMULT?')) # y-axis least count
yzero = float(scope.query('WFMPRE:YZERO?')) # y-axis zero error
yoff = float(scope.query('WFMPRE:YOFF?')) # y-axis offset
xincr = float(scope.query('WFMPRE:XINCR?')) # x-axis least count
xoff = float(scope.query('WFMP:PT_OFF?')) # x-axis offset
xzero = float(scope.query('WFMPRE:XZERO?')) # x-axis least count
ADC_wave = scope.query_ascii_values('CURV?', container=np.array)
Volts = (ADC_wave - yoff) * ymult + yzero
Time = np.arange(xzero, xincr * (len(Volts) - xoff) + xzero, xincr)
return (Time, Volts)
def main():
rm = visa.ResourceManager()
inst = rm.open_resource(rm.list_resources()[0])
# Save をトリガーする GUI のセットアップ
global is_save_requested
lock = threading.Lock()
t1 = threading.Thread(target=gui, args=(lock,))
t1.start()
data = get_data_from_inst(rm, inst)
x, y = data
x = x * 1e6 # sec to micro sec
y = y * 1e3 # V to mV
fig, ax = plt.subplots(1, 1)
ax.set_xlabel('time(μs)')
ax.set_ylabel('Intensity (mV)')
lines, = ax.plot(x, y)
while True:
manager = _pylab_helpers.Gcf.get_active()
if manager is None: break
# plot データの更新
data = get_data_from_inst(rm, inst)
x, y = data
x = x * 1e6 # sec to micro sec
y = y * 1e3 # V to mV
# data update
lines.set_data(x, y)
plt.pause(.01)
# Save 処理
lock.acquire()
if is_save_requested:
file = filedialog.asksaveasfilename(initialfile='', title = "保存場所を選択",filetypes = [("csv ファイル", ".csv")])
if file:
with open(file, mode='w',encoding="utf-8") as f:
writer = csv.writer(f, lineterminator='\n')
writer.writerows(np.array(data).T)
is_save_requested = False
lock.release()
if __name__ == "__main__":
main()
おわりに
pyVISA はとても便利ですね!