gpx プログラミング説明書

1. 概要

gpx はプロトタイピングに適した GPIB 制御用言語です。SRQ の処理が自動化され、 制御の流れが構造化されているのが最大の特徴で、構文を awk に合わせているため、 UNIX ユーザは理解が容易です。

GPIB 制御に関係しない部分はサンプルプログラムを書きませんので、 awk のマニュアルや解説書を参照してください。

2. 構文

gpx [オプション] -f プログラムファイル
	または、
gpx [オプション] 'プログラム'
コマンドララインオプションは次のとおりですが、 セパレータ文字とゲートウェイは、BEGIN セクションで指定、変更することができます。
  -F{セパレータ文字}	# フィールドセパレータ(文字)を指定
  -f ファイル名		# プログラムファイルを指定
  -g ゲートウェイ	# LAN/GPIB gateway を指定

3. プログラム構造

  BEGIN { アクション }
  [LOOP] { アクション }
  SRQ [アドレス] { アクション }
  END { アクション }
BEGIN セクションは、プログラムの最初に一度だけ実行され、 その後 LOOP セクションの実行に移ります。 LOOP セクションのアクションは next か exit が実行されるまで、 繰り返し実行されます。 END セクションは、LOOP セクションから next で抜けたとき、 プログラムの終了前に一度だけ実行され、終了します。

LOOP セクションを実行中、SRQ を検出すると、 SRQ セクションが割り込みルーチンとして実行されます。 SRQ セクションで "SRQ" の文字列の後にデバイスのアドレスが指定されていれば、 そのデバイスに対して自動的にシリアルポールが実行され、 RQS ビットがセットされている時にのみ、対応するアクションが実行されます。 デバイスのアドレスを省略した場合は、シリアルポールを行わず、 対応するアクションは無条件に実行されます。SRQ セクションは、 いくつあってもかまいません。 SRQ の検出により、最初の SRQ セクションから順に実行され、 最後の SRQ セクションの実行後、中断した LOOP セクションの続きに戻ります。 なお、BEGIN と END セクションでは、SRQ 割り込みが禁止されています。

BEGIN, LOOP, SRQ, END のいずれのセクションも、省略が可能で、 例えば、次のプログラムは、

F1,+1.23
といったデータを出力する、 GPIB アドレス 16 のデジタルボルトメータのデータを永久に読み続けます。 割り込みキー以外にプログラムを停止させる方法はありません。 割り込みキーが押されてからプログラムが停止するまで、 RPC timeout で遅れることがあります。
  LOOP {
    getline("GPIB 16")
    print
  }

BEGIN あるいは LOOP や END セクションのみのプログラムが、 役に立つことがあります。例えば、次のプログラムは、ユークリッドの互除法により、 キーボードから入力した2つの数値の、最大公約数と最少公倍数を求めます。

  BEGIN {
    printf "n1, n2 ? "
    getline
    m = $1; n = $2
    while (m) {
	if (m < n) {
		x = m; m = n; n = x;
	}
	m %= n
    }
    print n, $1 * $2 / n
    exit(0);
  }
ステートメント(命令)は、改行文字かセミコロン (;) で区切ります。 また、いくつかのステートメントを中括弧 { .. } でまとめて、 複文(実効的に1つの文)にすることができ、 これは while, for, then, else の後で必要になります。

GPIB の動作モードは、 Agilent E5810A の機能制限により、システムコントローラしかありません。

4. 定数

定数(constant)には、数値定数と文字列の2つがあります。

4.1. 数値定数

整数、小数点付き整数、浮動小数点数の3つがあり、内部的にはすべて浮動小数点 数として扱われます。例えば、次のようになります。

-12
12.3
5.678e-6

4.2. 文字列

