C/C++によるイベント計測

Cosmo-Zには、トリガが発生したaaaaaaa場合のみ記録するというイベント計測機能があります。

このページではイベント計測を行うプログラムの作成方法を説明します。

最初に、イベントキャプチャのプログラムの例を示し、個々の部分を解説していきます。

#include "cosmoz.h"
#include <unistd.h>

int main () {
    csz_open(INTERFACE_ZYNQ,NULL);
//  csz_adc_freq(80);
//  usleep(100000); // ADC周波数を変えた場合は30ms以上のWaitが必要
    const int DATASIZE=2048;
    CAPTURE_INFO info;

    csz_capture_abort(); // 既に何かのキャプチャが実行されていたら強制終了

    for(int ch = 0 ; ch < 8 ; ch++) { // 不要なチャネルはトリガオフ
        csz_trig_set_voltage(ch + 1,TRIGTYPE_OFF, 0);
    }
    csz_trig_set_voltage(1,TRIGTYPE_FALL, -0.05); // CH1を-0.1Vを閾値としてFALL条件

    // トリガ前20ポイント、トータル140ポイントをキャプチャする
    csz_pretrig_set(20);
    csz_posttrig_set(140);
    csz_posttrig_fixed(true); // 固定長トリガ

    // イベントキャプチャの実行
    // CH1をキャプチャし、10イベント発生したら終了する
    info = csz_evcap_execute(0x01, 10, CAPTRIG_EVENT, NULL, NULL);
    if(info.result != CAPRESULT_SUCCESS)
    {
        printf("Capture error %d\n",info.result);
        csz_close();
        return 0;
    }

    // 何個のイベントが発生したかを調べて、格納する配列を確保する
    printf("Total Event count = %d\n",info.iteration);
    uint16_t **data = new uint16_t*[info.iteration];
    int *evlen = new int[info.iteration];
    for(int i=0;i<info.iteration;i++) {
        data[i] = new uint16_t[DATASIZE];
    }

    uint64_t starttime48 = info.start_time48;// 開始時刻
    int maxlen = 0; // 一連の計測の中で一番長いもの

    EVHEADER_STR hdr;
    // 個々のイベント情報を取り出す
    for(int i=0;i<info.iteration;i++) {
        csz_evcap_get_next(&info, &hdr, data[i]); // イベントのヘッダと波形を取得
        evlen[i] = hdr.length; 
        if(hdr.length > maxlen) maxlen = hdr.length;
        printf("event %d on CH%d ",hdr.evcount, hdr.ch); // ヘッダ情報の表示
        printf("%.3f[ms] ",(hdr.time48 - starttime48) * 10 / 1000000.);
        printf("Len:%d Height:%d ",hdr.length, hdr.plsh);
        printf("\n");
    }

    // イベントキャプチャ波形の表示

    // タイトル行
    printf("Time[ns]\t");
    for(int i=0;i<info.iteration;i++) {
        printf("Event%d\t",i);
    }
    printf("\n");

    // 縦横を並べ替えて表示
    for(int j=0;j<maxlen;j++) {
        printf("%.1f\t",1000.0 / info.sampling_rate * j);
        for(int i=0;i<info.iteration;i++) {
            if(evlen[i] >= j) printf("%d\t",data[i][j]);
            else printf("\t");
        }
        printf("\n");
    }

    // 配列の解放
    delete[] evlen;
    for(int i=0;i<info.iteration;i++) {
        delete[] data[i];
    }
    delete[] data;
    
    csz_close();
}


 

以前のキャプチャの強制終了

csz_capture_abort関数は、現在実行中の波形キャプチャを強制終了します。

csz_capture_abort(); // 既に何かのキャプチャが実行されていたら強制終了

 

計測長の設定

下記の例では、csz_posttrig_set関数により長さ140の計測を行う設定を行います。固定長トリガに設定しているのでトリガがかかった時点を基準に後ろに140の長さの計測を行います。長さ140というのは、140×1/サンプリング周波数 秒になります。

csz_pretrig_set(20);
csz_posttrig_set(140);
csz_posttrig_fixed(true); // 固定長トリガ

イベントキャプチャの実行

