6
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

pyVISA でオシロスコープからデータを取得する

Last updated at Posted at 2020-04-16

はじめに

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 2DATA: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 はとても便利ですね!

6
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?