PythonでNI-DAQの利用2

NI-DAQのPythonプログラミングの覚えとして記載します。

NI-DAQのPCへのインストールはこちらを参照してください。
参考1：[PythonでNI-DAQの利用 #Python - Qiita](https://qiita.com/Gyutan/items/1d0caa638c01a52c289d)

Pythonで凝ったDAQの使い方をするときは、基本的にはLabVIEWのプログラムと同じようにコードを書く必要があります。(LabVIEWでもDAQをちょっと凝った使い方をしようとするといろいろと設定が必要になります。)

PythonでDAQのプログラミングには、こちらのドキュメントが参考になります。
参考2：[NI-DAQmxにおける10種類の関数の習得および多くのデータ集録アプリケーションに対処できる処理について](https://www.ni.com/ja/support/documentation/supplemental/06/learn-10-functions-in-ni-daqmx-and-handle-80-percent-of-your-dat.html)
参考3：[テキストベースのプログラミング環境でNI-DAQmxを使用する](https://www.ni.com/ja/support/documentation/supplemental/21/using-ni-daqmx-in-text-based-programming-environments.html)



例えば、参考2のドキュメントの中には、「外部サンプルクロックによる連続アナログ出力生成を構成する方法」があります。

> 「NI-DAQmxタイミング」VIのサンプルクロックインスタンスを使用して、外部サンプルクロックによる連続アナログ出力生成を構成する方法を示しています。
>
> サンプルクロックのソース、サンプルクロックのレート、収集または生成するサンプル数を設定するには、「Channel.Timinging.ConfigureSampleClock()」関数を使用します。以下のコードスニペットは、デバイスのPFI 0に接続された外部サンプルクロックに基づく連続収集を作成します。

![3_Configure_Task_Timing.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165987/f3ace63c-c074-144f-f6eb-51bb302691a9.png)

※LabVIEWでのアイコンは関数を表していて、配線で関数に引数を与えています。

例えば、一番左の`AO Voltage`は関数で、引数に`Minimum Value`、`Maximum Value`、 `Pysical Channels`があります。返り値はアイコンの右側になります。この場合、`Pysical Channels`、 `Error`です。
次の`Sample Clock`関数では、`SamplingRate`と`TrimingSource`、`ContinupousSampling`の引数があります。

右側のグレーの枠で囲まれているのがWhileループで繰り返しが行われます。



### （1）Sin波を連続でアナログ出力する

参考4：[On The Fly Waveform Configuration with DAQmx - NI Community](https://forums.ni.com/t5/Example-Code/On-The-Fly-Waveform-Configuration-with-DAQmx/ta-p/3491040?profile.language=ja)
参考5：[GitHub - ni/nidaqmx-python: A Python API for interacting with NI-DAQmx](https://github.com/ni/nidaqmx-python)
参考４に記載されているLabVIEWプログラムを参考にプログラムを作成します。
![Front Panel.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165987/c6170419-10f5-813a-7912-f785b502afda.png)

![bd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165987/6ed103e6-2a46-5363-4ffa-fc99887d8d67.png)


用いているDAQデバイスは、NI-USBの6001です。作成するプログラムは、100Hzで振幅0.5のSin波を生成し、連続出力します。LabVIEWには波形生成関数（上の図でWhileループ内の関数）が部品として与えられていますが、Pythonではそのようなプログラムはないので、こちらも作成します。出力波形はオシロスコープで確かめてください。

```Python
import numpy as np
import nidaqmx as ni
from nidaqmx.constants import Edge, AcquisitionType

# Sin波形生成関数
def t_unit_wave(freq=100, n_sampling=128, n_period=1):
    tx_ = np.linspace(0, n_period, freq*n_sampling)
    return tx_

def t_unit_sin(freq=100, amp=1, phi=0, dc=0, n_sampling=128, n_period=1):
    PI2=2*np.pi
    tx = t_unit_wave(freq=freq, n_sampling=n_sampling, n_period=n_period)
    wav = amp*np.sin(freq*PI2*tx+phi) + dc
    return tx, wav

# DAQの設定
DAQ_IO_ao = 'DAQ6001/ao0'
sampling_rate = 5000


freqency = 100
amplitude = 0.5

n_sampling = int(sampling_rate/freqency)

with ni.Task() as task:
    task.ao_channels.add_ao_voltage_chan(DAQ_IO_ao )  
    task.timing.cfg_samp_clk_timing(
                                    rate = sampling_rate,  
                                    sample_mode = AcquisitionType.CONTINUOUS
                                    )
    task.out_stream.regen_mode = ni.constants.RegenerationMode.DONT_ALLOW_REGENERATION # 波形の再生成を禁止
    
    try:
        while True:
            _, wav = t_unit_sin(freq=freqency, amp=amplitude, phi=0, dc=0, n_sampling=n_sampling, n_period=1) 
            task.write(wav, auto_start=True) # 波形出力    

    except KeyboardInterrupt: # Ctrl + c
        task.write(wav*0)
   
```



### （2）連続でデータを取り込む

連続でデータを取り込むときのプログラム。グラフをリアルタイムで更新するために`plt.pause()`を使っています。ループから抜けるには、`Ctrl + c`を入力してください。

```python
"""
データを連続収集してグラフ表示
"""

import time
import numpy as np
import matplotlib.pyplot as plt
import nidaqmx as ni
from nidaqmx.constants import AcquisitionType, TerminalConfiguration


# Configure NI DAQmx settings
DAQ_IO_ai = 'DAQ6001/ai0'

sampling_rate = 5000
samples = 500

fig, ax = plt.subplots()

with ni.Task() as task:
    task.ai_channels.add_ai_voltage_chan(DAQ_IO_ai, 
                                            min_val=-10, 
                                            max_val=10,
                                            terminal_config=TerminalConfiguration.DIFF) # 収集モード：差動
                                            # terminal_config=TerminalConfiguration.RSE) # 収集モード：RES
    task.timing.cfg_samp_clk_timing(rate=sampling_rate, 
                                       sample_mode=AcquisitionType.CONTINUOUS)
                                        # sample_mode=AcquisitionType.FINITE # バッファーのメモリが足りないときはこちらの方が良い時がある

    
    try:
        while True:
            new_data = task.read(samples)  

            ax.cla()
            ax.plot(new_data,'ro-')

            ax.grid()
            plt.pause(0.01)
        
    except KeyboardInterrupt: # Ctrl + C
        task.stop()
        plt.ioff()  # Turn off interactive mode
        print('Stop')
    
```



### （3）一定電圧を出力する

これは、私の実験で必要だったのでおまけです。（サンプルプログラムと同じ）。

AOチャンネルから5Vを出力、5秒後に0Vにして終了するプログラム。

```Python
import numpy as np
import matplotlib.pyplot as plt
import nidaqmx as ni
from nidaqmx.constants import AcquisitionType, TerminalConfiguration
import time


# 自身のデバイスに合わせて変更する
DAQ_IO_ao = 'DAQ6001/ao1'

with ni.Task() as task:
    set_bias = 5
    task.ao_channels.add_ao_voltage_chan(DAQ_IO_ao) 
    task.write(set_bias)
    
    time.sleep(5)
    task.write(0)

```

どなたかの参考になれば。