OWON VDS3104 をpythonで制御 (その4)

[前回](https://qiita.com/nachtele/items/ad2cdbe3cfa6f6e9f774)までで、OWON の USBオシロスコープ VDS3104(LAN無しモデル) をpythonで制御することができるようになりました。私にとって、VDS3104の一番の不満点は波形のズーム機能がまともに使えないことです。そこで、VDS3104から保存したバイナリーデータを解析して、matplotlibで表示するようにしてみました。

#データ解析
何パターンかのデータをバイナリーエディターで眺めてみて、データフォーマットが分かってきましたので、下記のデータ解析クラスを作りました。コード中、d0～d8、およびstruct.unpackのパデング(x)にした部分のデータが今のところ謎なのですが、とりあえず、表示に必要な項目はわかりました。

```vdsDat.py
import struct

TBScales = [float(str(a)+'e'+str(b)) for b in range(-9,3) for a in [1,2,5]][1:34]
VScales = [float(str(a)+'e'+str(b)) for b in range(-3,1) for a in [1,2,5]][1:12]

class vdsDat:
    sizHed = 32 #ヘッダーのサイズ
    sizChI = 67 #チャネル情報(1ch分)のサイズ
    sizTbI = 27 #タイムベース情報のサイズ
    class chInfo:
        def __init__(self, src):
            self.dat = src
            name, self.sizInfo, self.inv, d0, self.samplesPerScreen, d1, d2, d3, d4, \
             self.sizDat, d6, self.vOffset, self.scaleCode, self.probeRateCode, d7, d8, self.ofsDat \
             = struct.unpack('>3s16i', src)
            self.name = name.decode()
            self.probeRate = 10**self.probeRateCode
            self.scale = VScales[self.scaleCode] * self.probeRate
            self.VPP = self.scale / 25

    def __init__(self, src):
        self.header = src[0:self.sizHed]
        model, self.sizFile, self.sizDat = struct.unpack('>10s13x1i1x1i', self.header)
        self.model = model.decode

        pt = self.sizHed + self.sizDat  #データポインター
        self.tbInfo = src[pt:pt+self.sizTbI]
        self.tbScaleCode, self.tbOffset, self.sizChIs = struct.unpack('>17x1b1i3x1H', self.tbInfo)
        self.tbScale = TBScales[self.tbScaleCode]
        self.numCh = self.sizChIs // self.sizChI
        pt += self.sizTbI
        self.chI = [self.chInfo(src[p:p+self.sizChI]) for p in range(pt, pt+self.sizChIs, self.sizChI)]

        self.sizChD = self.sizDat // self.numCh
        pt = self.sizHed
        f = str(self.sizChD) + 'b'
        self.chDat = [struct.unpack(f, src[p:p+self.sizChD]) for p in range(pt, pt + self.sizDat, self.sizChD)]
```

#matplotlibで表示

```vds_datPlot.py
import sys
import matplotlib.pyplot as plt
import numpy as np
from vdsDat import *

pltColor = {
    'CH1' : 'red',
    'CH2' : 'orange',
    'CH3' : 'blue',
    'CH4' : 'purple'
}

if __name__ == '__main__':
    srcFile = sys.argv[1]
    with open(srcFile, 'rb') as f:
        srcDat = f.read()
        dat0 = vdsDat(srcDat)
        unitTime = 20 * dat0.tbScale / dat0.chI[0].samplesPerScreen
        xmin = unitTime * (dat0.chI[0].samplesPerScreen * dat0.tbOffset / 1000  - dat0.chI[0].sizDat / 2)
        xmax = unitTime * dat0.chI[0].sizDat + xmin
        xax = np.arange(xmin, xmax, unitTime)

        fig, ax = plt.subplots(2, 1, sharex = True)
        for chi, chd in zip(dat0.chI, dat0.chDat):
            name = chi.name
            ax[0].plot(xax, chd, label=name, color=pltColor[name])
            dat = (np.array(chd) - chi.vOffset) * chi.VPP
            ax[1].plot(xax, dat, label=name, color=pltColor[name])
        ax[0].legend(loc='upper right')
        ax[0].set_xlim(xmin, xmax)
        ax[0].set_ylim(-128, 127)
        plt.show()
```

この例では、上にオシロの画面に近いイメージ、下に電圧に換算した値をプロットしてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/80724b13-a07f-cf39-e198-6e47bb164b34.png)

これだけでも、matplotlibの機能を使って気になるところをズームして見られるので便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/9be5fb77-d58b-2569-4f18-ada3834eba8b.png)