google-site-verification: google3bd66dd162ef54c7.html
fc2ブログ

ルビジウムオシレーターの周波数調整(2022年、最終)

◆まえがき
GPSの1PPS信号を使ったルビジウムオシレーターの周波数調整が完了したのでその内容のまとめです。なお詳しい調整方法は前回の記事を参照ください。

◆設定値と調整結果
3回の調整を経て誤差を-7.64E-13まで追い込みました。以下にその様子をグラフで振り返ってみます。なお最初の2つのグラフは前回の記事に掲載したものと同じです。

・初期状態
補正前(初期状態)
この時の設定値は、0737(0x02E1)で、誤差は 1.06E-11でした。

前回(2019年9月)校正した時の誤差は2.3e-12だったので、エージングレートはおよそ3e-12/年ということになります。これって悪くない値だと思います。

データーシートに記載されている補正係数は6.80789μHz/LSB (6.81E-13/LSB)なので、この値を使って1回目の補正を行いました。

・1回目調整後
1回目補正後
設定値:0753(0x02F1)、誤差:3.88E-12

まだ誤差が三分の一以上残っています。今回の変化量から補正係数を求めると4.25μHz/LSBとなるのでこの値を使って2度目の補正を行いました。

・2回目調整後
2回目補正後
設定値:0762(0x02FA)、誤差:-2.71E-12
なぜかゼロを通り過ぎてマイナス側に行ってしまいました。そこで1回目と2回目の結果を案分した補正値を使い3回目の調整を行いました。

・3回目調整後
3回目調整結果
設定値:0758(0x02F6)、誤差:-7.64E-13。
やっとマイナス13乗台の値が得られたのでこれで調整は完了としました。

誤差が少ないので傾きを求めるのに長時間のデーターが必要になっています。グラフの欠測部は測定結果の記録を失敗していたためです。

◆調整の経緯
以上の調整結果を数値でまとめると、
・初 期 補正値:732(0x02E1), 誤差:1.06E-11
・1回目 補正値:753(0x02F1), 誤差:3.88E-12
・2回目 補正値:762(0x02FA), 誤差:-2.71E-12
・3回目 補正値:758(0x02F6), 誤差:-7.63E-13

グラフにすると以下のような経緯になります。
調整の履歴

左上から始まり、誤差ゼロを目指して調整して行った様子です。一発で決まって欲しかったのですが、測定誤差などの問題でそうはいかなかったです。

◆まとめ
何とか10のマイナス13乗台の確度で調整することが出来たと思います。一番大きいのはGPSのご利益ですが、Arduinoを使って精密な位相比較が出来るツールを作ったのが今回の調整に役立ちました。

ところで測定結果のグラフのばらつきが大きいのが気になります。時間差測定の分解能が62.5nsであるのが一番影響が大きいはずですが、それ以外に波のような変動があります。これはひょっとしたら衛星の配置によってGPSの1PPS信号のタイミングが変化しているかも知れません。となると、GPSDO(GPS Defined Oscillator)の精度に悪影響があるのかも知れません。まあそんなこと言っても、GPSのアンテナは窓際に置いていて受信状態があまり良くないので偉そうなことは言えませんが、ちょっと気になる挙動です。

ルビジウムオシレーターの周波数調整(2022年、その1)

まえがき
前の記事で測定ツールの準備が出来たのでいよいよルビジウムの校正を行います。

◆外観
何度か記事に登場していますがこんな外観の物です
リビジウムオシレーター
中には中国の携帯基地局の更新に伴って大量に放出されたルビジウムオシレーターモジュール(FE-5680)が入っています。
なお、このオシレーターについてはルビジウム FE-5680A のカテゴリに関連記事をまとめています。興味のある方はご覧ください。

◆測定回路
回路図は前回の記事にありますが、ブレッドボードの写真です。写真の上の方にあるBNCコネクタにGPS(NEO-8)とルビジウムの1PPS信号を引っ張って来ています。
測定回路

・Arduino UNOのボードはクロックオシレーターが水晶の互換品を使っています。純正のボードはセラロックを使っているので精密なタイミング測定には向いていません。

・UNOの電源をパソコンから供給すると、電源電圧変動の影響を受けて測定結果がふらついたので、別ルートから電源を供給しました。こうするとUSBコネクタ経由の通信が出来なくなるので、シリアルデーターは別ルートのUSBシリアルアダプタを通してパソコンに送りました。