イベントキャプチャはcsz_evcap_execute関数で行います。

この関数のプロトタイプは

CAPTURE_INFO csz_evcap_execute(
    uint32_t chmask,
    int count,
    CAPTURE_TRIGGER type,
    CALLBACK_EVENTCAP func,
    void *param);

です。

第一引数はチャネルの指定でビットフィールドになっています。例えば、CH1~CH8を記録する場合は0xffを指定します。CH1とCH3とCH5を記録する場合は0x15を指定します。

第二引数は時間またはイベントの個数です。

第三引数にCAPTRIG_MESUNITが指定された場合は秒数で、CAPTRIT_EVENTが指定された場合はイベント数が終了条件となります。

 

次の例は、100個のイベントを、CH1に関してキャプチャします。

info = csz_evcap_execute(0x01, 100, CAPTRIG_EVENT, NULL, NULL);

次の例は、100秒間のイベントを、CH1に関してキャプチャします。

info = csz_evcap_execute(0x01, 100, CAPTRIG_MESUNIT, NULL, NULL);

 

第四、第五引数はイベントが入るとコールバックされる関数の登録とその引数です。例えば、イベントが発生した場合にTCP/IPで通信を行ったり、時々刻々と記録していくような場合に使用します。

イベントキャプチャの戻り値

csz_evcap_execute関数はイベントキャプチャが完了するかエラーで終了するまで制御を返しません。戻り値はCAPTURE_INFO型の構造体で、様々な情報がセットされて戻ります。

この中で重要なのはresultとiterationです。resultは、キャプチャが成功したかエラーで終了したかを示します。iterationは、キャプチャしたイベントの個数を示します。

変数名 機能
CAPTURE_RESULT result キャプチャ実行結果
CAPRESULT_SUCCESS キャプチャは成功した
CAPRESULT_ABORTED ユーザからの指示で中断された
CAPRESULT_ERROR データの転送が間に合わない
CAPRESULT_ANOTHERRUN  他のキャプチャが動作中
CAPRESULT_TRIGGER_TIMEOUT トリガ待ち状態
CAPRESULT_CORRUPTED 取得したデータが壊れている
uint32_t chmask キャプチャしたチャネル
int chcount キャプチャしたチャネル数
int length キャプチャ長
int iteration 繰り返し回数
int maxseq 最大シーケンス長
CAPTURE_TRIGGER trig キャプチャトリガの種類
CAPTRIG_AUTO   オシロのAUTOと同じ
CAPTRIG_NORMAL  オシロのNORMALと同じ
CAPTRIG_MESUNIT イベントモードで秒数で指定
CAPTRIG_EVENT イベントモードでイベント数で指定
CAPTRIG_LAST  最後に表示した波形を再取得
time_t start_time 開始時刻
time_t end_time 終了時刻
double sampling_rate サンプリングレート
int adc_freq 実行開始時のADC周波数
int adc_div 実行開始時のデシメーション比
int resolution 分解能(bit単位)
uint32_t gps_time 実行開始時のGPS時刻
uint32_t gps_10ns 実行開始時のGPS時刻 10ns単位
uint32_t gps_maxcount 実行開始時のGPS最大カウント
uint64_t start_time48 実行開始時の48bitタイムスタンプ
uint32_t top_addr 格納される物理アドレスの先頭
uint32_t bot_addr 格納される物理アドレスの最後尾
uint32_t last_addr 格納された物理アドレスのlast
int mesp 現在読み出し中のデータのポインタ
int evcount 現在読み出し中のデータの数
uint32_t prev_capaddr イベントキャプチャのデータをここまで読んだポインタ
uint32_t start_addr イベントキャプチャのデータが入っている先頭アドレス

RESULTは実行結果で、CAPRESULT_SUCCESSを返した場合はキャプチャは成功です。

この構造体を参照することで、キャプチャ時のADC分解能やサンプリングレート、開始時刻、データ長などを知ることができます。

ヘッダの値の詳しい解説はファイルフォーマットのページご覧ください。

イベントデータの取り出し

csz_evcap_execureが終了した時点では波形メモリの中(基板上のDDR3 SDRAM内のOS管理外領域)に波形が入っていて、ユーザのバッファにはコピーされていません。

