非職業的技師の覚え書き

JK1EJPの技術的検討事項を中心に記録を残します。

Teensy(11)Keiths' SDRのHilbertオブジェクト

修正(2022/07/28)
信号処理連鎖を流れるデータの単位は128サンプルのデータブロックであることを認識し、修正しました。

修正(2022/07/23)
フィルタ特性をLPFとして見ていましたが、BPFとして見るのが適切でした。特にCW用の狭帯域フィルタはBPFとして見る必要があります。通過帯域幅以外のフィルタ設計諸元は不明ですが、周波数特性図に帯域中央周波数を推定して記入し、コメントも修正しました。

Keiths' SDRのRx信号処理連鎖ブロック図

Keiths' SDR(K7MDL局Mike OM版)のRx信号処理連鎖をコードから読み解いて抜き出したもの(α版)を下記に再録します。

今回は、朱色点線内★でマーキングした3つのオブジェクトを調べました。ノイズブランカヒルベルトフィルタ、加算器です。

Block diagram (alpha version) of the Rx signal processing chain and the NoiseBlanker, Hilbert, and Summer objects under consideration here.

NoiseBlankerオブジェクト(radioNoiseBlanker_F32クラス)

役割

衝撃性パルス雑音を空白(blank)に置換して抑制します。

空白置換は急峻な信号変化となるため、周波数帯域が広がってしまいます。そのため、Noise Blankerは高域をカットするHilbert FIRフィルタの前に挿入されています。

信号処理連鎖の更新処理

update()関数では以下の信号処理を行っています。

  1. データブロックの128サンプルに以下の処理を逐次適用
    ・長さ256(5.3msec)のリング・バッファの1stデータに新規データの絶対値を積み、その総和を算出
    ・総和にしきい値(1.0E3f)を乗じた値よりデータの絶対値が大きければ、衝撃性ノイズと見做し、データ値をゼロに置換(blanked out)
  2. 次の信号処理オブジェクトにデータを送信
  3. データブロックの解放

ディフォルトでは、平均信号強度の約4倍のパルス入力をノイズと見做す設定のようです。しきい値を変更する関数も準備されています。

RX_Hilbert_...オブジェクト(AudioFilterFIR_F32クラス)

役割

±45度のHilbert FIRフィルタのオブジェクトです。信号の低域通過によるエイリアス等の除去と、位相シフトによるイメージ除去の準備を行います。(後段の加算器Summerにてイメージ除去が行われます。)

PJRC社のオーディオ・ボードはアナログ・アンチエイリアス・フィルタを備えていません。RFフロントエンドにRS-HFIQ(HobbyPCB)を使用した場合は、RS-HFIQ側でQSDの出力をオペアンプのLPFに通してから出力しています。カットオフ周波数はfc=145kHz(C=220pF、R=4.99kΩ)です。販売元のHobbyPCBが推奨するUSBサウンドカード(StarTech - ICUSBAUDIO2D)は96KHz、24-bitの仕様です。これに対して、PJRC社のオーディオ・ボードは48KHz、16-bitの仕様です。サンプリング周波数fs=48kHz、ナイキスト周波数fn=24kHzに対してfc=145kHzのLPFは、有効なアンチエイリアス・フィルタにはならないようです。将来、エイリアス雑音が課題になる可能性があります。

Hilbert FIRフィルタの係数はオブジェクト宣言時ではなく、後にbegin()関数を呼んで設定しています。予め複数の遮断周波数(500Hz、700Hz、1.0kHz、1.8kHz、2.3kHz、2.8kHz、3.2kHz、4.0kHz)に対応したフィルタ係数が、ヘッダ・ファイルHilbert.hの中に定義されています。タップ数は151タップで共通です。GUI操作を通して適切なバンド幅のフィルタ係数に切り換えられるようになっているようです。SDRの利点として、好みのフィルタを追加することも容易と思います。

立ち上げ時には、最も帯域の広い4.0kHzのフィルタを設定しています。

    RX_Hilbert_Plus_45.begin(Hilbert_Plus45_40K,151);   // Left channel Rx
    RX_Hilbert_Minus_45.begin(Hilbert_Minus45_40K,151); // Right channel Rx

信号処理連鎖の更新処理

update()関数では以下の信号処理を行っています。

  1. 128サンプルのデータブロックに以下の処理を一括適用
    ・入力データブロック(のポインタ)の取得
    FIRフィルタのタップ数に合わせてデータブロック長を調整
    ・arm_fir_f32()関数のコール
  2. 次の信号処理オブジェクトへのデータ送信
  3. メモリ・ブロックの解放

単純な積和演算ですが、メモリ・ブロックの生成、解放に注意を払った実装がされています。余計なデータの複写を避けて、可能な限り演算時間を最小にする工夫と思われます。

CMSIS DSP Library: arm_fir_f32()

RX_Hilbert_...オブジェクトのupdate()関数では大量の積和演算が発生するため、CMSIS(Cortex Microcontroller Software Interface Standard)DSP Libraryのarm_fir_f32()関数をコールしています。このライブラリ関数はCortex-MプロセッサのDSP演算に最適化されてコンパイル済みです。

入力データブロック長128サンプルに対して、フィルタのタップ数は151で一致しません。不一致の場合は、内部のデータブロック長を調整するCMSIS DSP Libraryのarm_fir_init_f32()関数を呼んで再初期化をしています。フィルタを切り換えて再びタップ数が変更されたら、arm_fir_init_f32()関数を呼んで再々初期化を行います。このように、タップ数の異なるフィルタ切換を任意に適用することが保証されています。

CMSIS DSP Libraryのarm_fir_f32()関数は、フィルタのタップ数に合わせた内部データバッファの状態をarm_fir_instance_f32インスタンスとして、関数から戻った後も保持します。次回の信号処理連鎖が起動した時に、次回のデータブロックに含まれる128サンプルの頭からフィルタ計算を継続して行うために、現在の内部データバッファの状態を引き継ぐためです。

CMSIS DSP Libraryの関数はコンパイル済みのため、中身を調査することはできないと思っていましたが、githubにコードが公開されていました。

https://github.com/ARM-software/CMSIS/blob/master/CMSIS/DSP_Lib/Source/FilteringFunctions/arm_fir_f32.c

フィルタ特性

Hilbert.hのフィルタ係数をPython(Jupyter Notebook)にExportし、各フィルタのカットオフ周波数やスロープ特性等を確認するために可視化しました。上段に位相シフト+45度のフィルタの特性を、下段に位相シフト-45度のフィルタの特性を示します。左からインパルス応答、周波数特性(ゲイン)、周波数特性(位相)を示します。

カットオフ周波数狙い値4.0kHz

Hilbert FIR filter with a cutoff frequency of 4.0 kHz.

カットオフ周波数狙い値4.0kHzにおいて、-3dBの減衰が達成されているように見えます。-3dBの通過帯域幅は設計仕様通りの4.0kHzです。帯域中央周波数は2.0kHzより若干大きい値です。スロープは急峻です。

カットオフ周波数狙い値3.2kHz

Hilbert FIR filter with a cutoff frequency of 3.2 kHz.

カットオフ周波数狙い値3.2kHzにおいて、-10dB以上の減衰過達に見えます。-3dBの通過帯域幅は3.2kHzより狭いようです。

カットオフ周波数狙い値2.8kHz

Hilbert FIR filter with a cutoff frequency of 2.8 kHz.

カットオフ周波数狙い値2.8kHzにおいて、-10dB減衰が達成されているように見えます。-3dBの通過帯域幅は2.8kHzより狭いようです。

カットオフ周波数狙い値2.3kHz

Hilbert FIR filter with a cutoff frequency of 2.3 kHz.

カットオフ周波数狙い値2.3kHzにおいて、-10dB弱の減衰が達成されているように見えます。-3dBの通過帯域幅は2.3kHzより狭いようです。

カットオフ周波数狙い値1.8kHz

Hilbert FIR filter with a cutoff frequency of 1.8 kHz.

カットオフ周波数狙い値1.8kHzにおいて、-3dBの減衰が達成されているように見えます。-3dBの通過帯域幅は設計仕様通りの1.8kHz。帯域中央周波数は約800Hz。スロープは緩慢です。

カットオフ周波数狙い値1.0kHz

Hilbert FIR filter with a cutoff frequency of 1 kHz.

カットオフ周波数狙い値1kHzにおいて減衰はなく、通過域の中央のように見えます。-3dBの通過帯域幅は設計仕様通りの1kHz。帯域中央周波数も約1kHz。スロープは緩慢です。

カットオフ周波数狙い値700Hz

Hilbert FIR filter with a cutoff frequency of 700 Hz.

カットオフ周波数狙い値700Hzにおいて減衰はなく、通過域の中央のように見えます。-3dBの通過帯域幅は設計仕様通りの700Hz。帯域中央周波数も約700Hz。スロープは緩慢です。

カットオフ周波数狙い値500Hz

Hilbert FIR filter with a cutoff frequency of 500 Hz.