・風などの影響を減らすため、測定中は上の写真の回路全体に小さな段ポール箱を被せました。

◆測定結果
・パルス幅(pw)、周期(pp)の測定結果
パルス幅の生データー GPS1PPS周期測定結果

16時間分のデーターです。グラフを見るとパルス幅(位相差)は8μs増加しています。しかし周期も同じ傾向で変化していて実際に8μsもの位相の変化があった訳ではありません。そもそも周期はGPSの1PPS信号なのでこんなに変動するはずがありません。

つまり、左上のグラフは(ほぼ)Arduino UNO のCPUクロックの変動を見ていることになります。1秒パルスの周期が8μs変化したのだからクロック変動は 8E-6 (8ppm) ということになり、水晶オシレーターとしては妥当な値だと思います。

ともかくこれでは10のマイナス10乗よりはるかに小さな変化の測定は出来ません。そこで上のグラフの両者を割り算して正規化しました。

・補正後のパルス幅(位相)変化
GPS-ルビジウムの位相差変化

パルス幅(pw)の測定結果を周期(pp)の測定結果で割った値でプロットしたグラフです。これでようやくGPSとルビジウムの位相の変化が見えてきました。グラフの傾きがルビジウムの誤差でその値は 1.068E-11であることが判ります。なお、位相差は増加しているのでルビジウムの周波数が低いことになります。

◆ルビジウムの周波数調整
誤差の量が判ったのでルビジウムの周波数を調整します。その方法は前回ルビジウムの周波数を調整した時の記事に書いたのでポイントだけ紹介しておきます。

・調整量の計算
調整量 = ズレ量/調整ステップ = 1.068E-11 / 6.81E-13 = 15.69 ≒ 16LSB

従来の補正値は737(0x02E1) だったのでこれに16プラスして、753(0x02F1)を設定すれば良いことになります。

0x02F1に設定するための設定コマンドは、2C 09 00 25 00 00 02 F1 F3
注:2Cは書き込みコマンド、09はコマンドの総バイト数。4バイト目の25は1-3バイトのXORで求めたチェックサム。9バイト目は5-8バイトのXORで求めたチェックサム。結果確認は2D040029コマンドで行う。エラーがあっても無くても何も通知されない。

これをバイナリーモードで通信できるシリアルソフトを使って書き込みます。

・書き込み画面(RS232Cの画面)
補正量送信画面

◆調整後の結果
調整した結果を20時間くらいかけて確認したデーターです。
調整1回目結果
誤差は3.88E-12と-12乗台まで減りましたが、残念ながらまだ少し傾きが残っています。

◆まとめ
以前のやり方はマニュアル測定だったので数時間に1回程度の記録しか出来ず、細かい変化までは判りませんでした。今回は自動測定なので変化の様子が判り易くなりました。

ATmega328Pのインプットキャプチャー機能を使えば精密な位相変化の検出が可能になる、ということを「居酒屋ガレージ店主」さんから教えて頂いたのですが、狙い通りの効果が出ました。やって見て良かったです。なお、まだプログラムにバグがあって異常値が記録されてしまうことがあるのですが、とりあえずEXCELで手作業で修正しています。

まだ少し誤差が残っているので再度調整してみたいと思います。そうは言ってももう10のマイナス12乗台なので一般家庭でこんな精度はいらないのですが、ともかくもう少しやってみます。これを周波数の精度沼と言うんでしょうね、どっぷり浸かるのも悪くはありません。

Arduino のタイマー1 インプットキャプチャでパルス幅を精密測定

◆まえがき
そろそろルビジウムオシレーター(以下ルビジウムと略します)の校正をやりたい時期です。前回(2年半前)校正を行った時は、ユニバーサルカウンタを使いその値を手書きのメモに残すという形で行いましたが、今回はもう少し自動化したいと考えました。

ルビジウムの誤差を確認するにはGPSの1PPSパルスとの位相差を測定するのが一番簡単だと思います。その方法としてはインターフェイスに出力が出せるカウンターを使うのが一番確実だと思いますが、そういう測定器は持ってません。

ということでArduino UNOで何とかすることにしました。

◆Arduinoでパルス幅測定
Arduinoで精密なタイミングの測定を行う場合、タイマー1のインプットキャプチャ機能を使うのが良いそうで、居酒屋ガレージ日記さんがプログラムを公開されています。また、今回のような、2つのパルスエッジからパルス間隔ゲート信号を作る、時の回路も教えて頂いています。

