著者:斉藤 博文
プログラミング言語「AWK」は、データストリーム(データの流れ)を逐次処理するのに適しています。本連載では、電子回路の分野でその特徴を生かし、シェルスクリプトを組み合わせてデジタル信号を処理します。最終回は前回の続きとして、心電図データの解析について解説します。
シェルスクリプトマガジン Vol.84は以下のリンク先でご購入できます。

図5 ecg.awk内のlpf()関数
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | # low pass filter function lpf(arr_x, arr_y, idx_x, idx_y, ord, len_x, len_y,     _ret, _gain) {     _ret = 0.0;     _gain = 1.0 / (ord * ord);     _ret += 2.0 * get_buffer(arr_y, idx_y - 1, len_y);     _ret -= 1.0 * get_buffer(arr_y, idx_y - 2, len_y);     _ret += 1.0 * _gain * arr_x[idx_x];     _ret -= 2.0 * _gain * get_buffer(arr_x, idx_x - ord, len_x);     _ret += 1.0 * _gain * get_buffer(arr_x, idx_x - 2 * ord, len_x);     return _ret; } | 
図6 ecg.awk内の最終結果を出力するprintf()文で群遅延を補正
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |     # print results     printf("%f,%f,%f,%f,%f,%f,%f,%f\n",             get_buffer( \                     arr_data_raw,                     idx_data - (val_gd_lpf + val_gd_hpf + val_gd_dif + val_gd_sma) - 2 * val_fs,                     len_data),             get_buffer( \                     arr_data_lpf,                     idx_data - (val_gd_hpf + val_gd_dif + val_gd_sma) - 2 * val_fs,                     len_data),             get_buffer( \                     arr_data_hpf,                     idx_data - (val_gd_dif + val_gd_sma) - 2 * val_fs,                     len_data),             get_buffer( \                     arr_data_dif,                     idx_data - val_gd_sma - 2 * val_fs,                     len_data),             get_buffer( \                     arr_data_squ,                     idx_data - val_gd_sma - 2 * val_fs,                     len_data),             get_buffer( \                     arr_data_sma,                     idx_data - 2 * val_fs,                     len_data),             cnt_rri_curr * 0.005 * 1000,             get_buffer( \                     arr_data_flg,                     idx_data - (val_gd_dif + val_gd_sma) - 2 * val_fs,                     len_data)); } | 
図12 ecg.awk内のしきい値(val_data_sma_threshold)を設定している箇所
| 1 2 3 4 5 6 7 8 9 | # update threshold every interval if (num_data % tap_interval == 0) {     val_data_sma_threshold = val_data_sma_max * val_peak_rate;     val_data_sma_max = 0; } else {     if (val_data_sma_max < arr_data_sma[idx_data]) {         val_data_sma_max = arr_data_sma[idx_data];     } } | 
図13 ecg.awk内のval_data_flgを「1」に設定している箇所
| 1 2 3 4 5 | if (val_data_sma_curr > val_data_sma_threshold && val_data_sma_threshold >= val_data_sma_last) {     val_data_hpf_max = 0;     idx_data_hpf_max = 0;     val_data_flg = 1; } | 
図14 ecg.awk内のidx_data_hpf_maxを求めている箇所
| 1 2 3 4 5 6 7 8 9 10 11 12 | # search maximum index if (val_data_flg == 1) {     val_data_hpf_curr = get_buffer(arr_data_hpf, idx_data - (val_gd_dif + val_gd_sma), len_data);     idx_data_hpf_curr = idx_data - (val_gd_dif + val_gd_sma);     idx_data_hpf_curr = idx_data_hpf_curr >= 0 ? idx_data_hpf_curr : idx_data_hpf_curr + len_data;     if (val_data_hpf_max < val_data_hpf_curr) {         val_data_hpf_max = val_data_hpf_curr;         idx_data_hpf_max = idx_data_hpf_curr;     } } | 
図15 ecg.awk内のarr_data_flg[idx_data_hpf_max]を「1」に設定している箇所
| 1 2 3 4 | # end point to detect main peak if (val_data_sma_curr < val_data_sma_threshold && val_data_sma_threshold <= val_data_sma_last) {     arr_data_flg[idx_data_hpf_max] = 1; | 
図18 ecg.awk内のRRIを求めている箇所
| 1 2 3 4 5 6 7 8 9 | # calculate peak to peak interval if (idx_data - idx_data_hpf_max >= 0) {     cnt_peak_curr = num_data - (idx_data - idx_data_hpf_max); } else {     cnt_peak_curr = num_data - (idx_data - idx_data_hpf_max + len_data); } cnt_rri_curr = cnt_peak_curr - cnt_peak_last; cnt_peak_last = cnt_peak_curr; |