文字列は、任意の文字列をダブルクォート (") で括ったものです。 文字列を空白文字で区切って並べると、 それらを連結したあたらしい文字列が得られます。例えば、次の print 命令は、 "she" をプリントします。

print "s" "he"
特殊文字は、次のような8進形式で記述することができます。
\nnn
nnn は、8進形式の数値で、例えば \007 は ASCII の BEL コードを表します。 また、良く使われる文字については、次のような形式も用意されています。
\n	改行文字
\t	タブ文字
\"	タブルクォート
\\	円記号(ASCII 文字を表示するシステムでは、バックスラッシュ)

5. 変数

変数(variable)には、数値あるいは文字列を記憶するための単純変数と、 1次元の配列があります。変数名は、アルファベットで始まらなければなりませんが、 2字目以降は、数字も使えます。数値と文字列の区別はありません。 数値として解釈できる文字列は数値、 数値として理解できない文字列は文字列としてあつかわれます。 また、加算や連結演算子を使って数値と文字列の変換を行なうことができます。 例えば、

a + 0
は数値で
a ""
は文字列になりますので、 あいまいな内容を、数値や文字列に強制したいとき、この手法を使います。 何もない演算子は連結演算子です。 つまり、変数を並べて書くと、連結した文字列が得られます。

1次元配列は

配列名[インデックス]
で表現します。 インデックスは、任意の文字列です。数値の必要はありません。 この結果、連想配列としての機能が得られます。 例えば、
a["count"] += n
といった具合です。 また、次のような手法で、疑似的な2次元配列が作れます。
array["2 3"]

6. 組み込み変数

次のような、組み込み変数が用意されています。

GATEWAY   VXI-11 LAN/GPIB Gateway の IP アドレスか DNS で引けるホスト名
IFS       SRQ 入力レコードのフィールドセパレータ(ディフォルトは空白とタブ文字)
RS        LOOP 入力レコードのレコードセパレータ(ディフォルトは、改行文字)
IRS       SRQ 入力レコードのレコードセパレータ(ディフォルトは、改行文字)
NF        LOOP 入力レコードのレコードのフィールド数
INF       SRQ 入力レコードのレコードのフィールド数
NR        LOOP 入力レコードの通し番号
INR       SRQ 入力レコードの通し番号
PI        円周率(3.1415..)
OFMT      数値をプリントする際のprint文のフォーマット(ディフォルトは、%.6g)
OFS       出力フィールドセパレータ(ディフォルトは空白文字)
ORS       出力レコードセパレータ(ディフォルトは改行文字)
STB       GPIB のステイタスバイト(シリアルポールで入手したもの)

GATEWAY の初期値は「e5810a」です。

7. フィールド

また、特殊な変数として、フィールドがあります。 gpx は、レコードを1つ入力する度に、 指定されたフィールドセパレータ (FS, IFS) によりフィールド分割をおこない、 SRQ セクションで入力したレコードについては ?1, ?2, ?3, .. 、 その他のレコードについては $1, $2, $3, .. という変数にフィールドの内容をセットします。 フィールドの最大値は 1024 です。

入力したレコード全体を必要とする場合は、 それぞれ ?0, $0 で参照することができます。フィールドは、変数名を使って、 例えば、

i = 3
print $i
のように間接的に参照することもできます。

8. 演算子(優先順)

次のような演算子が用意されています。上から下に進むにしたがって、 優先度が上ります。

= += -= *= /= %= ^=	代入
||			論理OR
&&			論理AND
!			論理否定
~ !~			正規表現のマッチ、アンマッチ
< <= > >= != ==		関係演算子
空白			結合演算子
+ -			加算、減算
* / %			乗算、除算、モード
+ -			単項プラス、マイナス
++ --			インクリメント、デクリメント
$ ?			フィールド

代入演算子 +=, -= /= %= ^= は、左辺と右辺の値に対して 、それぞれ、 +, -, /, %, ^ の演算を行ない、結果を左辺に代入します。例えば、

x += a
x = x + a
と同じです。

~, !~ は、正規表現(文字パターン)のマッチ、 アンマッチをチェックするときに使います。文字列が等しいかどうかのチェックは、 ==, != で行ないます。

インクリメント、デクリメント演算子 ++, -- は、 変数の前につけるか後につけるかによって、 式の評価の前に実行するか後で実行するかがきまります。

9. 正規表現

正規表現は、 if, while, for の条件式で、

変数 ~ /正規表現/
変数 !~ /正規表現/
の形式で使用することができ、これは、文字パターンの認識に威力を発揮します。 例えば、次のプログラムは、GPIB アドレス 17 の計測器から入力したデータの 先頭に O, U, E のいずれかの文字があると、エラーメッセージを表示して、 処理を終了します。
  if ($0 ~ /^[OUE]/) {
    print "デ―タ エラ―"; exit(1)
  }
正規表現で使用可能なパターンは次のとおりです。
c		メタキャラクタ以外の文字 c にマッチ
\c		リテラルキャラクタ c にマッチ
.		改行以外の任意の文字にマッチ
^		行または文字列の先頭にマッチ
$		行または文字列の最後にマッチ
[abc...]	abc...のいずれかにマッチ
[^abc...]	abc...以外の文字にマッチ
r1|r2		r1,r1のいずれかにマッチ
r1r2		結合: r1にマッチした後r2にマッチ
r+		1つ以上のrにマッチ
r*		1個以上のrにマッチ
r?		0または1個のrにマッチ
(r)		グルーピング: rにマッチ

10. データ入力

データ入力には、getline() 関数を使用します。 getline() 関数は、レコード単位の入力を行なう関数で、 入力したレコード全体を SRQ セクションの実行中であれば "?0" 、 そうでなければ "$0" レコード変数に代入し、その後、 フィールドセパレータ(SRQ セクションなら IFS 、 そうでなければ FS 変数の内容)に従ってフィールドに分割し、 SRQ セクションなら ?1, ?2, .. そうでなければ $1, $2, .. のフィールド変数に代入します。 入力したフィールドの数は、それぞれ INF, NF でわかります。 入力バッファの大きさは 32768 バイトです。

フィールドセパレータ FS, IFS は、 初期状態では空白とタブ文字に設定されていますが、

FS = ","
のように任意の文字を代入することにより、変更可能です。 FS, IFS は、それぞれ、LOOP セクション、 SRQ セクションの getline() で使用されるフィールドセパレータで、 gpx の組み込み変数として用意されています。

レコードセパレータのほうは、 GPIB からの入力なら getline() 関数で指定されるデリミタ、その他の場合は、 LOOP セクションなら RS、SRQ セクションなら IRS 変数の内容が使われます。 RS, IRS の初期値は、改行文字です。

10.1. GPIB からのデータ入力

GPIB からのデータ入力は、次の形式の getline() 関数で行います。 2つめの構文で変数名を指定した場合はフィールド分解せずに変数に読み込み、 変数を省略した場合は最初の構文と同じでフィールド分解が行われます。

  getline("GPIB [アドレス [デリミタ [カウント]]]")
  getline [変数] <"GPIB [アドレス [デリミタ [カウント]]]"
アドレス は、トーカに指定すデバイスのアドレスを10進数で表したもので、 セカンダリアドレスを含む場合は、
  プライマリアドレス * 100 + セカンダリアドレス
と指定します。 例えば、プライマリ、セカンダリのアドレスがそれぞれ5と7なら、 507 となります。

デリミタ はつぎの3つが指定でき、省略すると 1 になります。

  0	テリミタなし
  1	EOI
  2	CR+LF
  3	CR
1 は IEEE488 の標準、2 は HP社の標準、 3 は TECTRONIX社の標準的なデリミタです。2,3 を指定した場合、 CR と LF の文字は、入力データから削除されます。

カウント は、入力を強制的に打ち切るためのバイト数の指定で、 省略すると 32768 になります。 32768 バイト以上の入力はできません。

getline() 関数は GPIB あるいは RS-232 から入力した場合、 入力データのバイト数を返します。

GPIB アドレスに 31 を指定すると、 E5810A の RS-232 ポートから入力します。

10.2. ファイルからのデータ入力

getline() 関数で、ディスクのファイルを読むことも可能で、 その場合は次の構文を使います。

  getline("ファイル名")
  getline [変数] < "ファイル名"
2つ目の構文で変数を指定した場合はフィールド分解せずに変数に読み込み、 変数を省略した場合は $0 に読み込んでフィールド分解が行われます。

レコードセパレータは、RS, IRS になります。 ファイル名を省略した場合は、標準入力から読み込みます。この場合は、単に

  getline
と書いても構いません。 getline() 関数で GPIB 以外から入力した場合は、読み込みが出来た時 1 、 EOF (ファイルの終わり)により読み込みができないとき 0 を返します。

10.3 コマンドからのデータ入力

下記の構文でコマンドの実行結果を入力することができます。

  "コマンド" | getline [変数]
変数を指定した場合はフィールド分解せずに変数に読み込み、 変数を省略した場合は $0 に読み込んでフィールド分解が行われます。

11. データ出力

データの出力は、print 及び printf 文で行ないます。

11.1. print 文

print 文の構文は、次のとおりです。

  print [プリント・リスト]
プリント・リスト は、数値、文字列、 変数名をコンマか空白文字で区切ったものです。区切り文字がコンマのときは、 それぞれのデータの間に、 出力フィールドセパレータすなわち OFS 変数の内容が挿入され、空白の場合は、 データの間に何も置かずに出力が行なわれます。 また、データの最後にレコードセパレータ RS, IRS が追加されますので、 GPIB出力の時は注意してください。GPIB出力では、 printf 文を使うのが無難です。

プリント・リスト を省略すると、 print $0 と解されます。空白行を出力する場合は、次のようにして下さい。

  printf "\n"

printf 文

printf 文は、出力編集を行なう print 命令で、構文は次のとおりです。

  printf フォ―マット, プリント・リスト

フォ―マット は、変換仕様とその他の文字から構成される文字列で、変換仕様以外 はそのままコピーされ、変換仕様の形式は次のとおりです。

  %[-][印字幅][.印字桁数]変換文字
印字幅の前にハイフン (-) があると左詰め、 なければ右詰めの編集が行なわれます。印字幅は、ANK文字の幅で計算されます。 印字桁数は、文字列から印字すべき最大文字数、あるいは、 数値の小数点以下の桁数です。変換文字とその意味は、次のとおりです。
  d	引数を10進数に変換する。
  o	引数を符号なし8進数に変換する。
  x	引数を符号なし16進数に変換する。
  c	引数を1文字として受け取る。
  s	引数を文字列として受け取る。文字列の終りか、指定された印字桁数まで出力する。
  e	引数を [-]m.nnnnnnE[+-]xx 形式の10進数に変換する。
  f	引数を [-]mmm.nnn 形式の10進数に変換する。
  g	引数を %e か %f のいずれか文字数の少ない方に変換する。
具体例をいくつか示します。 %c で 97 を表示させると文字 a になるのは、 文字 a のASCII 値が 97 のためです。ASCII 値というのは、 ASCII(アメリカの標準文字コード)の何番目の文字かという意味です。 %o と %x は、コンピュータ技術者が良く使います。 実用上は %s と %f で間に合いますので、まずこの2つを覚えてください。

  fmt        $1            printf(fmt, $1)
  ----------------------------------------
  |%c|       97              |a|
  |%d|       97.5            |97|
  |%5d|      97.5            |   97|
  |%e|       97.5            |9.750000e+01|
  |%f|       97.5            |97.500000|
  |%7.2f|    97.5            |  97.50|
  |%g|       97.5            |97.5|
  |%.6g|     97.5            |97.5|
  |%o|       97              |141|
  |%06o|     97              |000141|
  |%x|       97              |61|
  |%s|       January         |January|
  |%10s|     January         |   January|
  |%-10s|    January         |January   |
  |%.3s|     January         |Jan|
  |%10.3s|   Janualy         |       Jan|
  |%%|       January         |%|

11.2. GPIB へのデータ出力

GPIB へのデータ出力は、print 文か printf 文を次の形式で使用します。

  print プリント・リスト > "GPIB [アドレス [デリミタ [カウント]]"
  printf フォ―マット, プリント・リスト > "GPIB [アドレス [デリミタ [カウント]]"
アドレスとカウントは、getline() の場合と同じです。

デリミタ には、つぎの 4 つが指定できます。

  0	デリミタなし
  1	最終データに EOI を付与。
  2	データの最後に CR,LF を追加。
  3	データの最後に CR を追加。

GPIB アドレスに 31 を指定すると、 E5810A の RS-232 ポートに出力します。

11.3. ファイルへのデータ出力

次の構文で、ディスクのファイルにデータを出力することができます。

  print プリントリスト > "file"
  print プリントリスト >> "file"
リダイレクトの記号として ">>" を使うと既存のファイルに追加され、 ">" を使うと既存のファイルと置き換えられます。

11.4. コマンドへのデータ出力

次の構文で、コマンドの標準入力にデータを渡すことができます。

  print プリントリスト | "コマンド"

12. 制御文

if, while, for の 3 つが用意されています。

if (式) ステ―トメント1[ else ステ―トメント2]
式が成立すれば ステ―トメント1 、 成立しなければステ―トメント2を実行します。
while (式) ステ―トメント
式が成立する間、ステ―トメント を実行します。
for (式1; 式2; 式3) ステ―トメント
最初に式1を実行し、その後、式2の成立している間、 ステ―トメント と式3を実行します。
for (変数名 in 配列変数名) ステ―トメント
配列全体をアクセスするための構文です。指定された変数に、 指定された配列のすべての要素名を順次セットして、ステ―トメントを実行します。
break
for, while ループから抜け出します。
continue
for, while ループの先頭に戻ります。 式の値は、gpx の戻り値になります。
next
BEGIN, LOOP, END セクションから抜け出して、次のセクションの実行に移ります。
exit [式]
プログラムの実行を打ち切ります。

次のプログラムは、いずれも、1 から 10 までの合計を求めます。

  i = s = 0
  while (i++ < 10)
    s += i
  print s
  ..

  for (i = s = 0; i++ < 10; )
    s += i
  print s
  ..

  i = s = 0
  while (1) {
    if (i++ < 10)
	s += i
    else
	break
  }
  print s
  ..

条件式の値は、真のとき 0 以外、偽のとき 0 になります。 複文は、中括弧 { .. } で括ることにより実現します。

13. 組み込み関数

GPIB 操作、数値演算、文字列操作のために、いくつかの関数が用意されています。

13.1. GPIB 用の関数

良く使われる GPIB の操作に付いては、次のような組み込み関数が用意されていま す。

readbin(アドレス, バイト数)
GPIB の指定されたデバイスをトーカに指定し、 指定されたバイト数のバイナリデータを入力し、 その結果を 10 進数として持ち帰ります。 最初のバイトが、上位桁として扱われます。 バイト数は 1 から 4 までです。
gbspol(アドレス)
指定されたデバイスに対してシリアルポールを実行し、 ステイタスバイトを持ち帰るとともに、組込変数 STB にセットします。
gbtrg(アドレス)
指定されたデバイスをトリガします。
gbclr(アドレス)
指定されたデバイスをクリアします。
disable
SRQ 割り込みを禁止します。
enable
SRQ 割り込みを可能にします。
gbwait(フラグ)
フラグ が 0 のとき SRQ が出ていれば 64、出ていなければ 0 を返します。 フラグが 1 なら、SRQ が出るまで待ちます。 フラグが -1 の場合は、下記のステイタスワードを返します。
  ビット 0 - 最後の GPIB データ入力が指定バイト数に達して終了した
  ビット 1 - 最後の GPIB データ入力がデリミタ文字で終了した
  ビット 2 - 最後の GPIB データ入力が END メッセージで終了した
  ビット 6 - SRQ を検出した

フラグが -2 の場合は、キーボードから 1 文字の入力を待って、 入力されたキーの ASCII 値を持ち帰ります。

gblcl(アドレス)
指定されたアドレスのデバイスをローカル状態にして、 押釦操作を有効にします。
gblost(I/Oタイムアウト値)
GPIB 関数の I/O タイムアウト値を秒単位で指定します。 初期値は 5 秒です。
gblost2(I/Oタイムアウト値, lockタイムアウト値)
GPIB 関数のタイムアウト値と(排他制御の)ロック待ち時間を秒単位で指定します。 lockタイムアウト値の初期値は 0 です。
gbabort()
GPIB 制御を中止します。
gbget(アドレス、ファイル、デリミタ)
指定されたアドレスのデバイスからデータを入力して直接ファイルに格納します。 オシロスコープの波形データ等、 getline() で読み込めない 1024 バイトを越える文字列の入力に使います。 ファイル名を "-" と指定すると、標準出力になります。
gbput(アドレス、ファイル、デリミタ)
指定されたアドレスのデバイスにファイルの内容を直接出力します。 getline(), print, printf() え扱えない、 1024 バイトを越える文字列の出力に使います。 ファイル名を "-" と指定すると、標準入力になります。

13.2. 数値関数

abs(x)
x の絶対値を求めます。
atan2(y, x)
tan(a) = x / y になるような a を求めます。
cos(x)
三角関数、cos(x) を求めます。
erf(x)
誤差関数、erf(x) を求めます。
exp(x)
指数関数、exp(x) を求めます。
int(x)
x の整数部を求めます。
log(x)
x の(自然)対数を求めます。
rand()
0 以上 1 未満の疑似乱数を作ります。
sin(x)
三角関数、sin(x) を求めます。
sleep(x)
x 秒間休止します。タイミング調整に使用します。
srand(x)
rand() の新しい種を x にします。
sqrt(x)
x の平方根を求めます。
tan(x)
三角関数、tan(x) を求めます。
and(x, y)
ビット毎の AND(論理積)を求めます。
or(x, y)
ビット毎の OR(論理和)を求めます。
xor(x, y)
ビット毎の EOR(排他的論理和)を求めます。

13.3. 文字列関数

gsub(r, s[, t])
文字列 t の中で正規表現 r に適合する文字列をすべて r で置換する。 t を章楽すると $0 を対象にする。
index(s, t)
文字列 s 中の、文字列 t の位置を求めます。先頭は 1 になります。 存在しない場合は、0 が戻ります。
length(s)
文字列 s の長さを求めます。
match(s, r)
正規表現 r に適合する部分文字列が s に含まれているかどうかを検査して、 マッチする文字位置を返します。
split(s, a[, f])
フィールドセパレータ f によって、文字列 s を分割し、配列 a にセットします。 f を省略した場合は、FS をフィールドセパレータとします。
sprintf(フォ―マット, プリント・リスト)
プリント・リストをフォ―マットによって編集した文字列を返します。 フォマットは printf 文と同じです。
sub(r, s, t)
t の中の正規表現 r に適合する文字列を s で置換します。
substr(s, n[, m])
文字列 s のn字目から m 字長のサブストリングを作ります。 m を省略すると、n 字目から最後までになります。

14. ユーザ定義関数

ユーザ定義関数は下記の形式で記述します。

  function 関数名(引数リスト) {
    プログラム
    ..
    [return 式]
  }
関数の最後に return 文があれば、その式の値を持ち帰ります。 引数リストは変数か定数を「,」で区切ったものです。

関数呼出の構文は下記のとおりですが、 関数の引数が関数呼出の引数より大きい場合は、 残りの引数はローカル変数になります。 引数はすべて値渡しです。

  関数名(引数リスト)

15. サンプル・プログラム

15.1. 単純な入出力

次のプログラムは、HP3478A MULTIMETER を直流電圧測定(F1)、5 桁測定(N5)、 内部トリガ(T1)、SRQ なし (M00) に設定し、10 回の測定を行っています。

gpx '
BEGIN {
  GATEWAY = "202.23.252.165"
  print "F1N5T1M00" >"GPIB 23"
}
LOOP {
  if (NR > 10)
	break
  getline("GPIB 23")
  print $0
}
'

このマニュアルのプログラム例では、 わかり易いように「GPIB アドレス」形式で GPIB デバイスを記述しますが、 将来のアドレス変更とか、システムのわかり易さを考えれば、 下記のように書くのも良いと思います。

gpx '
BEGIN {
  GATEWAY = "202.23.252.165"
  HP3478A = "GPIB 23"
  print "F1N5T1M00" >HP3478A
}
LOOP {
  if (NR > 10)
	break
  getline(HP3478A)
  print
}
'

15.2. SRQ 割り込み

HP3478A MULTIMETER を直流電圧測定(F1)、5 桁測定(N5)、外部トリガ(T1)、 測定完了 SRQ (M01) に設定し、10 回の測定を行っています。

gpx '
BEGIN {
  GATEWAY = "202.23.252.165"
  print "F1N5T2M01" >"GPIB 23"	# 機能設定
  gbspol(23)			# SRQ があればクリア
  gbtrg(23)	# トリガ
}
{
  if (INR > 10)
	break
} 
SRQ 23 {
  print STB, INR
  getline <"GPIB 23"	# データ入力
  print ?0
  gbtrg(23)	# トリガ
}
'

15.3. SRQ 待ち

HP3478A MULTIMETER を直流電圧測定(F1)、5 桁測定(N5)、外部トリガ(T1)、 フロントパネル SRQ (M20) に設定し、10 回の測定を行っています。 フロントパネル SRQ 釦が押される度に、トリガをかけて測定します。 LOOP の予約後は省略可能ですが、書いておくほうが動作がわかりやすいと思います。

gpx '
BEGIN {
  GATEWAY = "202.23.252.165"
  print "F1N5T2M20" >"GPIB 23"	# 機能設定
  gbspol(23)		# SRQ があればクリア
}
LOOP {
  if (NR > 3)
	break
  print "Press front panel SRQ button."
  gbwait(1)		# SRQ 釦が押されるのを待つ
  gbspol(23)		# SRQ をクリア
  gbtrg(23)		# トリガ
  getline <"GPIB 23"	# データ入力
  print $0
}
'

15.4 排他制御

gblock()/gbunlock() 関数で GPIB デバイスを排他制御して、 複数のプロセスで共有することができます。 下記のプログラムでは、他のプロセスが使用中のとき、 5 秒間は空くのを待っています。 同じプログラムを 2 つ同時に動かしてみると、 動作がよくわかると思います。

gpx '
BEGIN {
  GATEWAY = "202.23.252.165"
  print "F1N5T1M00" >"GPIB 23"
  gblost2(5, 5)	# 他のプロセスが使用中なら 5 秒まで待つ
}
LOOP {
  if (++n > 10)
	break
  gblock(23)
  getline("GPIB 23")
  gbunlock(23)
  print $0
  sleep(3)	# 他のプロセスがアクセスするチャンスを作る
}
'

15.5 gbget() 関数によるデータ入力

HP 54616B オシロスコープをアベレージングモードに設定し、 チャネル 1 の波形を 500 点でデジタイズして、 ASCII 形式で読み取ります。 出力される文字列が

  #80000399951,51,51,51,51,51,51,51,51,51,51,51,51,51, ..
といった形式ですから、 gbget() 関数で標準出力に書き出して、 sed(1) でデリミタの "," を改行文字に書き換えています。

gpx '
BEGIN {
  print ":ACQUIRE:TYPE AVERAGE" >"GPIB 7"
  print ":ACQUIRE:COMPLETE 100" >"GPIB 7"
  print ":WAVEFORM:SOURCE CHANNEL1" >"GPIB 7"
  print ":WAVEFORM:FORMAT ASC" >"GPIB 7"
  print ":ACQUIRE:COUNT 8" >"GPIB 7"
  print ":WAVEFORM:POINTS 500" >"GPIB 7"
  print ":DIGITIZE CHANNEL1" >"GPIB 7"
  print ":WAVEFORM:PREAMBLE?" >"GPIB 7"
  getline("GPIB 7")	# Preamble
  print
  print ":WAVEFORM:DATA?" >"GPIB 7"
  gbget(7, "-", 1)	# wave data
}' | sed -e 's/,/\
/gp'

15.6 getline() 関数によるデータ入力

FS を "," にして、フィールド分解した結果を print しています。

gpx '
BEGIN {
  print ":ACQUIRE:TYPE AVERAGE" >"GPIB 7"
  print ":ACQUIRE:COMPLETE 100" >"GPIB 7"
  print ":WAVEFORM:SOURCE CHANNEL1" >"GPIB 7"
  print ":WAVEFORM:FORMAT ASC" >"GPIB 7"
  print ":ACQUIRE:COUNT 8" >"GPIB 7"
  print ":WAVEFORM:POINTS 500" >"GPIB 7"
  print ":DIGITIZE CHANNEL1" >"GPIB 7"
  print ":WAVEFORM:PREAMBLE?" >"GPIB 7"
  getline("GPIB 7")	# Preamble
  print
  print ":WAVEFORM:DATA?" >"GPIB 7"
  FS = ","
  getline("GPIB 7 1")	# wave data
  # $(NF) は "\n"
  for (i = 1; i < NF; i++)
	print i, $i
  exit(0)
}
'

15.7. 他のプログラムへの接続

次のプログラムは、 1 MHz から 1 GHz の帯域を有するインピーダンス測定器 YHP4191 の自動スイープ機能を利用して、 1 MHz から 2 MHz まで 0.05 MHz 間隔で反射係数を測定し、 スミスチャートにプロットしています。入力フィールドセパレータをコンマに設定し、 データの数値部分だけをスミスチャート作成コマンド smith に渡していることに注意して下さい。 smith コマンドは自分で作ります。 if 文は、自動スイープの終わりをチェックしています。

BEGIN {
  FS = ","
  print "A6TF1ENPF2ENSF.05ENWU" >"GPIB 17"
}
LOOP {
  getline("GPIB 17")
  x = substr($2, 4)
  y = substr($3, 4)
  print x, y | "smith"
  if (!gbspol(17) & 16)
	exit
}

15. あとがき

私が gpx の最初の版を書いたのが 1980 年代ですが、 それ以来、MS-DOS、ミニコンの Unix、Sun ワークステーションの Unix、 Minix、FreeBSD などいろいろな版を社内で使い続けてきました。 この版は Minix 版を元に、 今回公開した VXI11 GPIB ライブラリで使えるように書き換えて、 GPIB のハードウェアに依存しないようにしたものです。

lex() や yacc() が使われていないのは、 当時の Minix が 16 bit CPU を前提にしたもので、 64kB+64kB という小さなメモリ空間しか使えなかったためでした。

平林 浩一, 2008-07-14