ということで、そのままコピペして作れば良いのですが、それではつまらないので自分なりの解釈で回路とプログラムを考えてみました。

◆回路図
タイマー1インプットキャプチャーを使ったパルス幅測定回路
GPSとルビジウムから出ている1PPSの信号(正パルス)をU3, U4のRSフリップフロップで1本の信号にまとめ、UNOのD8(ICP1)に入力し、その時間差をパルス幅として測定しています。

1PPSの信号はパルス幅が100ms程度あり、そのまま入力するとRS-FFの禁止状態(両方Low)になることがあるので、入り口のCRで微分しています。U1,U2はシュミットトリガ入力にしておいた方が良かったかも。

◆プログラム
// ATmega328Pを使ってパルス幅を測定 20220514_PulseWidthMeasure
// D8に信号を入力 Timer1 インプットキャプチャで測定。オーバーフローのミスカウントをソフトで対策
// 2022/05/15 ラジオペンチ http://radiopench.blog96.fc2.com/

volatile int       irqFlag  = 0;           // タイマー1インプットキャプチャ割り込みフラグ
volatile uint16_t  ovfCount = 0;           //         オーバーフローカウンタ
volatile boolean execRising = true;        // 波形測定方向フラグ
volatile uint16_t t1, t2, t3;
volatile uint16_t t2MSB, t3MSB;

void setup() {
  Serial.begin(115200);

//  タイマー1をインプットキャプチャに設定
// (参照:http://igarage.cocolog-nifty.com/blog/2020/08/post-ba853b.html)
    TIMSK1 = 0b00100001;
    //           |  ||+---  TOIE1 オーバーフロー割込on
    //           |  |+----  OCIE1A
    //           |  +-----  OCIE1B
    //           +--------  ICIE1 キャプチャー割込on
    TCCR1A = 0b00000000;
    //         ||||  ++---  WGM 標準動作
    //         ||++-------  COM1B
    //         ++---------  COM1A
    TCCR1B = 0b00000001;
    //         || ||+++---  CS 16MHz入力(下位3ビットでプリスケーラー設定)    
    //         || ++------  WGM
    //         |+---------  ICES1 ICP1入力↓エッジ(プログラム内で動的に変更)    
    //         |----------  ICNC1 ノイズキャンセルなし
  startUp();
}

void loop() {
  uint32_t  tw, tp;           // クロック数で表したパルス幅、周期
  uint32_t last_tw, last_tp;
  float     t_tw, t_tp;       // パルス幅、周期
  while (irqFlag == 0) {      // IRQフラグが立つまで待つ
  }
  irqFlag = 0;

  tw = ((uint32_t)t2MSB << 16) + (uint32_t)t2 - (uint32_t)t1; // パルス幅の計算
  tp = ((uint32_t)t3MSB << 16) + (uint32_t)t3 - (uint32_t)t1; // パルス周期の計算

  if((tw - last_tw) > 30000) tw -= 65536;  // TC1オーバーフローミスカウント対策の暫定対策
  if((last_tw - tw) > 30000) tw += 65536;
  if((tp - last_tp) > 30000) tp -= 65536;
  if((last_tp - tp) > 30000) tp += 65536;
  
  t_tw = tw * 62.5E-9;
  t_tp = tp * 62.5E-9;
  Serial.print(t_tw, 9); Serial.print(", "); Serial.print(t_tp, 9);
  Serial.println();
  t1 = t3;              // 次回の測定のためにパルスの先頭位置を記録
  last_tw = tw;
  last_tp = tp;
}

void startUp() {
  Serial.println();
  Serial.println("Pulse measurement start");
  Serial.println("width, period");
  for (int i = 0; i < 3; i++) {      // 変数が落ち着くまで空廻し
    while (irqFlag == 0) {           // IRQフラグが立つまで待つ
    }
    irqFlag = 0;
    t1 = t3;
  }
}

ISR(TIMER1_OVF_vect) {      // Timer1オーバーフロー割り込み処理
  ovfCount ++;
}

