約2年前に自作した14MHzのツェップ型アンテナのSWRを、久々にチェックしてみたらバンド内に収まっていました。
↓ 本日晴天時。
↓ 以前はこんな感じでした。上下共にその以前に測定。↓の上は降雨時、下は晴天時。
前回のGnuplotアニメーションに続いて、今回はX軸をプロットする毎に16秒分変化させて、以前より滑らかに動くようにしてみました。
動画にした方法は、MacのVNC上で画面録画しただけです。録画時間は約5時間35分、これを2分10秒まで縮めました。
↑ offset値が頻繁に上下していますが、これは生活ノイズ(ここでは電化製品のオンオフなど電気的ノイズを表現)で上下しています。多分、裸のGPSモジュールとラズパイの配線がワニ口クリップの長い配線のせいかも知れません。また、ラズパイのアップグレード中も30μs位変化した所を見たので、そんな要因も含まれます。
↓ 生活ノイズが無いと、このように落ち着いています。
↓ この動画の元になったGnuplotのスクリプト(C言語)です。なぜC言語なのかと言うと、Gnuplotのスクリプトだけで現時刻に応じてX軸の範囲を設定する方法が分からなかったからです。
/*
* loopstats-plot.c
*
* gcc -Wall -o loopstats-plot loopstats-plot.c
* 2020/02/10
*
* loopstatsのoffset値をGnuplotでグラフ化する。
*
* X軸は現時刻から-3600秒前の値を使用。
* X軸は16秒毎に16秒分変化する。
*
*/
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#define YOHAKU 150
int main(void)
{
FILE *gp;
int s1, s2;
gp = popen("gnuplot -persist", "w");
fprintf(gp, "set encoding iso_8859_1\n");
fprintf(gp, "set grid y2tics\n");
fprintf(gp, "set y2range [-0.00004:0.00004]\n");
fprintf(gp, "set y2tics ('40' 0.00004, '30' 0.00003, '20' 0.00002, '10' 0.00001, '5' 0.000005, '0' 0.00000, '-5' -0.000005, '-10' -0.00001, '-20' -0.00002, '-30' -0.00003, '-40' -0.00004)\n");
fprintf(gp, "set xzeroaxis lt -1\n");
fprintf(gp, "set timestamp\n");
fprintf(gp, "unset ytics\n");
fprintf(gp, "set title 'loopstats offset' font 'Helvetica,16'\n");
fprintf(gp, "set xlabel '-3600 (s)' font 'Helvetica,14'\n");
fprintf(gp, "set y2label 'offset ({\265}s)' font 'Helvetica,14'\n");
fprintf(gp, "plot '< tail -225 /var/log/ntpstats/loopstats' using 2:3 with points axis x1y2\n");
while (1) {
time_t t = time(NULL);
s1 = t % 86400 + YOHAKU;
s2 = t % 86400 - 3600 - YOHAKU;
// X軸設定 [現時刻−3600秒:現時刻]
fprintf(gp, "set xrange [%d:%d]\n", s2,s1);
fprintf(gp, "replot\n");
fflush(gp); // ここで描画
sleep(16); // 再描画まで16秒待つ
}
fprintf(gp, "unset output\n");
fprintf(gp, "exit\n");
pclose(gp);
return 0;
}
$ gcc -Wall -o loopstats-plot loopstats-plot.c
$ ./loopstats-plot
今テスト中の1PPS利用Stratum 1 NTPサーバーのoffset値を、Gnuplotでほぼリアルタイムでグラフ化してみました。表示範囲は過去60分間です。
118倍速の動画にしたので見て下さい。全画面で見ないと、小さ過ぎて見れませんが。
↑ 動画にした方法は、ラズパイの画面をVNCでMacに表示させて、スクリーンショットの選択部分を収録で約1時間録画しました。Gnuplotの機能にあるGIF出力を利用した物ではありません。
Gnuplotの操作方法は以下の通りです。過去60分間の部分は、< tail -225 であり、ログの末尾から225行読み込む事を意味しています。そしてログの記録が16秒毎(衛星をロストしない限り)ですので、これを計算すると
225×16秒=3600秒(60分間)
となります。
↓ pause 16.0は、このスクリプトファイルの再読み込みまでの待機時間で、結果16秒毎にグラフを更新させています。
$ vi loopstats-plot.cmd set yrange [-0.0001:0.0001] set format y '%6.5f' set size ratio 0.5 set grid set xzeroaxis lt -1 set timestamp set title 'loopstats offset' font 'Helvetica,16' set xlabel '-3600 [s]' font 'Helvetica,14' set ylabel 'offset [s]' font 'Helvetica,14' plot '< tail -225 /var/log/ntpstats/loopstats' using 2:3 w l pause 16.0 reread
$ gnuplot loopstats-plot.cmd
ラズパイでStratum 1 NTPサーバーを試してみました。その方法は全てネット上にある記事のつまみ食いです。
↓ そのつまみ食いの結果がこれです。精度もStratum 1と言って良いのでは?
↑ グラフ中央の跳ね上がり(下がり)の原因は、ntpとgpsbを再起動させた為です。
どんなつまみ食いだったかを備忘録として残して置きます。
秋月のGPSキット(K-13850)とラズパイ4で試しました。
始めに見落としがちな事を。
このGPSを使用する場合、1PPS出力がアクティブLowである事を意識して置かないと、私みたいにハマってしまいます。そのパルスは ↓
このような波形の時は、/boot/config.txt に assert_falling_edge=true が必要になります。これを記述して置かないと100msズレて参照リストから外れたサーバー “x”印になってしまいます。
GPSとラズパイの結線
インストール
$ sudo apt update
$ sudo apt install ntp
$ sudo apt install gpsd gpsd-clients pps-tools
設定
$ sudo vi /boot/cmdline.txt console=serial0,115200の削除
$ sudo vi /boot/config.txt 末尾に追加 enable_uart=1 dtoverlay=pps-gpio dtparam=gpioin=18 dtparam=assert_falling_edge=true
$ sudo vi /etc/modules 末尾に追加 pps-gpio
$ sudo vi /etc/default/gpsd START_DAEMON="true" USBAUTO="false" DEVICES="/dev/ttyS0 /dev/pps0" GPSD_OPTIONS="-n"
$ sudo vi /etc/ntp.conf server 127.127.28.2 minpoll 4 maxpoll 4 prefer fudge 127.127.28.2 refid GPS stratum 14 server 127.127.22.0 minpoll 4 maxpoll 4 fudge 127.127.22.0 refid PPS stratum 0 server ntp.nict.jp iburst
自動再起動設定
$ sudo systemctl enable gpsd.service
$ sudo reboot
上記の手順で作動確認出来たRaspbianは ↓
2019-09-26-raspbian-buster-lite
2019-09-26-raspbian-buster
2020-02-05-raspbian-buster-lite
2020-02-05-raspbian-buster
このフローチャートに従って、共有メモリに書き込むプログラムAのその部分 ↓
while (1) { // 無限ループ
if ((fp=popen("ntpq -c rv", "r"))==NULL) {
string s = "N"; // ntpp -c rv 実行失敗時 N を共有メモリに書き込む
sprintf(shared_memory, s.c_str());
} else {
fgets(str, 512, fp); // 一行読み込む
ptr = strstr(str, "sync_ntp");
if (ptr != NULL) {
string s = "S"; // S を共有メモリに書き込む
sprintf(shared_memory, s.c_str());
sleep(16);
} else {
ptr = strstr(str, "sync_unspec");
if (ptr != NULL) {
ptr = strstr(str, "no_sys_peer");
if (ptr != NULL) {
string s = "M"; // M を共有メモリに書き込む
sprintf(shared_memory, s.c_str());
} else {
string s = "N"; // N を共有メモリに書き込む
sprintf(shared_memory, s.c_str());
}
} else {
string s = "N"; // N を共有メモリに書き込む
sprintf(shared_memory, s.c_str());
}
}
}
pclose(fp);
sleep(1);
}
プログラムAによって共有メモリに書き込まれた文字により、表示を変化させるラズパイ7桁時計のその部分 ↓
static int segdata[22][8] = { // 7セグメントデータ
// a b c d e f g dp
{H, H, H, H, H, H, L, L}, // 0
{L, H, H, L, L, L, L, L}, // 1
{H, H, L, H, H, L, H, L}, // 2
{H, H, H, H, L, L, H, L}, // 3
{L, H, H, L, L, H, H, L}, // 4
{H, L, H, H, L, H, H, L}, // 5
{H, L, H, H, H, H, H, L}, // 6
{H, H, H, L, L, L, L, L}, // 7
{H, H, H, H, H, H, H, L}, // 8
{H, H, H, H, L, H, H, L}, // 9
{H, H, H, H, H, H, L, H}, // 0. 小数点付き
{L, H, H, L, L, L, L, H}, // 1. 小数点付き
{H, H, L, H, H, L, H, H}, // 2. 小数点付き
{H, H, H, H, L, L, H, H}, // 3. 小数点付き
{L, H, H, L, L, H, H, H}, // 4. 小数点付き
{H, L, H, H, L, H, H, H}, // 5. 小数点付き
{H, L, H, H, H, H, H, H}, // 6. 小数点付き
{H, H, H, L, L, L, L, H}, // 7. 小数点付き
{H, H, H, H, H, H, H, H}, // 8. 小数点付き
{H, H, H, H, L, H, H, H}, // 9. 小数点付き
{L, L, L, L, L, L, L, L}, // Blank
{L, L, L, L, L, L, L, H}, // dp
};
// 中略
while (1) { // 無限ループ 1サイクル14.8ミリ秒 1秒間に約67回繰り返す
// 共有メモリの文字により表示を変える
switch (*shared_memory) {
case 'S': // +++++ 時計表示 + 同期LED点灯 +++++
disp = 2000; // 2ミリ秒表示維持
LEDstate = 1; // 同期LED点灯
break;
case 'M': // +++++ 時計表示 ++++++++++++++++++
disp = 2000; // 2ミリ秒表示維持
LEDstate = 0; // 同期LED消灯
break;
case 'N': // +++++ チラツキ表示 ++++++++++++++
disp = 10000; // 10ミリ秒表示維持
LEDstate = 0; // 同期LED消灯
break;
}
gpioWrite(SYNC_LED, LEDstate); // 共有メモリに応じて同期LED点灯・消灯
// 時刻の取得
clock_gettime(CLOCK_REALTIME, &ts);
localtime_r(&ts.tv_sec, &lotm); // ローカル時間に変換
num[0] = ts.tv_nsec / 100000000; // 1/10秒の桁取得
num[1] = lotm.tm_sec % 10 + 10; // 1. 秒の桁取得(小数点付き)
num[2] = lotm.tm_sec / 10; // 10 秒の桁取得
num[3] = lotm.tm_min % 10; // 1 分の桁取得
num[4] = lotm.tm_min / 10; // 10 分の桁取得
num[5] = lotm.tm_hour % 10; // 1 時の桁取得
num[6] = lotm.tm_hour / 10; // 10 時の桁取得
// 7桁ダイナミック表示ルーチン
for (j = 0; j < 7; j++) {
for (i = 0; i < 9; i++) { // 7セグメントデータセット
gpioWrite(nanaseg[i], segdata[num[j]][i]);
}
gpioWrite(common[j], SEG_ON); // 7セグLED表示
gpioDelay(disp); // 共有メモリに応じて表示維持時間変化
gpioWrite(common[j], SEG_OFF);// 7セグLED消灯
}
}
↓ 7セグLEDのコモンをオシロで観測した画像。