カットオフ周波数狙い値500Hzにおいて減衰はなく、通過域の中央のように見えます。-3dBの通過帯域幅は設計仕様通りの500Hz。帯域中央周波数は約700Hz。スロープは緩慢です。

フィルタ特性のまとめ
  1. +45度と-45度のフィルタのステップ応答は鏡像関係にある。
  2. +45度と-45度のフィルタの位相は90度シフトしている(明瞭ではない)。
  3. カットオフ周波数通過帯域幅狙い値4.0kHzのフィルタが最もスロープが急峻である。
  4. カットオフ周波数が低く通過帯域幅狙い値が狭くなるほどスロープが緩慢になり、設計が難しくなるように見える。

位相シフトのシミュレーション

+45度と-45度のフィルタの位相は90度シフトしているようですが、明瞭ではありません。そこで、シミュレーションにより確認しました。

2kHzのSin信号を生成し、+45度と-45度のFIRフィルタ(カットオフ周波数4.0kHz)に入力しました。計算にはscipy.signal.lfilterを用いました。S(青)は入力した2kHzのSin信号、S_Plus(橙)は+45度のFIRフィルタの出力、S_Minus(緑)は-45度のFIRフィルタの出力です。

esults of checking phase shifts by inputting 2 kHz Sin signal to plus and minus 45 degree Hilbert FIR filters (cutoff frequency 4.0 kHz).

S_Plus(橙)は125サンプル点目(2.6msec)までに入力信号Sに目視上重なります。カットオフ周波数4.0kHzのフィルタのインパルス応答は125サンプル点目までに目視上収束していることと呼応しています。+45度のFIRフィルタの位相シフトは0度でした。これに対して、S_Minus(緑)の-45度のFIRフィルタの位相シフトは-90度でした。±45度のFIRフィルタと言うより、0度と-90度のFIRフィルタと言う方が適切と思います。

RX_Summerオブジェクト(AudioMixer4_F32クラス)

役割

Audioミキサは信号加算器です。RFミキサの乗算器とは異なります。0度と-90度のFIRフィルタを通したIQ信号を加算し、イメージ信号を抑制することが役割です。

オブジェクトはゲインを持ち、ゲインを乗じてから加算します。2チャンネルのゲインは通常(1,1)であり、LSBモードの時に(1,-1)に切り換えます。-90度のFIRフィルタの出力に対して、180度の位相反転を行うことに相当します。通信モード毎のゲイン切り換えは、Mode.cppファイルに定義されています。

信号処理連鎖の更新処理

update()関数では以下の信号処理を行っています。

  1. 128サンプルのデータブロックに以下の処理を一括適用
    ・最初のチャネルの信号(I信号)データを取得し、共有出力変数に格納し、ゲインを乗算
    ・次のチャネルの信号(Q信号)データを取得し、ゲインを乗算し、一時的変数に格納
    ・共有出力変数と一時的変数を加算し、結果を共有出力変数に格納
  2. 次の信号処理オブジェクトへのデータ送信
  3. メモリ・ブロックの解放

単純な加算演算のため、RX_Summerオブジェクトのupdate()関数では大量の積和演算は発生しませんが、乗算はCMSIS DSP Libraryのarm_scale_f32()関数を、加算はarm_add_f32()関数をコールし、128サンプルのデータブロックに一括適用しています。

イメージ信号抑制のシミュレーション

イメージ信号の抑制を確認するシミュレーションのために、Sin/CosのIQ信号を生成し、それぞれ+45度と-45度(0度と-90度)のHilbert FIRフィルタ(カットオフ周波数4.0kHz)に入力し、その出力を加算しました。

チューニング信号

チューニング周波数のUSB側の対象信号と想定したIQ信号を生成しました。下記IQ試験信号は2kHzで半時計方向に回転します。

IQ signal assumed as the target signal on the USB side of the tuning frequency.

このIQ試験信号をHilbert FIRフィルタに入力し、出力を加算した結果を下記に示します。

Result of inputting the IQ test signal to the Hilbert FIR filter and adding its outputs.

2kHzのIQ試験信号はフィルタの通過帯域内にあるため、フィルタのタップ数に依存した過渡応答経過後は位相シフトのみが適用されます。位相シフトによってIQ信号の位相が揃った結果、定常状態の振幅は加算によって2倍になっています。フィルタのタップ数は151タップですが、目視上は125サンプル点目(2.6msec)で収束しています。先に見たように、カットオフ周波数4.0kHzのフィルタのインパルス応答は125サンプル点目までに目視上収束していることと呼応しています。

イメージ信号

チューニング周波数より高い周波数のLSB側のイメージ信号と想定したIQ信号を生成しました。下記IQ試験信号は時計方向に回転します。

IQ signal assumed to be the image signal on the LSB side at a frequency higher than the tuning frequency.

このIQ試験信号をHilbert FIRフィルタに入力し、出力を加算した結果を下記に示します。

The result of inputting the image-assumed IQ test signal to the Hilbert FIR filter and adding its outputs.

位相シフトによってIQ信号が逆位相の関係になった結果、定常状態の振幅は加算によってゼロに収束しています。フィルタのタップ数は151タップですが、目視上は125サンプル点目(2.6msec)までに収束しています。

Keiths' SDR(K7MDL局Mike OM版)に予め実装されたHilbert FIRフィルタによって、2.6msec程度の遅延を伴ってイメージの抑制が図られることがシミュレーションによって確認されました。

Teensy(10)Keiths' SDRのInputオブジェクト

注記(2022/10/30)
Keiths' SDRのサンプリング周波数は48KHzであることが実機による実験で確認できました。

修正(2022/07/28)
信号処理連鎖を流れるデータの単位は128サンプルのデータブロックであることを認識し、修正しました。

Keiths' SDRのRx信号処理連鎖ブロック図

Keiths' SDR(K7MDL局Mike OM版)のRx信号処理連鎖をコードから読み解いて抜き出したもの(α版)を下記に再録します。

今回は、Rx信号処理連鎖の先頭のInputオブジェクト(朱色点線内★)を調べました。

Block diagram (alpha version) of the Rx signal processing chain and the Input object under consideration here.

Inputオブジェクト(AudioInputI2S_F32クラス)

Teensy Audio BoardからF32(32bit浮動小数点)型のステレオ・オーディオ信号(IQ信号)を取り込むオブジェクトです。Rx信号連鎖の起点になります。 なお、SGTL5000を搭載するディフォルトのPJRC社のオーディオ・ボードには、IQ信号が順不同になるバグがあるらしいのですが、その解決策等を含めたコミュニティの成果の詳細は別の機会に報告します。

Inputオブジェクトが、IQ信号入力に係わるハードウェアの初期化も請け負っていると思われます。IQ信号入力に係わるハードウェア(青)とソフトウェア(赤)の関係図を下記に示します。なお、この図はコードとi.MX RT1060データシートから読み取ったものであり、将来修正の可能性があります。

Diagram of the relationship between hardware and software involved in IQ signal input.

コンストラク

IQ信号2channel分のサンプリング周波数は96/2=48kHzです。サンプリングはMCU(i.MX RT1060)のPLLがマスターとなってオーディオ・ボードを管理します。Inputオブジェクトのコンストラクタはbegin()関数をコールし、その関数の中で以下のMCU(i.MX RT1060)の初期化を実行しています。

  1. DMAチャネル・オブジェクトへのチャネルの割り当て
  2. MCUとオーディオ・ボード間のPLL Clockを含めたI2S通信の設定
  3. DMAチャネル・オブジェクトへのISR(割込みサービス・ルーチン)の割り当て

MCUの機能ブロック図を下記に示します。

Functional block diagram of the MCU (i.MX RT1060) and modules involved in the Input object (red dotted circle).

Inputオブジェクトに係わる朱色点線MCUモジュールについて、以下にメモします。

eDMA

MCU(i.MX RT1060)のDMA(Direct Memory Access)モジュールは、eDMA(enhanced DMA)と称されており、TCD(Transfer Control Descriptor)を用いて柔軟なデータ転送を可能にしています。Keiths' SDRは、DMAチャネル・クラスのbegin()関数内で、IQサンプリングデータ転送のためのDMAチャネルをTCDに設定しています。eDMAが転送するデータの単位は、128サンプルのデータブロックです。I信号128サンプルとQ信号128サンプルを交互に転送しています。

MCU内の eDMAがCPUコア(Arm Cortex-M7)とは独立にIQサンプリングデータの転送を取り仕切るため、低速なI/O処理に対するCPUコアの空転は発生しません。Teensyが搭載するMCUのCPUコアは600MHzで動作する高速1コアであるため、CPUコアを空転させない実装が求められます。

I2S/SAI

MCU(i.MX RT1060)のI2S/SAI(Inter-IC Sound / Synchronous Audio Interface)モジュールがI2Sバスを介して、オーディオ・ボードからIQ信号サンプリング・データを受け取り、eDMAでメモリに転送しているものと思われます。クラス名のAudioInputI2S_F32は、この「I2S」を冠しています。

