非職業的技師の覚え書き

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

Teensy(15)Keiths' SDRの"Twin Peaks"問題解決方法の調査

"Twin Peaks"問題とは

概要

SGTL5000コーディック(ADコンバータ、DAコンバータ)を搭載した、PJRC社のTeensy用オーディオアダプタには、起動時にIQ信号の片方の先頭データが欠落することがあるという問題が指摘されています。音声や音楽のステレオオーディオ信号を扱う場合は問題にならないようですが、IQ信号では大問題になります。これによって、それ以後のIQ信号相互の位相関係がシフトするため、IQ信号演算によるイメージ抑圧が正常に働かなくなります。その結果、搬送波の両側に信号ピークが立つ状況になるため、"Twin Peaks"と称されているようです。

この問題が存在することを知らないと、ある日突然、昨日まで順調に動いていたトランシーバの復調が出来なくなり、無暗にPCの再起動や再コンパイルを繰り返す等の都市伝説的対処に走ることになります。

幸いなことに、コミュニティにて原因の特定とSDRソフトによる解決策の造り込みが図られているため、調査した結果をここにまとめます。

経緯

  1. 2017/02/24
    Convolution SDRで著名なDD4WH局Frank OMが、PJRC社のフォーラムに「Reset audio board codec SGTL5000 in realtime processing」という名称のスレッドを立て、コーディックの問題提起をされたのが最初かと思います。
    Frank OMはこの時点で既に、IQ信号が欠落することが"Twin Peaks"の原因だと気付いておられます。mcHFトランシーバのコーディックWM8731でも同じ問題があり、コーディックのリセットで解決したことも記されています。コーディックSGTL5000でも同じ手段が取れるかどうかに問題が絞られています。
    人が気付いて対処するか(手動)、Teensyが気付いて対処するか(自動)が課題になります。
  2. 2018/04/16
    W7PUA局 Bob Larkin OMが議論に参加。様々なサンプリング周波数の設定で"Twin Peaks"問題が発生することを報告。
  3. 2018/05/22
    TeensyからSGTL5000に出力するClockのジッターが根本原因ではないかとの仮説を立て、これを減らすことを検討してスレッドの更新は止まっています。
    Clockのジッターが"Twin Peaks"の根本原因ではなかったようです。根本原因はTeensy側ではなくSGTL5000側に内包されているとして、対処療法で解決することが必要になりました。
  4. 2022/03/09
    W7PUA局 Bob Larkin OMからPJRC社のフォーラムの「 Floating-Point Audio Library Extension」スレッドに投稿があり、"Twin Peaks"問題を解決するAudioAlignLR_F32クラスをOpenAudio_ArduinoLibraryに追加したとのアナウンスがありました。提案された解決方法は後述します。
  5. 2022/03/10
    W7PUA局 Bob Larkin OMからkeithsdr@groups.ioに「Codec mis-aligned L-R timing」と題する投稿があり、"Twin Peaks"問題を解決するAudioAlignLR_F32クラスをOpenAudio_ArduinoLibraryに追加したとの同じアナウンスがこちらにもありました。
  6. 2022/04/09
    N0CTL局 John Scherer OMからkeithsdr@groups.ioの「PCB for Teensy + Teensy audio shield + RA8875」スレッドに投稿があり、Scherer OM設計のPCBに"Twin Peaks"の能動的自動検出のための回路パターンを搭載した旨の報告がありました。
  7. 2022/05/19
    K7MDL局Mike OMからkeithsdr@groups.ioの「Codec mis-aligned L-R timing」スレッドに投稿があり、AudioAlignLR_F32クラスを用いた"Twin Peaks"問題解決機能をKeiths' SDR(K7MDL局Mike OM版)ソフトウェアに標準搭載した旨の報告がありました。

以上で、ハードウェアとソフトウェア(ライブラリとアプリ)の両面で"Twin Peaks"問題は解決されました。こうして経緯をまとめると、集合知による課題解決絵巻のようです。

"Twin Peaks"問題の解決方法

問題

コーディックSGTL5000からのDMA転送データには、起動時にIQ信号の片方の先頭データが欠落することがあるという問題が存在します。Hilbertオブジェクトの調査の際にイメージ抑圧のシミュレーションに用いたPythonプログラムを流用して、具体的に何が起こるか見てみましょう。

I/Q信号の欠落シフトが無い健全な場合のイメージ抑圧結果を再録します。目的のIn-phase信号はHilbertフィルタ後段のSummerを通過し、Image信号はSummerで相殺されることが分かります。

Image suppression results for a healthy case with no shift due to missing I or Q signals.

