以前、「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くらいのようですので、その辺りが微弱無線の上限と思われ、
超えないようにしなければなりません。
以上です。