以前、「M5StickCによるJJY Simulatorで電波時計の時刻を設定」という記事を

書きましたが、今回はパソコンから USBシリアル変換モジュールを使って

JJY模擬信号を出力する JJY Simulatorを製作しました。

 

製作途中に下記の動画を見つけてしまい、

結果的に後追い記事みたいになってしまいました…。

UARTで電波時計を同期させる FakeJJY - ニコニコ動画 (nicovideo.jp)

 

■ 部品表

 

まずは部品表と完成写真です。

 

記号 員数 品名 品番 コード
L1 1 八ヶ岳クラブ 小型バーアンテナコイル 340uH MM-B-ANT 340UH 千石電商
C1,C2 2 絶縁ラジアルリード型積層セラミックコンデンサー 0.1uF 50V RD15W104K1HL2L 秋月 P-04064
R1 1 金属皮膜抵抗 1kΩ 1/4W MFS25F1KB 秋月 R-08535
PCB 1 16ホール ユニバーサル基板 (2.54mm) Board 1 16holes 秋月 P-02515
CN 1 ピンヘッダ(オスL型) 1x40 PH-1x40RG(2) 秋月 C-01627
CN 1 ピンソケット(メス) 1x4 FH-1x4SG/RH 秋月 C-10099
- 1 FT234X 超小型USBシリアル変換モジュール AE-FT234X 秋月 M-08461
- 1 GROOVY USB Type Aオス - Micro Bオス 変換コネクタ GM-UH010 ヨドバシ

 

 

バーアンテナコイルは、秋葉原にある千石電商の店舗で偶然見つけたもので、

せんごくネット通販では見つかりませんでした。

 

 

■ アンテナ回路

 

送信したいJJYの周波数が 40kHzで、アンテナコイルのインダクタンスが 340uHですので、

共振周波数の計算式 f=1/(2*PI*sqr(L*C)) より、必要な

キャパシタンスは C=1/((2*PI*f)^2*L)=1/((2*PI*40kHz)^2*340uH)≒0.0466uF になります。

 

ただ手持ちのコンデンサが 0.1uFしかなかったため 2個直列にして約0.05uFにしました。

また、FT234XDチップの TXDポート出力電流が最小設定で Max 4mAですので

直列に 1kΩの抵抗を追加しました。

 

LTspiceでシミュレートすると、下記のようなインピーダンス周波数特性になりました。

 

 

ADALM2000 Scopyで実測すると、下記のようなインピーダンス周波数特性になり、

シミュレーションとほぼ同じ結果となりました。

 

 

 

■ パソコンソフト

 

USBシリアル変換モジュールの UART通信設定は、

・ボーレート: 80 kbps (2ビットで 40kHzとするため 2倍となる)

・データ長: 8 ビット

・パリティ: なし

・ストップビット: 1 ビット

になります。

 

データとして 0x55 を 1バイト送信すると、スタートビットとストップビットを含めて

125usの時間で 0/1が 5回繰り返されることになります。

800バイト連続で送信すると 0.1s(=125us*800)になりますので、これを 1セットとして、

JJYのパルス幅 0.2/0.5/0.8sに応じて 2/5/8セットを送信します。

 

 

プログラムはPowerShellスクリプトで作成しました。

 

当初は、1パルス毎に 1回の wirte()で済むように、

0.2s:1600バイト、0.5s:4000バイト、0.8s:6400バイトを送信するように作ったのですが、

FT234Xドライバの制約なのか分かりませんが、4000バイト程度を超えるデータが

なぜか実際には送信されず消失してしまうので、0.1sずつ write()を分けるようにしました。

 

本当に0/1が全部連続しているかは未確認ですが、

電波時計で受信する分にはおそらく問題ないでしょう。

 

# Out-SerialPortJJYSimulator.ps1

Write-Host "JJY Simulator with Serial Port"

if ($portName -notmatch "COM\d+") {
    Write-Host ("Serial Port Names: " + [System.IO.Ports.SerialPort]::GetPortNames() -join ",")
    $portName = Read-Host "Enter Port Name"
}