I信号の欠落シフトが有る不健全な場合のイメージ抑圧結果を示します。目的のIn-phase信号はHilbertフィルタ後段のSummerを歪を伴って通過しています。ただし、その歪は目視で確認できるほど大きくはありません。

一方、Image信号はSummerで完全には相殺され得ないことが分かります。これにより、In-phase側とImage側の両方にピークが立つことになります。これがTwin Peaksです。

Image suppression results for the unhealthy case where there is a shift due to the missing I signal.

Q信号の欠落シフトが有る不健全な場合のイメージ抑圧結果を示します。Imageの位相が異なるだけで、同様にTwin Peaksが発生することが分かります。

Image suppression results for the unhealthy case where there is a shift due to the missing Q signal.

僅かにデータ1個分シフトしただけで、このような大きな影響が出ることが確認できました。

解決方法の原理

"Twin Peaks"問題が顕在化するタイミングは不定です。よって、SDR起動時に能動的に検出する方法が探られました。

IQサンプリングデータの欠落の有無を検出するために、起動時にコーディックの両L/Rチャンネルに同じテストデータを能動的に注入して比較する方法が採用されました。Teensy 4.x自体はDACを搭載していないため、サイン波を注入することは断念し、有り余るディジタル出力ピンの1つ(22番)から矩形波を注入しています。テスト時にRF信号を切断できると良いのですが、リレー回路等が必要になり複雑化するため、RF信号にテスト信号を重畳する方法が取られています。したがって、起動時に静かなバンドで"Twin Peaks"検出を行う必要があります。

サンプリング周波数の1/4の周波数の矩形波をコーディックに注入しています。下記に示すように、1周期に4点のサンプリングを行うことになります。コーディックからのデータ転送ブロック長は128ワードのため、32個の矩形波をサンプリングして判定します。

Normal conversion data of SGTL5000 for test square wave from Teensy's digital output pin 22.

Lチャンネルに対してRチャンネルのサンプリングシフト3までの相関関数を計算して、データ欠損によるシフトの有無を判定します。サンプリングシフトによってRチャンネルの最後の矩形波は欠損するため、相関関数の計算に使用するデータブロック長は124ワードとしています。31個の矩形波を使用することでノイズへの耐性を高めていることになります。

能動測定によって、LチャンネルもしくはRチャンネルのどちらかに欠落シフトがあることが検出された場合は、Rx信号処理連鎖に送出するデータブロックに整列補正シフトを施す対策を実施しています。コーディックのリセットは行っていないようです。SGTL5000コーディックがリセット命令を備えていないのかもしれません。

以下、サンプリングシフトの能動的測定方法の詳細を場合分けして記します。

Case 1:欠落シフトが無い場合

Case 1:  Normal L-channel data without missing and normal R-channel data without missing.

Lチャンネル(I信号)とRチャンネル(Q信号)の両方に欠落シフトが無い場合は、Rチャンネルのシフトが0の時の相互相関(正規化していないので相互相関係数ではなくベクトル乗算)xc[0]が最大の62になり、シフトが2の時のxc[2]が最小の-62になります。その他のxc[1]とxc[3]は0になります。ノイズ耐性を高めるために差分を取って正規化すると、xc[0]-xc[2]が1となり、他は0です。しきい値0.7と比較して、欠落シフト無し(In phase)と判定しています。

Case 2:Lチャンネルが欠落シフトしている場合

Case 2: Shifted L-channel data with one missing and normal R-channel data without missing.

Lチャンネル(I信号)が欠落シフトしている場合は、Rチャンネルのシフトが1の時の相互相関xc[1]が最大の62になり、シフトが3の時のxc[3]が最小の-62になります。差分xc[1]-xc[3]の正規化値が最大の1となり、Lチャンネル(I信号)の欠落シフト有り(Shift I)と判定しています。

Case 3:2つ分欠落シフトしている場合

Case 3: Shifted L-channel data with two missing and normal R-channel data without missing.

Lチャンネル(I信号)が2つ分欠落シフトしている場合は、Rチャンネルのシフトが2の時の相互相関xc[2]が最大の62になり、シフトが0の時のxc[0]が最小の-62になります。差分xc[2]-xc[0]の正規化値が最大の1となりますが、DNApplyと判定してエラーを返しています。DNApplyは「Do not apply」ということでしょうか。この場合は、サンプリングシフトの再測定を行うようです。

Case 4:Rチャンネルが欠落シフトしている場合

Case 4: Normal L-channel data without missing and shifted R-channel data with one missing.

