データ収集システム UTTAC- CCDAQ
マニュアル
v0.5 2006 / 7 / 2 8
hiro [email protected] u k u b a.ac.jp
目次
1概要---2
1.1 ハードウェア構成...2
1.2 ソフトウェア構成...2
2基礎知識---3
2.1NIM信号...3
2.2CAMAC...3
2.3LAM...3
3起動と停止---4
3.1 起動...4
3.2 停止...4
3.3 データ救出...4
4使用例(その1)---5
5プログラム詳細---7
5.1 デバイス・ドライバー...7
5.2GUI...8
5.3 本体...10
5.4 ユーザーコード...11
5.5 ラン番号とファイル名...15
6トリガー---16
6.1 トリガーモジュールを使用...16
6.2 モジュール自身...16
6.3 ヒットパターンの使用...16
7性能---17
エラーメッセージ---18
10.1NIM ADCをsingles モードで使用する..24
10.2 割り込みレジスターをトリガーとする...26
10.3 生データをファイルに記録する...27
10.4 ヒストグラムをファイルに保存する...29
10.5 ゲートをかけたヒストグラムを作成する....30
10.6 インプットレジスターでデータを振り分ける ...31
10.7 経過時間でランを止める...32
11 ユーザーコードで使用可能な関数(その2)- -33 12 使用例(その3)---35
12.1 デッドタイムを計算する...35
12.2 定期的にデータを読み出す...37
12.3 カレント・インテグレータでストップさせる.38 12.4 割り込みレジスタでラン・ストップさせる. .39 12.5 イベントを監視し、リセットする...40
12.6NIM ADCでコインシデンス・データを取る ...41
13 ユーザーコードで使用可能な関数(その3)-43 14 使用例(その4)---44
14.1 データを割算する...44
14.2 別プロセスでコマンドを実行する...45
15 Drms の使い方---46
15.1 起動と終了...46
1 概要
このシステムは簡潔で柔軟です。オンライン(マシンタイム中でのデータ収集)だけでなくオフライン(実験 準備や装置のテスト)でも使用できます。必要なのはCAMACクレートとPCだけです。使用するCAMACモ ジュールはユーザの好みで選択できます。その代わり、モジュール構成に合わせて、ユーザがコード(サブ ルーチン)を書く必要があります。
このシステムはユーザーコードが無ければデータを収集することができません。ただ、雛形がいくつも用 意してあるので、それらを組み合わせたり、一部を修正したりして使用することになります。
1.1 ハードウェア構成
クレートコントローラに東陽テクニカ社の CC/7700 あるいはW-Ie-Ne-R社のCC32を使用しますの で、CAMACクレート1台とPC1台だけあれば使用できます。特定のCAMACモジュールに依存していませ んので、手持ちのモジュールを使用できます。
NIM ADCが必要なら、筑波大ではセイコー社のADCと専用のCAMACインターフェイスをペアで使用し ます。
1.2 ソフトウェア構成
プログラムはデバイス・ドライバー、本体、GUI、ユーザーコードの4つに分かれています。
GUI
ユーザーコード
本体
ドライバー
ヒストグラム領域
(共有メモリー)
CAMAC
2 基礎知識
2.1 NIM 信号
CAMACモジュールのロジック入力(GATE、CLEAR、)、レジスター やカウンターの入力のほとんどは、高速NIM信号です。NIM回路 の出力コネクタではNEG OUTと書かれており、 800mVの鋭い 信号です。
CAMACモジュールの説明書にはNIM信号とだけしか書かれてい ないことが多いですが、LEMOコネクタは大抵、高速NIM信号で す。また、ADCの入力信号も0~+10Vではなく、0~+2V程度がほ とんどです。
中には、TTL(0~+5V)を入力するモジュールもありますので、説明 書を良く読んでから配線して下さい。
2.2 CAMAC
CAMACクレート(筐体)には各スロットの下に左から1、2、3、と番号が書かれています。これをステーショ ン・ナンバーと呼び、Nで表します。つまり、左からN1、N2、N3となります。
多チャンネルのADCなど、1つのモジュールに複数の回路が内蔵 されている場合は、それらを区別するためにサブアドレスを使用し、
Aで表します。AはA0からA15まであります。A0がA(0)と記述 されることもあります。
CAMACモジュールの機能を制御するための制御コードをファンク ションと呼び、Fで表します。FはF0からF31までありますが、どの コードが使用できるかはモジュール毎に違います。F0がF(0)と 記述されることもあります。
これらN、A、FでCAMAC命令が決定されます。例えば、モジュール
がN3にあって、データを読み出す命令がF2・A0であれば、N3・A0・F2で命令が一意に決まります。
2.3 LAM
LAM(Look At Me)はCAMACモジュールからホスト(PC)への割り込み信号です。大抵はモジュール からデータを読み出す準備が整ったことを示すために使用されます。そして、ほとんどのモジュールは LAMを出すことを許可したり無効にしたりする機能が付いています。LAMは1から24までありますが、
CC/7700ではN1はLAM1、N2はLAM2に対応しています。
3 起動と停止
3.1 起動
システムの起動は、daqloadコマンドで行います。
% daqload USER-CODE
ここで、USER-CODEはユーザーコードのファイル名です。例えば、
% daqload test1
なら、test1.cとtest1.camacという2つのファイルが使用されます。test1.cは通常のC言語の ファイルで、主にデータ処理を記述するために使用します。test1.camacはCAMAC命令だけを記述す るファイルで、CAMACモジュール固有の操作(初期化など)や、データを読み出す命令などを記述するた めに使用します。
つまり、ユーザーコードは.cと.camacという拡張子の2つのファイルに分けて記述します。C言語のファ イルは必須ですが、.camacのファイルは無くても構いません。
ユーザーコードにエラーが無ければ、GUIが起動され、RUNボタンでデータ収集を開始させることができ るようになります。
3.2 停止
GUIを閉じると、本体も終了します。
3.3 データ救出
もしも、システムが異常終了してしまったら、直ちにdaqrescueを実行して下さい。メモリー中に残って いるヒストグラムをファイルに保存できます。しかし、システムを再起動してしまったり、PCを再起動した後 では救出できません。
% daqrescue
saved hist1 to hist1.sav (SHINE) 保存したファイルの拡張子は全て .sav になります。
4 使用例(その1)
簡単な使用例として、CAMAC ADCを1台使用する場合のユーザーコードを示します。ファイルは
test1.cとtest1.camacです。データ収集のトリガーはADCのLAMで、2チャンネル分のデータを読 んでヒストグラムにしています。
test1.cのプログラムでは、先ず、initでヒストグラムを作成し、イベント処理でデータを+1していま す。
/* test1.c */
#include “daqlib.h”
int ch0,ch1;
int init() /* 初期化を行う */
{
ch0 = hist_new1(“ch0”,2048); /* ヒストグラム作成 */
ch1 = hist_new1(“ch1”,2048);
return OK;
}
int event (int data[],int n) /* イベント処理 */
{
int d0,d1;
d0 = data[0] & 2047; /* LAM1:のf0,a0 で読んだデータを取り出す */
d1 = data[1] & 2047; /* LAM1:のf0,a1 で読んだデータを取り出す */
hist_add1(ch0,d0); /* ヒストグラムに+1する */
hist_add1(ch1,d1);
return OK;
}
一方、test1.camacでは、CAMAC命令を記述しています。このファイルでは1行に1つのCAMAC命令を 書き、init:やrun:で命令を何時実行するかを指定します。
/* test1.camac */
#define ADC n1 /* ADCはN1にある */
init: /* 初期化 */
ADC,f11,a12 /* ADCリセット */
run: /* ラン処理 */
ADC,f26,a12 /* LAM許可 */
stop: /* ストップ処理 */
ADC,f24,a12 /* LAM禁止 */
LAM1: /* LAM処理 */
ADC,f0,a0 /* ch0データ読み出し */
f0,a1 /* ch1データ読み出し */
f11,a12 /* リセット,LAMクリア */
(使用例 続き)
% daqload test1
を実行すると、下の図のようなGUIが起動されます。後は、STOPボタンを押せば、データ収集が開始さ れます。
なお、STOPと表示されたボタンだけは 他のボタンと違って、現在の状態を表 示しています。
[STOP]を押して、ラン状態になると表 示は[RUN]になります。
[RUN]を押すと、ランは停止し、
[STOP]という表示になります。
ユーザーコードは次のような順序で実行されます。
● 先ず、起動時にはtest1.camacのinit:とtest1.cのinit()が実行されます。
● STOPボタンを押した時にはrun:とrun()が実行されてラン状態になり、再度押した時には stop:とstop()が実行されて停止状態になります。
● ラン状態時にADCに信号が入りLAMが発生すると、test1.camacのLAM1:が実行され、
ADCからデータが読み出されます。
● 読み出されたデータはtest1.cのevent()に渡されます。
5 プログラム詳細
5.1 デバイス・ドライバー
CAMACのLAMを高速に処理するために、デバイスドライバーが仲介します。LAMが出た時に実行するべ きCAMAC命令をドライバーに登録しておくことによって、LAM発生からデータ読み出しまでが自動的に行 われるようになります。
CAMACステーションとLAMは1対1に対応しているので、ステーション1(N1)に入れたモジュールから LAMが出ると、ドライバーのLAM1に登録されたコードが実行されます。通常このコードはモジュールから データを読み出しますが、読み出されたデータはドライバー内部のバッファに蓄積され、データ収集シス テムの本体プログラムが読み出します。
先の例のtest1.camacでは、LAM1:と書かれた行以下の3つのCAMAC命令がドライバーのLAM1用 に登録され、ADCからLAMが出る度に実行されます。最初の2つの命令はデータの読み出しなので、デー タはドライバー内部のバッファに蓄積されます。
最後の命令はADCをクリアし、LAMをクリアする命令ですが、命令の終了までには必ずLAMをクリア(あ るいは禁止に)する必要があります。
CAMAC
クレートクレートコントローラ
CAMAC
モジュール
データバッファ
割り込み ハンドラー
デバイス ドライバー
割り込み ユーザー
CAMAC
コードLAM
5.2 GUI
システム起動後に画面に表示されるこのGUIはユーザーコードと密接に関係しています。ボタンを押す と、ボタンに割り当てられたサブルーチンを呼び出します。
ラン・ストップ データ収集を開始・停止させるボタンです。ユーザコードからは
acq_run()とacq_stop()で同様に開始・停止できます。開始時には run()を、停止時にはstop()を呼び出します。
ラン番号 ラン番号はランの直前に1つ増やされます。番号はlastrunというファ イルに保存されますので、再起動しても引き継がれます。ユーザコードか らはget_runnumber() で得ることができ、set_runnumber()で 設定できます。
ヒストグラム保存 ユーザコードのsave()を呼び出しますが、save()が定義されていない 場合には、全てのヒストグラムをSHINE形式でファイルに保存します。
ヒストグラムクリア ユーザコードのclear()を呼び出しますが、clear()が定義されてい ない場合には、全てのヒストグラムをクリアします。
ラン・ストップ ラン番号 ヒストグラム保存 ヒストグラムクリア
収集時間
イベント数 ユーザ定義
ボタン
ログ・メッセージ 表示領域 ユーザ定義
エントリー
ヒストグラム 一覧
トグラムの名前と大きさが表示されます。
収集時間 ランの経過時間を秒単位で表示します。ユーザコードからは get_elapsed()とget_delta()で得ることができます。
イベント数 ユーザコードのevent()で処理したイベントの数を示します。ユーザ コードからはget_events()で得ることができます。
ユーザ定義ボタン 左からボタン1、ボタン2、ボタン3、ボタン4と呼び、それぞれがユーザコー ドのbutton1()、 button2()、 button3()、 button4()を呼び出 します。ユーザーコードからはset_button1()、 set_button2()、
set_button3()、set_button4()でラベルを変更できます。文字列 の漢字コードがEUCなら、日本語も表示できます。
ユーザ定義エントリー 左からエントリー1、エントリー2、エントリー3、エントリー4と呼び、それぞ れの値をユーザーコードからset_entry1()、set_entry2()、
setentry3()、set_entry4()で設定でき、現在の値は get_entry1()、get_entry2()、get_entry3()、
get_entry4()で得ることができます。
ログ・メッセージ表示領域 log_fmt()とlog_error()でここに表示されると同時にLOGという ファイルに保存されます。文字列の漢字コードがEUCなら、日本語も表示 できます。
GUIプログラム自身は画面の構成をpanel.gladeというXMLファイルから起動時に作成します。ま た、ボタンの色やフォント指定はpanel.rcというファイルから決定します。
5.3 本体
本体とGUIはそれぞれ独立したプロセスで、プロセス間通信によって情報を交換し合っています。
本体は起動されると、ヒストグラム用の共有メモリーを作成します。MSAやdrmsもこの共有メモリーにア クセスするので、オンラインモニターが可能となります。
続いて、ユーザーコードをコンパイルし、本体自身に組み込みます。CAMACコマンドファイルがあれば、解 析し、必要ならドライバー内部に登録します。
本体はラン番号を管理しています。起動時にはlastrunというファイルから前回の最終番号を取得し、
各ランの直前に番号を+1し、lastrunに書き出します。
ログも管理しています。ログはLOGというファイルに書き出すと同時にGUIへも送ります。
GUI
ユーザーコード
本体
ドライバー
ヒストグラム領域
(共有メモリー)
CAMAC
5.4 ユーザーコード
ユーザーコードは2つのファイルに分けて記述します。1つはC言語のファイルで、最低限データ処理(ヒス トグラミング)のコードは書く必要があります。もう1つはオプションですが、CAMACコマンドのファイルで、
ドライバーで実行されるコマンドを記述します。
ユーザーコードの雛型は /usr/local/daq/skel/にあります。
5.4.1 C 言語ファイル
C言語のファイルでは、ヘッダーファイルdaqlib.hをインクルードしなければなりません。
呼び出されるルーチンは以下のようになっており、この中から必要なルーチンだけを記述します。C言語で は名前の大文字小文字は区別されるので、initはInitやINITではいけません。
処理が正常に終了したら返り値としてOKを、でなければNGを返して下さい。
int init() 起動時に1度だけ呼ばれます。通常、ヒストグラムを作成するコードを記述
します。
int run() 収集の開始(RUNボタンやacq_run()の実行)時に呼ばれます。
int stop() 収集の停止(STOPボタンやacq_stop()の実行)時に呼ばれます。
int save() SAVEボタンやacq_save()で呼ばれます。定義していない場合には、
全てのヒストグラムはSHINE形式でファイルに保存されます。
int clear() CLEARやacq_clear()ボタンで呼ばれます。定義していない場合に は、全てのヒストグラムがクリアされます。
int idle() デフォルトでは0.1秒に1回程度繰り返し呼ばれますが、
set_idle_time()で設定できます。
int event(int data[],int n)
CAMACコマンドのLAMn:で収集されたデータがあれば、1イベント分の データが渡されます。データの並び順はLAMn:での記述順になります。
引数のnはデータの数です(バイト数ではない)。
int button1 () ボタン1が押される度に呼ばれます。
int button2 () ボタン2が押される度に呼ばれます。
int button3 () ボタン3が押される度に呼ばれます。
int button4 () ボタン4が押される度に呼ばれます。
int LAM () 何れかのLAMが立ったら呼ばれます。
5.4.2 CAMAC コマンドファイル
ファイルにはC言語形式のコメントと#ifdefや#defineなどを書くことができます。ファイルの拡張子 は.camacでなければなりません。
CAMACコマンドはC、N、A、FとD(データ)の組み合わせで指定し、1行に1つの命令だけを記述します。
CAMACクレートが1台だけの場合にはCは省略できます。このCNAFDは大文字でも小文字でも構いま せん。
命令が何時実行されるかを指定するために、セクション名で命令を括って指定します。命令は最大で1セ クションあたり60行指定できます。
セクションには以下のようなものがあります。名前の大文字小文字は区別されるので、initはInitや INITではいけません。
init: 起動時に1度だけ呼ばれます。通常、CAMACモジュールを初期化するコ
マンドを記述します。
run: 収集の開始(RUNボタンやacq_run()の実行)時に呼ばれます。通常、
モジュールのLAMを有効にするような命令を記述します。
stop: 収集の停止(STOPボタンやacq_stop()の実行)時に呼ばれます。通
常、モジュールのLAMを無効にするような命令を記述します。
save: SAVEボタンやacq_save()で呼ばれます。
clear: CLEARボタンやacq_clear()で呼ばれます。
button1:~button4: ボタン1~4が押される度に呼ばれます。
LAMn: ドライバー内部に登録し、CAMACのLAMで実行されるコマンドを記述し
ます。ここでnはLAMの番号1から24です。LAMnセクションでは必ず LAMをクリアするような命令を記述しなければなりません。読み出した データはevent()に渡されます。
例えば、左のコードの場合、ステーション1(N1)のモジュールから出た LAM(つまりLAM1)でPCに割り込み、N1,F2,A0という命令が実行さ れます。
reset: ラン中に一定時間イベントが無い時に呼ばれます。
監視時間はset_reset_time()で設定し、初期値は2秒です。
セクション名:
CAMAC命令 [CAMAC命令]
LAM1:
N1,F2,A0
5.4.3 LAMn:と LAMn()
割り込み処理はCAMACコマンドファイルでのLAMn:とC言語ファイルでのLAMn()のどちらでも記述で きますが、以下のような違いがあります。
LAMn: LAMn( )
応答速度 5 ~ 7 µS 20 ~ 2 3 µS
実行場所 ドライバー内部 本体内部
使用可能命令 CAMAC命令のみ 全ての命令
データの処理 読み出したデータは自動的に event()に渡される
データを読み出したら、自分で処理を する
1つのLAMに対して両方を定義することも可能ですが、LAMn:が先に実行されるので、その時にLAMが クリアされたらLAMn()は実行されません。しかし、LAM番号を指定しないLAM()を定義していたら、必 ず実行されます。
5.4.4 イベント
LAMnセクションの1回の処理で読み出されたデータが1イベントデータとして、ユーザーコードの
event()に渡されます。1回のLAMで1つのデータしか読み出さないのであれば、1イベントは1データと なりますし、8つのデータを読み出せば、1イベントは8データとなります。
複数のLAMnセクションが記述できますので、それぞれのLAMで異なる数のデータを読み出すことがで きます。それぞれのデータが識別できるようにデータの最上位8ビットは以下のようにドライバーが設定し ます。
000 LAMn イベントの最初のデータ(24 ビット)
000 LAMn データ(24 ビット)
100 LAMn イベントの最後のデータ(24 ビット)
1イベントが1データの場合は、各々がイベントの最後のデータという形式になります。
注意:LAMnセクション以外でCAMACのREAD命令を発行してもデータは捨てられ、event()には入っ てきません。
5.4.5 ユーザーコードの呼び出し順序
CAMACコードとC言語のルーチンは以下のようにペアで呼び出されます。
プログラムが起動された時 1. init:
2. init()
[STOP]ボタンが押されたとき(RUNになる)
1. run:
2. run()
[RUN]ボタンが押されたとき(STOPになる)
1. stop:
2. stop()
[SAVE] ボタンが押されたとき 1. save:
2. save()
[CLEAR] ボタンが押されたとき 1. clear:
2. clear()
[BUTTON1] ボタンが押されたとき 1. button1:
2. button1() 一定時間イベントが無い時
1. reset:
2. reset() LAM1が立った時
1. LAM1:
2. LAM1() (ただし、LAM1:でLAMをクリアしたら呼ばれない)
5.5 ラン番号とファイル名
ラン番号は0から9999で、ランの直前に+1されます。
番号は何時でも変更できますが、次のランの時には設定した数の次の数になります。なお、9999の次は 0に戻ります。
ラン番号上部の表示はラン中は「Cur.Run#」となり、停止中は「Last Run#」
となります。
ラン番号はデータを保存する時のファイル名の一部として使用されます。
[SAVE]ボタンでヒストグラムを保存しようとすると、デフォルトではname****.datというパターンで
**** にラン番号を入れた名前でファイルを作成します。例えば、ヒストグラムの名前がad1でラン番号 が52ならば、ad10052.datというファイルになります。
しかし、同じ名前のファイルが既に存在していたなら、次のような確認ダイアログが表示され、[OK]を押さ ない限りファイルに上書きはしません。
この場合、元のファイルを消したくないならば、一度キャンセルし、ラン番号を変更してから再度[SAVE]
すればOKです。
6 トリガー
CAMACからデータを読み出すきっかけとなるトリガーは、ユーザ自身で決定します。
6.1 トリガーモジュールを使用
割り込みレジスターなどのLAMを出すことができるモジュールがあれば、トリガーとして使用できます。
ADCでも構いません。トリガーとなるLAMでデータを読み出すことになりますので、LAMが出た時には目 的のモジュールからデータが読み出し可能になっていることが必要です。
例えば、N20にトリガーモジュールがあるとして、そのLAMでN1、N2、N3からデータを読み出すユーザー コードのCAMACファイルは次のようになります(実際はこれ以外に初期化のコードも必要です)。
LAM20:
N20,F9,A0 /* トリガーモジュールのLAMをクリア */
N1,F2,A0 /* データ読み出しとクリア */
N2,F2,A0 /* データ読み出しとクリア */
N3,F0,A0 /* データ読み出し */
N3,F0,A1 /* データ読み出し */
N3,F9 /* クリア */
6.2 モジュール自身
1つのトリガーで全体を収集するのではなく、モジュール毎のトリガーで個別にデータを読み出すこともで きます(singlesモードと呼ばれます)。
例えば、N1、N2、N3それぞれにデータを読み出すユーザーコードのCAMACファイルは次のようになりま す。
LAM1: /* N1のLAM */
N1,F2,A0 /* N1読み出しとクリア */
LAM2: /* N2のLAM */
N2,F2,A0 /* N2読み出しとクリア */
LAM3: /* N3のLAM */
N3,F0,A0 /* N3読み出し */
N3,F0,A1
N3,F9 /* N3クリア */
6.3 ヒットパターンの使用
ヒットしたデータだけ読み出すには、先ずインプットレジスターでパターンを読み出し、その内容に応じた モジュールからデータを読み出すという処理が必要になるのですが、CAMACファイルによるコードで記述 する場合にはパターンと全てのデータを読み出し、event()でデータを選別することになります。
読み出しの無駄を減らすなら、Conlam()で本体側に割り込むように設定し、Camac()でCAMACにアク セスする方法もあります。
7 性能
ユーザーコードでのCAMAC命令の実行時間はおよそ次のとおりです(PCによって多少は変動しま す)。
CC/7 7 0 0 CC32
Read /Write 命令 6 µS 2 µS
その他の命令 4 µS 2 µS
LAMが出てから、ユーザーコードの最初の命令が実行されるまでの時間はおよそ次のとおりです。
ドライバーに登録したLAMn 5 ~ 7 µS 本体側に割り込む場合 20 ~ 2 3 µS
なお、ドライバーに登録したLAMnとは.camac ファイルに定義したLAMn:セクションのことで、本体側 に割り込む場合とは、C言語ファイルのLAMn()ルーチンがLAMでコールされる場合です(このマニュア ルの後半に使用例があります)。
8 エラーメッセージ
● kernel: cc77 intr: LAM is still active 0xNNNN このメッセージは/var/log/messagesに記録されます。
これはドライバーで割り込みを処理した後で、再度割り込みレジスタを調べたら、依然として割り込みが出 ていた場合に記録されます。行末の0xNNNNが割り込みレジスターの値で、LAMのビットマップです。つ まり、0x1ならLAM1がONで、0x6ならLAM2とLAM3がONということです。
こうなると、ドライバーは処理を続行できないので、CAMACの割り込みを禁止にして割り込み処理を終了 します(割り込みを禁止にしないと無限ループに陥る)。
このシステムはLAMで駆動されるので、LAM処理ができなくなると、データ収集も停止してしまいますの で、データ収集の本体は定期的(0.1秒毎)にドライバーの状態をチェックしており、上記の状態になって いたら、CAMACの割り込みを許可に戻します。そして、ランを停止した時には、この処理をラン中に何回 行ったかを表示しログに記録します。
stacked LAM count: NN
このエラーが発生すると、LAM表示のLEDが長い時間点灯したり、ADCのデッドタイムが一時100%に なったりします。
このエラーの原因と対策は以下の通りです。
● 予定外のCAMACモジュールからLAMが出た
モジュールのLAM表示を見ればどのモジュールがLAMを出しているかが判りますので、LAM処 理のコードを追加するか、LAMが不要なら初期化でLAMを禁止にします。
● モジュールが次のLAMを直ぐに出した
ADCがノイズで動作したり、トリガーが誤動作していないか点検して下さい。
● 他のジョブがCPUを消費している
topコマンドを使って、CPUを消費しているプロセスがいないか点検して下さい。
9 ユーザーコードで使用可能な関数(その1)
以下の関数を使用するには、次のinclude文が必要です。
#include "daqlib.h"
int log_fmt (char *str {,par...})
ログメッセージを書き出します。ログはGUIの画面に表示され、同時にLOGというファイルに保存 されます。
引数はprintfと同様の形式です。
漢字コードがEUCなら日本語も使用できます。
例:
log_fmt(“this is a test”);
log_fmt(“data = %d”, data);
int log_error (char *str {,par...})
メッセージを書き出し、ブザーを鳴らします。引数はlog_fmt()と同じです。
int hist_new1 (char *name,int x)
1次元ヒストグラムを作成(共有メモリーに領域を確保)します。
nameはヒストグラムの名前で、英数字で指定します。
xはヒストグラムのサイズで、512から8192です。
正常に作成できたならヒストグラムのidが返り、失敗ならNG(-1)が返ります。
例:
/* ヒストグラムid用変数は他にルーチンで参照されるので、init()の外で定義する */
int ch0;
int init() {
ch0 = hist_new1("ch0",2048);
}
int hist_new2 (char *name,int x,int y)
2次元ヒストグラムを作成(共有メモリーに領域を確保)します。
nameはヒストグラムの名前で、英数字で指定します。
xは横軸のサイズ、yは縦軸のサイズですが、今の所、どちらも同じサイズでなければなりません。
正常に作成できたならヒストグラムのidが返り、失敗ならNG(-1)が返ります。
例:
int EdE;
int hist_add1 (int id,int data) 1次元ヒストグラムのデータを+1します。
idはhist_new1の戻り値です。
dataは+1するデータです。
例:
int event(int data[],int n) {
hist_add1(ch0, data[0] & 2047);
}
int hist_add2 (int id,int xdata,int ydata) 2次元ヒストグラムのデータを+1します。
idはhist_new2の戻り値です。
xdata、ydataは+1するデータです。もしもデータのコンバージョンゲインとヒストグラムの大きさ が異なるならば、自分で調整しなければなりません。
例:
int event(int data[],int n) {
int dx,dy;
dx = (data[0] & 2047) / 4;
dy = (data[1] & 2047) / 4;
hist_add2(EdE, dx, dy);
}
int hist_save (int id,char *name,int style) ヒストグラム1つを指定した名前と形式でファイルに保存します。
idはhist_new1あるいはhist_new2の戻り値です。
nameはファイル名で、文字*はラン番号に置換されます。例えば、"test***,dat"でラン番号 が91ならば、作られるファイルはtest091.datとなります。
styleは保存形式で、以下のどれかです。
STYLE_SHINE 加速器部門独自のバイナリー形式 STYLE_LABO Labo社独自のバイナリー形式 STYLE_ASCII データだけのASCII形式
STYLE_ASCII_XY ”チャネル、データ”(カンマ区切り)のASCII形式 STYLE_ASCII_X_Y ”チャネル データ”(スペース区切り)のASCII形式 例:
int save() {
hist_save(ch0, "ch0****.txt", STYLE_ASCII);
}
int hist_save_all (int style)
全てのヒストグラムを同じ形式でファイルに保存します。
ファイル名はname****.datとなります。nameはヒストグラムの名前です。
int hist_clear (int id)
ヒストグラムのデータをゼロにします。idはhist_new1()、hist_new2()の戻り値です。
int hist_clear_all ()
全ヒストグラムのデータをゼロにします。
int set_button1 (char *label) int set_button2 (char *label) int set_button3 (char *label) int set_button4 (char *label)
ボタンのラベル(ボタン上の文字列)を変更します。漢字コードがEUCなら日本語も使用できます。
int get_entry1 () int get_entry2 () int get_entry3 () int get_entry4 ()
エントリーの現在の値を取得します。
int set_entry1 (int v) int set_entry2 (int v) int set_entry3 (int v) int set_entry4 (int v)
エントリーの表示値を設定します。
int get_runnumber () 現在のラン番号を取得します。
int set_runnumber (int n)
次のランの番号を設定します。ラン番号は0から9999の範囲です。ただし、ラン中には設定できま せん。
int get_elapsed ()
ランの経過時間を秒単位で取得します。
double get_delta ()
ランの経過時間を高精度に取得します。
event()の中では使用する必要はありません。
int acq_run ()
ラン処理を行います。ラン中でなければ、ユーザーコードのrun:とrun()を実行します。
int acq_stop ()
ストップ処理を行います。ストップ中でなければ、ユーザーコードのstop:とstop()を実行しま す。
int isrun ()
ラン中なら1を、でなければ0を返します。
int acq_save ()
セーブ処理を行います。ユーザーコードのsave:とsave()を実行します。
save()が定義されていなければhist_save_all(STYLE_SHINE)を実行します。
データがゼロなら保存はスキップされます。保存しようとするファイルが既に存在する場合は確認 ダイアログが表示され、[ OK ]ボタンを押さない限りは上書きはされません。
int acq_clear ()
クリア処理を行います。ユーザーコードのclear:とclear()を実行します。
clear()が定義されていなければhist_clear_all()を実行します。
int open_new (char *name)
ファイルを新規作成します。nameはファイル名で、*の文字はラン番号に置換されます。
書き出しはバッファリングされません。
例:
int fd;
fd = open_new(“EdE****.mar06”);
if(fd >= 0){
write(fd,buf,bufsiz);
}
close(fd);
FILE* fopen_new (char *name)
ファイルを新規作成します。nameはファイル名で、*の文字はラン番号に置換されます。
書き出しはバッファリングされます。
例:
FILE *fd;
fd = fopen_new(“EdE****.asc”);
if(fd != NULL){
fprintf(fd,”%f6.2,%d\n”,data,num);
}
fclose(fd);
void set_reset_time (double t)
ラン中にイベントの有無を監視する時間を設定します。単位は秒です。
設定した時間イベントが無ければ、ユーザーコードのreset:とreset()が実行されます。
デフォルトは2.0です。
void set_idle_time (double t)
idle()を呼び出す周期を設定します。単位は秒で最小は0.1です。
デフォルトは0.1です。
10 使用例(その2)
10.1 NIM ADC を singles モードで使用する
複数台のNIM ADCのデータをそれぞれのLAMをトリガーとして収集します。この例では2台のADCを使 用します。
まず、.camacファイルでは、LAM1で1台目のADCを、LAM2で2台目のADCを読み出します。
/* sadc-sing.camac */
#define AD1 n1 /* 1台目のADCの位置 */
#define AD2 n2 /* 2台目のADCの位置 */
#define READ f2,a0 /* データ読み出しとLAMクリア */
#define INH f24,a0 /* inhibit. disable conversion */
init: /* モジュールの初期化 */
AD1,INH AD2,INH AD1,READ AD2,READ
AD1,f26,a13 /* enable LAM */
AD2,f26,a13 /* enable LAM */
run:
AD1,f26,a0 /* enable ADC conversion */
AD2,f26,a0 stop:
AD1,INH AD2,INH AD1,READ AD2,READ
/* 割り込み処理コード */
LAM1: /* LAM1でAD1を読む */
AD1,READ
LAM2: /* LAM2でAD2を読む */
AD2,READ
次に .cファイルでは、event()で、どのLAMで読み出されたデータかを判断して、ヒストグラミングして います。
/* sadc-sing.c */
#include "daqlib.h"
static int ad1,ad2; /* ヒストグラムの id */
int init () /* 初期化ルーチン */
{
ad1 = hist_new1("ad1",4096); /* ヒストグラムを作成する */
ad2 = hist_new1("ad2",4096);
return OK;
}
int event (int data[],int n) /* イベント処理ルーチン */
int d;
d = data[0];
switch((d>>24)&31){ /* LAM番号を取り出す */
case 1: /* LAM1のデータ */
hist_add1(ad1,d & 4095);
break;
case 2: /* LAM2のデータ */
hist_add1(ad2,d & 4095);
break;
}
return OK;
}
event()に渡されるデータは、LAM1で読み出されたデータの場合は
100 00001 イベントのデータ(24 ビット)
そして、LAM2で読み出されたデータは
100 00010 イベントのデータ(24 ビット)
となります。
10.2 割り込みレジスターをトリガーとする
ADCの割り込みは使用せず、割り込みレジスターの割り込み(LAM20:)でADCのデータも読み出すよう にします。
/* intreg-acq.camac */
#define INTR n20 /* interrupt register */
#define LAMDS f24,a0 /* LAM disable */
#define LAMEN f26,a0 /* LAM enable */
#define CLRR f9,a0 /* clear register & LED */
#define AD1 n1 /* ADC */
#define READ f2,a0 /* データ読み出しとLAMクリア */
#define INH f24,a0 /* inhibit. disable conversion */
init:
AD1,INH AD1,READ
/*INTR,f17,a0,d511 /* mask all input */
INTR,LAMDS /* LAM disable */
run:
AD1,f26,a0 /* ADC enable */
INTR,CLRR /* clear register & LED */
INTR,LAMEN /* LAM enable */
/*INTR,f11,a0 /* clear mask */
stop:
/*INTR,f17,a0,d511 /* mask all input */
INTR,LAMDS /* LAM disable */
AD1,INH AD1,READ LAM20:
INTR,f0,a1 /* read register */
INTR,CLRR /* clear register & LED */
AD1,READ /* intreg-acq.c */
#include <unistd.h>
#include "daqlib.h"
static int ad1;
#define GAIN 4096 int init ()
{
ad1 = hist_new1("ad1",GAIN);
return OK;
}
int event (int data[],int n) /* イベント処理ルーチン */
{
/* data[0] にレジスターの値が入っている */
hist_add1(ad1, data[1]&0xffff); /* data[1]はADCのデータ */
return OK;
}
10.3 生データをファイルに記録する
生データ(リストデータ)を記録するには、event()でイベントデータをファイルに書けば良いのですが、ラ ン毎にファイルを更新するために、run()でopen_new()で新しいファイルを作成します。
/* rawdata.c */
#include "daqlib.h"
/* RAWFILE はファイル名の指定。'***' はラン番号で置換される */
#define RAWFILE "list***.out"
/* ファイルへの書き出しフラグ。1なら書く */
static int rawout = 1;
static int outfd;
int run () /* ラン毎にファイルを作成 */
{
if(rawout){
outfd = open_new(RAWFILE); /* 新しいァイルを作成する */
if(outfd < 0){
log_fmt("can't open raw file");
rawout = 0;
return NG;
} }
return OK;
}
int stop () /* STOPでファイルを閉じる */
{
if(rawout) close(outfd);
return OK;
}
データはevent()で書き出しますが、引数のnはデータの数なので、n * sizeof(int)がバイト数 になります。
/* イベント処理ルーチン */
int event (int data[],int n) {
if(rawout) /* 生データの書き出し */
write(outfd,data,n * sizeof(int));
/* その他のデータ処理 */
return OK;
}
set_button1("生データ"); /* ボタン1でON/OFF */
return OK;
}
int button1 () /* ボタン1が押されると call される */
{
if(!rawout){
log_fmt("raw output is ON");
rawout++;
}else{
log_fmt("raw output is OFF");
rawout = 0;
}
return OK;
}
データをCLEARした時もデータファイルをクリアするならば、clear()を定義します。
int clear () {
hist_clear_all(); /* ヒストグラムをクリア */
if(rawout){ /* 生データファイルもクリアする */
close(outfd);
outfd = open_new(RAWFILE);
} }
10.4 ヒストグラムをファイルに保存する
save()を定義することで、いろいろな形式で保存することができるようになります。
/* save.c */
int save () {
/* 全てのヒストグラムをファイルに保存する */
/* save() を定義しない場合のデフォルト */
hist_save_all(STYLE_SHINE);
/* ファイル名や形式を指定する場合は以下のようにする */
/* 名前の * はラン番号に置換される */
/* ch1 を SHINE 形式で保存 */
hist_save(ch1,"ch1****.dat",STYLE_SHINE);
/* ch1 を LABO 形式で保存 */
hist_save(ch1,"ch1****.labo",STYLE_LABO);
/* ch1 を ASCII 形式で保存 */
hist_save(ch1,"ch1****.asc",STYLE_ASCII);
hist_save(ch1,"ch1****.asc2",STYLE_ASCII_XY);
return OK;
}
10.5 ゲートをかけたヒストグラムを作成する
GUIのエントリー1と2を使って、ゲートの下限と上限を与え、ゲートデータがその範囲なら目的のデータを ヒストグラミングします。
この例では、イベントの1つ目がゲート用で、2つ目が目的のデータとしています。
/* gate.c */
#include "daqlib.h"
int gate;
int dest;
int init () {
gate = hist_new1("gate",2048); /* ゲートのデータ */
dest = hist_new1("dest",2048); /* 目的のヒストグラム */
set_entry1(0); /* ゲート下限値のデフォルト */
set_entry2(2047); /* ゲート上限値のデフォルト */
return OK;
}
int event (int data[],int n) {
int l,h,g,d;
l = get_entry1(); /* ゲート下限値を読み出す */
h = get_entry2(); /* ゲート上限値を読み出す */
g = (data[0]) & 2047; /* ゲートのデータ */
d = (data[1]) & 2047; /* 目的のデータ */
if(g >= l && g <= h) /* 範囲をチェックする */
hist_add1(dest,d);
hist_add1(gate,g);
return OK;
}
ゲートの設定をevent()で読み出していますので、エントリーの値を変更すると直ちに反映されます。
10.6 インプットレジスターでデータを振り分ける
ADCのLAMでインプットレジスターも同時に読み出し、その入力パターンに応じて、ヒストグラミングしま す。
camacファイルのLAMnでは次のように記述します。
/* sadc-c005.camac */
/* 割り込み処理コード */
LAM1:
AD1,READ /* read ADC */
REG,f2,a1 /* read register */
プログラムの方ではイベントの2つ目のデータのビットでヒストグラムを振り分けます。
/* sadc-c005.c */
#include "daqlib.h"
static int ad1,in1,in2,in3,in4;
#define GAIN 4096 /* conversion gain */
int init () {
ad1 = hist_new1("adc",GAIN); /* ADCのデータそのもの */
in1 = hist_new1("in1",GAIN); /* 入力1が1の場合 */
in2 = hist_new1("in2",GAIN); /* 入力2が1の場合 */
in3 = hist_new1("in3",GAIN); /* 入力3が1の場合 */
in4 = hist_new1("in4",GAIN); /* 入力4が1の場合 */
return OK;
}
/* イベント処理ルーチン */
int event (int data[],int n) {
int d,reg;
d = data[0] & (GAIN-1); /* 最初のデータはADC */
reg = data[1]; /* 次が input register */
hist_add1(ad1,d);
if(reg & 1) hist_add1(in1,d);
if(reg & 2) hist_add1(in2,d);
if(reg & 4) hist_add1(in3,d);
if(reg & 8) hist_add1(in4,d);
return OK;
}
10.7 経過時間でランを止める
idle()が定期的に呼ばれるので、そこでランの経過時間とリミットの指定とを比較します。
リミットの指定にエントリーを使用しています。
/* timer.c */
#include "daqlib.h"
static int timer = 0;
int init () {
set_button4("Timer"); /* ボタン4をタイマー用に使用 */
return OK;
}
/* ボタン4でタイマーを ON/OFF */
int button4 () {
if(!timer){
timer = 1;
log_fmt("Timer is ON");
}else{
timer = 0;
log_fmt("Timer is OFF");
}
return OK;
}
/* エントリー4に設定された値と現在の経過時間を比較 */
int idle () {
int limit,d;
if(!isrun()) return OK; /* ランしている? */
if(timer){
limit = get_entry4();/* リミット時間指定 */
d = get_delta(); /* 経過時間 */
if(d >= limit)
acq_stop(); /* ランを停止させる */
}
return OK;
}
11 ユーザーコードで使用可能な関数(その2)
ユーザーコードから直接CAMACを操作することが可能です。
以下の関数を使用するには、次のinclude文が必要です。
#include "daqlib.h"
NAF(n,a,f)
N,A,FからCamac()で使用するCAMAC命令を生成するマクロです。
(Nn|An|Fn)
N,A,FからCAMAC命令を生成します。N,A,Fは大文字でなければなりません。
int Camac (int naf,void *data,int *q,int *x)
CAMAC 命令を1つ実行し、CAMACのQとX応答を返します。データは24ビットです。
引数のnafはCAMAC命令です。
例:
int data,q,x;
#define MCLEAR NAF(2,0,9) /* MCLEAR = N2,A0,F9 */
Camac(NAF(1,0,2),&data,&q,&x);
Camac(MCLEAR,&data,&q,&x);
Camac((N3|A0|F8),&d,&q,&x);
int Cenlam (void)
CC/7700またはCC32がCAMACのLAMでPCに割り込みをかけることを許可します。
int Cdslam (void)
CC/7700またはCC32からPCへの割り込みを禁止します。
int Crdlam (void)
CC/7700またはCC32のLAMレジスタを読み出します。
int Cgetstatus (int *stat) ドライバーの内部状態を取得します。
• CC_BUFF_FULL -- データバッファが満杯
• CC_STACK_INTR -- 割り込み処理の終了時にLAMがクリアされていなかったので、次の割り 込みを許可することができなかった
• CC_RAPID_CHECK -- 割り込み処理で頻繁にLAMをチェックする(必要に応じてドライバー がセットする)
int Cenlamext (int lam) int Cdslamext (int lam)
LAMの外部処理を有効あるいは無効にします。つまり、CwtlamとConlamを許可あるいは禁止 にします。
lamでLAMの番号を指定しますが、CC_ALLLAMなら全てのLAMを意味します。
int Conlam (void (func)())
CAMACのLAMで呼び出すルーチンを登録します。登録できるルーチンは1つだけです。
LAMでsignalが出され登録したルーチンがcallされます。Signalは必ず配送されるため、
他に優先順位の高いジョブが走っていて実行が遅延されてもルーチンは必ずcallされます。
多重割り込みを防止するためにドライバーはLAMを検出するとCC/7700またはCC32を割り込 み禁止にしますので、次の割り込みを許可するためにはCenlamを実行する必要があります。
登録を解除する場合には引数にNULLを指定します。プログラムが終了することでも解除されま す。
ユーザーコードでLAMn()を定義するとこの本体内部が呼び出し先に設定されるので、ユーザー が別のルーチンを設定すると正しく動作しなくなりますので注意して下さい。
int Cexecmd (int lam)
ドライバーに登録した命令の並びを実行します。登録した命令のテスト用に使用できます。
int spawn (char *cmd)
プロセスをforkし、/bin/shを使ってcmdを実行しますが、終了を待ちません。つまり、cmdは並 列に実行されます。一方、標準関数のsystem()は終了を待ちます。
cmdは1行のコマンドか、スクリプトファイルを指定します。
ユーザの環境変数は引き継がれます。
int wait_confirm (char *str)
文字列strの確認ダイアログを表示し応答を待ち、ユーザの応答に応じて結果のコードを返しま す。文字列strの漢字コードがEUCなら日本語も表示できます。ダイアログを表示している間は 他の処理は何もできなくなります。
結果のコード:
CONFIRM_OK ユーザが [OK] ボタンを押した CONFIRM_CANCEL ユーザが [CANCEL] ボタンを押した
CONFIRM_NONE ユーザがダイアログを閉じた(ボタンは押されなかった)
12 使用例(その3)
12.1 デッドタイムを計算する
CAMAC ADCのAD811は変換時間が一定なので、簡単にデッドタイムを計算できます。
/* ad811.c */
#define NDATA 4 /* 1イベントで読み出すデータ数 */
int stop () {
double d,de,dt;
d = get_delta() * 1000000.; /* RUN時間をマイクロ秒に */
/* イベント当たりの busy 時間(変換時間+読み出し時間) */
de = 80. + 6. * (double)NDATA;
if(d > 0.){
dt = ((de * (double)get_events())/ d) * 100.;
log_fmt("デッドタイム = %4.1f %%",dt);
}
return OK;
}
トリガーモジュールを使ってデータを収集する場合は、トリガーのカウント数とイベント数(実際に収集した データ数)からデッドタイムを計算できます。
この例では、トリガーパルスをスケーラーの入力0に入れると仮定しています。
/* intreg-deadtime.c */
#define CNT n18 /* scaler */
int stop () {
int dc,q,x,ev;
double dt;
Camac((CNT|F0|A0),&dc,&q,&x); /* read scaler input 0 */
ev = get_events();
if(dc > 0){
dt = (double)(dc - ev) * 100. / (double)dc;
log_fmt("scaler: %d dead time: %4.1f %%",dc,dt);
}
return OK;
}
/* intreg-deadtime.camac */
#define CNT n18 run:
#define DTC N6 /* デッドタイムカウンターの位置 */
int stop () {
int d0,d1,q,x;
double dt;
Camac((DTC|A0|F0),&d0,&q,&x); /* true timeを読み出す */
Camac((DTC|A1|F0),&d1,&q,&x); /* live timeを読み出す */
if(d0 > 0 && d0 >= d1){
dt = (double)(d0 - d1) * 100. / (double)d0;
log_fmt("dead time: %4.1f %%",dt);
}else{
log_fmt("counter error: true=%d, live=%d",d0,d1);
}
return OK;
}
デッドタイムカウンターを使うためにcamacファイルは以下のように記述します。この例ではADCは1台 だけ使用しています。
/* sadc-deadtime.camac */
#define AD1 n1 /* ADCの位置 */
#define READ f2,a0 /* データ読み出しとLAMクリア */
#define INH f24,a0 /* inhibit */
#define DTC n6 /* デッドタイムカウンターの位置 */
#define DSCLK f24,a0 /* クロック停止 */
init: /* モジュールの初期化 */
AD1,INH AD1,READ
AD1,f26,a13 /* enable LAM */
DTC,DSCLK /* disable clock */
/* 次の命令を有効にするとクロックが 10ms になる */
/* DTC,f17,a0,d1 /* 10ms clock */
run:
DTC,f9,a0 /* clear counter #1 */
DTC,f9,a1 /* clear counter #2 */
DTC,f26,a0 /* enable clock */
AD1,f26,a0 /* enable ADC conversion */
stop:
AD1,INH
AD1,READ /* ADC clear */
DTC,DSCLK /* stop clock */
LAM1:
AD1,READ
run:でカウンターのクロックが動きだし、stop:で停止します。stop:に続いて、stop()が呼び出され、
デッドタイムが計算されます。
12.2 定期的にデータを読み出す
何かをモニターするために、CAMACのカウンター等を定期的に読み出したい場合にはidle()を定義し ます。この例では、ランの間、30秒毎にファイルに記録しています。
/* periodic.c */
#include <stdio.h>
#include "daqlib.h"
#define DELTA 30 /* 周期(秒) */
static int laste;
static FILE *fp;
int run () {
laste = -(DELTA);
/* テキストファイルを作成。'***'はラン番号で置換される */
fp = fopen_new("count***.txt");
if(fp == NULL){
log_fmt("file open error");
return NG;
}
return OK;
}
int stop () {
fclose(fp);
return OK;
}
/* STOP/RUN中に 繰り返しcall される */
int idle () {
int e;
int d,q,x;
if(!isrun()) return OK; /* RUN中? */
e = get_delta();
if(e - laste >= DELTA){
laste = e;
Camac(NAF(1,0,0),&d,&q,&x); /* CAMACにアクセス */
fprintf(fp,"%d: %d\n",e,d); /* ファイルに記録 */
log_fmt("count: %d",d); /* ログ画面にも表示 */
}
return OK;
}
12.3 カレント・インテグレータでストップさせる
電流積分値でランを停止させるために、カレント・インテグレータ(ORTEC439)のDIGITIZED出力
(TTL)をプリセットカウンター(BiRa2204)に接続します。
カウンターの割り込みでストップさせるために、LAMをC言語のコードで処理します。モジュールがN22に あるのでLAM22()を定義し、そこでacq_stop()を呼びます。
/* current.c */
#include "daqlib.h"
#define N2204 22 /* 2204の位置 */
int LAM22 () /* 2204の割り込み処理 */
{
int d,q,x;
Camac(NAF(N2204,0,2),&d,&q,&x); /* F2,A0 */
acq_stop(); /* ランを停止させる */
return OK;
}
int init () /* 初期化 */
{
set_button1("COUNT");
return OK;
}
int button1 () /* ボタン1の処理 */
{
int d,q,x; /* エントリー1を積分値の設定に使用 */
d = get_entry1(); /* 積分値を読み込む */
Camac(NAF(N2204,0,16),&d,&q,&x); /* 2204に書き込む */
return OK;
}
int run () /* ラン */
{
button1(); /* 積分値を読み、2204に書き込む*/
return OK;
}
/* current.camac */
#define N2204 n22 run:
N2204,f10,a0 /* LAMクリア */
N2204,f26,a0 /* LAM許可 */
stop:
N2204,f24,a0 /* LAM禁止 */
12.4 割り込みレジスタでラン・ストップさせる
入力でLAMが発生するレジスターを使って、ラン・ストップさせることができます。
/* intreg-runstop.c */
#include <unistd.h>
#include "daqlib.h"
#define INTR 20 /* レジスターの位置 */
#define RDINTR NAF(INTR,0,0) /* レジスター読み出し */
#define CLINTR NAF(INTR,0,21) /* クリアレジスター */
#define ENLAM4 NAF(INTR,0,26) /* LAM許可 */
#define INTR_RUN 1 /* input1 = run */
#define INTR_STOP 2 /* input2 = stop */
int init () /* 初期化ルーチン */
{
int d,q,x;
Camac(CLINTR,&d,&q,&x);
Camac(ENLAM4,&d,&q,&x);
return OK;
}
int LAM20 () /* 割り込み処理ルーチン */
{
int d,q,x;
Camac(RDINTR,&d,&q,&x); /* レジスターを読み出す */
if(d & INTR_RUN && !isrun()){ /* インプット1? */
acq_run(); /* RUNさせる */
log_fmt("RUN by intreg");
/* レジスターの入力信号は 15 マイクロ秒より短いこと */
/* それよりも長い場合にはここで待ちを入れる */
/* usleep(100); */
}
if(d & INTR_STOP && isrun()){ /* インプット2? */
acq_stop();
log_fmt("STOP by intreg");
/* usleep(100); */
}
Camac(CLINTR,&d,&q,&x); /* レジスタークリア */
}
12.5 イベントを監視し、リセットする
コインシデンスのロジックなどが、何かのはずみでデッドロックに陥ることがあるなら、イベントを監視し、必 要に応じて回路をリセットするようにしなければなりません。そのために、ユーザーコードでreset:と reset()が用意されており、set_reset_time()で監視時間を設定できるようになっています。
ランの間、設定した時間内にイベント数に変化がなければ、reset:とreset()が実行されます。
ランを停止した時には、ラン中に何回リセットが発生したかがログに書き出されます。
06/10 10:20:00 STOP #50
06/10 10:20:00 reset count: 4 int init ()
{
set_reset_time(1.0); /* 1秒間データが入らないとリセット */
...
}
/* リセットするためのコマンド */
reset:
N1,F9,A0 N2,F9,A0
12.6 NIM ADC でコインシデンス・データを取る
NIM ADCを使った場合は、ADCへの入力信号が同期していても、信号の大きさによって変換時間に差 ができてしまうので、LAMの出る時間が同期しません。しかし、Seiko ADCのように高速なADCを使用 するなら、読み出しをわざと遅延させることで同期したデータを取ることが可能です。
この例では4台のADCを使い、2組のコインシデンス・データを収集します。AD1とAD2で1組、AD3と AD4でもう1組です。Seiko ADCを2kchで使用すると、変換時間は最大10µSです。CAMAC read/writeの実行に6µSかかるので、その時間をダミーをに使います。ただし、8kchで使用すると 40µS必要なので、別の方法を使用した方が良いかもしれません。
(データを読み出す camacコードは次のページです)
先ず、イベント処理のコードを示しますが、収集した時のLAMによって、データの順番が異なりますので注 意が必要です。更に、ミス・コインシデンスの場合はどちらかのデータがゼロになるのを忘れないようにし なければなりません。
/* sadc-coin3.c */
int event (int data[],int n) {
int lam,d1,d2;
if(n != 2) return NG;
d1 = data[0] & 0xffff;
d2 = data[1] & 0xffff;
if(d1 == 0 || d2 == 0) tos++;
lam = (data[1]>>24) & 31; /* LAM番号を取り出す */
switch(lam){
case 1: /* データはAD1,AD2の順 */
/* d1 = AD1, d2 = AD2 */
break;
case 2: /* データはAD2,AD1の順 */
/* d1 = AD2, d2 = AD1 */
break;
case 3: /* データはAD3,AD4の順 */
/* d1 = AD2, d2 = AD4 */
break;
case 4: /* データはAD4,AD3の順 */
/* d1 = AD4, d2 = AD3 */
}
return OK;
}
/* sadc-coin3.camac */
/* 使用するADC */
#define AD1 n1
#define AD2 n2
#define AD3 n3
#define AD4 n4 /* 使用する命令 */
#define READ f2,a0
#define CLADC f9,a0
#define CLLAM f10,a0
#define DSADC f24,a0
#define ENADC f26,a0
#define DSLAM f24,a13
#define ENLAM f26,a13
/* 遅延のためのダミー(readは不可) */
#define DUMY f16,a0 init: /* 初期化 */
AD1,DSADC AD2,DSADC AD3,DSADC AD4,DSADC AD1,CLADC AD2,CLADC AD3,CLADC AD4,CLADC AD1,CLLAM AD2,CLLAM AD3,CLLAM AD4,CLLAM AD1,ENLAM AD2,ENLAM AD3,ENLAM AD4,ENLAM
run: /* 収集開始 */
AD1,ENADC AD2,ENADC AD3,ENADC
AD4,ENADC
stop: /* 停止 */
AD1,DSADC AD2,DSADC AD3,DSADC AD4,DSADC
/* AD1の割り込み処理。AD2は遅れてLAMが出るの で、それまでDUMYで遅延させる */
LAM1:
AD1,DUMY AD1,DUMY AD1,READ AD2,READ
/* AD2の割り込み処理。AD1,AD2の順に読もうと すると遅延を余分に入れなければならない */
LAM2:
AD2,DUMY AD2,DUMY AD2,READ AD1,READ
/* AD3の割り込み処理。AD1と同様 */
LAM3:
AD3,DUMY AD3,DUMY AD3,READ AD4,READ
/* AD4の割り込み処理。AD2と同様 */
LAM4:
AD4,DUMY AD4,DUMY AD4,READ AD3,READ
13 ユーザーコードで使用可能な関数(その 3 )
標準のCNAF以外に次のようなCAMAC命令が使用できます。これらはドライバー内部の変数にアクセス するだけで、CAMACにアクセスすることはありませんので仮想的な命令です。
Mn (Mark)
ドライバー内部の変数の値を変更します。変更する値はDnで指定します。変数の値はライブラ リーのCgetmark/Csetmarkでも参照・変更できます。
M0, Dn CC_BITCLR. Dnで指定したビットをクリアします。
M1, Dn CC_BITSET. Dnで指定したビットをセットします。
M2, Dn CC_OVERWT. Dnで指定した値を上書きします。
例えば、M1,D4は下位から3ビット目のビットだけを1にしますが、M2,D4はそれ以外のビットを0に します。
Tn (Test)
変数の値をテストします。テスト結果が真なら次の命令を実行しますが、虚ならそこで実行を終了し ます。
T0, Dn Dnで指定したビットがクリアされていたら真。
T1, Dn Dnで指定したビットがセットされていたら真。
T2, Dn Dnと同じ値だったら真。
T4, Dn Dnで指定したビットがクリアされていなかったら真。
T5, Dn Dnで指定したビットがセットされていなかったら真。
T6, Dn Dnと同じ値でなかったら真。
14 使用例(その4)
14.1 データを割算する
割算回路を省くためにデータを割算する場合には、割算の誤差に注意が必要です。
ADCのコンバージョン・ゲインを上げて取り込み、割算結果を丸めても良いですが、それができない場合 には、乱数を加えるという方法もあります。
上の左図は、割算した結果に+0.5を加えたデータでヒストグラムを作成したもので、所々に現れているノ イズ状のものが計算誤差の累積したものです。
右図は、割算の結果に乱数(-0.5から+0.5)を加えたデータでヒストグラムを作成したものですが、明ら かな誤差はなくなっています。
このプログラムは次の様に書きます。
#include <stdlib.h>
#define GAIN 1024
int event (int data[],int n) {
int d1,d2,dx;
double r;
d1 = data[0] & 0xffff;
d2 = data[1] & 0xffff;
/* -0.5から+0.5の乱数を得る */
r = (double)random() / ((double)(1<<30) * 2.) - 0.5;
/* 割算結果に乱数を加える */
dx = (double)d1 * (double)GAIN. / (double)d2 + r;
hist_add1(sp,dx);
return OK;
}
14.2 別プロセスでコマンドを実行する
例えば、データ収集を停止した時にサウンドで知らせたい場合には、spawn()を使って別プロセスで好み の音を鳴らすことができます。標準関数のsystem()は指定したコマンドの終了を待つので、時間がロス します。
int stop () {
spawn(“play /usr/local/daq/sound/redalert.wav”);
return OK;
}
データを保存した時にrsyncでバックアップを取る場合も同様です。
int save () {
hist_save_all();
spawn(“rsync -a . /u/dest/data”);
return OK;
}