波形メモリからデータを取り出してユーザバッファにコピーする関数が、

BOOL csz_evcap_get_next(CAPTURE_INFO *info,EVHEADER_STR *hdr,uint16_t *data);

です。

この関数を呼び出す前に、EVHEADER_STR型のインスタンスと、データを受け取るためのバッファを作っておく必要があります。csz_evcap_get_nextを実行すると、hdrに波形の情報が、dataに個々のイベントキャプチャされた波形が入ります。

info.iterationに示された回数だけcsz_evcap_get_nextを実行を繰り返し実行すると、すべての波形を取得することができます。

 

第一引数のinfoには、csz_evcap_executeの戻り値を入れます。

第二引数のhdrは、EVHEADER_STR型のインスタンスへのポインタを入れます。EVHEADER_STRは波形のヘッダ情報が入る構造体ですが、

EVHEADER_STR hdr;

でインスタンスを作り、初期化しないまま&を付けてアドレスを与えれば十分です。構造体の各要素はcsz_evcap_get_next関数内でセットされます。

hdrの各要素には次のような情報が格納されます。

変数名 機能
int evcount このイベントが何番目のイベントか
uint16_t hdr ヘッダ情報
uint16_t ch 発生したイベントのチャネル番号
uint16_t length 波形の長さ
uint64_t time48 発生した時刻。FPGAの起動時から10ns単位で表される
uint16_t plsh パルスハイト(イベント波形の最大値)
uint32_t sum パルス面積(トリガ期間中の値の総和)
uint16_t ped ペデスタルレベル(無信号時の値)
uint16_t meslen トリガ長
int mesp システム内部で使用

ヘッダの値の詳しい解説はファイルフォーマットのページご覧ください。

 

第三引数のdataは、dataは波形が入るバッファのアドレスを入れます。イベントキャプチャされた波形の長さはプレトリガとポストトリガの長さの和なので、最大でも2048あれば足ります。new uint16_t[2048]とすれば十分なバッファが作れます。

この部分のサンプルプログラムを以下に示します。

    // 何個のイベントが発生したかを調べて、格納する配列を確保する
    printf("Total Event count = %d\n",info.iteration);
    uint16_t **data = new uint16_t*[info.iteration];
    int *evlen = new int[info.iteration];
    for(int i=0;i<info.iteration;i++) {
        data[i] = new uint16_t[DATASIZE];
    }
    uint64_t starttime48 = info.start_time48;// 開始時刻
    int maxlen = 0; // 一連の計測の中で一番長いもの
    EVHEADER_STR hdr;
    // 個々のイベント情報を取り出す
    for(int i=0;i<info.iteration;i++) {
        csz_evcap_get_next(&info, &hdr, data[i]); // イベントのヘッダと波形を取得
        evlen[i] = hdr.length; 
        if(hdr.length > maxlen) maxlen = hdr.length;
        printf("event %d on CH%d ",hdr.evcount, hdr.ch); // ヘッダ情報の表示
        printf("%.3f[ms] ",(hdr.time48 - starttime48) * 10 / 1000000.);
        printf("Len:%d Height:%d ",hdr.length, hdr.plsh);
        printf("\n");
    }

 

上記プログラムの実行結果

本ページの最初に掲載されたプログラムを動作させた結果を示します。このプログラムはCH1に発生した10個のイベントをキャプチャして、それを縦横を並べ替えて表示します。

これをテキストファイルに保存すれば、Excelなどで波形を重ねて表示できます。

 

イベントプログラムでは具体的に何をすればよいか?

取り出したイベントの波形データの処理方法は計測の目的によって異なります。

時間ごとのカウントの変化だけを見ればよい場合は、取得された波形や時刻情報は捨ててしまってもよいでしょう。例えば、PET-CT後に人体から放出される放射線の数を測りたい場合などが該当します。

時刻と波高値を記録したい場合には、time48とplshを記録していけばよいでしょう。

天文現象などで正確な時刻が必要な場合はGPS関係の値も保存する必要があるかもしれません。

逆に、レーザーを使ったポンプ光プローブ光の測定の場合はdataも保存する必要があるかもしれません。