Rチャンネル(Q信号)が欠落シフトしている場合は、Rチャンネルのシフトが3の時の相互相関xc[3]が最大の62になり、シフトが1の時のxc[1]が最小の-62になります。差分xc[3]-xc[1]の正規化値が最大の1となり、Rチャンネル(Q信号)の欠落シフト有り(Shift Q)と判定しています。

ハードウェアの要件

Teensyのdigital output pin 22から矩形波を出力して、コーディックSGTL5000のLINE INの2つのチャンネル(L/R)に同時に入力する配線が必要になります。インピーダンス整合のために、当初は100kΩ抵抗を挟んでいましたが、10kΩ抵抗が最適であることがMike OMによって見出されています。

Hardware wiring for active detection of "Twin Peaks".

N0CTL局 John Scherer OMの設計による「Teensy 4.1 Ra8875 Backpack with Audio Shield」(REV.3)ボードには、"Twin Peaks"の能動的測定のための配線パターンが準備されています。

信号の名称は「I2S_COR」となっていますが、I2S信号ではないことに注意する必要があります。I2S通信プロトコルに乗るIQ信号(L/R信号)の相関を能動的に調べるためのディジタル出力信号という意味からの名称と思います。

公開されているBackpack PCBのガーバーファイルから、裏面のパターンを可視化して下記に示します。黄色の文字は当局の注記です。"Twin Peaks"の能動的測定のための配線パターンが確認できます。

Backside wiring of the Backpack PCB.
(You can see the wiring from the Tennsy 4.1 digital output to the audio adapter's LINE L/R.)

組立中のBackpack PCBの裏面写真を下記に示します。透視で表面から見たガーバーファイル可視化イメージとは鏡像関係にあることにご注意ください。

Photo of the backside of the Backpack PCB during assembly.

信号配線は表面にあり、本来、裏面にはバイパスコンデンサとプルアップ/ダウン抵抗が配置されているだけだったようですが、そこに重要な"Twin Peaks"能動的検出パターンが追加配置されたようです。

ソフトウェアへの実装

"Twin Peaks"の能動的測定のためのハードウェアの仕掛けに合わせて、ソフトウェアの準備も必要になります。Keiths' SDR(K7MDL局Mike OM版)における実装を見ていきたいと思います。

W7PUA局 Bob Larkin OMが開発したAudioAlignLR_F32クラスのTwinPeakオブジェクトを、K7MDL局Mike OMがRx信号処理連鎖の先頭に挿入しました。このオブジェクトによって、先頭データの欠落シフトの能動的測定と、必要に応じて後段のRx信号処理連鎖に送出するデータブロックの整列補正シフトを実施しています。

Insertion of a TwinPeak object at the beginning of the Rx signal processing chain.

必要な信号処理オブジェクトをコード上で結線して行くPJRC社の作法を取り入れたKeiths' SDRのコーディングスタイルによって、TwinPeakオブジェクトのようなオプション機能を提供するオブジェクトの後付け挿入が容易になっています。コンパイラディレクティブ("W7PUA_I2S_CORRECTION")によって、上記ハードウェアの要件を満たしている場合はTwinPeakオブジェクトへの挿入結線をコンパイラ前処理に指示し、要件を満たしていない場合は点線の迂回結線を指示します。

mainプログラムに対するTwinPeakオブジェクトの動作を下記に示します。左側がsetup()関数とloop()関数から成るArduinoのmainプログラムを、右側がTwinPeakオブジェクトを示します。TwinPeakオブジェクトは、「計測モード」と「実行モード」の2つのモードを持ちます。

TwinPeak object behavior.

setup()関数からTwinPeakオブジェクトを「計測モード」に設定します。この時、上述の矩形波をTeensyのdigital output pin 22から出力します。タイマー等を用いることなく、mainプログラム内で経過アイドル時間を監視して、所定の周期になるように出力を反転させています。Inputオブジェクト経由でSGTL5000コーディックからI2S/DMA転送されてきたデータブロックに対して、欠落シフトの有無/種類を検出し、補正方法を選択します。「計測モード」が完了したら矩形波出力を終了して、TwinPeakオブジェクトを「実行モード」に切換えます。

mainプログラムがloop()関数に制御を移すと、SGTL5000コーディックからのI2S/DMA割込みによってRx信号連鎖を構成する各信号処理オブジェクトのupdate()関数が順番に起動することを、所定のサンプリング周期で繰り返します。「実行モード」にあるTwinPeakオブジェクトのupdate()関数は、「計測モード」で選択された欠落シフト補正をデータブロックに施し、後段の信号処理オブジェクト(I_Switch/Q_Switch)に送出します。