begin()関数内で、スレーブ動作のオーディオ・ボードに対してI2S/SAIに必要なClock信号を提供するために、CCM内のAnalog System PLLを設定しています。

CCM

MCU(i.MX RT1060)のCCM(Clock Controller Module)がAnalog System PLLの中のAudio / Video PLLを用いて、オーディオ・ボードに供給する様々なClock信号を生成します。

I2S/SAIに必要なClock信号は、①audio master clock、②bit clock、③bus clockの三つです。それぞれTeensy 4.1のピンに、①23:MCLK、②21:BCLK、③20:LRCLKとして割り当てられ、オーディオ・ボードに供給されます。③bus clock(LRCLK)がサンプリング周波数96kHz48KHzに設定されます。

NVIC

IQ信号サンプリング・データのDMA転送完で割込を発生させ、ISR(割込みサービス・ルーチン)の中のupdate_all関数によって信号処理連鎖を起動しているものと思われます。ただし、この関数の中身は下記1行のみです。

static void update_all(void) { NVIC_SET_PENDING(IRQ_SOFTWARE); }

CPUコア(Arm Cortex-M7)の割込コントローラNVIC(Nested Vectored Interrupt Controller)に対して、IRQ_SOFTWARE番号の割込を保留状態にセットしているだけです。IRQ_SOFTWARE番号にはsoftware_isr()関数を割り当てています。この関数が信号処理連鎖起動の割込処理本体になります。

ここでの「保留」は実行中断という意味ではなく、実行予約という意味で使われていると思われます。割込優先順位に応じて順番が回って来れば実行されると思います。IRQ_SOFTWAREの割込優先順位は208/255番と低く設定されています。ハードウェア割込を優先するためと思われます。

規模の小さい組込ソフトウェアでは、割込でフラグセットのみ行い、ポーリングループでフラグを見て割込処理を起動するといった実装方法を取ることがあります。割込処理を長くすると多重割込処理が複雑になるからです。ここでは、そのような処理をシステマチックに行っている印象です。

IRQ_SOFTWAREはIRQ70番のエイリアスのようですが、ARM v7-M Architecture Reference Manualでは、IRQ70番は「Reserved」になっていてユーザには解放されていないようです。使用されていない割込番号をHackして独自に活用しているのでしょうか・・・?。OSが載っている訳ではないため、将来、「Reserved」が「Used」になっても問題はないと思いますが、Teensy 5?にバージョンアップした時には要注意です。別のハードウェア・モジュールがIRQ70番を使用している可能性もあり得ます。

保留設定後に実行されるsoftware_isr()関数の中身は、信号処理連鎖を構成するオブジェクトに対する更新処理update()関数の順次適用の繰り返しです。Inputオブジェクトに対する更新は、I信号とQ信号の個別チャネルの更新です。チャネル更新の中では以下の処理を行っています。

  1. F32型オーディオ・データのスケール正規化
  2. 次の信号処理オブジェクトへのデータ送信
  3. メモリ・ブロックの解放

メモリ解放と言っても参照カウンタを減じるだけで、メモリ・ブロックを参照する他の信号処理オブジェクトが存在する限り解放はされません。こうして安全に、メモリ・ブロック上のIQ信号データは信号処理連鎖を構成するオブジェクトに転送されて行きます。

サンプリング周波数の再考

悩み

Rx信号処理連鎖の後段のHilbert FIR Filterを調べているときに、フィルタの設計仕様と通過特性の確認計算値が合わないことに悩みました。確認計算した遮断周波数が設計仕様に対して倍異なるのです。

