非職業的技師の覚え書き

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

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

背景

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に対してサンプリング・クロック96kHzを設定する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に96kHzのサンプリング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です。機能設定のレジスタの数も膨大です。ハードウェアがオブジェクト指向によって抽象化されていることもあり、まだ全貌を把握するには至っていません。分かった範囲、あるいは予想がついた範囲の内容をメモし、後で正誤が判明した場合は修正したいと思います。