« 2022年2月 | トップページ | 2022年4月 »

2022.03.31

meta name="viewport"の役割

レスポンシブデザインを実現するといわれている

<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />

の役割がようやくわかってきました。

スマホでニュースサイトとかを読んでいると横幅にぴったりになるように適切な文字サイズになって拡大縮小ができないというサイトがありますが、そのように「拡大縮小できない」ようにするのがuser-scalable=noのようです。

initial-scaleは、デフォルトの拡大率を指定します。

これらの設定はスマホで閲覧するときには効果がありますが、PCでChromeで見ているとviewportの指定が無視される場合があるのかもしれません。

スマホ用のページでfont-size:x-largeとかを使うとすぐに画面の幅からあふれてしまいますが、スマホ用に合わせて標準サイズだけで360pxくらいの幅を前提に作ると、PCでは小さすぎて見にくくなります。

スマホとPCの両方で良い感じに見えるサイトを作るには、

<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />を記述。

② CSSで@mediaを使って、画面が小さい場合と大きい場合で、文字のサイズを変える

@media screen and (max-width: 679px) { /* 画面が小さい */

@media screen and (min-width: 680px) { /* 画面が大きい */

③ スマホ用のページでは、@media screen and (max-width: 679px) { の後に

body{
min-width: 360px;
margin: 0;
padding: 0;
}

として、画面の幅は360pxを前提として作るのがよいかと思います。

 

| | コメント (0)

2022.03.27

WebのユーザインタフェースからZYNQのboot.binを更新する

ZYNQやZYNQ UltraScale+は、SDカードの第一パーティションに書かれたboot.binから起動します。このboot.binを書き換えればFPGAのPLが更新できるのですが、やり方にはいろいろあります。

一番基本的で面倒なやり方はWindows PCにUSB SDカードリーダを挿して書き換えることです。納品した製品でお客様にファームウェア更新と称してSDカードの書き換えをやってもらうとかなりの確率で失敗します。Windows PCに挿したときに「このカードはWindowsでフォーマットされていません」と出てきて「はい」を押してしまい、第二パーティションを壊してしまうからです。手順書では防げません。

次の策は、SCPやFTPでファイルを転送してLinux上でcpコマンドを使ってSDカードに転送する方法。これをお客様にやってもらおうとしても、すべてのお客様がLinuxを使えるわけではないので、パーミッションとかroot権限とかsudoでハマってしまうので、これもよくありません。

やはり、ZYNQで動くLinuxの上でWeb画面を作って、Web上のユーザインタフェースからZYNQのboot.binを更新するようにするべきです。

実際にやってみた図が次のとおりです。

まず、Webアプリに管理者画面を作り、boot.binを送信するためのボタンを作ります。

Webupdate1

これを押すと、下の図のようなダイアログが開くようにしておきます。ダイアログはjQuery UIというもので簡単に作れます。

Webupdate2

「ファイルを選択」を押すと、Windowsのファイル選択ダイアログが開くので、boot.binを選びます。

Webupdate3

元の画面に戻り「送信」を押すと、ファイルが送信されて、そのファイルの情報が送り返されてきます。

Webupdate4

すると、SDカードの第一パーティション(/mnt にマウント済み)に、送ったファイルが書き込まれる、というしくみです。

Webupdate5

さて、これをどうやって実現しているかを説明します。まず、ダイアログとかをなくした必要最小限のHTMLファイルを示します。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="./js/jquery-2.1.3.min.js"></script>
<script>
<!--
var bootfile = null;
$(document).ready(function(){
$("#file_uploader").change(function(e){
bootfile = e.target.files[0];
});
$("#upload_button").click(function(e){
var reader = new FileReader();
reader.onloadend = function() {
upload("●●●●", bootfile.name, Math.floor(bootfile.lastModified / 1000), reader.result);
}
reader.readAsArrayBuffer(bootfile);
});
});
function upload(password, filename, lastModified, binaryData)
{
$.ajax({
type: "POST",
url: './cgi-bin/upload.pl?password=' + password + '&filename=' + filename
+ '&lastModified=' + lastModified + '&_=' + Date.now(),
cache: false, //キャッシュを使用しない
processData: false, // エンコードしない
data: binaryData
}).done(function(data){
console.log(data);// 成功時の処理
}).fail(function(){ //失敗時
console.log(data);// 通信失敗時の処理
});
}
-->
</script>
</head>
<input type="file" id="file_uploader"/><input type="submit" id="upload_button">
</body>

簡単に説明すると、まず、

<input type="file" id="file_uploader"/><input type="submit" id="upload_button">

で、ファイル選択ボタンを作っています。非常にシンプルなフォームですが、

Simpleform

というのが出来ます。

「ファイルを選択」ボタンが押されてファイルが選択されたときにbootfile = e.target.files[0];が実行されて、そのファイルの情報がbootfileという変数に入ります。

送信ボタンが押されると、以下の処理が実行されます。

var reader = new FileReader(); // ①
reader.onloadend = function() {
upload("●●●●", bootfile.name, Math.floor(bootfile.lastModified / 1000), reader.result);//②
}
reader.readAsArrayBuffer(bootfile);//③

分かりにくいのですが、①③②の順序で実行されます。

FileReaderというのはJavaScriptでファイルを読み込むためのクラスで、バイナリファイルを読み込むには、reader.readAsArrayBuffer(bootfile)を実行します。なお、FileReaderにはreadAsBinaryString()という関数もあるのですが、0x80以上の値を読み込むとエスケープ処理が行われてしまって正しいデータになりません。readAsArrayBufferを使ってください。

readAsArrayBuffer()の処理は非同期動作になっていて、読み込みが完了するとreader.onloadendで指定した関数が呼び出されます。上のコードでは、完了処理の中でuploadという関数を呼び出しています。

uploadは第一引数がApacheユーザ(デフォルトではwww-data)のパスワード、第二引数がファイル名、第三引数がファイル更新時刻のUnix時刻、第四引数がファイルの実体です。

ファイル名と更新時刻は、bootfile.filename,とbootfile.lastModifiedに入っています。JavaScriptの時刻はミリ秒単位なのでUnix時刻に直すには1000で割ります。

upload関数は

$.ajax({
type: "POST",
url: './cgi-bin/upload.pl?password=' + password + '&filename=' + filename
+ '&lastModified=' + lastModified + '&_=' + Date.now(),
cache: false, //キャッシュを使用しない
processData: false, // エンコードしない
data: binaryData
}).done(function(data){

を呼んでいます。

送信方法はPOSTにします。GETだとデータがURIにエンコードされて送られるので、ファイルという大きな実体を送信するのには向いていません。ところが、POSTは標準入力に送られるので「区切り」というものがなく、あえて?や=やエンコードを駆使して「区切り」を作るのは無駄だし処理速度も落ちます。

そこで、GETとPOSTを併用した方法で送るようにします。ファイルの実体はPOSTで送るためtype: "POST"としておきつつ、URLにファイル名や最終更新日などを自分でくっつけてGET風にすればよいのです。

これで、ファイルの情報はGETで、ファイルの本体はPOSTで送られるというハイブリッドな送信になります。

それから、生のバイナリデータを送るためには、$.ajax関数のオプションを

cache: false, //キャッシュを使用しない
processData: false, // エンコードしない
data: binaryData

とします。送っているbinaryDataというのは、FileReaderのreader.resultの部分です。

 

さて、これを受ける側のCGIですが、Perlで書くことにします。

作ったPerlのスクリプトの全体像は以下のようになります。

#!/usr/bin/perl
if ($ENV{'REQUEST_METHOD'} ne 'POST') {
die "not POST";
}
read(STDIN, $bindata, $ENV{'CONTENT_LENGTH'});
foreach $data (split(/&/, $ENV{'QUERY_STRING'})) {
($key, $value) = split(/=/, $data);
$value =~ s/\+/ /g;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
$value =~ s/\t//g;
$in{"$key"} = $value;
}
my ($sec, $min, $hour, $mday, $mon, $year);
($sec, $min, $hour, $mday, $mon, $year) = localtime($in{'lastModified'});
$year += 1900;
$mon += 1;
print "Content-Type: text/plain; charset=UTF-8\n\n";
if ($in{'filename'} eq '') {
print "ファイル名が空です";
}
print "ファイル名は $in{'filename'} です。<br>";
print "ファイルの日付は";
printf("%04d/%02d/%02d %02d:%02d:%02d\n", $year ,$mon, $mday, $hour, $min, $sec);
print "です。<br>";
open(OUT, "> /var/tmp/$in{'filename'}"); # /var/tmpに一時ファイルを保存
binmode OUT; # バイナリモード指定
print(OUT $bindata); # 読み込んだデータを出力
close(OUT);
utime $in{'lastModified'}, $in{'lastModified'}, "/var/tmp/$in{'filename'}"; # 更新時刻を変更
chmod 0666, "/var/tmp/$in{'filename'}"; # パーミッションを666にして誰でも消せるようにする
my $file_size = -s "/var/tmp/$in{'filename'}"; # ファイルサイズを取得
print "保存したファイルサイズは $file_size です。";
system("echo $in{'password'} | sudo -S cp -p /var/tmp/$in{'filename'} /mnt/$in{'filename'}"); # コピー
unlink "/var/tmp/$in{'filename'}"; # 一時ファイルを削除
exit;

最初に read(STDIN, $bindata, $ENV{'CONTENT_LENGTH'}); を実行し、POSTで送られてきたファイルの実体を$bindataに入れます。

次に、$ENV{'QUERY_STRING'}で、URIにエンコードされたGET部分のデータを取ってきて連想配列に格納します。

そうしたら、$bindataを一時ファイル "/var/tmp/<ファイル名>"に出力し、そのパーミッションと日付を変更します。

最後に、この一時ファイルを目的の場所に配置するため、

echo $in{'password'} | sudo -S cp -p /var/tmp/$in{'filename'} /mnt/$in{'filename'}

というコマンドを実行します。

ここで、/mntというのは、

/dev/mmcblk0p1 /mnt

で、SDカードの第一パーティションをマウントしたディレクトリです。/mntに書き込むにはroot権限が必要です。

root権限が必要なファイルをCGIから書き換えるために、一度 /var/tmp に作った後でsudo cpをします。

sudoにはパスワードを与えるそのもののオプションはありませんが、sudo -Sとすることでパスワードを標準入力から受け取ることができるようになります。そのため、echoからパイプでパスワードを渡して、sudo -Sを実行しています。

sudo cpをCGIから実行できるようにするには、/etc/sudoersに

www-data ALL=(ALL:ALL) /bin/cp

の設定が必要です。

以上のようにすることで、WebアプリからCGIを通じてboot.binが書き換えできるようになります。

ZYNQだけではなくZYNQ UltraScale+でも同じようにできるはずですし、このやり方を応用すれば、システムの重要なファイルをWebから送り込んで更新することもできるようになります。

GETで自由にオプションを送れるので、正規の者が作ったファイルかどうかを確かめるような鍵やハッシュなどを送るようにすることもでき、必要に応じてセキュリティを高めることもできます。

 

| | コメント (0)

2022.03.25

CGIからrebootなどのroot権限が必要なコマンドを実行する

ZYNQで作る組み込みIoT機器向けの管理者パネルを作っています。

rebootやserviceといったコマンドをCGIから動かしたいのですが、ここには大きな壁が立ちはだかっています。

rebootはroot権限でしか動かすことができませんが、reboot.cgiの中でsudoをしてもリブートしてくれません。

#!/bin/bash
echo "Content-Type: text/plain"
echo ""
sudo reboot 2>&1

sudoの実行時にパスワードが求められるので、CGIの中からこのようには動かせないためです。

sudoのパスワードを自動的に与えるには-Sオプションを使います。-Sオプションを使うと標準入力からパスワードを入力するようになるので、

echo ●●●●● | sudo -S reboot 2>&1

として、CGIの引数にパスワードを渡せばこれでリブートできる・・と思ったのですが、これもダメでした。

apache2を実行しているユーザは、apacheのバージョンやディストリビューションにもよりますが、今の環境ではwww-dataというユーザになっているのですが、このユーザはsudoersに登録されていないためです。おそらく「reboot: ルートである必要があります」というエラーメッセージが闇へと消えていっているのでしょう。

cgiファイルの所有者をrootにしてsetuidしたりしてもダメでした。

結局どうするのが良いかというと、/etc/sudoersに

www-data ALL=(ALL:ALL) NOPASSWD: /sbin/reboot, /usr/bin/service

のように書いて、www-dataというユーザが、/sbin/rebootと/usr/bin/serviceをパスワードなしで使えるようにします。

これで、CGIの中から

sudo reboot 2>&1

だけで、再起動ができるようになります。

Reboot

よりセキュリティを高めるには、sudoersファイルにNOPASSWD:を指定するのではなく、

www-data ALL=(ALL:ALL) /sbin/reboot, /usr/bin/service

としておいて、CGIには

echo $1 | sudo -S reboot 2>&1

と書き、JQueryのajaxで呼び出すときのdataフィールドでパスワードを送ればよいのです。

$.ajax({
timeout: 5000,
url: './cgi-bin/ping.cgi',
data: password+"&"+addr,
async: true,
success: function(text){
var obj = JSON.parse(text);
$("#dialog_result").text(obj.message);
},
error: function (xhr, textStatus, errorThrown) {
if(textStatus == "timeout"){
$("#dialog_result").text("タイムアウトしました");
}
}
});

なお、www-dataユーザには、デフォルトではパスワードは設定されていないので、passwdコマンドで適当なパスワードを設定する必要があります。

 

serviceコマンドでWeb UIからサービスを起動/停止する機能も同様に実装しました。

具体的にはCosmo-Zのサーバプログラムcszserverと、WebSocketのサービス、それからSamba関係のsmbdとnmbd、Linuxでの名前解決に使われるavahi-daemonを操作対象としました。

Service 

ボタンが押されたら動くJavaScriptを、

$.ajax({
timeout: 10000,
url: './cgi-bin/servicectl.cgi',
async: true,
data: password+"&"+name+"&"+enable ,
success: function(text){
$("#dialog").html(text);
},
error: function (xhr, textStatus, errorThrown) {
$("#dialog").html(textStatus);
}
})

としておき、CGIのプログラムは、

#!/bin/bash
export TZ=JST-9
echo "Content-Type: text/plain"
echo ""
PASSWORD=`echo $1 | sed -e 's/\\\\//g' | awk -F\& '{print $1}'`
NAME=`echo $1 | sed -e 's/\\\\//g' | awk -F\& '{print $2}'`
ENABLE=`echo $1 | sed -e 's/\\\\//g' | awk -F\& '{print $3}'`
if [ "$ENABLE" == "true" ];then
ENABLE="start";
fi
if [ "$ENABLE" == "false" ];then
ENABLE="stop";
fi
echo $PASSWORD | sudo -S /usr/bin/service $NAME $ENABLE 2>&1

で、Webのボタンからサービスの起動/停止ができました。

sudoersに記述したプログラムだけをパスワード付きで実行できるようにしたので、セキュリティ的にもあまり脆弱性を作ることなく、Webから操作できる管理画面ができました。

 

| | コメント (0)

2022.03.24

DHCPが使えない環境でも使える「IoT機器の管理者画面」を作る

Linuxを使う組み込み機器の開発時には、/etc配下のファイルを自分でいろいろ書き換えてネットワークの設定は如何様にもできるのですが、お客様に納品した後だと急に難しくなります。

 

IPアドレスはDHCPで取得するか、組織のポリシーに従って固定すると思いますが、大学や研究機関やネットワーク管理が厳しい企業では、新しい機器をLANにつないでもDHCPからアドレスが得られません。

お客様に更新用ファームウェアをダウンロードしてもらおうと思っても、プロキシを通さないとIoT機器からインターネットに出られなかったりもします。時刻合わせでNTPを使うおうにも、NTPプロトコルが外に出られないため1970年1月1日のまま使い続けているという場合もあります。

そのため、ネットワークにつながらないと何が原因かを調べるのも大変です。お客様にUSB-UARTでログインしてコマンドを打ってもらって診断したり、ファイルを書き換えてもらうということがあったのですが、viの使い方を説明する必要があったりで、とにかく大変でした。

そこで、Web画面の管理者パネルからIPアドレスの確認や、PINGの試験をしたり、名前解決ができるか、WGETが通じるかという試験ができるようにしました。

Adminpanel1

目標は

  • USB UARTのコンソールからLinuxの設定ファイルを触らなくてもよいようにする
  • DHCPが使えない環境にも、DHCPが使える環境にも、どちらにも対応する
  • ネットワークセキュリティが厳しい組織でもIoT機器を簡単に使えるようにする

 

Adminpanel2

Adminpanel3

Adminpanel4

 

こういうことをするには、基本的にはajaxを使ってCGIの中でawkやsedを使いまくり、得られらた結果をjsonで整形して返すということで実現しています。

一例を挙げると、ホスト名やIPアドレスを得るスクリプトは、

HOSTNAME=`hostname`
IPADDR=`ip a | awk '/eth0\:/ {ETH0=1} (/inet/ && ETH0) {print $2}'`
MACADDR=`ip a | awk '/eth0\:/ {ETH0=1} (/link\/ether/ && ETH0) {print $2}'`
echo -n ","
echo -n "\"hostname\":\"$HOSTNAME\" "
echo -n ","
echo -n "\"ipaddr\":\"$IPADDR\" "
echo -n ","
echo -n "\"macaddr\":\"$MACADDR\" "

とか、

PINGの試験であれば

IP=$1
COUNT=0
if ping -c 1 $IP > /dev/null; then
COUNT=$(expr $COUNT + 1)
fi
if ping -c 1 $IP > /dev/null; then
COUNT=$(expr $COUNT + 1)
fi
if ping -c 1 $IP > /dev/null; then
COUNT=$(expr $COUNT + 1)
fi
if ping -c 1 $IP > /dev/null; then
COUNT=$(expr $COUNT + 1)
fi
if [ $COUNT -eq 0 ];then
echo "{\"message\":\"$IP へのPINGは失敗しました ($COUNT/4)\", \"result\":false}";
else
echo "{\"message\":\"$IP へのPINGは成功しました ($COUNT/4)\", \"result\":true}";
fi

などと、しています。

 

情報取得系は難しくなく出来たのですが、書き換え系の処理はセキュリティが絡んできて難しくなります。

例えばホスト名を変更する作業は、/etc/hosts 、/etc/hostname 、hostnameコマンドの実行、と3つやることがありますが、root権限が必要でもあり、こういう操作がWebのCGIからできるようにするとだんだんセキュリティが怪しくなってきます。

どうするのが一番いいんでしょうね。

 

そもそもこのような管理パネルを作るきっかけとなったのは、どんなお客様でも最初に使い始めるときにUSB-UARTのコンソールをつないで/etc/network/interfaceとかを書き換えることなく使い始められるようにするためです。

セキュリティに厳しい組織では、新規のIoT機器をつないでもDHCPでアドレスが設定されるわけではありません。何かの新しい機器を所内LANにつなぐには情報システム部門に申請しないといけないとか、そもそもLANに接続することが許されていないとか、いろいろあります。

DHCPでアドレスがもらえない環境ではPCと1対1でつないで通信をする場合のために、169.254.0.0/16のAPIPAのアドレスが使えるようにもしました。

やり方は、apt install avahi-autoipd でインストールして、avahi-autoipd eth0 & で開始です。Ubuntu 14ならば、/etc/init.rcに書いておけば、システム起動時に自動的に起動させることもできます。

/etc/init.rcに avahi-autoipd eth0 & を書いて再起動してみると、

Autoipd1_20220325185601

このような感じでアドレスが自動で設定できました。

 

avahi-autoipdは169.254のIPアドレスを設定するサービスで、avahi-daemonは名前解決のサービスです。いずれもZEROCONFというシリーズのものです。

avahi-daemonというサービスが名前解決をしてくれるので、IoT機器と1対1でつないだWindows PCのブラウザにホスト名を入れれば、Webから管理者パネルにアクセスできました。

Apipa_20220325185901

詳しいことはわかりませんが、avahi-autoipdで設定したIPアドレスはavahi-daemonで名前解決しないといけないようで、nmbdではうまく行かないようでした。

DHCPが使えない環境でも1対1でつないで、IoT機器に「名前」で接続することができ、管理パネルを通じてアドレス情報を得ることができるようになりました。

さて、次はIPアドレスの手動設定機能などを作っていくことにします。

 

| | コメント (0)

2022.03.23

ZYNQで作るロックインアンプ、0.1uVへの挑戦

ZYNQで作るロックインアンプでどこまで小さな電圧を測定できるかにチャレンジしています。

EDU33212Aの自動コントールができるようになったので、1mV~1000mVまでの正弦波を作り、それを抵抗で1/1000にしてロックインアンプに入れてみました。

下の図は横軸がファンクションジェネレータの出力電圧(を1000分の1にしたもの)、縦軸はロックインアンプの出力。

Lockin_1000uv

非常に綺麗な直線に見えるのですが、(出力電圧-入力電圧)のグラフを作ってみると、結構凸凹しているのが見えてきました。

Lockin_1000uv2

特に、上のオレンジの矢印の部分で誤差の傾向が変わるのが見えます。

この電圧を過ぎるときにファンクションジェネレータからカチカチと音が聞こえてくるのです。おそらく、ファンクションジェネレータの中でアッテネータのリレーが切り替わっているのでしょう。

なるほど、市販のファンクションジェネレータは幅広い電圧範囲で高いS/Nを確保するため、DACの出力値に振幅を乗算しているのではなく、できるだけDACの振幅は大きく取って複数のアッテネータを切り替えてダイナミックレンジを確保しているのでしょう!!

だから、切り替えポイントでは2~3mVの誤差が生じることがあるのでしょう。今回の実験でその切り替えが見えてしまいました。

 

このファンクションジェネレータでは、0~1000uVの範囲でフラットで正確な出力振幅を出すのは難しいので、0~100uVの範囲に絞って検証を続けるしましょう。

電圧を変更した後は0.5秒ほど待たないと電圧が安定しないようなので、次のグラフはゆっくりとスキャンして取ってみました。

 

ゼロに近いところが_/という形になっているのが見えますでしょうか?本来ならば0Vを入れたら0Vを出さなければならないのですが、そうなっていません。

Lockin_100uv_3v3trig_20220324021601

今回も(出力-入力)の誤差グラフを作ってみました。

Lockin_100uv

オレンジのところがリレー切り替えポイントです。横軸は印加電圧、縦軸は誤差です。

全体的に誤差が+のほうに出ていて、特に0~10uVくらいのときに誤差が大きく出てしまっているのがわかります。

つまり、0Vを入れても0Vと言わない計測器になってしまっています。

 

この原因ですが、ロックインアンプの参照信号としてファンクションジェネレータからのトリガ信号を使っていたのですが、この振幅が3.3Vもあるからです。3.3Vの振幅をCosmo-Zの別のアナログ入力に入れると、クロストークして信号の入力に回り込んでしまいますが、クロストークの減衰が-120dBあったとしても3.3uVのノイズとして乗ってしまいます。

ロックインアンプでuVオーダーの信号を測るときに、Vオーダーの信号が近くにあってはいけなかったのです。

トリガの代わりにファンクションジェネレータのもう一つのチャネルで振幅10mVくらいのマイルドな信号を作り、リファレンス入力としたところ、見事にゼロ付近の誤差が消えました。

Lockin_100uv_lowtrig

誤差グラフを取ってみると、計測値の誤差は約0.4uV以内に収まっていることがわかります。

Lockin_100uv_lowtrig_err

0~10uVの範囲での誤差も無視できるほどになったので、さらに低いレンジを目指します。

EDU33212Aの最小振幅は1mVなので、抵抗のアッテネータを1/10000にして、0.1uVの信号まで出せるようにします。

0.1uVの信号って、1000倍しても下の青い波形なんです。人間には全く見つけられない。

01uv

でも積分計算するとホワイトノイズや相関のないノイズなら打ち消しあって信号の振幅が見えてきます。

0.1uV~1.5uVの正弦波を入れてみた結果です。

Very_low_voltage

 0.4V以下ではプラス方向の誤差が生じてきてしまっていますが、0.4V以上では概ね直線に乗っているのがわかるかと思います。

つまり、入力電圧が0.4uV以上あれば正確に振幅が測れるようになりました。

 

さて、この様子を動画に残そうとしたら波形が不自然にピクピクと動ていていたのです。

この状態で誤差のグラフを作ってみると酷い有様です。

Wlan

原因は動画を撮ろうとしてノートPCでリモートデスクトップ接続したため、無線LANの電波を拾ってしまったことが原因のようでした。

無線LANのノイズは50usくらいの短い時間に入るのですが、繰り返し周期とは関係なしに入ります。積分しても0になりません。長時間平均しても0になりません。だから、ノイズとして残ってしまいます。

初段のOPアンプでμVオーダーの信号を1000倍に増幅しているので、こういったものも拾ってしまうのかもしれませんね。

 

| | コメント (0)

2022.03.22

SCPIを使ってKeysightのファンクションジェネレータを自動操作

Keysightのファンクションジェネレータを入手したので、ZYNQ&高速ADCでロックインアンプを作る実験を再開しました。

Lockin_config1

ファンクションジェネレータから出力した振幅1mV~100mVくらいの正弦波をアッテネータで1000分の1にして、それをCosmo-Zの基板に入れてロックインアンプで元の振幅を推定するということをしようとしています。

Lockin_config2

Keysightの比較的新しい計測器はLANで接続することができて、遠隔操作ができるようになっています。

下の画面の右のほうに映っているPATHWAVEというツールがKeysightの計測器をLANで遠隔操作できるツールなのですが、

Keysight_remote

せっかくなら、自分で作ったプログラムから操作したいじゃないですか。

ファンクションジェネレータのパラメータを変えて、計測される値を自動的に記録していきたい。

そうすれば、家から会社の装置を操作できるので、出勤しなくても、夜中に布団の中からでも操作できる。なんて素晴らしい。

 

Keysightの計測器はSCPIという体系のコマンドで操作できるようになっています。読み方は確かスキッピーだったと思います・・

イメージとしてはGPIBの近代化といった感じです。

 

SCPIを使うには、ポート5024にアクセスすればよいようです。

TeraTermからTELNETを使ってログインしてみると、このような感じで対話型でアクセスできます。

Scpi1

コマンドはテキストでやりとりし、エラーが起きると本体からピッと音が鳴ります。遠隔操作だと音が聞こえないのが残念。

このコマンドを、TeraTermを開くのではなくて、ZYNQ上のシェルスクリプトやプログラムから出力したりできればいいんです。

 

最初はncという万能ネットワークツールでやろうとしたのですが、実は、SCPIは" > "のプロンプトが出てから次のコマンドを送らないといけなかったり、意外と「待ち」が重要なようです。

そこで、どのようなデータがやり取りされているかを調べてみたら、ただのテキストではなく、FF FB 01や、FF F9というバイナリなコードが含まれていました。

Scpi2

KeysightのマニュアルにはこのFF F9等の説明はなかったのですが、

http://www5e.biglobe.ne.jp/~aji/3min/55.html

のサイトによれば、0xffはTELNETエスケープシーケンスというもので、0xff 0xf9はGo Aheadという「送信するように受信側にうながす」という意味だそうです。つまり、" > "を待ってから送信するのではなく、0xff 0xf9を待ってから送信するようにすればよいようです。

ちなみに、先頭にある0xff 0xfb 0x01はオプションを使用することを宣言しているものであるようです。

SCPIのコマンドは、SOURce2:VOLT 0.001 のような非常に素朴なコマンドです。

こういうのを連続して送っていけばファンクションジェネレータの出力を自作プログラムから操作できるのですが、どうやら、振幅を変えたら0.5秒くらい待たないと安定しないようです。0.1秒だと計測時の誤差が大きくなります。

SCPIでEDU32212Aの振幅を操作し、Cosmo-Zで波形をキャプチャしているようすを動画にしました。

ソケットプログラミングができれば、SCPIの操作は実に簡単で、

    connect(sock, (struct sockaddr *)&server, sizeof(server));

か何かで開いておいたソケットに対して、以下のような関数を呼び出すことでコマンドを送ります。

void scpi(int sock, const char *cmd) {
unsigned char *buf = new unsigned char[4096]; // 良くないけど、バッファサイズ決め打ち!
printf("send command '%s'\n",cmd);
sprintf((char*)buf,"%s\r\n",cmd);
send(sock,buf,strlen((char*)buf),0);
get_prompt(sock, buf, 4096);
}
 
int get_prompt(int sock, unsigned char *buf, int len) {
int n;
int wp = 0;
while(1) {
n = recv(sock, &buf[wp], 256, 0);
for(int i=0;i<n-1;i++) {
if((buf[wp + i] == 0xff) && (buf[wp + i + 1] == 0xf9)) {
wp += n;
buf[wp] = '\0';
return wp;
}
}
}
}

なお、SCPIでは?を付けることで現在の設定値を知ることができるので、読み込んだ結果をprintfで表示しておくと便利です。

このような感じで、自分で作ったプログラムから計測器を遠隔操作することができ、パラメータを変えて自動計測できるようになります。

 

| | コメント (0)

2022.03.21

WindowsのGamebarでダイアログやポップアップも含めて録画する方法

Windowsでは、Windowsボタン+Gを押すと、XBox GameBarというのが起動して画面を録画する標準機能が備わっています。しかし、GameBarの録画ではダイアログやポップアップが映りません。

また、デスクトップ全体を録画することもできません。

そのためアプリの画面を全部撮っておいて手順書を作成するなどには使いにくいのですが、Windowsリモートデスクトップ経由でやると画面全体を録画することができてしまいます。

つまり、録画したいアプリをWindows 10 Proで動作させて、リモートデスクトップ画面をXBox GameBarで撮ればよいのです。

これで画面録画ソフトを使わずに、標準のGameBarだけで手順書の作成に使える動画が撮れますね。

 

| | コメント (0)

2022.03.20

水戸の梅まつりに行ってきました

この時期だけ開く常磐線の臨時駅「偕楽園」。

今日は天気もよかったので偕楽園に行ってきました。

少し肌寒いのですが梅は満開、見ごろでした。

 

ちょっと一服してお団子とお茶でもと思ったのですが、どこのお茶も「梅抹茶」とか「梅こぶ茶」とか、梅が入っている・・

普通のお茶が飲みたい・・

 

| | コメント (0)

2022.03.18

VSCodeで複数のCファイルをコンパイルしてリンクする方法

最近、ZYNQのプログラムをVSCodeで書いてarm-elf-gccでコンパイルまでするようにしています。

小さなプログラムなら下記のようなtasks.jsonを書いて、CTRL+SHIFT+B一発でコンパイルできてよかったのですが、

{
    // tasks.json 形式の詳細についての資料は、
    // https://go.microsoft.com/fwlink/?LinkId=733558 をご覧ください
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Deploy", //①ビルド後にターゲットボードへコピーする
            "type": "shell",
            "command": "cp",
            "args": [
             "${fileDirname}/${fileBasenameNoExtension}",
              "//cosmoz/Share" //コピー先のターゲットボードのホスト名とフォルダ名
            ],
            "problemMatcher": [],
            "dependsOn": "Build by ARM g++",
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "type": "shell",
            "label": "Build by ARM g++",
            "command": "D:/Xilinx/SDK/2018.3/gnu/aarch32/nt/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-g++.exe",
            "args": [
                "-g",
             "[${file}]",
                "-L${workspaceFolder}",
                "-I../cszapi/src", //ライブラリのインクルードディレクトリ
                "-L../cszapi/Debug", //ライブラリのあるフォルダ
                "-lcszapi", //ライブラリ名
                "-o",
             "${fileDirname}\\${fileBasenameNoExtension}"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": "build"
        }
    ]
}

 

これだと、現在編集中のファイルをgccでコンパイルして実行ファイルを作るので、プログラムを複数のファイルに分割した場合には対応していません。

ですが、プログラムがだんだん大きくなってきてくるとソースを複数に分割する必要が出てきました。

「VSCode C ファイル 複数」のようなキーワードで検索しても出てこないのですが、基本的にはcmakeを使うかべた書きするしかないようです。実際にVSCodeのcmake extensionを入れて試してみたのですが、cmakeが呼び出すmakeがborlandのmakeを呼び出してしまったり、cmakeの環境構築中に動くサンプルのビルドに失敗します。

cmake extensionを入れれば誰の環境でも同じように使えるというわけではなさそうです。

そこで、ベタベタに書く方法を試してみました。

次のtasks.jsonは、もともとcszserver.cppというファイルがあったのを、cszserver.cpp、eventcap.cpp、uppmca.cppに分割しています。

{
    // tasks.json 形式の詳細についての資料は、
    // https://go.microsoft.com/fwlink/?LinkId=733558 をご覧ください
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Deploy",
            "type": "shell",
            "command": "cp",
            "args": [
                "${fileDirname}/cszserver", // コピーしたい実行ファイル名を直接書く
                "//cosmoz/Share"
            ],
            "problemMatcher": [],
            "dependsOn": "Build by ARM g++",
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "type": "shell",
            "label": "Build by ARM g++",
            "command": "D:/Xilinx/SDK/2018.3/gnu/aarch32/nt/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-g++.exe",
            "args": [
                "-g",
                "${workspaceFolder}/cszserver.cpp", // 分割したソースファイル名を直接書く
                "${workspaceFolder}/eventcap.cpp", // 分割したソースファイル名を直接書く
                "${workspaceFolder}/uppmca.cpp", // 分割したソースファイル名を直接書く
                "-L${workspaceFolder}",
                "-I../cszapi/src",
                "-L../cszapi/Debug",
                "-lcszapi",
                "-o",
                "${fileDirname}/cszserver" // 作りたい実行ファイル名を直接書く
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": "build"
        }
    ]
}

これで、3つのファイルをコンパイルしてリンクしてくれるようになりました。

さらに一方進めて、ファイルごとに分割してコンパイルするようにしてみました。

{
    // tasks.json 形式の詳細についての資料は、
    // https://go.microsoft.com/fwlink/?LinkId=733558 をご覧ください
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Deploy",
            "type": "shell",
            "command": "cp",
            "args": [
                "${fileDirname}/cszserver",
                "//cosmoz/Share"
            ],
            "problemMatcher": [],
            "dependsOn": "Link",
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "Link",
            "type": "shell",
            "command": "D:/Xilinx/SDK/2018.3/gnu/aarch32/nt/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-g++.exe",
            "args": [
                "${workspaceFolder}/cszserver.o",
                "${workspaceFolder}/eventcap.o",
                "${workspaceFolder}/uppmca.o",
                "-L${workspaceFolder}",
                "-L../cszapi/Debug",
                "-lcszapi",
                "-o",
                "${fileDirname}/cszserver"
            ],
            "problemMatcher": [],
            "dependsOn": "Build by ARM g++",
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "group": {
                "kind": "build",
            }
        },
        {
            "type": "shell",
            "label": "Build by ARM g++",
            "command": "D:/Xilinx/SDK/2018.3/gnu/aarch32/nt/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-g++.exe",
            "args": [
                "-g",
                "-c",
                "${file}",
                "-I../cszapi/src",
                "-o",
                "${fileDirname}\\${fileBasenameNoExtension}.o"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": "build"
        }
    ]
}

 

dependsOnとLabelを利用して、DeployというタスクがLinkに依存するよーうに、LinkがBuild by ARM g++に依存するようにしています。これで、一番上にあるDeployを起動しようとすると、編集中のファイルをコンパイルして.oを作り、3つの.oをリンクして実行ファイルを作ってコピーするようになりました。

Vscode

ただし、すべての.oを個別に作ってあげないとリンク時にmissingになってしまうので、まだ完璧ではありません。

さらに完成度を上げるには、タイムスタンプでコンパイルするべきか否かを判断するような書き方を調べる必要があると思います。

 

| | コメント (0)

2022.03.16

流通在庫業者に問い合わせてみた結果わかったこと

TI社のTPS82085という電源IC(モジュール?)が必要で、流通在庫業者にいろいろ問い合わせてみました。

このICは8ピンのSOPサイズのPoL電源で3A取れるというもので、正規業者のDigikeyやMouser等では400~500円程度のICなのですが納期が約1年となっています。昨年秋からこのような状況が続いています。

TPS82085にはTPS82085SILTとTPS82085SILRがありますが、流通在庫検索のOctopartで調べてみると、正規業者(緑)の枠内は在庫ゼロ。

Silt

流通在庫業者(赤)は在庫がありそうだけど単価205 USD、つまり2万2千円くらいでしょうか。500円の部品がいきなり2万円超えです。

TPS82085SILRのほうも同じような感じでした。

Silr_20220317121401

Octopartには単価は出てこないものの在庫を持っていそうな業者も含めると、Win Source Electronics、Cytech Systems、Heisener Electronics、Lanka Micro、Bison Technologies、でしょうか。あと、googleで探してUtmelというのもありました。

こういった業者に片っ端から見積依頼を出してみました。

  • Extreme・・在庫なし
  • Utmel(香港?中国系)・・$161 1~3日で出荷 DC21
  • Heisener・・見積もり受付の返事の後具体的な回答なし
  • Bison・・見積もり受付の返事の後具体的な回答なし
  • Vylian(アメリカ)・・$224 1~2週間
  • Cytech(香港)・・$155 2~3週間 DC21
  • Demsay(トルコ?)・・$40
  • Win Source(中国深圳?)・・$185(Webの記載) 1週間くらいで出荷
  • epart123(香港?)・・・$158(Webの記載) MOQ250 DC20+
  • Lanka Micro(?)・・・返事なし。注文可能。単価不明

どうやら中国系はだいたい$160くらいが相場なので、2021年に出た在庫を買いまくって数千個ストックしていて、中国系の業者間で情報を共有しているのではないでしょうか。Utmelだけは納期が1~3日ということなので、在庫が実際にあるとしたらここにあるのかもしれません。Vylianはアメリカ系ですが、流通在庫を探して中国の上記のものを引用したのでしょう。

買い占めて値段を釣り上げて転売する転売ヤーなわけですね。

それとは一線を画すのがトルコのDemsayです。ここはさっぱり予想ができませんが、交渉してみる価値はあるかもしれません。

ただ、上に出てくる会社はまだ「実物」を扱っている可能性があるだけマシと言えます。

 

マジなやつはi-components。なかなか検索にも出てきませんが、

Icomp1

なんと、定価以下で在庫がたっぷりあるではないですか!!XILINXのFPGAの在庫も、驚くほどたっぷりあるし、定価の半額程度。

ところが、調べてみると、i-compontnsとcomponents-store、components-hkなどいろいろな名前があるらしく、先人による注意喚起がみつかりました。

ただし、韓国の「I-Components Co Ltd」というKOSDAQ上場企業とは別のようです。

 

supplierblacklistというサイトがあって、詳しいレポートが投稿されていました。

https://www.supplierblacklist.com/2019/10/25/components-store/

https://www.supplierblacklist.com/2019/03/05/i-components-4/

https://www.supplierblacklist.com/2021/06/05/i-components-6/

その手口をまとめると銀行送金で支払いを行うと「商品が発送できなくなった」旨のメールが来るそうです。ある被害者さんはsupplierblacklistに投稿したら、i-componentsの人が「私たちも被害者です」という投稿をしたらしい。

なるほどね。訴えられたら「自分たちも被害にあって入荷できない」という被害者ポジションを取るのかもしれません。こんなのを野放しにしている香港警察って仕事してるのだろうかと思って、所在地を調べてみたら、住所は公式サイトに載っているのですがgoogleマップには出ていません。

Icomp2

 

この住所で検索してみると、別の会社が見つかりました。

Purplestone

 

ワンフロアに2社入っているのか、それともここには存在していないのか。香港なら良くあることですよね、きっと。

検索に出ないのも理由があります。

みなさん、ご注意を!

| | コメント (0)

2022.03.14

波形を重ねて表示する機能がひとまず動いた

Cosmo-Z Scopeで波形を重ねて表示する機能がひとまず動くようになりました。

まず、Cosmo-Zのデータファイルをインポートします。

下の波形はNaIシンチレータで取ったバックグラウンド+K40です。

Wave1_20220315142401

ここで右クリックして、「イベント波形を重ね合わせて表示」を選びます。

Wave2_20220315142401

すると、下の図のように波形が重ね合わせて表示されます。

コントロールパネルで何番から何番までの波形を表示するかが選べます。

Wave3_20220315142401

ただ、使い勝手はよくないのでユーザインタフェースの改良が今後の課題になってくると思います。

| | コメント (0)

2022.03.13

波形の重ね合わせプログラムが破綻した原因

波形を重ね合わせて表示するプログラムが破綻した原因はデータ構造にありました。

Cosmo-Zでは「イベント」と言って、放射線などのイベントが発生したときだけデータを記録するというモードがあります。

このイベントを上手に扱おうと、波形データの内部構造を2次元配列にして、下の図のような構造に書き換えました。

Dataarray

不連続な波形を扱うためこのような仕組みに書き換えたのですが、この変更が間違いでした。

各data[n]は長さが任意なので、data[n][i]を指すポインタを1増やしたいときには、iがdata[n].Lengthと比較してオーバーするならnを1増やすということをしなければなりません。

これは想像以上に面倒な処理でした。

非常に注意深くプログラミングすることで無事に表示はできるようになりましたが、描画速度は遅くなりました。

データはフラットにしなければならないと痛感し元に戻すことにしました。

結局のところ、データは連続した1次元配列にして、代わりに各イベントのスタート地点を示すcstart[]と、イベントの長さを示すclen[]いう配列を用意することにしました。

| | コメント (0)

2022.03.12

非連続波形の表示がうまくいかない

Cosmo-Zでは、イベント波形の表示させかたで、重ね合わせて表示させるというモードを作っています。

下の図は放射線のバックグラウンドを取ったものです。連続してはいないけど5秒間にたくさんのイベントが発生していることがわかります。

Wave1_20220313162001

これを重ねて表示すると、こうなります。 

Wave2_20220313162101

しかし、何かの具合によっては、最大値、最小値を表す縦棒がうまく表示されません。

10秒間に起きたイベントを表示させてみたのだけど、

Ijou

うーん、絶対にありえない。何かがおかしい。

重ね合わせは表示できるようになったけど、それを時間軸上に展開するほうがうまくいかなくなりました。

 

以前はうまく表示されていたのですが、今回、イベント波形を1つ1つの「チャンク」という単位に切り分けて二重配列にしたのですが、これはよくないアイデアだったようです。

このプログラムは気絶するほど難解で、いったい何がどうなっているのか・・

| | コメント (0)

2022.03.11

波形の重ね合わせ表示

Cosmo-Z Scopeの新しいバージョンのリリースをアナウンスしようと思ったのですが、お客様から「波形の重ね合わせモードを作ってほしい」というリクエストがあったので、先にCosmo-Zで波形を重ね合わせて表示するモードを作っています。

結果として、まぁなんとかできました。

Overlap

ただし、1万個のイベントを重ね合わせたりすると、描画を1万回やることになるので重くなります。

処理時間的には最大でも100回くらいが妥当かなと思います。

すると、どの部分のデータを重ね合わせるかという問題が出てくるのでユーザインタフェースをどうするか悩みます。

| | コメント (0)

2022.03.10

Mouserで購入した部品と計測器がアラスカで不時着

MouserでOPアンプと、Keysightの任意波形ジェネレータを購入したのですが、3月5日にmechanical failureと表示されてからちっとも動きません。

Ups_engine_failure

UPSに電話して聞いてみると、「エンジントラブルで、エンジンを交換しないと動かない」からしばらくは動かないということでした。

なんのこっちゃという感じですが、本当にトラブルが起きているみたいです。

Ups767

https://mentourpilot.com/incident-767-loses-engine-in-climb-but-cant-land/

 

要約すると、アラスカのアンカレッジを飛び立った飛行機のエンジンから火が出たのを目撃した人がいて、飛行機はアンカレッジに戻らずフェアバンクス空港に着陸したようです。人的被害がなかったのはよかったのですが、その後、積み荷を全く動かそうという気配がないですね。

代わりの飛行機を用意して貨物を移すとかそういうことはせずに、エンジン交換をいつまでも待っている感じです。

荷物が届かなくて困っている人がいるとか考えないのでしょうか。それとも、フェアバンクスには貨物を移すための設備がないのでしょうか?

UPSって速く着くときは速いけど、トラブルが起きたときに何とかしようしないんですね。

 

| | コメント (2)

2022.03.09

Cosmo-Z Scope v0.8.3.0をリリース

Cosmo-ZをWindowsから操作するアプリ「Cosmo-Z Scope v0.8.3.0」をリリースしました。

Cszscope830

今回のバージョンでの更新点はいろいろあるのですが、主に放射線計測回路とMCAが使えるようになったことです。

放射線計測回路というのは、下の図のようなコントロールパネルで波形を整形する回路です

Upp_20220313165001

MCAというのは、下のグラフのように放射線のスペクトラムを表示する機能です。

Mca_20220313165601

ほかにも、過去にCosmo-Zのcaptureコマンドで取った放射線のイベント波形のファイルをインポートして、

Filemode1

それを右クリックでスペクトラムに変換する機能も付けました。

Filemode2

上の図は昨年9月にF-18を取ったときのものです。フッ素18は陽電子を放出するので511keVのγ線を出します。一緒に映りこんでいたバックグラウンドのK-40とTl-208のエネルギで荒く校正したら、F-18と思われるピークが511keV付近であることが確かめられました。

 

また、Webサイト上に使い方の説明を書き始めました。 

ダウンロードと説明はこちらです。↓

https://cosmoz.jp/usage/cszscope/index.html

 

どうぞよろしくお願いします。

| | コメント (0)

2022.03.08

Cosmo-Z Scopeで配色を変えられるようにする

Cosmo-Z Scopeはオシロスコープをイメージしているので黒い背景に波形を描いていきますが、逆に白い背景に描きたいときもあります。例えば、印刷するときとか。

そこで、画面の配色を変えられるようにしようとしているのですが、これがなかなか難しいことに気が付きました。

まず、だいたいのWindowsのアプリは下の図のような構成になっていると思います。

Kouzou1

下の層にはデバイスドライバやラッパ、独自形式のデータファイルを読み書きするライブラリや、数値計算など「窓のないライブラリ」があります。

その上に、メインのアプリとは別に開くフォームや、MDI子ウィンドウのクラス、ダイアログなど「窓を持った部品」や、Windowsで使う独自コンポーネント(ツマミとか)が並列して存在します。

そして、最上位にはメインのアプリがあります。

 

問題はライブラリ間で参照できるかです。

 

アプリの規模が大きくなってくると部品ごとにDLLに分けると思うのですが、そうするとお互いのプログラム間では自由に関数を呼び出すことができなくなります。Visual Studioでは「参照の追加」をしてプロジェクトを追加してあげて初めて他のDLLの中の関数を呼べることになります。

しかし、上から下の関数は呼べるけれども、下から上は呼べません。

Sanshou

メインフォームは最上位に君臨するので、メインフォーム中の関数をライブラリや子フォームの中から呼び出せないことになります。

例えば、

「ダイアログの中で『ボタン』を押すと、メインフォームの挙動が変わる」

ということをするには、MainFormにdelegateを用意してそのdelegateをダイアログに登録して、mainformに何かを通知したいときにはそのdelegateを呼び出します。ただしdelegateは面倒です。delegateの型を作ってからdelegateの変数(?)に値を代入したりとかやらねばならず、名前を付けるのにも困ります。

 

では、アプリの配色のような構造体的なデータをどこに保存すればよいのでしょう。

メインフォームにデータを格納するのは、下位の層から上位の層へはアクセスできないので良くありません。

そこで、考えたのは、最も下位の層にアプリで使うデータを格納したりする構造体を置くということです。

Baselib

こうすればフォームやダイアログ、メインフォームのすべての部分からアクセスすることができます。

色のパレットをこういう最下層に置くことで、すべてのフォームで統一の取れた配色を使えるようになりました。

下の図は暗い背景のオシロ風表示。

Wave_dark

これを明るくすると、

Wave_light_20220313173101

 

放射線のスペクトラムも、暗い背景と

Mca_dark

明るい背景

Mca_light

こういうダイアログで選択できます。

Palette_select

どちらの色がお好みですか?

 

なお、上のダイアログで色の部分を突っつくと色を変更できます。

Color_palette

 

| | コメント (0)

2022.03.06

モナザイトをNaIの横に置くか上に置くか

モナザイトをNaIの横に置くか、上(厳密には下)に置くかでスペクトラムが変わるようです。

下のはNaIの上に置いたときのスペクトラム

Tate_20220308105401

次のはNaIの横に置いたときのスペクトラム。

Raw_20220308104901

だいたい似た感じなのですが、よく見るとTl-208の2614keVのピークが、校正した値よりも少し左側に出ていますね。

NaIの特性なのか、それとも信号処理回路の問題なのか。

 

横置きの場合から縦置きの場合のスペクトラムを秒数を換算して引き算してみると、

Escape

何本かのピークが出てきました。510keVくらい離れているのでシングルエスケープとダブルエスケープなのだと思います。NaIの直近の横に置くか縦に置くかで、計数率が変わるからピークが見えてくるのだろうと考えれば納得できます。

全吸収のピークが少しシフトするのはどう考えればいいのでしょう。

 

| | コメント (2)

2022.03.05

Cosmo-Zのロックインアンプで1uVを測る

昨日の測定では、ファンクションジェネレータで0.5mVを作り、抵抗で100分の1に分圧して5uV付近の性能まで測りました。

今日はアッテネータを1/1000にして、より低い電圧まで測ってみることにします。

Dsc_3246

ちなみに、上の写真で奥のほうから2個目のコネクタに刺さっているのはこの基板。

Np1124

もともとは万能のLCフィルタ用に作った基板で、直列並列にいろいろなチップ部品を乗せられるものなのですが、抵抗を乗せればアッテネータになります。

 

まず、FGで振幅2Vの信号を作り、このアッテネータでちゃんと1000分の1になっているかどうかをオシロで見ます。

Scope_96

確かに、アッテネータはちゃんと1000分の1になっています。

そして、基板上のアンプのゲインは1000倍に。・・・したはずなのですが、なぜか600倍程度にしかなりません。100Ωと100kΩじゃ何かが足りないのかっ・・・。周波数特性的な問題でもなさそうです。

ゲインが上がらない原因はわかりませんが、570倍で我慢しましょう。

Att570

上の波形は、元の信号と、1/1000してから570倍したものです。元の信号の振幅が40mVなので、減衰後には40uVになっているはずです。それなりにノイズが多いのも仕方がありません。

ちなみに、どのくらいのノイズが乗っているのかというと、

時系列ではこのくらい揺れています。

Raw_20220306003901

ヒストグラムを取ってみると、標準偏差が48LSBで1LSB=61nVですから、±2.93μVの誤差があります。

Hist_20220306004001

周波数成分で見て見ると、このノイズは1/fノイズであるようです。

1fnoise

さて、昨日と同じように、横軸に分圧前、縦軸に分圧してから増幅した値をプロットしていきます。

わかりやすいように、横軸は入力に加わっているであろう電圧(FG出力の1/1000)、縦軸は表示された計測値にしています。

Plot

OPアンプのゲインが1000倍にしたはずなのに570倍にしかならないので傾きは1になりませんが、概ね直線上に乗っていることがわかります。昨日は入力換算電圧が5uV~でしか測れませんでしたが、今日は1uVまで測れています。

この直線がゼロ付近でどこまで正確になっているかを見たいのですが、電圧が小さいところでは数値だけ直感的にみても直線的ではないのがわかります。計測値として表示される電圧は2.2μV程度が下限のようです。

Lowvolt

そこで、20uV~100uVという比較的大きな入力電圧のときに得られた、入出力の近似直線(y=0.5798x+1.1455)をゼロのほうに伸ばしていって、この直線とのずれを計算してみることにしました。

その結果が次のグラフです。

Gosa

横軸が入力電圧で、縦軸が近似直線からのずれです。

入力電圧が10uV~100uVの範囲では、推定された振幅は±0.1μVの範囲に入っていますが、10uV以下になると誤差が大きくなってくるのがわかります。

最後に、このグラフでいうところの誤差を、入力電圧で割ってみます。

Gosa_parcent

結論としては、測りたい信号の振幅が10uV以上であれば測定誤差は1%以下になり、それ以下の領域では計測値は真の値より高めに出て誤差が大きくなると言えます。

1/fノイズを積分してしまっているのかもしれませんし、もしかしたら測りたい振幅(1uV)に対してトリガ信号(数V)が120dBも大きいのでごく微量でも回り込んできているのかもしれません。

1uVまで正確に測れるようにするには1/fノイズの対策をしたり、トリガが絶対に回り込まないように改良するしかなさそうですね。

 

| | コメント (0)

2022.03.04

Cosmo-Zのロックインアンプで10uVを測る

Cosmo-Zにはロックインアンプというオプションがあります。FPGAの中でロックインアンプを作って特定周波数の微弱な信号の振幅や位相を計測するというものです。

この機能の能力を測定するため、ファンクションジェネレータで1mV程度の振幅の信号を作って、それを抵抗で100分の1に分圧してADCに入力してみることにしました。

Lockin_test

上の写真では、抵抗で分圧した電圧と、生の信号、それからトリガをCosmo-Zに入力しています。

ちなみに、私が持っている安いGW Instekのファンクションジェネレータで出せる最も小さな振幅の信号と、分圧した信号をCosmo-Zに入れてキャプチャしてみると、下の図のようになります。

Minimum_signal

青い線は生の信号、茶色は分圧した信号です。

さすがに何も信号が見えていなければロックインアンプでも見ることはできないので、Comso-Zの入力部にOPA656を使ったゲイン100のアンプを乗せます。100分の1にした信号をOPアンプで100倍してADCに入れるというわけです。

すると、ノイズに埋もれながらも、正弦波らしきものが見えてきました。

Lockin_amp100

この状態でFFTをかけてみると、ノイズに埋もれながらもピークの周波数では、赤のチャネル(1/100して100倍したもの)と、青のチャネル(生の信号)は同じ値を示しているので、

Fft_20220306000001

ロックインアンプで振幅を読み取れるのではないかと期待できるわけです。

ファンクションジェネレータから与える正弦波の振幅をいろいろ変えて測ってみたところ、

Lockin_6mv

元の信号の振幅が0.5mV~22mV(アッテネータ後では50uV~220uV)では、アッテネータ前の振幅と、アッテネータ後の振幅が直線的な関係になることが確認できました。

Lockin_22mv

Cosmo-ZでCosmo-Zを校正する感じなのであまり良い方法ではないのですが、mV、μVの振幅を正確に測る測定器がないのでどうしようもありません。Como-Zのロックインアンプは元の振幅がmVのオーダーがあればノイズに埋もれることはないので、横軸は正しいと考えて、この結果はある程度信用できるのではないかと思います。

なお、私が持っているこの古いファンクションジェネレータは、振幅調整がディジタルではない(アナログのツマミを回す方式)ので、電圧がわかりません。他にもディジタル設定式のオーディオアナライザや高周波発生器もあるのですがトリガが出せないので、ロックインアンプの評価には不向きです。

これでは先に進めないので、Agilent(Keysight)の任意波形発生器「33522B」を買いに、秋葉にいったのですが、品切れだそうです。納期1.5か月です。似たような任意波形発生器も全部品切れだそうです。お店の人曰く、半導体不足で入ってこないそうです。

幸いMouserにKeysightのEDU33212Aというのがあったので即効で注文しました。EDUという型番ですがエデュケーション専用ではないようですし、THDなどの数値もE33522Bと比べて遜色ありません。来週、これが来たらより正確に測ってみれそうです。

| | コメント (0)

2022.03.03

ZYNQのLinuxカーネルを5.15にアップデートしてKSZ9131が動作した

Cosmo-Zの6台の実装が上がってきました!

Cosmoz6x

今回のCosmo-Zは半導体入手難に対応するため電源ICと、イーサネットPHYを変えたのです。

早速、電源を入れてみると電源ICについては問題なく規定の電圧を出していましたが、なんと!ネットワークが動きません。

くぅ・・納期が迫っている状況でこれか・・

 

やはり、KSZ9031→KSZ9131のアップデートは、そのままではいかなかったのかと

いや、正確にいうと認識はしているのですが、ネットワークが疎通できないのです。リセット後の何秒かなのか、何パケットまでかなのかわかりませんが、DHCPは通ってIPアドレスは振られているのですがその後の通信が全くできなくなるのです。

LinuxのカーネルでのKSZ9131対応状況を調べるため、

https://elixir.bootlin.com/linux/latest/source/drivers/net/phy/micrel.c

のサイトでバージョンごとのソースコードの違いを追ってみると、私が好んで使っているカーネルv4.9ではKSZ9131に対応しておらず、KSZ9131の名前が出てくるのはカーネルv4.20からのようでした。

Ksz9031linux4_9

ためしにv4.20のmicrel.cの中身だけv4.9にコピーしてきてビルドしても、関数がないとかでエラーが出てうまくいきません。まぁ、横着はできないようです。

 

一方、XILINXがリリースしているLinuxカーネルは、v4.9の後はv4.14、v4.19、v5.4がリリースされていて、

Xilinx_linux

4系の最後であえるv4.19ではKSZ9131に対応していないので、結局のところZYNQでKSZ9131を使うには、カーネル5以上に上げなければならないというわけです。

いっそのこと最新版がいいだろうということで、XILINXのgithubから最新のlinux-xlnxの5.15をダウンロード(2.4GByte!?)してビルドしたところ、エラーなく通りました。

カーネル5.15のMenuconfigではM25P80互換のSPI ROMがなくなっていて(STMアーキテクチャのLinuxでなければ見えない)、ZYNQでは使えないのかなと思っていたのですが、そんなことはなくデフォルトで使えるようになっていました。カーネル4.9では手動でONにしなければならないオプションが自動的に組み込まれたということなのでしょうか。

ビルドしたカーネルをZYNQで起動したところ、

RTCについても、

i2c_dev: i2c /dev entries driver
rtc-ds1307 0-006f: registered as rtc0
rtc-ds1307 0-006f: setting system clock to 2022-03-03T16:27:11 UTC (1646324831)
cdns-i2c e0004000.i2c: 400 kHz mmio e0004000 irq 30
cdns-wdt f8005000.watchdog: Xilinx Watchdog Timer with timeout 10sと

と、認識され、問題なく動作していました。

肝心のKSZ9131のイーサネットPHYですが、

[Firmware Warn]: /amba/ethernet@e000b000/mdio/phy@7: Whitelisted compatible string. Please remove
macb e000b000.ethernet eth0: Cadence GEM rev 0x00020118 at 0xe000b000 irq 35 (54:10:ec: )
e1000e: Intel(R) PRO/1000 Network Driver
e1000e: Copyright(c) 1999 - 2015 Intel Corporation.

"Microchip KSZ9131 Gigabit PHY"と表示されるかと思ったのですが、 [Firmware Warn]なので何かが認識されていないのかもしれません。

KSZ9131としては認識されていませんが、PINGは通るし、ApacheのWebサーバも、独自プロトコルの通信も問題なく動きました。ネットワークは問題なく動作しているといえるでしょう。

Kernel15

結論として、Linuxカーネルv4.9→v5.15のアップグレードは全く問題ありませんでした。

UltraScale+ではない普通のZYNQでも、uioのデバイスドライバも含めて、カーネル5.15は問題なく動きました。

ただ一つ、xdevcfgがなくなったこと以外は。

 

| | コメント (0)

2022.03.01

NaIとCosmo-Zで測ったγ線のエネルギーをモナザイトで校正

この1か月くらいの間、全力でCosmo-Zの放射線計測機能とMCA機能を改良しています。

 

今日は測ったγ線スペクトラムと、参照するスペクトラムを引き算する機能を作りました。

まず、下のスペクトラムは、NaIの隣に塩化カリウム試薬を置いたときのものです。

Measuring

次のスペクトラムは、塩化カリウムなしのバックグラウンドを4時間測ったものです。

Ref_20220302120301

このようなバックグラウンドのスペクトラムを別途に測定しておけば、右クリックで「差分を表示」とするだけで、その差分が見えるようになりました。毎秒あたりのカウント数に換算して引き算するので、参照するスペクトラムと、測定中のスペクトラムは必ずしも同じ秒数でなくても構いません。

Diff_20220302120301

引き算したら塩化カリウムと思われる1460keVのピークより上のピーク、622keV付近の小さなピークは綺麗に消えてしまいました。1760付近や2640付近のピークはバックグラウンドであったことがわかります。

こんな実験をしていたら、モノタロウからモナズ石が届きました。漬物石みたいなのが来るかと思ったら、思っていたよりも小さい。

Mona1

肉まんの具みたいなのが入っていました。

Mona2

でも、放射線はバシバシ出ています。塩化カリウム試薬×2本を隣に置いた時よりも4倍くらいカウントをしている感じです。

スペクトラムを見て見ると、Bi-212@1620keV、Ac-228@911keV、Tl-208@583keV、Ac-228@348keV、Pb-212@238keVなどと思われる、いままで見たことのなかった場所にたくさんのピークが観測されました。

Monaz

それらしく思われるピークを1つ1つ設定しながら、パルスハイト(電圧,bin)とエネルギー(keV)の対応を関連付けていくのですが、こうしてCosmo-ZとNaIの計測システムが校正できるようになりました。

最小二乗法でフィットさせてだいたい直線上に乗っているような気がしますが、気がするではよくないので、どのくらい直線的になっているかを図示する機能が欲しいですね。

あと、測定する日の偶然よって何らかの測定条件(ハイボルの電圧とか、アンプのゲインとか)が変わると、パルスハイトとエネルギーの関係も変わってしまうので、条件が変わっても追随できるようなしくみが必要だと思われます。

 

| | コメント (0)

« 2022年2月 | トップページ | 2022年4月 »