信号処理の骨幹を成すサンプリング周波数の間違いが原因として濃厚と仮説を立てました。Decimation(Down Sampling)等の絡みも疑ったのですが、遮断周波数が倍異なることに対してロジックが合いません。(Keiths' SDRではDecimationを採用していないと暫定的に結論付けました。)

気付き

コードを精査している中で、サンプリング周波数を言及する時に2つの視点があることに気付きました。ADCの視点と信号処理の視点です。

通常、「サンプリング周波数」と言えば、後者の信号処理の視点で言及していると思い込んでいました。しかし、Inputオブジェクトのコードでは、暗黙の中に前者のADCの視点で言及していることに気付きました。

確信

InputオブジェクトのISR(割込みサービス・ルーチン)の中で、128サンプルのデータブロックを格納するi2s_rx_bufferに対するIOサンプリン・データの充填状況を調べています。バッファの前半部が充填中か、あるいは後半部が充填中かを調べ、前半部が充填中の時のみupdate_all関数を起動しています。つまり、ADCサンプリング周波数96kHz48KHz/(128x2)サンプルに同期した割込みの中の2回に1回しかRx信号処理連鎖の更新を起動していません。これにより、信号処理視点のサンプリング周波数は48KHzになります。(煩雑ですが、信号処理の起動周波数は96kHz48KHz/128サンプル/2回=375187.5Hz、信号処理の起動周期は2.75.4msecになります。)

具体的に見てみます。バッファの前半部にI信号を格納し、後半部にQ信号を格納すると仮定します。割込がかかった後にバッファ充填状況を確認に行き、バッファ前半部に時刻nのI信号を格納中であれば、時刻n-1のIQ信号は揃っていることを意味します。時刻n-1のIQ信号をRx信号処理連鎖の後段に送致することが可能です。

IQ data acquisition timing diagram and sampling frequency of the signal processing chain.

逆に、割込がかかった時にバッファ後半部にQ信号を格納中であれば、時刻n-1のIQ信号はまだ揃っていません。次の割込を待つ必要があります。

もっとスッキリと実装できないものかとも思ったのですが、eDMAがI信号あるいはQ信号のどちらのデータをメモリに転送したかを知りようがないため、このような実装になっているものと思います。MCU(600MHz)とI2S/SAI(96kHz48KHz)の速度が大きく乖離しているために成り立つ実装とも思います。

Teensy 4.1のIOピン数は多いため、PJRC社のオーディオボードを2枚スタックしてI信号およびQ信号専用のADCにすれば、サンプリング周波数96kHzを容易に実現できると思うのですが、そのような議論はないようです。サンプリング周波数と同時にbit数も拡張したいとのことから、別のADCへの換装が議論されています。

結論(暫定)(決定)

PJRC社のオーディオボードを採用したKeiths' SDRのRx信号処理連鎖のサンプリング周波数は48KHzである・・・と思います

(Keiths' SDRのソフトウェア開発に係わるOM諸氏には周知の暗黙知と思いますが、コードも含めて明示されている情報はまだ見つかっていません。)

悩み再び!?(2022/08/12)

信号連鎖ブロック図の後段に位置するFilterConvオブジェクトではサンプリング周波数96kHz48KHzでBPFが設計されていることが分かりました。もしかしたら、Hilbert FIR FilterはTeensyディフォルトのオーディオ周波数44.1kHz48KHzで設計されているのではないか?との疑念が浮上しました44.1kHz48KHzで設計したHilbert FIR Filterを96kHzで使用すると帯域が約2倍に拡大します。しかし、後段のFilterConvオブジェクトで帯域を絞るため、実害は無く仕様のミスマッチに気付かない可能性があるのではなかと推測しています。最終的にはハードウェアを組んで、信号処理連鎖の起動周期を測定しないと白黒が付かない状況です。

Teensy(9)Keiths' SDRのRx信号処理連鎖

注記(2022/10/30)
Keiths' SDRのサンプリング周波数は48KHzであることが実機による実験で確認できました。

背景

Keiths' SDR(K7MDL局Mike OM版)の信号処理連鎖を学び中です。Mike OMの光速開発には付いて行けていません。最近、Mike OMはUSBオーディオとCATをTeensy 4.1に実装し、PC上のWSJT-XとUSBケーブル1本でつながるようになった模様です。本命のSDR信号処理に加えて、使い勝手を左右するユーティリティ機能を光速で実装するスキルには敬服させられます。現在は、オーディオIQ信号のbit数を上げるためにコーディック換装の検討をされているようです。

Mike OMが夏のバカンスに出かけている間に、出来るだけキャッチアップしたいと思います。遅ればせながら、Rx信号処理連鎖の解読に着手しました。

Keiths' SDRの信号処理実装方法

オブジェクト指向による実装

Keiths' SDRの信号処理は「OpenAudio_ArduinoLibrary」と称するクラス・ライブラリを使用して実装されています。拡張子cppのC言語実装ではなく、C++オブジェクト指向で実装されています。

オブジェクト指向アルゴリズムを考える抽象化の階層を切り換えられるため、クラスを設計した当人には便利なのだろうと思います。しかし、個人的な感想としては、低レベルのハードウェアの操作がどこで行われているか分かり難いと感じます。

例えば、

  • コーディックのサンプリング周波数の設定はどのオブジェクトのコンストラクターで暗黙のうちに実行されるのか?
    コンストラクターは全てのクラスが持っている)、
  • サンプリング割込みはどこでトリガーされるのか?
    (実はタイマー割込みではなく、PLLで自動起動されるA/D変換処理の終点のDMA転送で割込みがトリガーされている模様)、

・・・等々のハードウェア処理を追うのが大変です。

IQ信号サンプリング周波数の設定

なぜハードウェア設定を気にしているのかと言いますと、Teensyのオーディオ信号処理はCDクオリティのサンプリング周波数44.1kHzをディフォルトにしているからです。開発元のPJRC社が標準コーディックSGTL5000用に提供するクラス・ライブラリ「AudioControlSGTL5000」はそのenable関数で定数44.1kHzをディフォルト引数にしています。

無線IQ信号処理に44.1kHzは低いため、Keiths' SDRではSGTL5000の仕様上限の96kHzに設定しているはずです。最初はどこで設定しているのか分からず、もしかしたら44.1kHzのままなのかと思ったほどです。Rx信号処理連鎖の先頭のオブジェクトのAudioInputI2S_F32クラスが怪しいと踏んでコンストラクターを追い駆けると、下記の通り4階層目にやっとハードウェアのi.MX RT1060チップ(MCU)のPLLに対してサンプリング・クロック96kHz48KHzを設定するset_audioClock関数が出現しました。

  1. AudioInputI2S_F32クラスのコンストラクター
  2. AudioInputI2S_F32クラスのbegin関数
  3. AudioOutputI2S_F32クラスのconfig_i2s関数
  4. imxrt_hw.cppのset_audioClock関数

SGTL5000はMCUのスレーブモードで動作する設定です。MCUのPLLからSGTL5000に96kHz48KHzのサンプリングClockを供給しています。混乱させることに、SGTL5000もPLLを持っており、マスタモードでも動作できるようです。

コンストラクター内で、InputクラスOutputクラスの関数を呼んでいるというのが分かり難いですね。ADCとDACを備えたコーディックなのでどちらかで設定すれば良いということですが、Rx信号処理連鎖で動いている時間の方が長いことから、Inputクラスに設定関数があった方が分かり易い気がします。SDRラジオの場合だったとしたら、Outputクラスが出てくるのは変ですし・・・。

Audio Adaptor Board for Teensy 4.x

注記(2022/07/09)
コードの精査から、96kHz/1channel、48kHz/2channelのようです。ADCの視点では96kHz/1channelであっても、2channel分のオーディオIQ信号を取り込む必要のある信号連鎖の視点からはサンプリングタイムfs=48kHzになります。次回に詳細を報告します。

OpenAudio_ArduinoLibrary

SDR信号処理連鎖の実装に用いられているオブジェクト指向コアライブラリの「OpenAudio_ArduinoLibrary」について簡単にメモします。

Teensy Development Boardの開発元であるPJRC社が提供する「Teensy Audio Library」が継承ベースになっています。PJRC社はオーディオ信号処理をTeensyの主要なアプリ分野の1つに据えている模様で、専用のコーディック・ドーター・ボードも提供しています。Keiths' SDRはこれを標準?コーディックとして使用しています。

オーディオ信号処理アプリ開発の敷居を下げるために、PJRC社はTeensy Audio Libraryを使用したアプリをビジュアル設計できるAudio System Design Tool for Teensy Audio Libraryも提供しています。ビジュアルプログラミング用ツールのNode-REDにインスパイアされて開発した模様です。

しかしながら大きな制約として、Teensy Audio Libraryは「I16」(16bit整数)型でしか信号を扱えません。通常のオーディオ信号処理アプリにはこれで十分と判断したのかもしれませんが、無線IQ信号処理にはダイナミックレンジが不足します。Teensy 4.xが搭載するi.MX RT1060チップのArm Cortex-M7コアは、FPUとDSP extensionsによって「F32」(32bit浮動小数点)型の積和演算を1クロックで実行する能力があるのに、宝の持ち腐れになってしまいます。なお、DSP extensionsはMCUと別のDSPコアではなく、MCUの内部でDSP積和演算を実行可能にする拡張機能を指します。

そこで、TeensyユーザのOM諸氏がF32型の信号を扱える上位互換の継承ライブラリを開発して公開しました。それが「OpenAudio_ArduinoLibrary」です。「OpenAudio_ArduinoLibrary」のクラス名には全て「_F32」の識別子が末尾に付き、継承している元のTeensy Audio Libraryと混在して使用することができます。下記に示すように、ビジュアルプログラミングも可能です。

Rx信号処理連鎖

ブロック図

Keiths' SDR(K7MDL局Mike OM版)のRxの信号処理連鎖をコードから読み解いて抜き出したものを下記に示します。α版です。

Rx signal processing chain implemented using the OpenAudio_ArduinoLibrary.

着色したブロックが信号処理に係わり、無着色のブロックはスイッチ等の信号連鎖の管理に係わっています。ブロックを表すオブジェクトのインスタンス同士はpatchCord...関数が結びつけます。ビジュアル設計ツール(Audio System Design Tool for Teensy Audio Library)を使用すれば、自動的に生成してくれるようです。

ビジュアル設計ツール上にブロック図の一部をトレースして、コードのExportを実験してみました。

Experiment with tracing a portion of a block diagram on the Visual Design Tool and exporting the code.

Exported code from the Visual Design Tool.

Exportされたコードを見ると、必要なヘッダーファイルの宣言、OpenAudio_ArduinoLibraryのオブジェクトの宣言、オブジェクト間を連結するpatchCord...関数の宣言が自動生成されていました。ビジュアル設計ツール上の座標はコメントとして付加され、Import時に配置が再現されるようです。

次回より、着色した信号処理ブロックの中身を調べて行きたいと思います。Teensy4.1に搭載されているArm v7-Mベースのi.MX RT1060は複雑なMCUです。機能設定のレジスタの数も膨大です。ハードウェアがオブジェクト指向によって抽象化されていることもあり、まだ全貌を把握するには至っていません。分かった範囲、あるいは予想がついた範囲の内容をメモし、後で正誤が判明した場合は修正したいと思います。

AFP-FSK Transceiver(13)FSK送信試験

試験方法

FT8等のFSKモードのあい路の1つは、送信電波の品質チェックが難しい点を挙げられると思います。SSB帯域外は従来のスプリアス確認で間に合いますが、オーディオ帯域内の品質、特にオーディオ高調波を見逃すと他局に混信を与えてしまう可能性があります。WSJT-Xは「疑似スプリット」まで用意し、送信電波の品質確保に注力していることが分かります。

AFP-FSK Transceiverは原理的にオーディオ高調波を生じないはずですが、ソフトウェアの実装に依存して意図しない動作をしないか予め確認しておく必要があります。試験システムの系統図を下記に示します。前記受信試験の13TR-FT8とAFP-FSK Transceiverを入れ替えるだけです。Dummy Loadにつなげて送信した際の近接場漏洩電磁波を評価します。漏洩電磁波の強度を試験用最小限にするため、AFP-FSK Transceiverの電源は12VDCとしました。

Systematic diagram of the test system for transmitting FSK radio waves.

WSJT-Xチューン・モード試験

500Hz チューン

まず、WSJT-Xチューン・モードを試しました。指定したTx周波数のAUDIO信号をWSJT-XがAFP-FSK Transceiverに入力してくれます。Tx周波数を500Hzに設定してチューンをクリックしました。

Results of ASP-FSK Transceiver transmission test using WSJT-X tune mode.

一般論では、混合器等の非線形素子によって低周波の500Hzから発生したオーディオ高調波(1k、1.5k、2k、2.5k、3kHz)は狭帯域フィルタ(<3kHz)を掻い潜って送信されてしまうリスクが高くなります。

しかし、AFP-FSK Transceiverからはオーディオ高調波に由来するRF高調波が一切出ていないことが確認できました。AUDIO周波数と搬送波周波数の混合はAFP-FSK演算の中で単なるスカラ値の加算として行われているため、原理的にRF高調波は出ません。その原理的帰結が念のため確認されたことになります。

気付き

送信開始時に周波数が少し右にシフトしています。周波数軸を拡大して、チューンを複数回試行した結果を下記に示します。

Minute frequency shift at the start of the tune.

間違いなく、右に約10Hzのシフトが繰り返し再現されています。WSJT-Xのチューン・モードの処理に依存しているのか、あるいはFSKの周波数遷移時の高調波を防止するWSJT-Xのフィルタ処理の副作用か、あるいはAFP-FSK TransceiverのAUDIO周波数計測の移動平均処理の遅れか、・・・原因は今のところ不明です。

AFP-FSK Transceiver送信と13TR-FT8受信

400Hz、1kHz、2kHz、4kHz AUDIO信号

WaveGenから400Hz、1kHz、2kHz、4kHzのAUDIO信号をAFP-FSK Transceiverに入力しました。AFP-FSK Transceiverがダミーロードに送信した電力からの漏洩電磁波をSDRplayで受信した結果を下記に示します。WaveGenの指示通り、周波数がシフトしていることが分かります。

Receiving results by SDRplay of the leaked electromagnetic wave transmitted by the AFP-FSK Transceiver to the dummy load show that the frequency is shifted as instructed by WaveGen.

この時、13TR-FT8が受信した漏洩電磁波のWSJT-Xワイドグラフを下記に示します。目視ですが、周波数シフトはSDRplayの受信結果と一致します。若干左にずれているのは、13TR-FT8の前記校正結果と一致します。

The WSJT-X wide graph received by 13TR-FT8 shows that multiple shifts of the AFP-FSK Transceiver's transmit frequency match the results received by SDRplay.

なお、AFP-FSK Transceiverはオーディーオ出力の帯域が許す限り可聴域を超えた4kHzでも送信可能ですが、国内で許可されるHFバンドの狭帯域データ・モードとするためには3kHz以下(副搬送波は2.95kHz以下)に制限する必要があります。

気付き

Windowsのオーディオ関係のAPIは、以下の順に進化してきたようです。

  1. MME(Multi-Media Extensions)
  2. Direct Sound
  3. ASIO(Audio Stream Input Output)(他社規格)
  4. WASAPI(Windows Audio Session API

WaveGenの再生デバイス設定では、MMEを使用し、オプションの「EXTENSIBLEを使う」にチェックを入れていました。ヘルプでは「多チャンネル多ビット長対応の拡張フォーマットである WAVE_FORMAT_EXTENSIBLE 形式でデバイスをオープン」となっています。

この設定で13TR-FT8は動作していましたが、AFP-FSK Transceiverは送信ON/OFFが頻繁に切り替わるなど動作が不安定になり送信周波数も安定しませんでした。「EXTENSIBLEを使う」のチェックを外すと安定になりました。USBサウンドカードとの不整合が発生し、おそらくAUDIO信号が間欠的になっていたと現象から推定されます。アナログの13TR-FT8の方がこの点ではロバストでした。ディジタルのAFP-FSK Transceiverは過敏だったようです。

Message送信試験

SDRplay受信

次に、User messageの送信試験として、WSJT-XからAFP-FSK Transceiverに、1kHz副搬送波に対するCQメッセージのModulationを指示しました。SDRplayによるModulation漏洩電磁波の受信結果を下記に示します。

Receiving results of electromagnetic leakage by SDRplay when the modulation of CQ message for 1 kHz subcarrier wave was instructed to AFP-FSK Transceiver from WSJT-X as a transmission test of User message.

SDRplayのRefresh rateを最大にしてコントラストを調整すると、ウオーター・フォールに8-GFSK変調波が浮かび上がってきました。7,075kHzからの占有周波数帯域幅が50Hzであることが確認できます。この中に「CQ」が隠されているはずです。

上側のFFTスペクトルは瞬時周波数スペクトルを示している訳ではありません。40mバンドに対するSDRunoのFFTの仕様は、データ長65,536点、サンプリング周波数333,333Hz、平均回数3回です。FFTが対象にする時系列データ区間は0.59秒になります。FT8の送信速度は6.25-baudであることから、0.59秒には3.7回の周波数遷移が含まれていると思われます。これにより、前記チューン・モードよりも幅の広いスペクトルになります。

50dBcより下には、お椀形状のスペクトルが占有周波数帯域幅50Hzの外側に広がっていることが確認できます。さらに、お椀の左右外側にも50dBc以下のスプリアスが見えます。このスペクトルがどこから来るのか、必然なのか、意図しない漏洩なのかはまだ不明です。

FT8プロトコルの深耕

AFP-FSK Transceiverのようなシンプルなトランシーバによって従来にも増して遠方と交信できる秘密は、FT8のプロトコルの中に隠されているはずです。FT8がこれだけ普及しても、プロトコルの分かり易い解説は流布が少ないようです。Taylor博士らの比較的新しい文献*1が見つかりましたので、Message送受信のベースにあるFT8プロトコルの概要を調べました。

Processing steps of the FT8 protocol on which the message transmission test is based.

FEC: forward error correction
LDPC: Low Density Parity Check
CRC: Cyclic Redundancy Check
GFSK: Gaussian Frequency-Shift Keying
BP: Belief Propagation
OSD: Ordered Statistics Decoding
AP: a priori

プロトコルに盛り込まれた多くの技術は、惑星探査機の通信技術で実績があるようです。FT8の生い立ちを考えると、納得の行く話です。

一番の特徴はMessageの内容が約束事で固められている点と思います。これによって誤り訂正を施し易くなるとともに、無味乾燥の印象を与えることがあるようです。Taylor博士らの工夫が随所に盛り込まれていることに反比例して、ユーザの工夫点や運用スキルを入れる余地が小さくなり、運用体験も一様化され、無味乾燥となってしまうのかもしれません。自らの工夫点を入れて、WSJT-Xに代わるソフトウェアを作るのは自由なようですが、敷居は遥かに高くなります。

送信174-bitの中の97-bit(56%)は誤り訂正用符号です。通信の信頼性を上げる基本はレポート交換時のように2回繰り返すことですから、bit数が2倍に膨れていても驚くことではないのかもしれません。デジタルの恩恵により、2回繰り返すよりも大幅に信頼性が上がっているはずです。

13TR-FT8受信

AFP-FSK Transceiverが正しくMessageを送信しているかどうかを試験するためには、送信Messageを受信してデコードする必要があります。AFP-FSK Transceiver送信用のWSJT-Xと、13TR-FT8受信用のWSJT-Xの2系統が必要になります。そこで、ジャンク箱に眠っていたRaspberry Pi 3Bを発掘して送信系に用いることにしました。(レガシーRaspberry Pi 3Bを立ち上げるに際に、何も調査せずに試行錯誤をしたところ時間を浪費してしまったため、立ち上げの記録を付録に残します。)

Messageの送受信を検証する試験システムの構成と写真を下記に示します。

System configuration for testing whether the AFP-FSK Transceiver is correctly sending Messages.

Photo of the system used to test whether the AFP-FSK Transceiver is correctly sending a Message.

CQメッセージを送受信した試験結果を下記に示します。Raspberry Pi 3B上のWSJT-XにてエンコードされたCQメッセージがAFP-FSK Transceiverから送信され、そのCQメッセージは13TR-FT8で受信され、Windows PC上のWSJT-Xにて正しくデコードされました。

A CQ message encoded in WSJT-X on the Raspberry Pi 3B was sent from the AFP-FSK Transceiver, which was received by the 13TR-FT8 and correctly decoded in WSJT-X on the Windows PC.

気付きとして、受信信号のワイドグラフを見ると、左下に舌状のスペクトルが出ています。前記チューン・モード試験で気付いた現象と同様に、送信開始時点の周波数が正しく送信されていないか、あるいは受信されていない可能性があります。

気付きとSDRplayによる目視デコード

CQメッセージ送信開始時点の頭出しに注意して、再びSDRplayで変調信号を受信しました。今回は、Bias-TをOFFにして余分な漏洩電磁波を拾わないように注意しました。13TR-FT8をケースに入れた効果と相まって、前回のようなお椀形状の外乱スペクトルは影を潜めました。

Result of receiving the modulated signal in SDRplay again, including the start of CQ message transmission.

先に調べたFT8のプロトコル仕様から、エンコード・トーン・シンボルの頭には同期用の7トーンのCostas array(3,1,4,0,6,5,2)が付加されているはずです。周波数シフトを目で追うと、「3,1,4,0,6,5」トーンまでは確認できますが、トーン「2」が約30Hz右にオフセットしたと仮定しないと辻褄が合いません。Tx周波数が2.000kHzであったことと、占有周波数帯域幅50Hzを考慮すると、逆に「3,1,4,0,6,5」トーンまでは本来より左にオフセットしていたと考えられ、これが舌状スペクトルの正体と思われます。

Costas arrayの次は、3-bitのMessage Type(Std Msg Type=001)か、28-bitの無指定CQメッセージ(0000000000000000000000000010)が来るはずです。どちらが先に来るかは、前述のプロトコル解説書には記載が無いようでした。3bitsを束ねたトーン・シンボルは、Message Type(1)もしくはCQメッセージ(000000001..)になります。Message Typeが先に来るとすると、目視デコードが間違っていたことになり、舌状スペクトルの解釈も怪しくなります。逆に、CQメッセージが先に来るとすると、目視デコードは正しく、舌状スペクトルの解釈も正しくなります。

FT8プロトコルの机上エンコード

WSJT-Xのマニュアルを見ても、Message Typeがメッセージの前に付くのか、あるいは後ろに付くのか分かりません。しかし、マニュアルに次の記述がありました。

Additional utility programs jt4code, jt9code, and jt65code let you explore the conversion of user-level messages into channel symbols or “tone numbers,” and back again. 

(また、ユーティリティプログラムjt4code、jt9code、jt65codeにより、ユーザーレベルのメッセージをチャンネル記号や「トーン番号」に変換したり、また元に戻したりすることができます。)

残念ながら、ft8codeユーティリティは無いようですが、マニュアルの改訂を失念しているのかもしれません。ダメ元で打ってみると幸いにもUsageが出てきました。「トーン番号」への変換機能はft8code.exeでもサポートされているようです。

CQメッセージのエンコード結果を下記に示します。緑がメッセージ、青がFEC符号、赤が「トーン番号」です。

Complete encoding result of the CQ message. Green bits are the message bits, blue bits is the FEC bits, and red simbols are the "tone symbols".

赤の「トーン番号」の"Sync"は7トーンのCostas array(3,1,4,0,6,5,2)です。その次は(000000001..)なので、CQメッセージです。緑のメッセージの最後は(001)bitsであり、Message Type(Std Msg Type=001)を表していると思われます。Source encodeの要の1つであるMessage Typeが語尾に付くことが確認されました。

これにより、SDRplayの目視エンコードが正しいことが確認され、舌状スペクトルの原因がAFP-FSK Transceiverの周波数計測遅れ等によるオフセットにあるのではないかという仮説が濃厚になりました。

しかし、30Hzぐらいの出だしのオフセットの影響を受けずにデコードできている結果から、FT8のロバスト性に改めて感服する次第です。同期用のCostas arrayを前、中、後の3ヵ所に配置した効果でしょう。

残された課題

  1. 送信開始時点の周波数オフセットの原因究明(例えば、移動平均処理の影響?)
  2. バンド・モジュール追加によるHFオールバンド化

追記(2022/07/02)

課題1の送信開始時点の周波数オフセットの原因究明のためにコードを熟読しましたが、初期の周波数が10~30Hz程度低くなる原因は見当たりませんでした。オーディオ周波数の計測において、重み付き移動平均の計算をしている箇所はありますが、計算値をリップルの判定に用いているだけであり、オーディオ周波数の計測に反映されることはないようです。FT8のAFP-FSK計算だけでなく、強制送信試験ピンによる単純搬送波出力の際にも初期周波数の低下が見られるため、別の所に原因があると考えるのが妥当です。

次に仮説を立てたのは、PLLシンセサイザーMS5351MのPLLの周波数ロック遅延です。チャージポンプの時定数等は分からないのですが、PLLリセットコマンドをMS5351Mに送信すると負帰還動作を始め、幾何かの遅延は生じる可能性があります。PLLリセットコマンドで受信音に雑音が入るという話もWeb上に見られます。

FT8のFSKチャネルのシンボル送信速度は6.25baudであるため、周波数がずれているCostas arrayの最初の6トーンの送信時間は約1秒(0.96 sec)です。PLLのロックに1秒かかるのは遅いような気がしますが、30Hzは7MHzに対して4ppm程度なのでチャージポンプのチャージに時間がかかるのでしょうか。

AFP-FSK TrasceiverのPLLシンセサイザーの制御コードは、PLLリセットを頻繁に発生させないように注意深く組まれているようです。PLLの後段の分周器設定に乖離が発生しない範囲の周波数変更については、PLLリセットを回避するコードになっています。試算してみると、40mバンドにおいて、0~1kHzの副搬送波変更に対してPLLリセットなし、1.5kHzに変更する時にPLLリセットが発生し、1.5~3kHzの変更に対してPLLリセットなしとなりました。FT8送信中の50Hz範囲の周波数シフトでPLLリセットが発生することはなさそうです。今回の同じTx周波数に対する送信試験で舌状の周波数ずれが繰り返し見られたのは、PLLリセットが原因ではなかったことになります。

振り出しに戻ってしまいました。Clock信号を止めないでQCXのようにゲート制御する方法を試してみたいところですが、改良が大掛かりになってしまいそうです。

付録:レガシーRaspberry Pi 3B 再立ち上げの覚え書き

今回の試験のために、2016年頃に触っていたRaspberry Pi 3Bをジャックボックスから引っ張り出して再立ち上げしました。何も調査せずに試行錯誤をしたところ時間を浪費してしまったため、再立ち上げの記録を付録に残します。

回顧のRaspbian

まず、当時のOS(Raspbian)のセキュリティを向上するためにupdateやupgradeを繰り返しましたが、404エラーが多発してパッチが当てられません。どうやら、パッチファイルが既にサーバから撤去されている模様。

調べると、Raspberry Pi OSは最新版と1つ前のLegacy版しかサポートされないことが分かりました。

最新版のBullseye

素直に最新版のRaspberry Pi OSを焼き直すことにしました。公式のSDカード作成ツールImagerのトップに表示される現時点の最新版は通称「Bullseye」でした。何も考えずに「Bullseye」を書き込み、時間を掛けて初期設定を行いました。これが無駄な徒労でした。

現時点のWSJT-XのRaspberry Piコンパイル済みパッケージは、一つ前のLegacy版の通称「Buster」用しかありませんでした。一応「Bullseye」へのインストールをトライしましたが、どうにもこうにも多数のlib***のバージョンが不適合でインストールできませんでした。ソースからコンパイルするのもRaspberry Pi 3Bには荷が重そうです。

Legacy版のBuster

ここも素直にImagerの下の階層の中にある「Buster」を焼き直し、初期設定をやり直しました。「Buster」を焼き直した甲斐あって、WSJT-Xはトラブル無く簡単にインストールできました。

WSJT-X installed on Raspberry Pi OS "Buster".

その他

次にTime.isを見ると「あなたの時計はちょうどぴったりです」と表示。Raspberry PiはRTCを持っていないはずですが、インターネットに接続して時間が経てば自動的に時計を合わせてくれるようです。

USBオーディオカードは、(1)廉価版と、(2)96kHzに対応した高級版?の2種類を保有しています。(2)高級版は、Raspberry Pi 用のドライバが無く、上手く立ち上がりませんでした。IQ信号用に96kHz対応カードを準備していたのですが、オーディオ用は44.1kHzで十分です。(1)廉価版は、プルダウンリストに表示される名前がWindowsより多く特定が厄介でしたが、何とかつながりました。

*1:Steve Franke, Bill Somerville and Joe Taylor, "The FT4 and FT8 Communication Protocols - Motivation and design of the digital modes FT4 and FT8, and some details of how they are implemented in WSJT-X", QEX July/August 2020(https://physics.princeton.edu/pulsar/k1jt/FT4_FT8_QEX.pdf

AFP-FSK Transceiver(12)FSK受信試験

試験方法

AFP-FSK Transceiverの前に組み立てた13TR-FT8(ディスクリート・アナログ・FT8トランシーバ)を活用した試験方法を思いつきました。13TR-FT8からダミーロードに送信して、近接場から漏洩した電磁波をAFP-FSK Transceiverで受信します。都市ノイズやPCノイズの海の中になりますが、自由にAUDIO周波数を設定できるため、ノイズとの切り分けも容易と予想しました。

試験システムの系統図と試験機材の写真を下記に示します。近接場の漏洩電磁波を捉えるためにSDRplayのアクティブ・ミニホイップ・アンテナはダミーロードの近くに置き、必要に応じてBias-Tをアクティブにしました。AFP-FSK Transceiverのモービル・ホイップもエレメントが近くに来るように室内の床に置きました。アンテナから送信はしないため、SWRは気にしないことにします。13TR-FT8はLOのトリマ再調査が必要になり、ケースから出す必要がありました。漏洩電磁波がどこからでも回り込みそうな系ではあります。

Systematic diagram of the test system for receiving FSK radio waves.

Photograph of the equipment for the test to receive FSK radio waves.

送信機13TR-FT8の周波数校正

まず、SDRplayを原器として、13TR-FT8の周波数を再校正しました。

校正前

両トランシーバの電源を投入すると、各々のLO(Local Oscillator)の信号が見えました。

When both transceivers were powered up, the respective LO (Local Oscillator) signals were visible.

AFP-FSK TransceiverのLO信号は、外部発振器をTCXOとしたMS5351Mの受信Clock信号です。一方、13TR-FT8のLO信号は、ノーマルXOによるコルピッツ発振回路の出力です。ディスクリートSBMの入力とするためか、13TR-FT8のLO信号の強度の方が高いようでした。もっとも、単にSDRplayのMini-Whip Antennaとの距離が影響しているだけかもしれません。

校正後

前回、13TR-FT8のLOも7,074,004Hzまで追い込んだはずですが、経時変化かあるいは暖機運転が足りないのか、約-40Hz程ずれているようです。これを±10Hz以内に再調整しました。

Readjust the LO of 13TR-FT8 to within ±10 Hz.

13TR-FT8送信とAFP-FSK Transceiver受信

2.5kHz AUDIO信号

WaveGenから2.5kHzのAUDIO信号を13TR-FT8に入力しました。13TR-FT8がダミーロードに送信した電力の漏洩電磁波をSDRplayで受信した結果を下記に示します。7,076.5(=7,074+2.5)kHzになる予定でしたが、約-2.5Hz程低くなりました。

Spectrum of electromagnetic leakage received by SDRplay when 13TR-FT8, with 2.5 kHz AUDIO signal input from WaveGen, outputs FSK power to a dummy load.

この時、AFP-FSK Transceiverが受信したWSJT-Xのワイドグラフを下記に示します。WSJT-Xは近接場の漏洩電磁波も捉えてくれました。目視ですが、約2.47kHzを受信しており、SDRplayの受信結果と一致します。約-2.5Hzは13TR-FT8のLOの校正誤差と思われます。

Wide graph of WSJT-X showing the results of the AFP-FSK Transceiver receiving the electromagnetic leakage when the 13TR-FT8 outputs FSK power to a dummy load with a 2.5 kHz AUDIO signal input from WaveGen.

2.3kHz AUDIO信号(-200Hzシフト)

次に、AUDIO周波数に対して2.5kHzから2.3kHzへの-200Hzの周波数シフトを行いました。7,076.3(=7,074+2.3)kHzになる予定でしたが、同様に約-2.5Hz程低くなりました。LO校正誤差を維持したまま、正確に周波数シフトした模様です。

A frequency shift of -200 Hz from 2.5 kHz to 2.3 kHz for the AUDIO frequency resulted in an accurate amount of frequency shift at the RF transmit frequency while maintaining the LO calibration error.

AFP-FSK Transceiverが受信したWSJT-Xのワイドグラフでも同じ結果を確認できました。AFP-FSK Transceiverの受信部が正常に稼働していることを確認できました。

The same results can be seen in the WSJT-X wide graph received by the AFP-FSK Transceiver, confirming that the receiving part of the AFP-FSK Transceiver is working properly.

AFP-FSK Transceiverによる交信受信試験

そのままAFP-FSK Transceiverを放置し、WSJT-Xの継続受信を行いました。室内床置き仮設のモービル・ホイップでは出力の大きい局やローカル局の電波しか捉えられないのですが、待っていると数局の電波を拾えました。ただし、DX(7.074MHz)も国内(7.041MHz)も交信相手局の電波は捉えられませんでした。

Receiving test results of QSOs on DX frequency and domestic frequency in the 40m band with AFP-FSK Transceiver.

DXにも国内にも両方に対応できるのがVFO Moduleを搭載したAFP-FSK Transceiverの利点です。受信用のVFO機能も正常に動作していることが確認できました。

AFP-FSK Transceiver(11)周波数の校正

AFP-FSK Transceiverでは10.000MHz標準電波をアンテナの関係で受信できなかったため、以下の手順で周波数の校正を行いました。

  1. 10.000MHz標準電波によるSDRplayの周波数校正
  2. AFP-FSK Transceiverからの送信試験とSDRplayによる受信
  3. AFP-FSK Transceiverの周波数校正

USBドングルSDRのSDRplay RSP1Aが周波数校正でも役に立ちました。

SDRplayの周波数校正

自動校正

SDRplayのPCソフトSDRunoは自動キャリブレーション機能を持っているため、機器任せで簡単に済むことを期待しました。RX CONTROLパネルのSETT.メニューの中にある校正パネルに従い、10.000MHz標準電波を受信して以下の手順を実行します。

  1. SAM(Synchronous AM)モード選択
  2. DSBモード確認
  3. VFOを校正用電波の公称周波数に設定
    (この時点では真の周波数からはズレている。)
  4. 自動キャリブレーション・ボタンをクリック

マニュアルに原理の説明はありませんが、受信したDSB変調波から検波に必要な基準搬送波をSAMモードの同期検波によって再生し、VFO公称周波数と基準搬送波再生周波数との差分がゼロになるように校正する・・・のではないかと想像しています。スペクトルの最大値を取れば簡単そうですが、RBW以下の精度は達成できないため、SAMモードを利用しているものと思います。

ところが、10MHzの標準電波に適用しても上手く校正できませんでした。何回も自動校正ボタンをクリックして偶然上手く行ったと思っても、周波数軸を拡大すると10Hzほどずれています。10Hzはわずか1ppmにすぎませんが、SDRunoの30mバンドのRBWは0.95Hzのため、RBWの10倍程度ずれていることになります。室内ミニホイップ・アンテナを使用してBias-Tをアクティブにしましたが、受信電波が弱すぎるのかもしれません。

Best trial result of multiple attempts of automatic calibration by SDRuno.

手動校正

これなら目視で合わせた方が正確です。RX CONTROLパネルのSETT.メニューではなく、MainパネルのSETT.メニューの中に直接CRYSTAL CALIBLATIONの値を入力するボックスがありました。こちらに-1.05ppmを入力すると目視ではピタリと校正が取れました。10MHzの-1.05ppmは-10.5Hzになるため、1目盛り左に動かしたことになります。TCXOでも±2ppm程度の変動はあるため、この辺が校正限界と判断しました。

Result of manual calibration in which the crystal correction value is directly input to SDRuno.

AFP-FSK Transceiverの周波数校正

10.000MHz標準電波による校正の試行

SDRplayと同様に、10.000MHz標準電波による周波数の校正を最初に試行しました。30m Band moduleが必要になるため、キットに付属していた30mと20mのBand moduleを組み立てました。

Band Modules of 40m, 30m and 20m included in the kit.

30m Band moduleを取り付け、VFOを9.999MHzに合わせてWSJT-xで受信します。10mのアンテナが無いため、20mモービル・ホイップのエレメントの調整代を最大に伸ばして流用しました。AFP-FSK Transceiverのダイレクト・コンバージョン受信回路によって、ウオーターフォールに1kHzを中心にした2本の線が表れるはずでしたが、ノイズと同レベルの細い線しか視認できませんでした。整合範囲の狭い20mモービル・ホイップの流用では、Bias-Tによるアクティブ・ミニホイップ・アンテナに勝てないのかもしれません。

SDRplayによる周波数校正

40m Band moduleに戻し、校正の取れたSDRplayを原器として、AFP-FSK Transceiverの校正を行うことにしました。系統図を下記に示します。Dummy LoadあるいはAFP-FSK Transceive本体から漏洩する電磁波をSDRplayで拾います。

Systematic diagram for calibrating AFP-FSK Transceiver using SDRplay.

Photograph showing the system configuration for calibrating the AFP-FSK Transceiver.

AFP-FSK Transceiverは強制送信テストピンを使うと、FSK計算は行わずにVFO表示周波数で送信します。40m FT8 ノミナルの7.074MHzのVFO表示で強制送信を行うと、SDRunoでは-760Hzずれた位置にスペクトルが立ち上がりました。+760Hzの校正が必要です。

Forced transmission on 40m FT8 at 7.074MHz on the VFO display resulted in a rising spectrum at a -760Hz shift in SDRuno.

AFP-FSK Transceiverのスイッチで校正モードに切り替えます。ここで強制送信を解除する必要がありました。強制送信のソフトウェアはテストピンを監視するだけの無限ループになっているため、テストピンを解除しない限りメニュー操作のUIポーリングに状態遷移できません。当然、解除すれば送信が止まり、SDRunoから校正の指針となるスペクトルが消えてしまいます。専用コードを書く必要があるのか・・・。

万事休すと思ったのですが、送信を止めてもSDRplayはMS5351Mからの漏洩電磁波を捉えているようでした。MS5351Mからの送信用Clock信号を止めても、ダイレクトコンバージョン受信用Clock信号は活きているはずです。FSK演算を行わない強制送信テストでは両者は一致します。

漏洩電磁波の強度は小さいのですが、SDRunoのウォーターフォールを光らせるには十分です。漏洩電磁波の軌跡を追えば校正は容易です。

The intensity of the leakage signal is small, but it is sufficient to illuminate the SDRuno waterfall; calibration is easy by following the trajectory of the leakage signal over the SDRuno waterfall.

校正結果をEEPROMに書き込んで、強制送信テストを行い、VFO表示通りの送信周波数が出ていることを確認できたら終了です。

The calibration results are written to the EEPROM, and the process is complete when it is confirmed that the transmit frequency is as indicated on the VFO display.

AFP-FSK Transceiver(10)ソフトウェアの変更

AFP-FSK Transceiverの利点の1つとして、ソフトウェアが公開されている点を挙げられます。学びの機会を提供してくれるとともに、国内仕様に合わせて変更することが出来ます。

ソフトウェアの構成

ソースコードの要所要所を深堀しただけですが、おおよそ以下の構成になっていると推定しています。

Embedded software configuration of ATMEGA328P for AFP-FSK Transceiver.

基本処理

セットアップとループの2段構成は、Arduinoでお馴染みのマイコン組込みソフトウェアの基本構成です。

セットアップでは割込み処理他のファームウェアの設定を行っています。ここはATMEGA328Pのデータシートを座右に置いて首ったけで調べる他ありません。ロジック回路と結びついた1-bit毎に機能上の意味があるため、初見での理解は大変です。

ループ処理では、

  1. 押しボタンスイッチUI(User Interface)のポーリング処理、
  2. 送信試験で活用している強制送信ピンの監視、
  3. AFP-FSK処理

を反復実行しています。

割込み処理

ループ処理の他に重要な役割を果たしているのが割込み処理(ISR)です。AFP-FSK Transceiverは2つの割込みサービスルーチンを利用しています。1つ目は(a)設定時間(1msec)のタイマー割込み処理、2つ目は(b)アナログコンパレータの割込み処理です。後者も16-bitタイマーユニットと密接に連携しています。

(a)設定時間タイマー割込み処理は、5桁の7セグメントLEDを順番に点灯させるダイナミック・ドライブ制御を行っています。ATMEGA328Pからの信号線を節約する処置です。他に、押しボタンスイッチの状態読み込みを実施しています。

(b)アナログコンパレータの割込み処理は、AFP-FSK方式を実現する根幹であり、AG6NS Kazu OMの素晴らしいアイディアが結実している箇所でもあります。AFP-FSK TransceiverはDigital Transceiverですが、SDRのようなサンプリング信号処理は行っていません。

ATMEGA328Pのデータシートから関連する内部ユニットを抜き出し、A2ピンから入力されるAUDIO信号と内部ユニットとの接続関係を下図に示します。

Analog comparator and 16-bit timer in the internal unit of the ATMEGA328P that measures the period of the AUDIO signal with an interrupt service routine.

Analog Comparatorによって、FT8等のAUDIO信号はBandgap Reference(シリコンのバンドギャップエネルギーに起因する基準電圧)と比較されます。ATMEGA328PのBandgap Referenceは1.1±0.1Vです。AUDIO信号は、AFP-FSK Transceiverの入力回路でバイアス1.35VのAC信号に変換されるため、ノイズしきい値は250mVになります。振幅250mV以上の反復再現性の高い信号が入力されれば、AFP-FSK処理を施されて送信されます。「反復再現性」は半周期計測値の突発変動を除去することで確保されているようです。半周期計測値の毎回25%更新の移動平均値に対して、31%以下の計測値はリップルと判定され無視されます。

Analog Comparatorは何もしなければ1周期毎に割込みを発生しますが、割込み処理ルーチンの最後にトリガーエッジの上下方向を毎回反転させています。これにより、AUDIO信号がBandgap Referenceを横断する時の方向、すなわち微分検出符号を反転させ、AUDIO信号波形の上側と下側で約半周期毎の計測を行っています。詳細は読み込めていませんが、AUDIO信号検出の信頼性を上げる処置ではないかと推測しています。

ソフトウェアの変更

以下の5項目の変更を加えました。

List of software changes.

1.バンド選択関数の調整

AFP-FSK TransceiverはBand Moduleに搭載された抵抗R8をバンドIDとして読み取り、自動的にバンドを切り換えます。Mainボードに搭載されたR5(10kΩ)とR8による電源電圧の分圧値を10-bitのADCで読み取り、バンド選択関数に書かれたしきい値と比較して切換えます。正しく動作すれば、間違った周波数の送信電力をLPFに注入することは避けられます。

各Band Moduleの抵抗R8とバンド切換しきい値を調べると、下記左表(a)のように設計されていました。下記表は国内で許可されるバンドのみ示しています。R8の公差は抵抗値の許容ばらつきを示します。5%精度の抵抗を使用すれば問題ないことが分かりました。

Band Module resistor R8 value and band threshold setting.

抵抗の精度をさらに緩和するために、1点だけ公差が厳しい10mバンドの上限しきい値を上記右表(b)に示すように800から805に調整しました。これにより、抵抗値(33kΩ)の許容公差を8.2%から11.4%に緩め、10%精度の抵抗でも問題がないように揃えました。

2.バンド仕様の設定変更

オフバンド防止のための上下限を設定するget_band()関数があり、米国仕様と思われるバンドとその上下限が設定されていました。国内で許可されないバンド設定を削除するとともに、許可されるバンドについてもその上下限を国内仕様に変更しました。

3.VFOコードの変更

AFP-FSK Transceiverには押しボタンスイッチによって搬送波周波数をUp/Down調整するVFO機能が搭載されています。その関数の中で前記のバンド上下限と搬送波周波数を比較して「out-of-band」フラグを設定する仕掛けになっていました。

しかしながら、搬送波周波数が許可バンド内であってもオーディオ周波数を加算したFSK周波数は逸脱する可能性が残ります。そこで、設定した搬送波周波数にオーディオ周波数の上限値を加えた値をバンド上限仕様と比較して「out-of-band」フラグを設定するように改めました。

4.強制送信コードの変更

PCBのテストピンによって搬送波周波数を強制的に送信するテスト機能ですが、「out-of-band」フラグを参照していませんでした。VFO関数によって「out-of-band」フラグが設定されていたら、送信を許可しないように改めました。

5.AFP-FSK送信コードの変更

FSK処理後の周波数をPLLシンセサイザーにリアルタイムに設定して送信する機能ですが、上記4と同様に「out-of-band」フラグを参照していませんでした。VFO関数によって「out-of-band」フラグが設定されていたら、送信を許可しないように改めました。

ソフトウェアの書き込み

手順

コンパクト設計のAFP-FSK Transceiverには、ATMEGA328Pへのソフトウェア書き込みピンが準備されていないため、以下の手順が必要になります。

  1. VFOボードをMainボードから取り外す。
  2. 両面実装のVFOボードに作業用のスペーサを逆向きに取り付ける。
  3. ATMEGA328Pをソケットから引き抜く。
  4. 外部のライターでATMEGA328Pに修正コードを書き込む。
  5. ATMEGA328Pをソケットに挿し戻す。
  6. VFOボードから作業用のスペーサを取り外す。
  7. VFOボードをMainボードに取り付ける。

大変な手間です。反復デバックが必要になった場合は、空中配線で書き込みピンを準備したくなるところです。

書き込み

動作確認が取れた現行のATMEGA328Pは温存し、変更版ソフトウェアの書き込み用に新規のATMEGA328Pを準備しました。(1)Boot Loader書き込み済みのATMEGA328Pと、念のため(2)素のATMEGA328Pも準備しました。これが活きました。

書き込みはAruduino UnoにAVR ISP Shieldを取り付け、「Arduino as ISP」で行いました。

ATMEGA328P software writing system with AVR ISP shield on top of Aruduino Uno.

(1)Boot Loader書き込み済みのATMEGA328Pには、なぜか書き込みができませんでした。Boot Loaderの再書き込みにも失敗しました。直前にリセットを押したり、AVR ISP Shieldの導通を確認したりしながら、5回ぐらい試したところで断念しました。手順のケアレスミスではなく、根本的な原因があるようです。

そこで、(2)素のATMEGA328Pに切り換えたところ、Boot Loaderの書き込み、それに続く変更版ソフトウェアの書き込みと簡単に進んで、1回で呆気なく完了しました。

(1)と(2)の書き込み時のログ出力を下記に示します。書き込みシーケンスの詳細は分かっていませんが、AVR PART(AVRマイコンの"devicecode" )を確認に行って失敗しているように見えます。

Excerpts from avrdude's log during a write process.

変更版の試験

40mディフォルト周波数の送信試験

AFP-FSK Transceiverのsetupシーケンスによって、7.074MHzが40m FT8のディフォルト周波数としてVFOに設定されました。前回と同じで問題ありません。

強制送信ピンをGNDに落としてDummy Loadに送信し、スペクトルを観察しました。

The modified version of the software transmitted a 7.073248 MHz radio wave in response to the 7.074000 MHz VFO indication, just as it did before the change.

変更版ソフトウェアも変更前と同じく、7.074000MHzのVFO表示に対して7.073248MHzの電波を送信しました。PLLシンセサイザの外部発振器の補正値をソースコード上で調整しなかったため、安定なTCXOによって変更前と同じ結果が再現されました。ソフトウェアの変更が終了したら、キャリブレーション機能によってEEPROMに補正値を書き込む予定です。

オフバンド送信抑制機能の試験

更新版で追加したオフバンド送信抑制機能をテストしました。

オフバンド6,999,000Hzと、40mバンド下端7,000,000HzのVFO設定で、Dummy Loadに対して強制送信テストを行いました。

Forced transmit test with VFO settings at 6,999,000 Hz off-band and 7,000,000 Hz at the lower limit of the 40m band.

オフバンド6,999,000Hzでは送信が抑制され、7,000,000Hzでは送信が許可されました。オフバンド抑制機能が正常に機能していることが確認されました。

なお、FSKモード運用時にはオーディオ周波数下限値(200Hz -5%)のチェックも行われ、下限値より低いFSK周波数は送信が抑制されるため、バンド下端の7,000,000Hz上で送信されることはありません。

次に、40mバンド上端付近7,195,000Hzと7,196,000HzのVFO設定で、Dummy Loadに対して強制送信テストを行いました。

Forced transmit test with VFO settings at 7,195,000 Hz and 7,196,000 Hz near the upper limit of the 40m band.

AFP-FSK TransceiverのFSKモードでは、最大上限値(4,000Hz +5%)のオーディオ周波数がVFO設定値に加算され得ます。7,195,000 +4,2000Hzはオンバンドですが、7,196,000 +4,2000Hzはオフバンドになります。

VFO設定値7,195,000Hzでは送信が許可され、オフバンドの可能性がある7,196,000Hzでは送信が抑制されました。オフバンド抑制機能が正常に機能していることが確認されました。