シチズン 前人未踏の年差±1秒の精度を実現したCaliber 0100 (延長)第七回~【私も買いました】四半期のズレ測定プログラム編

 By : CC Fan


今年の新作(藍染和紙文字盤)も発表
されたシチズンCaliber0100、四回にわたってお送りしてきた開発者インタビュー(水晶振動子編半導体・システム構成編機械編全体・総論編)の後、【私も買いました】として和紙文字盤を購入したのが2021年12月13日、その時に適当なプログラムで、「目視できない」としたズレともう少し向き合うためによりは詳細なプログラムを書いて確認をレポートします。

前回は、Linuxのシェルスクリプトと日付を取得するdateコマンド、指定時間分を待ち合わせるsleepコマンドを適当に組み合わせて作りましたが、この方法だと色々あって最大10ミリ秒程度のバラつきが出ていたので、今回はpythonを使ってより効率の良い実装を目指しました。

プログラム自体も記事の最後に添付します。



まずは出来上がったものを動画で、50ミリ秒単位で更新し、アスタリスク(*)マーク一つが50ミリ秒です、0秒ちょうどの200ミリ秒前から数値で描画するようになり、「秒2桁_マイクロ秒6桁」で描画、200ミリ秒後まで数値描画し、再びアスタリスクの表示に戻るというプログラムです。

前回の1秒(+数ミリ秒のバラつき)に比べると、だいぶ表示が細かくなり、目視でもわかりやすくなったと思います。



というわけで、先ずは上記の動画からズレを算出してみましょう。
フレーム番号付きでコマ送りを切り出します。


19フレームでプログラム側が32.8秒を表示、続いて20フレームで0100の針が1秒ステップで33秒に進みます、そして22フレームでプログラム側が32.85秒を表示、なので、0100側が0.2秒から0.15秒早く進んでいることになります。
線形に内挿するとプログラムに対して0100の歩度は+0.18秒あたりでしょうか。



1秒後(+60フレーム)にも同じ内容が繰り返されています。
80フレームでプログラム側が33.8秒を表示、81フレームで0100の針が動く、そして83フレームで33.85秒が表示されます。
こちらも1秒前と同じ相対関係で+0.18秒の進みになる、というある意味当たり前のことを示しています。

これが分かった後に、適当なプログラムの秒単位表示をよく見直すと、0100の針が動いてからプログラム側の表示が更新されているので、前回でも0100の方が進んでいたことが分かり、同様にコマ送りすると、0100が動いてからプログラム側が1秒進むまでの差は9~10フレームで、これは+0.15秒~+0.16秒に相当します。
プログラムが違うので単純な比較ではいけないかもしれませんが、プログラム側は常に日本標準時を作っているNICTの公開NTPに同期しており、ズレが(ほぼ)無い状態なので、3.5カ月で 0.03秒分だけ、0100が進んだことになります。

正直、今回のプログラムでも「直接」0100の精度を測るためには全く性能が足りず、特に「たった」60フレーム/秒の速度しか出ないうえに遅延が不確定なディスプレイが介在するのがダメ、という前回同様の結論にはなってしまいます。
ユーザーサイドでは手探りながら、今回得られた+0.18秒がさらに四半期経った後にどう変化するか、という「相対」評価しかとりあえず方法はなさそうです。

プログラムの表示方法ももう少し分かりやすくする方法はありそうなので検討は継続します。
ただ、この調子であれば「年差±1秒」のスペックは実現できそうかな?

【お問い合わせ】
シチズンお客様時計相談室
フリーダイヤル 0120-78-4807
(受付時間 9:30〜17:30 祝日除く月〜金)

付録:表示プログラム(chronometer.py)
openSUSE Tumbleweed 20220324で動作確認

#!/usr/bin/env python3

import time
import datetime

#timing (millisec)
tick_ms = 50
pre_ms = 800
post_ms = 250

# timing (microsec)
tick_us = tick_ms * 1000
pre_us = pre_ms * 1000
post_us = post_ms * 1000

print("33[H",end="",flush=True)
print("33[2J",end="",flush=True)
while True:
    print("33[K",end="",flush=True)

    for top in range(post_us,pre_us,tick_us):
        time.sleep(tick_us / 1250000)
        now = datetime.datetime.now(datetime.timezone.utc)
        while now.microsecond < top :
            now = datetime.datetime.now(datetime.timezone.utc)
        print("*",end="",flush=True)

    print("33[K",flush=True)

    for top in range(pre_us,999999,tick_us):
        time.sleep(tick_us / 1250000)
        now = datetime.datetime.now(datetime.timezone.utc)
        while now.microsecond < top :
            now = datetime.datetime.now(datetime.timezone.utc)
        print(now.strftime("%S_%f"),"33[K",sep="",flush=True)

    print("33[K",flush=True)

    now = datetime.datetime.now(datetime.timezone.utc)
    while now.microsecond > tick_us :
        now = datetime.datetime.now(datetime.timezone.utc)
    print(now.strftime("%S_%f"),sep="",end=" ",flush=True)
    now = datetime.datetime.now(datetime.timezone.utc)
    print(now.strftime("%f"),"33[K",sep="",end="nn",flush=True)

    for top in range(tick_us,post_us,tick_us):
        time.sleep(tick_us / 1250000)
        now = datetime.datetime.now(datetime.timezone.utc)
        while now.microsecond < top :
            now = datetime.datetime.now(datetime.timezone.utc)
        print(now.strftime("%S_%f"),"33[K",sep="",flush=True)

    print("sec_usec","33[H",end="",flush=True)