ISR(TIMER1_CAPT_vect) {      // Timer1 インプットキャプチャ割り込み処理
  uint16_t t;
//  sei();                     // 途中でovfCountの値が動くと困るので割り込み禁止で処理(これは逆効果)
  t = ICR1;
  if (execRising == true) {  // 立ち上がりの処理だったら、
    t3 = t;                  // パルス周期測定のための立ち上がり時刻として記録
    t3MSB = ovfCount;
    ovfCount = 0;            // オーバーフローカウンタをクリア
    TCCR1B &= 0b10111111;    // 次回はネガエッジでキャプチャ(ICES1=0)
    execRising = false;      // 次回は立下りの処理
    irqFlag = 1;             // フラグセットしてメインに通知
  } else {                   // 立ち下がりの処理だったら
    t2 = t;                  // 立下り時刻として記録
    t2MSB = ovfCount;
    TCCR1B |= 0b01000000;    // 次回はポジエッジでキャプチャ(ICES1=1)
    execRising = true;       // 次回は立ち上がりの処理
  }
//  cli();
}
上の方でリンクを貼った居酒屋ガレージ日記さんの記事中にリンクがある、duty_checker1.c のコードを参考にさせていただきました。動作説明も詳しく書かれているのでそちらを見て頂くのが宜しいかと思います。

パルス1発毎に、そのパルス幅と周期をシリアルに出力するようになっています。カウンターのクロックは16MHzなので分解能は62.5nsになっています。

16MHzのクロックなので16ビットカウンターは4.096msでオーバーフローし、測定周期は1秒なのでその間に244回オーバーフローが発生することになります。

41,42行は割り込み禁止状態にして実行した方が良さそうですが、実行されるタイミングは固定されているのでこのままでも大丈夫か?

パルス幅の測定は16ビットカウンタのオーバーフロー回数とカウンタの値を合成して計算する必要があります。そのあたりの処理を居酒屋ガレージ日記さんは巧妙に処理されているのですが、一部で理解出来なかったところがあったので、私のプログラムは対症療法的な処置になっているところがあります(44-47行目)。毎回大きな変動がある場合はこういうズルしちゃいけませんが、とりあえず臭い物には蓋をしておいて話を先に進めることにしました。

ちなみに、もしカウンターの値が0xFFFFから0x0000に変化した瞬間にインプットキャプチャの信号が入った場合、割り込みの優先度からインプットキャプチャ割り込み処理 (TIMER1_CAPT_vect) が先に実行されます。その時点ではオーバーフロー割り込み(TIMER1_OVF_vect) はまだ実行されていないので、そのあたりの辻褄合わせをしておく必要があるということだと思います。

◆測定結果
1秒毎にパルス幅と周期の値をシリアルに流しており、パルス幅はGPSに対するルビジウムオシレーターの位相差を表わします。つまりパルス幅の変化でグラフを描けば位相差の変動、つまりルビジウムの誤差が見えてくるはずです。

しかし、実際にやって見るとArduinoのクロックの変動の影響の方が大きくてルビジウムの誤差がはっきりと見えて来ませんでした。なお、位相誤差は累積するので何日も測定を続ければ見えてくるはずです。

幸いパルスの周期の値(tw)も測定しているので、パルス幅(Tp)をパルスの周期(tw)で割ることでGPSの精度でパルス幅の値を得ることが出来ます。

そういう計算を行って作成したのが次のグラフです。

・ルビジウムの位相変化
測定結果
右肩上がりになっているのでパルス幅は広がっています。

近似直線の傾きは2E-5μsなので、1秒毎に 2E-11秒ルビジウムの位相が遅れていることになります。つまり、10MHzのクロックの出力は 0.2mHz低かったというのがとりあえずの結論になります。

グラフのばらつきが大きいのがちょっと不満です。原因として考えられるのは、
1) 測定クロック周期が62.5nsなのでその分は量子化誤差が出る。
2) GPSの1PPSのジッタの公称値は30nsなのでその分はばらつきが出る。
3) 波形のトリガ精度が悪い(ノイズの影響などがある)
4) 1秒間の中でのArduinoのクロック周波数の変動(電源電圧変化他)
5) ルビジウムの変動?、GPSの変動?

◆まとめ
プログラムの一部で手抜きのところがありますが、これでルビジウムの誤差の変化を何とか自動測定出来るようになりました。

このままもう少し測定して様子を見たいと思います。

◆追記
この記事を書く前に行った予備調査の状況です
1)インプットキャプチャの動作確認
2)ばらつきがひどいので温度変化防止のために箱に入れたけどダメ
3)ボード(Arduino NANO)のクロック精度が悪かったことが判明
4)UNOの互換品でうまく測定出来るようになった。入力はパルジェネで作った幅100msのパルス

カレンダー
05 | 2023/06 | 07
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。
記事の内容のご利用は読者の自己責任でお願いします。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード