IWATSU製オシロスコープから得たcsvデータを自動でグラフ化したい！

<h1>自動化したい！</h1>
私は研究を行う上で、頻繁にIWATSU製のオシロスコープのお世話になっております。<br>
オシロスコープから電圧やらの波形を得て、それをcsvデータとしてUSBに出力することが度々あるのですが、オシロから直接出力されるcsvデータを再びグラフ化するには少し面倒な手段を踏む必要があります。<br>
と言いますのも、IWATSUのオシロから得られるcsvデータは以下のような形式にまとめられています。

![csv.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955221/555c44a2-a93b-b50a-2c8b-6f03f73cf1a1.png)

1~19行目までに計測条件、20行目にチャンネル、21行目以降から電圧値が表示されます。
もちろんこの計測条件の表示は重要な要素なのですが、やはりグラフ化や信号処理の足かせになります。また、電圧値の表示はありますが時間軸のデータはありません。
csvデータが2,3個しかないなら手動でグラフ化しても良いのですが、私の研究ではデータが100個ほど生じます。全部手動でやったら気が狂っちゃいます。<br>

そこでPythonでコーディングを行い、以下のようなグラフを自動生成します。
![signal1_modified.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955221/4b1c199e-4218-a96a-9390-15c17f489659.jpeg)

<h1>動作環境</h1>
OS : Windows10
Python : 3.0系
開発環境 : Anaconda Spyder 4.1.5
オシロスコープ : IWATSU DIGITAL OSCILLOSCOPE DS-5412

<h1>自動化プログラム</h1>

```Python:oscilloGraph.py

import tkinter
import tkinter.filedialog
import glob
import os
import csv
import numpy as np
import matplotlib.pyplot as plt

DeltaRow = 6                #時間幅が格納された行
DataRow = 21                #電圧値が格納された最初の行
global delta                #サンプリング周期

#----------GUIからcsvを選び、選ばれたデータのみを戻す----------#
def file_open():
    #---ファイルオープン---#
    root = tkinter.Tk()
    typ   = [('csvファイル', '*.csv')]
    DirIn = r'C:/Users'
    fni = tkinter.filedialog.askopenfilenames(filetypes = typ, initialdir = DirIn)
    root.destroy()
     
    #選択したcsvデータのパスを格納
    print(fni[0])
    return fni

#----------GUIからcsvを選び、ディレクトリ内の全csvデータを戻す----------#
def files_open():
    #---ファイルオープン---#
    root = tkinter.Tk()
    typ   = [('csvファイル', '*.csv')]
    DirIn = r'C:/Users'
    fni = tkinter.filedialog.askopenfilenames(filetypes = typ, initialdir = DirIn)
    root.destroy()
    #ディレクトリ内の全てのcsvを選択
    for i in range(len(fni)):
        fno  = fni[i][0:len(fni[i])-4]              #fniのタプルからパスのみを抽出
        filename= os.path.basename(fno)
        csv_path = fno.replace(filename,'')  
        csv_files = glob.glob(csv_path + "*.csv")    #ディレクトリ内の全てのcsvを格納
    
    #戻り値:選択されたファイルのパス(type of tuple)
    return csv_files
    
#----------csv_file内の数値をdata,timeリストに格納する----------#
def data_storing(csv_file_path):
    count = 0
    global delta    #グローバル関数宣言
    data = []   #Ch1の電圧
    time = []   #時間を格納
    with open(csv_file_path,'r') as file:
        for row in csv.reader(file):
            if(count == DeltaRow - 1):  #サンプリング周期の格納
                delta = float(row[1])
                count += 1
            elif(count >= DataRow - 1):
                data.append(float(row[0])) #Ch1をdata1に格納
                count += 1
            else:
                count += 1
    #時間の格納
    for time_count in range(len(data)):
        time.append(time_count * delta)
    #戻り値 : 電圧値、時間軸が格納されたリスト
    return data,time

#----------グラフ出力----------#
def plot_graph(data,time,csv_file):
    #data,timeをnp.array化
    data = np.array(data)
    time = np.array(time)    
    
    #パラメータの設定
    fig = plt.figure(figsize=(15,10))           #Figure設定
    fig.align_labels()
    ax = fig.add_subplot(111)                   #Axes設定
    ax.set_xlabel("Time[s]",fontsize=35)        #xlabel
    ax.set_ylabel("Voltage[V]",fontsize=35)     #ylabel
    
    #---全体のパラメータ設定---#
    plt.rcParams["font.family"] = "Times New Roman"
    plt.rcParams["xtick.direction"] = "in"
    plt.rcParams["ytick.direction"] = "in"
    plt.rcParams["axes.linewidth"] = 1.0
    
    #---上、右にも目盛を置く---#
    ax.tick_params(top = True)
    ax.tick_params(right = True)
    ax.tick_params(labelsize = 25) 

    #---目盛範囲制限---#
    plt.xlim(0,5)
    plt.ylim(-0.05,0.06)
    plt.grid()

    #---軸設定---#
    plt.tick_params(width = 2,length = 15)  #目盛りサイズ
    plt.xticks(np.arange(0,5.1,0.5),position=(0.0,-0.015))   #時間軸設定
    plt.yticks(position=(-0.015,0,0))
    plt.plot(time,data,color="red",linewidth=3)
    
    #画像の保存
    graphs_path = csv_file.replace(os.path.basename(csv_file),"") + "/Graphs"
    if(not(os.path.exists(graphs_path))):
        os.mkdir(graphs_path)
    plt.savefig(graphs_path + "/" + os.path.basename(csv_file)[:-4] + "_modified.jpg")    
    plt.show()
    plt.close()
    
#----------取得したデータをcsv保存----------#
def save_csv(data,time,csv_path):
    #保存先のパス
    save_path = csv_path.replace(os.path.basename(csv_path),"") + "/modified_data"
    if(not(os.path.exists(save_path))):
        os.mkdir(save_path)
    
    output = []
    
    for j in range(len(data)):
        if(j == 0):
            output.append("Time[s]" + "," + "Voltage[V]" )
        else:
            output.append(str(time[j-1]) + ',' + str(data[j-1]))
    #csv保存先
    save_path = save_path + "/" + os.path.basename(csv_path)[:-4] + "_modified.csv"
    print("save_path : " + save_path)

    #保存
    with open(save_path, 'w',encoding="UTF-8") as fo:
        writer = csv.writer(fo, delimiter='\n')  
        writer.writerow(output)
   

if __name__ == "__main__":
    csv_files = files_open()   #複数のcsv_fileを処理
    #csv_files = file_open()     #単一のcsv_fileを処理

    #csv_files内のデータを1つづつ関数に送り処理
    for csv_file in csv_files:
        csv_data = data_storing(csv_file)   #csv_data[0] : 電圧 ,　csv_data[1] : 時間
        
        data = csv_data[0]
        time = csv_data[1]
                
        plot_graph(data,time,csv_file)
        save_csv(data,time,csv_file) 
```

<h1>プログラム実行後</h1>
正常に処理が終了するとディレクトリ内部にGraphsディレクトリとmodified_dataディレクトリが自動生成されます。Graphsには生成されたグラフ画像がjpg形式で格納され、modified_dataには時間軸とそれに対応する電圧値が出力されたcsvファイルが格納されます。

処理後のディレクトリ
![ディレクトリ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955221/b82cfe57-81fb-3bda-4171-d62d3b6a32ca.png)
出力されるmodified.csvデータ
![modified.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955221/4fff34f2-d771-8976-638c-77dc8c9cf0ca.png)

<h1>コードの解説</h1>
プログラムを実行すると、tkinterからGUIが起動するので目的のcsvデータを選択します。
main関数内の

```.py
    csv_files = files_open()   #複数のcsv_fileを処理
    #csv_files = file_open()     #単一のcsv_fileを処理
```
をコメントアウトで選択することで処理するcsvの数を制御できます。
file_open()を選択すると選んだcsvデータのみを処理し、files_open()を選択すると選んだcsvデータの存在するディレクトリ内部の全てのcsvを一気に処理します。

file_open(),files_open()内の変数DirInに代入するパスを書き換えると、プログラム実行時にそのパスのディレクトリが開きます。

```.py
    DirIn = r'C:/Users'
```

data_storing()の引数として、対象となるcsvデータの絶対パスを送ることでリスト型変数にcsvの値を格納します。IWATSU製オシロから出力されるcsvデータには、セルB6にサンプリング周期が格納されているので、まずこれを変数deltaに格納します(DeltaRowに行数の6が代入されています)。
その後、リスト型変数dataに電圧値を追加していきます。

```.py
    with open(csv_file_path,'r') as file:
        for row in csv.reader(file):
            if(count == DeltaRow - 1):  #サンプリング周期の格納
                delta = float(row[1])
                count += 1
            elif(count >= DataRow - 1):
                data.append(float(row[0])) #Ch1をdata1に格納
                count += 1
            else:
                count += 1
```
次に時間軸の値を設定する必要があります。n番目の電圧値に対応する時間は、

<h6>(サンプリング周期 delta) * n</h6>

で計算ができますので、データ番号とdeltaを掛けてリスト型変数timeに追加していきます。

```.py
    #時間の格納
    for time_count in range(len(data)):
        time.append(time_count * delta)
```

これで電圧軸と時間軸の準備は完了です。これらをplot_graph()に送ることでグラフを自動生成し、ディレクトリに保存します。
グラフの表示範囲を変更したい場合は、以下の数値を書き換えます。

```.py
    #---目盛範囲制限---#
    plt.xlim(0,5)
    plt.ylim(-0.05,0.06)
    plt.grid()

    #---軸設定---#
    plt.tick_params(width = 2,length = 15)  #目盛りサイズ
    plt.xticks(np.arange(0,5.1,0.5),position=(0.0,-0.015))   #時間軸設定
    plt.yticks(position=(-0.015,0,0))
    plt.plot(time,data,color="red",linewidth=3)
```
最後にsave_csv()で電圧値と時間軸のみが格納されたcsvデータを保存します。これで信号処理などが簡単になると思います。

<h1>最後に</h1>
IWATSU製のオシロスコープでのみテストを行っています。他のオシロから得たcsvでも作動するのかは不明です。他社のオシロを持っている方がいらっしゃいましたら、csvの出力形式をご教授いただければ有難いです。

本プログラムは自分の卒業研究に用いるために作ったものです。非常に限定された用途ではありますが、お役に立てれば幸いです。