Pythonでオシロスコープ波形のタイミング解析(その3)


[前回](https://qiita.com/nachtele/items/9d09e0a1f5b081b30f30)は、TimingAnalyzer による解析について解説しました。今回は、解析結果の資料(一覧表、波形画像)出力について解説します。

# class TASettings

タイミング解析関連の設定を class TASettings にまとめています。

```python
class TASettings:
    def __init__(self, type, logicLevel, ths, chn):
        if type == 'SPI':
            self.tas = TASettings_SPI(ths, chn)
            self.disp = DispSettings_SPI(chn[0], chn[1], chn[2], chn[3])
            self.strs = StrSettings_SPI(chn[0], chn[1], chn[2], chn[3])
        elif type == 'I2C':
            self.tas = TASettings_I2C(ths, chn)
            self.disp = DispSettings_I2C(chn[0], chn[1])
            self.strs = StrSettings_I2C(chn[0], chn[1])
        self.hratios = [1, 2, 0.5]
        self.ylim = [[-logicLevel*0.3, logicLevel*1.52], [-logicLevel*0.2, logicLevel*1.2], [0, 1]]
        self.yticks = [[0, int(logicLevel)] for i in range(2)]
        self.lny = [[1.4, 0.7], [1.4, 0.4]]
        self.vly = 0.3
        self.vty = 0.0
        self.mainZoom = None
        self.figsize = (6.4, 4.8)
        self.dpi = 100
```

インスタンス変数の tas は前回解説した解析設定リストで、それ以外は全て結果出力関係の設定です。
disp は、タイミング解析結果出力設定リストです。出力すべき解析式を選択し、表示名を設定するとともに、波形カーソルの対象チャネルと、全件('all')/最小値('min')/最大値('max')出力を設定します。

```python
    [
         ['tSCL', 'tSCL: SCL period', [cSCL, cSCL], ['min', 'max']]
        ,['tR_SCL', 'tR_SCL: SCL rise time', [cSCL, cSCL], ['min', 'max']]
        …
        ,['tSU_STA', 'tSU_STA: Start condition setup time', [cSDA, cSCL], ['all']]
        ,['tHD_STA', 'tHD_STA: Start condition hold time', [cSCL, cSDA], ['all']]
        …
    ] 
```

規定値として上限が定められている項目は最大値を、下限が定められている項目は最小値を、最低限確認すべきですね。また、値の変動の様子を把握するために、最大値と最小値の両方を確認したり、全件を確認しておいたりするのも意味のあることでしょう。

# 波形画像生成

下図に示す形式の波形画像を生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/9fe33e32-55c7-4a38-40fb-8587ec7dcad3.png)

オシロスコープのズーム表示のように、上側のメインウィンドウに広い範囲の波形を表示し、下のズームウィンドウに着目する部分を拡大した波形を表示します。ズームウィンドウには測定値の元になる2つの時刻カーソルを表示し、下部にはそのカーソルの示す時刻値と、それらから求めた測定値を表示します。

メインウィンドウには、解析式の値を文字列に変換したイベント文字列を表示することもできます。この例では、スタートコンディション(S)、ストップコンディション(P)、データの0/1、ライト(W)/リード(R)、アクノリッジ(K)/ノット・アクノリッジ(N)を表示しています。これらは TASettings のインスタンス変数 strs で設定しています。
```python
def StrSettings_I2C(cSCL, cSDA):
    return [
        ['St', 1, 'S', cSDA, 3.5]
        ,['Sp', 1, 'P', cSDA, 3.5]
        ,['Read', 1, 'R', cSDA, 3.5]
        ,['Read', 0, 'W', cSDA, 3.5]
        ,['Ack', 1, 'K', cSDA, 3.5]
        ,['Ack', 0, 'N', cSDA, 3.5]
        ,['DataBit', 1, '1', cSDA, 3.5]
        ,['DataBit', 0, '0', cSDA, 3.5]
    ]
```

メインウィンドウ、ズームウィンドウ、測定値表示部の高さの比率は、インスタンス変数 hratios で設定しています。
メインウィンドウとズームウィンドウの波形の縦軸範囲はインスタンス変数 ylim で、縦軸目盛りは yticks で設定しています。

メインウィンドウの表示範囲は、波形全体とすることもできますし、前後の空白時間が長い場合は最初のイベントから最後のイベントが収まる範囲にしたり、さらにそれを複数範囲に分割して拡大することもできます。下図に、メインウィンドウを最初のイベントから最後のイベントが収まる範囲にした例と、さらに2分割にして拡大した例を示します。

![tHD_STA_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/60c90cc3-8749-48f1-fefa-55521d834387.png)

![tHD_STA_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/e38a792f-95bf-16f3-581a-b1465c556e8e.png)

メインウィンドウの表示範囲は下記のように、インスタンス変数 mainZoom にリストをセットすることで設定できます。デフォルト(None)の場合は波形全体を表示します。

```python
#mainZoom算出
minmax = [1E9, -1E9]
for ds in tas.disp:
    hg = ta.hg[ds[0]]
    if len(hg):
        if minmax[0] > hg[0][1]: minmax[0] = hg[0][1]
        if minmax[1] < hg[-1][0]: minmax[1] = hg[-1][0]
mainSplit = int(args['mainSplit'][0]) if len(args['mainSplit']) else 1
tRange = (minmax[1] - minmax[0]) / mainSplit
tas.mainZoom = [[minmax[0] - tRange*0.05 + i*tRange, minmax[0] + tRange*1.05 + i*tRange] for i in range(mainSplit)]
dispList = plotTa(tim, dat, ta, tas, chs, zs, dstDir)
```

# xlsxファイル生成

解析結果の波形画像は何十枚もの枚数になり、手動で資料にまとめると大変な作業になります。
そこで、openpyxlを使用して、エクセルファイル(xlsx)を自動生成し、解析結果一覧と波形画像を記録します。xlsxファイルは Microsoft Excel はもちろんのこと LibreOffice Calc 等でも開けます。
また、ソースファイル名などの情報もシート内に付加することができます。

```python
# xlsx生成
info = [
    ['Data File', srcFile, '']
    ,['Time Step', timestep, '##0E+0']
    ,['Record Length', len(tdat), '']
]
taXlsx(ta, tas, dispList, info, dstDir)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/49963643-777b-6867-64f3-b7404484547a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/87375f11-67ac-bdf0-6d7f-9ec5d80e8334.png)