公開日:2019/05/25
title.png

5th Season


#5-9 JJYのエミュレーション

 「JJY」と言うのは、NICT(情報通信研究機構)が運用する、標準周波数局のコールサインです。所謂、「電波時計」が受信している電波を送信している送信局です。

  NICT、JJYについて検索する(別ウインドウが開きます)

 現在、日本には、福島県と九州(佐賀県)に送信局があり、福島からは40kHz、九州からは60kHzのキャリア周波数でタイムコードが送信されています。

 この電波が結構シビアみたいで、昔、同じ市内のアパートに住んでいた時にはちゃんと受信したのですが、今のマンション(直線距離で約800m移動)に引っ越してから、窓際に置こうが、ベランダ(外)に置こうが、一向に受信してくれなくなってしまいました。
 つまり、居住領域という大雑把な要素だけではなく、建物の構造、向き、位置、周囲の建物の状態、等々、様々な要因によって、受信できたり、出来なかったりと言うことが起こるようです。

 少なくとも、現在の筆者宅では、電波を受信できず、折角の電波時計が意味をなしていません。そこで、JJYが送ってくるタイムコードをArduinoでエミュレートしてみよう、と言うのが今回のテーマです。

スマホでJJYエミュレータ

 きっかけは、「JJYのエミュレーションをスマホでできるアプリ(別ウインドウが開いて検索します)」を見かけたことです。
 何だか、トリッキィな方法を考える方がいらっしゃるのですね。スマホにイヤホンを接続すると、イヤホンからは13kHzの音が流れるそうです。13kHz≒40kHz/3ということでしょうか??音が割れるほどの音量にすることで波形を崩し、フーリエ変換とかした時に出てくる高調波成分で40kHzの信号を作っているみたいです。しかも、イヤホンのケーブルをアンテナ代わりに使うとは……。

 ここのページでも前回(「#5-8 MMLで音楽を奏でる」で)、Arduinoを使ってイヤホンから音楽を奏でてみたので、スマホと同じ様にJJYエミュレータを作れるはずだ、と言うのがスタートです。

実験可能な環境

 この実験が可能な環境は、かなり限られますので、以下の条件を満たせているか、まずはチェックお願いします。

 これらの条件を満たしている方は、先に進みましょう。

 多分、普通に電波が受信できる環境にお住まいの方は、こんなことで煩わされたりしないのでしょうね……。標準電波を受信できない環境では、電波時計がただの時計になり下がり、定期的に合わせて回るのが煩わしくてなりません……。

JJY標準電波の仕様

 冒頭で検索したNICT、JJYのページから、電波の仕様を詳しく知ることができます。
 要約すると、1分毎にタイムコード(下図)が送信されています。

ar59_jjy.png

 「電波」であることを除けば、よくある通信方式ですね。「#4-3 赤外線リモコン(送信)」で試した、赤外線リモコンの送信波形(キャリア周波数38kHz、デューティ1/3)が近いです。

Arduinoでの実現方法

 Arduinoでは、高調波を使わずとも直接40kHzのPWMを出力できます。スマホアプリの様に、13kHzの可聴域を使うと、イヤホンから漏れる高音が耳障りなので、仕様通り40kHzのPWM(デューティ50%)を使います。
 ちょっと心配なのは、信号の出力強度です。スマホのアプリは13kHzの主信号に対する、高調波成分を使っている(主信号より出力は低下する)のに対して、Arduinoでは直接40kHzの信号を使うので、同じ感覚で出力すると電波法に違反する「違法電波」になりかねません。
 この実験においては、電波法に違反しないように細心の注意を払う必要があります。具体的な注意点は、後ほど説明します。

 タイマを使って時間を計測しながら、1秒毎にデータを送信します。キャリアの発信時間(0.2s、0.8s、0.5s)が1/10秒単位なので、1/10秒間隔で割り込みを発生して制御します。
 8ビットタイマ(TC2)ではカウンタ(0〜255)が足りなくて、1/10秒間隔の割り込みを発生することができません。……clkio/1,024のカウントクロックを使った時で、約16msが一番長い間隔の割り込み周期となります……。
 今回のアプリケーションでは、もう一つ、キャリアとなる40kHzのPWMを出力するのにもタイマが必要なので、TC2はPWM出力用に使用することにして、1/10秒間隔の割り込みは16ビットタイマ(TC1)を使います。

 以下にTC1、TC2のタイマ設定値を示します。

TC1(1/10秒毎割り込み設定)
レジスタ名 アドレス 設定値 説明
TCCR1A 0x0080 0x00 波形モード:CTC(OCR1A) Mode
出力モード:Normal port
TCCR1B 0x0081 0x0b 波形モード:CTC(OCR1A) Mode
クロック選択:clkio/64
OCR1A 0x0088 0x61a7 割り込み周期1/10秒
TIMSK1 0x006f 0x02 OCI1A割り込み有効
TC2(40kHzデューティ50%のPWM出力設定)
レジスタ名 アドレス 設定値 説明
TCCR2A 0x00b0 0x43 波形モード:Fast PWM Mode (OCR2A)
出力モード:toggle OC2A
TCCR2B 0x00b1 0x08 波形モード:Fast PWM Mode (OCR2A)
クロック選択:clkio/1
OCR2A 0x00b3 0xc7 PWM周波数40kHz

 タイムコードの送信は毎分0秒から始まって、60秒間続きます。この期間をさらに1/10に分割しますので、1分間のタイムコード中に600回の割り込みが発生することになります。従って、毎分の0秒でリセットして、1/10秒毎に599までカウントするカウンタjjy_cntを用意し、タイムスロットを管理することにします。
 以下、制御コードの一部です。

  ISR (TIMER1_COMPA_vect)
  {
    // OCI1A interrupt service routine
    uint8 cnt1;
    uint8 cnt2;

    // counter
    jjy_dsec++;
    jjy_cnt++;
    if (jjy_cnt>=600)
      jjy_cnt=0;

    // send data
    cnt1=jjy_cnt/10;
    cnt2=jjy_cnt - cnt1*10;
    if (jjy_dat[cnt1]==jjy_BIT0)
    {
      // bit "0"
      if (cnt2==0)
      {
        // pwm on
        TCCR2B |= 0x01;
      }
      else if (cnt2>=8)
      {
        // pwm off
        TCCR2B &= (~0x07);
      }
    }
    else if (jjy_dat[cnt1]==jjy_BIT1)
      :


 タイムスロットjjy_cntのカウント部分と、それを秒カウントcnt1、1/10秒カウントcnt2に分離した後で、キャリアの発信と停止を制御している部分を示しています。全体像は、完成版プログラムでご確認ください。

うるう年の判定

 カレンダ日付を取り扱うとき、どうしても避けて通れないのが、「うるう年」です。定義に従って、「4で割り切れて100で割り切れない年、または、400で割り切れる年」をうるう年と判定します。

  jjy_lp=false;
  if (jjy_yr%400==0 || (jjy_yr%4==0 && jjy_yr%100!=0))
    jjy_lp=true;

日(経過日)の計算方法

 JJYのタイムコードには、月/日と言う概念が無く、1月1日からの経過日数が使われています。よって、mo月dy日という日付から、経過日数に換算する必要があります。
 まず、必要なのが、各月の日数。覚え方は、「西(2,4)向く(6,9)士(サムライ、11)」です。これをテーブルにしておきます。

  const char jjy_dim[13]=  // days in the month
  {
    0,  // +1 offset
    31, 28, 31, 30, 31, 30,
    31, 31, 30, 31, 30, 31
  };


 あとは、2月を越える月だった場合に、うるう年判定結果が必要になります。

  jjy_dy=dy;
  for (idx=1; idx<mo; idx++)
    jjy_dy += jjy_dim[idx];
  if (mo>2)
    jjy_dy += jjy_lp;

曜日の計算方法

 曜日の計算式って、考え出すと、「キィーッ」って、なりますネ……。ところが、ちゃんと曜日を求めることのできる「ツェラーの公式」というものがあるそうです。ツェラーの公式を使って、日付から曜日を求める関数を作っておきます。

  int jjy_day_of_week (
  int yr,
  char mo,
  char dy)
  {
    // calculate day of week in jjy format
    // return: Sun0 .. Sat6
    char yy;
    char cc;
    char rr;
    char hh;

    if (mo<=2)
    {
      yr--;
      mo += 12;
    }
    cc=yr/100;
    yy=yr - cc*100;
    rr=-2*cc + cc/4;
    hh=dy + 26*(mo + 1)/10 + yy + yy/4 + rr;
    hh=(hh + 7 - 1)%7;

    return (hh);
  }


 この関数が返すのは、日曜日を0〜土曜日を6とする、JJYの曜日コードです。

コマンド仕様

 いつもの様に、シリアルモニタからコマンドを実行可能にしておきます。
 現在時刻を取得するため、「#2-5 リアルタイムクロックを使ってみる」で使ったRTCを使用可能としておきます。ただし、RTCが無くても、マニュアルで現在時刻を設定できる様にしてあります。


微弱無線局を理解する

 「イヤホンケーブルをアンテナ代わりにする」と言っても、インピーダンスマッチングを取っていない回路では、大した電波が飛ぶはずがありません。言ってみたら、「電子機器からノイズが漏れた」程度の話です。しかし、ノイズだろうが何だろうが、規定された強度を越えたら、それは「違法電波」です。

 電波法という法律があって、電波を飛ばすには免許が必要となります。無免許で電波を飛ばすのは違法です。今回の実験(JJYエミュレータ)の根拠になっているのは、免許を必要としない「微弱無線局」と言う物です。微弱無線局については、以下の検索リンクから、総務省のページを参照して、よく理解してください。

  微弱無線局(総務省)を検索(別ウインドウが開きます)

 上で検索した、総務省の関連ページの内容を理解できなかった場合は、絶対にこの先の実験に進まないでください。気づかない内に違法電波を送信して、法律違反を犯す恐れがあります。法律ですので、一瞬でも違反すると、罰金や懲役などの罰則が適用される恐れがあります。

 総務省のページにある通り、具体的な数値が規定されていて、この数値を超えると免許が必要となります。無免許で数値を超えた電波を送信すると違法です。
 しかし、専門ではない個人の場合、電波の強度を測る装置を持っていません。今、自分がどの程度の電波を送信しているのか、知る術が無いのです。「こう使えば絶対大丈夫」と、保証できるものでもないので、くれぐれも、違法電波を飛ばさないように、各自注意してください。

 総務省のページに、「微弱な電波は、遠くまで届きません。」として、目安の記載がありますので、そういったページも含め、よく読んで理解した上で実験してください。


電波法を理解した上で実験

 ここまで到達した皆様は、これから実験する内容に関係する電波法について理解したとみなします。

 まず、RTC(DS1302)をお持ちの方は、以下の様に接続します。RTCはオプションなので、お持ちでない場合は、接続しなくてもOKです。

UNO 接続 RTC(DS1302)
5V VCC
GND GND
D5 RST
D6 DAT
D7 CLK

 JJYエミュレーション信号の出力回路構成は以下の通りです。

ar59_schem.png

 前回(「#5-8 MMLで音楽を奏でる」)と、ほぼ同じ回路となっています。ただし、信号の振幅を調整するLPFの回路定数(時定数)は、十分大きな値(C1=10μF、R1=1MΩ)からスタートします。異なる回路定数を使用する場合は、最初の一回目で、万が一にも「微弱無線局」で規定された電波強度を超えないように、細心の注意を払って回路定数を決めてください。

 イヤホンのコードを電波時計の周りにループ状に配置します。実験の様子は以下の様な感じでした。

ar59_jjy.jpg

※可聴域外(40kHz)の信号を、そこそこの振幅で送り込むことになるので、イヤホンにダメージを与えないとも限りません。壊れても惜しくないようなイヤホンをお使いください。ありませんか?何かのおまけについてきた形ばかりのイヤホン。音も安っぽいので、とても音楽を聴く気になれずに、しまい込んであるような……。ちょっと探したら、こんなに(↓)ありました。

ar58_earphone.jpg

 では、Arduinoで動作させるプログラムをリンクしておきます。

  完成版プログラム:jjy_emulator.ino

 コンパイルしてArduinoへ書き込んでください。シリアルモニタを開いて、以下の様に入力します(もちろん、日時は、実験時の実際の時間に変更してください)。

  実験サンプル:【 】内はシリアルモニタからの送信データ
  【mset,19,05,25,09,57,20】
  OK: mset 0
  JJY: 19/5/25[145](Sat) 9:57

  【jjy,1】
  OK: jjy 1(ON)
  JJY: 19/145(Sat) 10:0
  JJY: 19/145(Sat) 10:1
  JJY: 19/145(Sat) 10:2
    :

 【mset】コマンドで、現在の日時をArduinoに設定します。【jjy】コマンドでJJYエミュレーションを有効にすると、JJYタイムコードの送信を開始します。
 電波時計を「受信開始」状態にしてください。製品毎に異なりますが、大抵の電波時計には、電波の強制受信ボタンが備わっています。LCD表示などに電波の受信状態が反映される機器では、受信しているかどうか受信状態を確認してください。
 これも製品によって異なりますが、理論上は、3分間、連続で信号を受信できれば、時刻を設定することができるはずです。手持ちの電波時計では、早いもので6分程度、遅いものだと20分以上かかるものもあり、様々でした。各製品のマニュアル等で確認してください。

 おそらく、上の回路図で示した回路定数で始めた場合には、受信できないはずです。LPFの時定数が大きすぎるので、ほとんど振幅の無い信号が出力されています。ここから、少しづつ、R1の抵抗値を小さなものに変えながら、受信状態を確認して行ってください。
 根気のいる作業ですが、端折らずに少しづつ下げて行ってください。どこかで電波時計が受信したら、成功です。絶対に、それ以上抵抗値を下げないでください。

 LPFの抵抗値R1を小さくする ⇒ 時定数CRが小さくなる ⇒ 出力信号の振幅が大きくなる ⇒ イヤホンケーブルから発せられる信号が強くなる、という関係です。今、徐々にR1を小さくしたことで、電波時計が信号を受信する、ギリギリの抵抗値R1を見つけたわけです。
 念のため、最初に受信したその状態で、電波時計を1mほど離れたところに移動して、電波を受信できないことを確かめてください。離しても電波を送受信できてしまうようだと、「微弱無線局」で規定された電波強度を超えている恐れがありますので、即刻実験を中止してください。……そうならないように、大きな抵抗値から徐々に下げる様、提案しています。くれぐれも慎重に。


 今回は、手動(マニュアル)で、Arduinoに日時を設定したのですが、もっと良い応用として、ESP32などを使ってSNTPで時刻を取得(「#4-5 WiFiインターネット通信」)し、JJYエミュレータで電波時計を自動的に設定すると、かっこ良いかもしれませんね?……「WiFi電波時計」になります。……ん?そこまで行ったら、Arduinoで時計表示したら良いことか??(1^_^)


btn_arduino.png

btn_prev.png btn_next.png

home