$codeName = @(
    "   M", " 40m", " 20m", " 10m", "   0", "  8m", "  4m", "  2m", "  1m", "  P1",
    "   0", "   0", " 20h", " 10h", "   0", "  8h", "  4h", "  2h", "  1h", "  P2",
    "   0", "   0", "200d", "100d", "   0", " 80d", " 40d", " 20d", " 10d", "  P3",
    "  8d", "  4d", "  2d", "  1d", "   0", "   0", " PA1", " PA2", " SU1", "  P4",
    " SU2", " 80y", " 40y", " 20y", " 10y", "  8y", "  4y", "  2y", "  1y", "  P5",
    "  4w", "  2w", "  1w", " LS1", " LS2", "   0", "   0", "   0", "   0", "  P0"
)
$baudRate = 40000 * 2
$pulseTime = @{ "M" = 2; "P" = 2; "1" = 5; "0" = 8 }
$pulseData = [byte[]](@([byte]0x55) * ($baudRate / 10 / 10))

try {
    $serial = New-Object System.IO.Ports.SerialPort $portName, $baudRate, None, 8, One
    $serial.WriteBufferSize = $pulseData.Length
    $serial.WriteTimeout = 1000
    $serial.RtsEnable = $true
    $serial.Open()

    $date = Get-Date
    $second = $date.Second
    $code = $null
    while ($true) {
        while ($second -eq $date.Second) {
            Start-Sleep -Milliseconds 1
            $date = Get-Date
        }
        $second = $date.Second
        if (($second -eq 0) -or ($null -eq $code)) {
            $code = @(
                "M",
                ($am = [System.Convert]::ToString(
                    ([System.Math]::Truncate($date.Minute / 10) -shl 5) +
                    ($date.Minute % 10), 2).PadLeft(8, "0")),
                "P00",
                ($ah = [System.Convert]::ToString(
                    ([System.Math]::Truncate($date.Hour / 10) -shl 5) +
                    ($date.Hour % 10), 2).PadLeft(7, "0")),
                "P00",
                [System.Convert]::ToString(
                    ([System.Math]::Truncate($date.DayOfYear / 100) -shl 5) +
                    ([System.Math]::Truncate($date.DayOfYear / 10) % 10), 2).PadLeft(7, "0"),
                "P",
                [System.Convert]::ToString(($date.DayOfYear % 10), 2).PadLeft(4, "0"),
                "00",
                (($ah -replace "0").Length % 2),
                (($am -replace "0").Length % 2),
                "0P0",
                [System.Convert]::ToString(
                    (([System.Math]::Truncate($date.Year / 10) % 10) -shl 4) +
                    ($date.Year % 10), 2).PadLeft(8, "0"),
                "P",
                [System.Convert]::ToString([int]$date.DayOfWeek, 2).PadLeft(3, "0"),
                "000000P"
            ) -join ""
            Write-Host "Time Code: $(
                $code.Insert(50, " ").Insert(40, " ").Insert(30, " ").Insert(20, " ").Insert(10, " "))"
        }
        $codeValue = [string]$code[$second]
        Write-Host "$($date.ToString("yyyy/MM/dd HH:mm:ss")) -> $($codeName[$second]) : $codeValue"
        for ($i = 0; $i -lt $pulseTime[$codeValue]; $i++) {
            $serial.Write($pulseData, 0, $pulseData.Length)
        }
    }
} catch {
    Write-Warning ($_ | Out-String)
} finally {
    $serial.Close()
}

 

■ 電波時計で受信

 

実際に電波時計をアンテナコイルから離して受信させたところ、

・約1m … 問題なく時刻が設定された。

・約2m … 電波時計の電波強度表示は大きくなるものの設定されず。

・約3m … 電波時計の電波強度表示は低いままで設定されず。

という結果になりました。

 

もう少し正確にアンテナマッチングを行えば、

より長い距離でも受信できるかもしれませんが、ここで終わりにしたいと思います。

 

市販されている微弱無線の電波時計用リピーターの製品仕様を見ると、

長くて距離10mくらいのようですので、その辺りが微弱無線の上限と思われ、

超えないようにしなければなりません。

 

以上です。