非職業的技師の覚え書き

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

室内ロングワイヤーアンテナはDXCCの夢を見るか(29)新着QSL

QSLマネージャ(M0OXO)からまとめて3通のQSLカードが届きました。

HD8R(ガラパゴス諸島)

以前、CW交信を報告したContest DX-Expedition局HD8RのQSLカードが届きました。裏面には地図が配置されていました。HD8RのQTHはガラパゴス諸島の東端にあるサン・クリストバル島です。ダーウィンが最初に上陸した島とのことです。群島において唯一の淡水の供給源である火山湖があり、2番目に人口の多い島とのこと。

一方、過去にFT8で交信したペディション局HD8GのQTHを再確認したところ、ガラパゴス諸島の中央にあるサンタ・クルス島でした。こちらが、群島の中で人口が一番多い島とのことです。

P29YY(パプアニューギニア)

ペディション局P29YYのQSLカードが届きました。ワンオペのペディション局だったため、オールバンドを狙うにはコンディションとタイミングを合わせるのが難しく、何とか15m/17m/40mのFT8で交信できました。 CWはスケジュール交信のみだったようです。

特に40m FT8の交信が記憶に残ります。7回のコールバックに対してRR73を得られず、8回目に何とかRR78をデコードできました。

パプアニューギニアはオセアニアに属し、オーストラリアの北、ソロモン諸島の西、インドネシアの東、ミクロネシア連邦の南に位置します。周辺のエンティティとは室内LW×10Wでも交信済みであり、パプアニューギニアも交信可能な位置です。交信成立時のレポートも、his/my = -14 / -18 dB で悪くありません。ただし、バンドスコープを見るとローバンド特有のQSBによって信号が安定せず、輪郭がぼやけています。これに打ち勝つためにはパワーが必要とされ、室内LW×10Wが苦手とするバンドです。

3通の中、この封筒だけビニール袋に入れられ、「湿潤した状態」で到着したとの郵便局のメモが貼付されていました。交信証明書として重要な部分は裏面のログシールですが、特に滲み等はありませんでした。角の折れがあった以外は問題ありません。「できる限り乾燥させました」とのこと。日本の郵便局のサービスには頭が下がります。

VK9WX(ウィリス島)

30m FT8の偶発的一期一会で交信できました。秋はペディションが多かったため、FT8の常用周波数以外に過去にペディションが使用していた周波数を時々チェックしていました。すると、誰も見えない周波数で突然VK9WXが浮かび上がりました。ATNOです。直ぐにコールを始めました。

3回コールを繰り返しましたが、VK9WXは消えてしまいました。「ダメかな?コールを止めよう」と思った瞬間にVK9WXが浮かび上がり、イタリア局へのRR73に続いてコールバックが返ってきました。天恵とばかり、レポート返送を5回繰り返しましたが、またVK9WXは消えてしまいました。「やはりダメかな?」と思った瞬間にVK9WXが浮かび上がり、RR73をデコードすることができました。タイムアウトせずに粘り強くレポート返送を待ってくれたものと感謝致します。

室内LW×10Wから見ると30mバンドもローバンドです。レポートは his/my = -18 / -16 dB で悪くありませんが、バンドスコープを見ると前記のP29YYよりも信号の輪郭がぼやけていました。10WではQSBの頂上でしか交信できないコンディションです。

タイムアウトせずに待ってくれたのは、VK9WXがペディション局ではないからだと思います。固定局でもありません。長期(半年)滞在局です。QSLカードを見ると分かる通り、ウィリス島は約7.7ヘクタール(0.077 km²)、標高約9mの小さなサンゴ礁の島です。一般人は立ち入り禁止ですが、島の中央に建物が見えます。オーストラリア気象局の有人観測所で、4名の公務員の方が半年交代で滞在されているようです。その中の1名の方がたまたまHamで、自由時間に電波を出してくれているようなのです。この状況は南鳥島と瓜二つです。

新着ビューローQSL

JARLビューロからもDX QSLが届きました。D73G(IOTA/AS-060)は、2023/7のCW交信分と、2025/9のFT8交信分が同時に届きました。どこかに滞留していたのかな?

IC-705用APFの検討(11)Spresense実装IIR型APFのコード見直し

APF処理シーケンスの前回改良案の見直し

前回の改良案

前回(IC-705用APFの検討(10)Spresense実装IIR型APFの計算時間測定)、DMA割込に同期したフィルタ処理サイクルへのフィルタ係数更新計算時間の影響を回避するために、下記の改良案を考えました。

この案では、Spresenseのサブコアでフィルタ処理と同時に実行していたフィルタ係数更新計算を、メインコアのloop関数内に移設します。

しかし、メインコアのloop関数では、フィルタ処理結果をサブコアから受け取って、ミキサー(スピーカー)に出力する処理を行っています。そのため、loop関数の反復サイクルもDMA割込サイクル(16msec)以下である必要があります。移設したフィルタ係数更新計算時間がDMA割込サイクルを超過すると、音飛び等が発生する可能性があります。

このような制約が発生する原因は、メインコアしかオーディオ入出力を扱えないからです。

改良案の見直し

そこで、以下の3案を新たに考えました。

  1. フィルタ係数更新計算を新たなサブコア2へ移設する案
  2. ミキサー出力処理をDMA割込コールバック関数内へ移設する案
  3. サブコアの活用を諦めてメインコアに全ての処理を集約する案
改良案(1)

改良案(1)は、下記に示すように、フィルタ係数更新計算をメインコアではなく2つ目のサブコア2に移設します。これにより、メインコアのloop関数はサブコア1からのフィルタ処理結果の受信に専念できます。サブコア2からのフィルタ係数更新結果の受信はDMA割込サイクルに同期する必要はないため、ノンブロッキング(ポーリングモード)で受信します。

Spresenseのアーキテクチャは、低消費電力化を狙って処理能力が落ちた分をマルチコア化で挽回する思想です。マルチコアを有効に使うことが、Spresenseの思想に合致します(・・・とこの時までは考えていました)。

改良案(2)

改良案(2)は、下記に示すように、メインコアのloop関数内にあったフィルタ処理結果のミキサー出力処理までDMA割込コールバック関数内に移設します。これにより、loop関数内の重たい処理によってフィルタ処理結果の受信が遅れることを防ぎます。

DMA割込コールバック関数内でフィルタ処理が完結すれば、メインコアのloop関数内で時間の長い処理を実行しても影響がありません。

しかし、コールバック関数内でフィルタ処理結果のブロッキング受信が発生するため、サブコアのフィルタ処理が終了するまでメインコアは待ち状態になります。これでは、スレッド遷移のプログラミングをしない限りは、サブコアに処理を切り出す意味が無くなり本末転倒です。こうなる理由は、コア間通信のソフトウェア割込み機能がないからです。タイマー割込みで疑似的にソフトウェア割込みを実現するのは煩雑過ぎると考えました。

改良案(3)

改良案(3)は、下記に示すように、改良案(2)と同様にメインコアのloop関数内にあったフィルタ処理結果のミキサー出力処理をDMA割込みのコールバック関数内に移設した上で、サブコアにあったフィルタ演算処理もメインコアのコールバック関数内に移設する案です。

コア間通信のオーバーヘッドを嫌い、サブコアの活用を諦める案です。Spresenseらしくない案とも言えますが、原点回帰のシンプルな案です。

APF処理シーケンスの改良案の試験

改良案(1)

メインコアのloop関数内からフィルタ係数更新計算を新たなサブコア2へ移設する案を実装して試験しました。

結果、メインコアからサブコア2へフィルタ係数更新要求の通信を送った瞬間にダンプしました。

AIに相談したところ、共有メモリの使い方や、メモリ関係の初期化関数の後のスリープ時間の取り方についてアドバイスをもらいましたが、何を対策しても解決しませんでした。

改良案(2)

メインコアのloop関数内にあったフィルタ処理結果のミキサー出力処理までDMA割込コールバック関数内に移設する案を実装して試験しました。

結果、音は出ましたが、連続的なポップ音が重畳しました。周波数特性を測ると、包絡線上にフィルタ特性は辛うじて見えましたが、包絡線から下に激しく上下して連続的な特性は得られませんでした。フィルタ係数の計算は正しくできているようですが、音飛びが発生しているようです。

AIにコードを付けて相談したところ、驚愕の回答が返ってきました。Spresenseのオーディオライブラリがサブコアを若い番号から順番に使用するため、ユーザーアプリは最後のサブコア5を使うべきだと言うのです。そのような記述は見たことがありません。今回のAPFアプリは、SONYのチュートリアルのサンプルプログラムを参考に作成しており、サブコア1の使用は変更していません。

AIにマニュアル記載箇所のエビデンスを求めました。エビデンスが見つからないと、「大変申し訳ありませんでした。間違えました」と白旗を上げるのが通例です。しかし、今回は違いました。「マニュアルに記載はありません。だからこそ、コミュニティのノウハウなのです」と自信に満ちた回答が返ってきました。

しかし、Spresenseの実行コードはユーザがコア毎にフラッシュにアップロードします。オーディオライブラリはメインコアのコードにリンクされ、メインコアのフラッシュにアップロードしているものと考えていました。実行時にメインコアから自動的にサブコアのフラッシュにアップロードされるとは思えません。

その点をAIに質しました。すると、「Spresenseのオーディオライブラリは、メインコアがサブコア用のコードをSRAMに展開してサブコアで実行する」との回答。そう言えば、SDカードに格納したDSPプログラムを読み込んで実行するチュートリアルがあることを思い出しました。Spresenseもノイマン型コンピュータです。フラッシュだけでなくSRAMにコードを展開して実行してもおかしくありません。

ここまでくれば、ハルシネーションを疑っているより、実験した方が早くなります。サブコア1をサブコア5に焼き直して実行しました。ポップ音は消えました。AIが正解でした。周波数特性を以下に示します。

中心周波数fc=560Hz、周波数帯域fw=100Hz

振幅特性は、前回(IC-705用APFの検討(9)Spresense実装IIR型APFの測定)よりも綺麗な連続曲線が得られました。サブコア1を使用した前回測定時は、音飛びによって曲線が脈動していたようです。

振幅特性が綺麗に整ったことで、高周波数帯域のノイズが目立つようになりました。振幅特性の下限は、20kHz付近で約-80dBまで落ちています。そこから再び上昇し、200kHz~300kHz付近でブロードに約-40dBのノイズが見られます。可聴域を超えているためAPFとしては問題ありません。

AIが列挙する原因は、Spresenseの(1)DC-DCコンバータ、(2)S-Master(D級アンプ)のキャリア残留・折り返し、(3)BtoBコネクタの寄生インピーダンスによる基準電位振動、の3つです。何れも良い推定ですが、ブロードな特性を良く説明できる推定原因は(1)と思います。

CXD5247には内蔵のPMIC(電源管理IC)としてPFM(パルス周波数変調)方式のDC-DCコンバータが3系統内蔵されています。PFMは、負荷の軽重に応じてスイッチングの「回数(周波数)」を動的に変化させることで、低消費電力を実現する優れた方式です。しかし、固定周波数で動くPWM方式とは異なり、負荷の状態(CPUの動作やアンプの消費電流)によってスイッチング周波数が数十kHz〜数百kHzの間で激しく変動します。これが電源ラインやGNDプレーンを通じてオーディオ回路に回り込むと、単一の鋭いスパイクではなく、200kHzあたりを中心とした「ブロードな(裾野の広い)雑音の山」として観測されます。

PMICのスイッチング周波数は広範囲に変動するようなので、この推定原因が正しければ、たまたまAPFプログラム実行時の消費電力に見合うスイッチング周波数が200kHz~300kHz付近だったということになります。低消費電力化の副作用ですね。

中心周波数fc=560Hz、周波数帯域fw=800Hz

同様に、振幅特性は、前回よりも綺麗な連続曲線が得られました。

中心周波数fc=560Hz、周波数帯域fw=3Hz

同様に、振幅特性は、前回よりも綺麗な連続曲線が得られました。

拡大すると、音飛びによる振幅特性の脈動が皆無であることを確認できます。中心周波数fcにおける位相特性の変曲点も綺麗に出ています。

計算時間の測定

DMA割込コールバック関数の始点からミキサー出力までの時間を測定した結果を下記にまとめます。改良案(2)では、ミキサー出力もコールバック関数内で実行しています。micros関数で1,000回測定し、平均値、最大値、最小値を求めました。占有率は、DMAサイクル(16msec = 768 / 48kHz)に占める割合です。

DMA割込コールバック関数処理時間 計算時間 占有率
平均値 733 usec 4.6 %
最大値 12,695 usec 79.3 %
最小値 610 usec 3.8 %

フィルタ係数更新計算をDMA割込サイクルから追い出し、loop関数内に移設したにも拘らず、最大値の占有率は79.3%と巨大になりました。前回と同様の結果です。平均値と最小値との比較から、最大値は稀にしか発生しないイベントに関係した処理時間と推測されます。そのため、フィルタ係数更新計算に費やす時間と推定したのですが、違ったようです。

改良案(3)

上記改良案(2)に加えて、サブコア5のフィルタ処理を全てメインコアのDMA割込コールバック関数内に移設する案を実装して試験しました。APFコードでサブコアは使用していません。

APFの出力音と周波数特性の測定結果は、改良案(2)と変わりありませんでした。問題は処理時間です。

計算時間の測定

まず、中心周波数fcもしくは周波数帯域幅fwの変更に伴う、フィルタ係数更新計算時間の測定コードを追加しました。10回更新の測定結果を下記にまとめます。

フィルタ係数更新計算時間 計算時間 占有率
平均値 264 usec 1.7 %
最大値 306 usec 1.9 %
最小値 244 usec 1.5 %

フィルタ係数更新計算時間のばらつきは小さく、占有率は最大でも2%未満に過ぎません。前回、AIに問診した三角関数テイラー展開の問題は存在していませんでした。実測は大事です。

次に、DMA割込コールバック関数の始点からミキサー出力までの時間を1,000回測定した結果を下記にまとめます。改良案(3)では、フィルタ処理はコールバック関数内で完結しています。

DMA割込コールバック関数処理時間 計算時間 占有率
平均値 621 usec 3.9 %
最大値 641 usec 4.0 %
最小値 610 usec 3.8 %

処理時間のばらつきは小さく、占有率は最大でも4%以下に過ぎません。1,000回測定しても、最大値が大きくばらつくケースは解消されました。Spresenseは6個のマルチコアを使い倒すことが真骨頂と思っていましたが、オーディオライブラリが使い倒してくれているようなので、APF開発ユーザはおとなしくメインコアでシンプルなプログラミングを行えば良いという結論に落ち着きました。

前記の改良案(2)で処理時間のばらつきが発生した真因は依然として不明ですが、SpresenseのRTOS(Apache NuttX)によってコア間通信に係る何らかの処理が裏で時々動いている可能性を推測しています。最後に、これに係るAIの答えを収録します。

Spresenseで採用されているNuttXでは、ユーザーが明示的に作成したタスク以外にも、システムを維持するために「裏(バックグラウンド)」で自動的に動いている処理が存在します。これらは一般的なLinuxのデーモンのように重い処理ではありませんが、RTOS(リアルタイムOS)としてシステムを正常に稼働させるために必要不可欠なものです。
主に以下のような処理がバックグラウンドで時々、または定期的に動作しています。

1. システム維持に関する処理
• IDLE(アイドル)タスク
概要:
実行すべきユーザープロセスが何もないときに動作する最優先度が最も低いタスクです。
裏での動き:
主にSpresenseの 省電力制御(ローパワーモードへの移行) や、破棄されたタスクのメモリ解放(クリーンアップ)を裏で行っています。
• タイマー割り込み(システムティック)
概要:
NuttXの時間を管理するためのミリ秒単位の定期的な割り込み処理です。
裏での動き:
タスクのスケジュール切り替え(コンテキストスイッチ)や、usleep() などの時間待ち解除の判定が裏で常に動いています。

2. Spresense特有・ハードウェア管理に関する処理
• メモリマネージャー(Worker Thread / ワークスレッド)
概要:
NuttXはカーネルやドライバの非同期処理を処理するために、バックグラウンドで「Worker Thread(HPWORK / LPWORK)」を持っています。
裏での動き:
メモリの断片化を防ぐための自動的なプール管理や、ドライバ層の遅延処理がここで実行されます。
• マルチコア(ASMP)の同期・通信管理
概要:
Spresenseは最大6コア構成ですが、メインコアとサブコアの間でデータをやり取りするための管理機構が裏で動いています。
裏での動き:
サブコアからの割り込み通知の受信や、共有メモリの同期チェックが時々発生します。
• オーディオ・センシングドライバのバッファ管理
概要:
SDKのAudioライブラリやセンシングフレームワーク(Sensor Framework)を使用している場合です。
裏での動き:
ユーザーコードがデータを読み出すまでの間、DMA(ダイレクトメモリアクセス)転送の完了割り込みを受けて、裏でひっそりとバッファの入れ替えやキューの維持を行っています。

上記AIの回答の中で、改良案(3)に無くて改良案(2)に有るバックグラウンド処理は「 マルチコア(ASMP)の同期・通信管理」になります。ただし、あまり重そうなタスクではない記述です。APFはC++でコーディングしているため、ガーベージコレクションは働かないはずですが、メモリマネージャーがメモリの断片化を防ぐための自動的なプール管理を行っているようです。コア間のデータ共有でメモリマネージャーが働く可能性はありそうです。

IC-705用APFの検討(10)Spresense実装IIR型APFの計算時間測定

APFの改良を図る上で、サンプリングタイム(正確にはバッファ処理サイクル時間)の中で、APF信号処理が占める消費時間を知っておく必要があります。

Spresenseマイコン処理時間の測定方法

測定方法の選択

Spresenseで処理時間を測定する方法には、次の2種類があります。

  • ソフトウェアの処理時間取得関数を用いた測定方法
  • ハードウェアのGPIOピンのL/Hをオシロスコープで観測する測定方法

後者もソフトウェアでGPIOピンのL/Hを制御しますが、処理のオーバーヘッドは小さいと見做せます。ただし、測定の都度、GPIOソケットにポストを立ててプローブを装着するのは煩雑です。今回は、前者のソフトウェア関数を用いた処理時間測定機能をAPFシステムのメニューに組み込むことにしました。

ライブラリ関数の選択

処理時間を測定する関数には、次の2種類があります。

  • Arduino環境のmicros()関数
  • NuttX(リアルタイムOS)SDK環境のup_perf_gettime()関数

両者は互いに開発環境別のエイリアスではありません。AIにまとめを依頼した比較表を下記に示します。

項目 micros() up_perf_gettime()
解像度 1マイクロ秒 ナノ秒レベル(CPUクロック同期)
オーバーヘッド やや大きい 極めて小さい
用途 ミリ秒以上の周期処理の管理 割込処理内での微小な時間測定

この表を見ると、up_perf_gettime()を選択すべきですか、単位変換処理が必要になるなど実装が煩雑です。今回は、サンプリングタイマーの割込ではなく、DMA転送の割込によるバッファ処理サイクル時間内での時間測定となるため、まずmicros()関数を利用することにしました。

APF処理時間測定機能の実装

APFシステムメニュー

今後、APFの機能拡充の都度、消費時間の測定が必要になるため、APFシステムのメニューに「APF処理時間測定(measure time)」を準備しました。

MainAPF> **********************
MainAPF> **  APF Main Menue  **
MainAPF> **********************
MainAPF> (h) help 
MainAPF> (i)(f) IIR/FIR ftype = IIR 
MainAPF> (c) fc = 560.0 
MainAPF> (b) fb = 100.0
MainAPF> (t) toggle tone = OFF
MainAPF> (m) measure time = ON
MainAPF> (e) exit and start APF!

測定結果の出力

時間測定を「ON」にしてsetup()関数を抜け、APFを開始すると、100回の測定を反復し、シリアルモニターに以下のログを表示します。

MainAPF> Filter processing time (ave): 791.019775 [usec]
MainAPF> Ratio in DMA cycle time: 4.943874 [%]
MainAPF> Filter processing time (max): 794 [usec]
MainAPF> Filter processing time (min): 763 [usec]

100回の平均値、最大値、最小値を表示するようにしました。最大値を見逃さないようにすることが重要です。

実装

APFソフトウェアのブロック図を下記に示します。

フィルタ処理の起点になるポイントは、DMA転送終了割り込みで呼び出されるコールバック関数 frontend_pcm_callback() になります。この関数の先頭に開始時間測定コードを追加します。

バッファに格納されたデータは、サブコアに転送され、フィルタ演算され、演算結果がメインコアに戻され、出力ミキサからDA変換器に送られます。よって、フィルタ処理の終点になるポイントは、loop() 関数内で出力ミキサに演算結果を出力した後になります。ここに終了時間測定コードを追加します。

1回の測定では、キャッシュ等の影響を受けます。100回測定して、平均値、最大値、最小値を上記のログに出力します。これを10回繰り返して、合計1,000回の測定で評価しました。

APF処理時間の測定結果

IIR型フィルタ演算時間

1,000回の測定結果を下記にまとめます。この測定中に、DMA割り込みコールバック関数を起点にサブコアで実行しているのは 「IIRフィルタ演算」だけです。

ウオッチドックタイマーを叩かないために重要な評価値は最大値です。IIRフィルタ演算時間の最大値は、 824  \mu \text{sec}でした。バッファ長 768サンプル、サンプリング周波数 f_s = 48 \text{kHz}から、DMAサイクルは 16 \text{msec}( = 768/48,000)です。IIRフィルタ演算時間の占有比率は 5.2 \text{\%}( = 824 \text{E3} / 16)に過ぎませんでした。

マルチコア環境の場合は、コア間のバッファ転送などのオーバーヘッドが心配されましたが、杞憂だったようです。

IIR型フィルタ計算時間+フィルタ係数更新時間

今回のディジタルAPFの要は、実行中にオンデマンドで任意の仕様(中心周波数、通過周波数帯域)に変更できることです。そのため、仕様変更要求があった場合はサブコアで「中心周波数 f_cと周波数帯域 f_b変更によるIIR係数更新関数」を実行してから、「IIRフィルタ演算」を実行する実装としています。

APF仕様変更コマンドを随時に押下しながら、1,000回の測定を行った結果を下記にまとめます。この測定中に、DMA割り込みコールバック関数を起点にサブコアで実行しているのは、随時の「 中心周波数 f_cと周波数帯域 f_b変更によるIIR係数更新関数」と毎回の「IIRフィルタ演算」になります。

随時のフィルタ仕様更新命令がヒットした際の最大値は 10.3  \text{msec}と、一挙に 12.5倍になりました。占有比率は 64.3 \text{\%}( = 10.3 / 16)に及びます。

フィルタ演算は、CMSIS(Cortex Microcontroller Software Interface Standard)-DSPライブラリの中で、Cortex-M4Fのパイプラインの挙動を踏まえた最適化が図られています。これにより、元々IIRフィルタのタップ数が少ないこともあって、高速実行が可能になっています。

一方、「IIR係数更新関数」は特に最適化は意識していませんでした。Cortex-M4FはFPU(浮動小数点演算ユニット)を備えているため、三角関数の計算も速いのではないかと漠然と期待していましたが、根拠のない期待だったようです。改めて調べると、FPUハードウェア自体に「三角関数を直接計算する命令」はないとのこと。これにより、標準ライブラリの三角関数は、汎用的にテイラー展開などの多項式近似を行うために、計算に数百サイクル以上かかるとのこと。

Armが公式に提供しているDSPライブラリ(arm_sin_f32 など)を使用すると、テーブルルックアップと線形補間を組み合わせ、FPUとDSP命令をフル活用して計算するため、標準関数の数倍〜十数倍のスピードで高速処理が可能とのこと。「IIR係数更新関数」も最適化が必要なようです。

改良方針

今後、FIR型APFを実装するとタップ数が増えるために、フィルタ演算時間が増加することが見込まれます。フィルタ演算時間だけなら余裕があると思いますが、「FIR係数更新関数」を同時に実行すると、余裕がなくなることが危惧されます。

「IIR係数更新関数」がフィルタ演算を任務とするサブコアに置かれている現状は、「IIR係数更新関数」の処理時間が長いことが判明した今となっては望ましくない状態と言えます。そこで、「IIR係数更新関数」のメインコア loop() 関数内への移設を検討することにしました。

メインコアのloop() 関数内であれば、DMAサイクルとは非同期に実行でき、DMAサイクル時間内に処理を完遂しなければならない制約も無くなります。その代わりに、フィルタ係数をバッファに追加で積んでサブコアに送付する必要が生じます。今回の評価結果を見る限り、 768サンプルのPCMデータ転送のオーバーヘッドは問題にならないようなので、将来のFIRの 512係数列程度の転送追加なら問題ないと判断しました。

UIが主任務のメインコアloop() 関数内の処理が移設によって逼迫するようなら、二つ目のサブコアで「IIR係数更新関数」を実行する方法もあります。しかし、メインコアではDMAサイクルをブロッキングしないで更新結果を受け取る必要があるため、デバックの難易度が高そうです。プランB とします。

IC-705用APFの検討(9)Spresense実装IIR型APFの測定

前回、USBオシロAnalog Discovery 2 のネットワーク解析機能を用いて、オーディオ信号入力用の絶縁I/Fボード単体の周波数特性を測定しました。今回は、入力I/FボードをSpresenseに装着して、Spresenseに実装したIIR型APFの周波数特性を測定します。

I/Fボードは小型にしたつもりでしたが、Spresenseに装着すると絶縁トランスの存在感が大きいことに気付きます。周波数の低いCWトーン信号を通すためには、大きな磁束密度を許容できるコアと、十分なインダクタンスを確保する多くの巻線が必要になるということですね。

Spresense実装IIR型APFの特性測定方法

測定実験の構成図を示します。Spresenseへ入力するオーディオ信号は、Analog Discovery 2 のWavegenを用いて生成します。Spresenseのオーディオ出力は、アンプスピーカで再生します。

測定のためにはテストピンが重要になります。I/Fボードには予めテストピンを実装しておきましたが、Spresenseのオーディオ出力を測定するテストピンはありません。そこで、Spresense拡張ボード故障時に原因調査のために切断したオーディオ3芯ケーブルを再結線して、はんだ付け接続部をプローブポイントとして活用しました。

入力I/FボードのVRは、絶縁トランスの1次側1Vに対して2次側 0.4 \text{V}になるように調整しました。Spresenseのマイク入力レンジは ±0.45 \text{V}と思います。

Spresenseマイク入力の入力インピーダンスは良く分からなかったため、入力I/Fボードには電源を必要とするボルテージフォロワを特に設けませんでした。不必要な能動素子を追加すると、ノイズ源になることもあり得ます。VRによる想定通りの調整結果を見る限り、入力インピーダンスは十分に大きいようです。Scopeの入力インピーダンスも、プローブを「×10(10:1減衰プローブ)」に設定して、大きくしました。

入力信号周波数の変更実験

Analog Discovery 2 の信号波形生成機能(Wavegen)を用いて、正弦波入力信号の周波数を変更し、APFの出力信号の変化を測定しました。APFの設定は、中心周波数 f_c=560 \text{Hz}、通過周波数帯域 f_w=100 \text{Hz}としました。

入力信号周波数 fg=560Hz

入力信号の周波数 f_gを、APFの中心周波数 f_cと同じ 560 \text{Hz}とした場合の実験結果を下記に示します。左がAnalog Discovery 2 を制御するWaveFormsアプリのWavegen信号生成機能の画面、右が2CH Scopeの画面です。

入力信号振幅の実測値 0.975 \text{V}(1次側)に対して、出力信号振幅は 1.08 \text{V}程度になっています。入力I/Fボードを含めたAPFシステムの総合ゲインは約 0.9 \text{dB}ということになります。Spresense処理系のゲイン設計は特に行っていないため、出来成りです。絶縁トランス2次側の入力信号振幅は 0.4 \text{V}のため、Spresense処理系のゲインは約 2.75倍( 8.8 \text{dB})になります。絶縁トランス2次側で信号振幅を半分以下に落とす設計は、ちょうど塩梅が良かったようです。

入力信号周波数 fg=230Hz

次に、入力信号の周波数 f_gを、APFの中心周波数 f_cの約半分の 230 \text{Hz}とした場合の実験結果を下記に示します。( f_cの半分は 280 \text{Hz}であるポカ間違いに今気付いたため、取り急ぎ「約」を付けました。)

入力信号振幅の実測値 0.975 \text{V}(1次側)に対して、出力信号振幅は 0.1 \text{V}程度になっています。APFによる抑圧は -19.8 \text{dB}です。アンプスピーカのボリュームが小さいと、聞こえなくなりました。この時間領域の抑圧測定値は、後でネットワーク解析を行った際に、APFの周波数特性と照合します。

入力信号周波数 fg=1.12kHz

最後に、入力信号の周波数 f_gを、APFの中心周波数 f_cの倍の 1,120 \text{Hz}とした場合の実験結果を下記に示します。

入力信号振幅の実測値 0.975 \text{V}(1次側)に対して、出力信号振幅は 0.125 \text{V}程度になっています。APFによる抑圧は -17.8 \text{dB}です。この時間領域の抑圧測定値も、後でネットワーク解析を行った際に、APFの周波数特性と照合します。

APF通過周波数帯域の変更実験

ディジタルAPF開発の目的は、APFの仕様を自由に変更できるようにすることでした。APFの通過周波数帯域を変更し、APFの出力信号の変化を測定しました。

APF通過周波数帯域 fw=400Hz のノイズ抑圧特性

前記の入力信号周波数 f_g=230 \text{Hz}、APF通過周波数帯域 f_w=100 \text{Hz}に対して、APF通過周波数帯域を4倍の f_w=400 \text{Hz}に変更した結果を下記に示します。

APF通過周波数帯域を広げた結果、信号の抑圧は -7.7 \text{dB}に留まっています。この入力信号をノイズとした場合、逆にAPF通過周波数帯域を f_w=400 \text{Hz}から上記で見た 100 \text{Hz}に狭めると、 230 \text{Hz}ノイズの抑圧は 12.1 \text{dB} (=-7.7 +19.8\text{dB})改善することになります。

APF通過周波数帯域 fw=3Hz のノイズ抑圧特性

APF通過周波数帯域を 32(=2^5)分の 1の 𝑓_𝑤 =3.125⁢ \text{Hz}に変更した結果を下記に示します。帯域が中途半端な数値になっているのは、 100 \text{Hz}を起点に半減を5回適用したからです。

APF通過周波数帯域を極端に狭めた結果、ノイズの抑圧は -32 \text{dB}に拡大しています。APF通過周波数帯域を f_w=100 \text{Hz}から 3.125 \text{Hz}に狭めると、 230 \text{Hz}ノイズの抑圧は 12.2 \text{dB} (=-19.8 + 32\text{dB})改善することになります。

APF通過周波数帯域 fw=3Hz の信号通過特性

 230 \text{Hz}ノイズを -32 \text{dB}抑圧する上記APFが、 560 \text{Hz}の目的信号を問題なく通過させられるかどうかを確認しました。

この結果は、最初の f_w=100 \text{Hz}設定のAPFと同じ結果です。通過周波数帯域 f_wを 3.125 \text{Hz}まで狭めても、目的信号の通過には抑圧等の劣化が生じないことが確認されました。 230 \text{Hz}ノイズとのSN比は約 33 \text{dB}になります。

APFの方形波応答実験

無線で方形波信号が入力されることはありませんが、目的の正弦波信号にノイズが付加された信号と捉え、フィルタリング効果の確認として実験しました。

APF通過周波数帯域 fw=800Hz

入力方形波信号の測定波形が歪んでいます。ここでプローブのトリマ・キャパシタの調整不足に気付きましたが、久方ぶりの実験で絶縁ドライバーが行方不明のため、このまま実験しました。

APF通過周波数帯域を f_w=800 \text{Hz}に広げた場合は、方形波の角は取れますが、大きな歪は残ったままになります。

APF通過周波数帯域 fw=100Hz

ディフォルトのAPF設定の f_w=100 \text{Hz}の場合は、正弦波に近付きますが、歪は少し残っています。

APF通過周波数帯域 fw=3Hz

APF通過周波数帯域を f_w=3.125 \text{Hz}に狭めた場合は、見た目で気付く歪は無くなり、正弦波になります。フーリエ変換をすると歪に気付くと思いますが・・・。

APFの周波数特性測定実験

前回報告と同様に、Analog Discovery 2 のネットワーク解析機能を用いて、APFの入力から出力までの周波数特性の測定実験を行いました。

APF通過周波数帯域 fw=100Hz

APFのディフォルト設定は、中心周波数 f_c^{design}=560 \text{Hz}、通過周波数帯域 f_w^{design}=100 \text{Hz}と仮決めしています。そのディフォルト設定の周波数特性を下記に示します。

赤で示すように、実際の中心周波数および通過周波数帯域は設定通りの特性になってることが確認できます。また、上記の時間領域実験で見たように、 230 \text{Hz}の振幅が -20 \text{dB}、、 1.1 \text{kHz}の振幅が -18 \text{dB}であることも確認できます。

その他に以下の気付きがありました。

  • 中心周波数より低周波数側の振幅にリップルが乗っています。測定ノイズと思いましたが、対数10倍区間の周波数掃引ステップ数の割り当ては250点であり、リップル数より粒度が細かく同期しないようです。
  • 中心周波数より高周波数側の 1 \text{kHz}から  10 \text{kHz}の区間では振幅が大きくばらついています。極大値をつないだ包絡線が正しい振幅特性に見えます。サンプリング周波数 f_s=48 \text{kHz}より高い 100 \text{kHz}から  500 \text{kHz}付近にノイズがあるため、ナイキスト周波数  24 \text{kHz}で折り返しているのかと思いましたが、計算してみると整合しません。
  • 下段の位相が一見ランダムにばらついています。これは、周波数が高くなるにつれて位相が遅れるのに対して、周波数掃引ステップの粒度が粗いことにより、位相の折り返しを精度よく捉えられないことに原因があります。後ほどの周波数掃引範囲の縮小検討で、位相の折り返しが明確になります。

APF通過周波数帯域 fw=800Hz

APFの設定のうち、中心周波数 f_c^{design}=560 \text{Hz}はそのままにして、通過周波数帯域を f_w^{design}=800 \text{Hz}に広げた場合の周波数特性を下記に示します。

実際の中心周波数は f_c^{actual}=400 \text{Hz}となり、設定より 160 \text{Hz}小さくなりました。また、通過周波数帯域も f_w^{actual}=740 \text{Hz}となり、設定より 60 \text{Hz}狭くなりました。

通過周波数帯域を f_w^{design}=1 \text{kHz}以上に広げると、Spresenseはハングアップしませんが、音が出なくなります。今回採用した設計方法は通過周波数帯域を広くする方向が不得意なのかもしれません。APFとしては、次の狭くする方の性能の方が重要と考えます。

APF通過周波数帯域 fw=3Hz

APFの設定のうち、中心周波数 f_c^{design}=560 \text{Hz}はそのままにして、通過周波数帯域を f_w^{design}=3.125 \text{Hz}まで極端に狭くした場合の周波数特性を下記に示します。

フィルタとして破綻する可能性もあると思いましたが、想定以上に切れの良いフィルタが得られています。振幅特性のピークゲインが低下しているように見えますが、周波数掃引分解能のマジックです。周波数掃引範囲を 520 \text{Hz}~ 600 \text{Hz}に狭めた場合の測定結果を下記に示します。

振幅特性のピークゲインは低下していないことが確認できます。実際の中心周波数は f_c^{actual}=561 \text{Hz}となり、設定との誤差は僅か 1 \text{Hz}でした。また、通過周波数帯域も f_w^{actual}=3 \text{Hz}となり、目視読み取りの精度では設計と一致しました。

周波数掃引分解能が相対的に高くなったことにより、下段の位相特性も正確になりました。周波数が高くなるにつれて位相が遅れるのに対して、位相の折り返しを繰り返している様子が正確に捉えられました。

アナログフィルタを離散変換するシンプルな設計法に基づく、次数の低いIIRフィルタでここまでの性能が出るとは予想外でした。ディジタルAPFの基本形は完成したため、学習アルゴリズムを適用し易いFIRフィルタ等の検討を進める予定です。

室内ロングワイヤーアンテナはDXCCの夢を見るか(28)LoTWの棚卸と初秋のDX

昔は、夏休みが過ぎると朝晩に冷気を感じ、初秋に季節が移ったことが感じられましたが、近年は夏が冬まで延長する二季の様相を示し秋が消えていました。今年は久し振りに、朝晩の冷気が運ぶ初秋を感じることができました。長雨の異常気象以外に、サイクル25下降期の太陽活動が多少は影響しているのでしょうか。

今年の夏はDX QSOの機会が無く、QRT状態でした。しかし、8月下旬から9月下旬にかけての初秋には、アフリカや中東などの室内LW×10Wでは交信の難しかったエンティティと交信することができました。9月下旬を過ぎると、またDX QSOが難しくなりました。移ろい行く季節のひと時の僥倖だったのかもしれません。

LoTW DX QSO実績の棚卸

LoTWのDX QSO照合状況を示します。紙カード分も少しありますが、未整理です。

1年前の前回DXCCクレジット申請から、Mixedで29Upとなり総計160エンティティに到達しました。

LoTW実績のバンド別進捗

バンド別では、17m、15m、12mの各バンドでほぼ等しく22~23Upとなっています。一方、30m、20m、10mの各バンドでは16~17Upに留まりました。

総計では、15m(120)、17m(103)、10m(93)の順ですから、サイクル25の下降期で10mバンドが振るわなくなった代わりに、その下の12mバンドでニューエンティティとの交信の機会が増えたことが伺えます。

30mと20mは室内LWアンテナの効率が悪くなるバンドですが、コンディションの低下した10mと同じ程度には健闘しました。40mは室内LWアンテナの効率がさらに悪くなるバンドのため、近隣の東アジアやオセアニアの局が強く入感しても交信できなくなりました。サイクルピーク時に交信しておくべきだったと反省しています。

80mバンドに対する8m長の室内LWアンテナは、ATUがほぼダミーロードに近くなり、地表波で見通し距離の範囲しか交信できません。しかし、初秋にJD1BOI局(父島)と奇跡的に交信でき、1Upして総計2エンティティになりました。

その時のレポートは、his/my = -17/-15dBで当局の方が若干強いぐらいでした。ただし、他のJA局は+16~+22dBのレポートをもらっており、その差は30dB以上に及びます。初秋のコンディションに助けられて交信できたようです。

LoTW実績のモード別進捗

モード別では、CWが5Up(計110)、Digitalが40Up(計144)でした。サイクル25下降期の室内LW×10Wでは、FT8が救世主になっていることが分かります。

CWの貴重な5Upの内訳を下記に示します。5エンティティの中でHD8R(ガラパゴス諸島)のみがコンテストQSOで、残りの4エンティティは全てDxpedition局とのQSOです。

この中で、春のJIDXコンテストで交信したHD8R(ガラパゴス諸島)と、初秋のDXpedithionで交信したRI1FJL(フランツ・ヨーゼフ諸島)を記録に留めたいと思います。

春と初秋のCW DX

HD8R(ガラパゴス諸島)20m CW

サイクル25の下降期に入ってからはCWコンテストでATNOと交信することは久しくありませんでした。しかし、今春のJIDX(Japan International DX)コンテストでは、20m CWでHD8Rが599+10dBで強力に室内LWに入感する春の珍事がありました。以前、ガラパゴス諸島のペディション局と17m FT8で交信していましたが、CWモードも20mバンドもニューとなります。

この時、JAからの聞こえるパイルは無く、室内LW×10Wでも1回で取ってもらえるに違いないと自信を持ってコールしました。しかし、「?」しか返ってきません。片パスなのかなと思いコールを続けると、「STOP EU ONLY JA STOP EU」と聞こえてきました。SDRの受信タイムラグで「JK1」が「OK1」と間違われているのかなと思い、2回連続コールを試みましたが、「STOP EU」を繰り返すばかりです。こちらには聞こえませんが、クラスターを見たEU局がコールしているのかもしれません。そのうちに「STOP SPAIN」と聞こえてきたので、EUのQRMは間違いないようです。室内LW×10Wの電波はEU局よりよほど弱いのか、埒が明かないと見たHD8RはQRTしてしまいました。

EU局か散会して静かになった頃合いにカムバックしてくれると期待して、定期的にHD8Rが出ていた周波数のチェックを続けました。すると、数時間後にHD8Rが再度聞こえてきました。再開後もJA局と交信しながら「STOP EU」を打っています。信号はQRT前の599+10dBから599ジャストに落ちていました。コンディションは落ちてしまいましたが、何回かコールすると599強度の聞き間違えようのないコールバックが返ってきました。

20m CWで交信した日時のグレイマップを示します。JAからは海面反射のみでガラパゴス諸島に届きます。JAがちょうどグレイラインに入った時に交信しており、D層の電波吸収が無くなったことにより、室内LW×10Wの信号が幾分強く届いたのかもしれません。また、この間のガラパゴス諸島は夜間になるため、QRTは仮眠だったのかもしれません。

ガラパゴス諸島の視点で見たマップを示します。EUからもカリブ海を通って海面反射のみでガラパゴス諸島に届きそうです。距離もEUからの方がJAよりも近いため、HD8RにはEUの信号が強く届いていたことが想像されます。時間を遡ると、EUのQRMで交信できなかった時間帯は、EUの方が夜明けのグレイラインに入っていたと思われます。今回の交信成就にはグレイラインの入れ替わりというメカニズムが作用していたのかもしれません。

OQRSへのログアップは半年に1回ぐらいの頻度の様子です。最近、リクエスト可能になり、LoTWもコンファームできました。

RI1FJL(フランツ・ヨーゼフ諸島)17m CW

フランツ・ヨーゼフ諸島はオーストラリア・ハンガリー帝国の探検隊により発見されたとのことで、当時の皇帝の名前が冠されています。日本語では「諸島」になっていますが、英語表記は「Islands」ではなく「Land」です。皇帝に捧げる土地という意味でしょうか。
コールサインから分かる通り現在はロシア領です。サフィックスはエンティティ名(Franz Josef Land)の頭文字となっており、このコールサインが空いていたということは、このエンティティからのQRVが少ないことを表しているのかもしれません。北極圏という現代の機微なエリアにあり、よく認可されたものと思います。

実は、RI1FJLはノーマークでした。夏枯れでQRTしていたことと、室内LW×10Wによる北極圏のエンティティとの交信は追いかけても望みが薄いと思い込んでいたからです。しかし、交信できたとの報告がWebに複数上がっていたことから、ペディション後半から追いかけを始めました。

コンディションの良い状態が続いていたらしく、1日で20mを除く4バンドのFT8で交信できました。室内LW×10Wが苦手とする30m FT8でも交信できたのに、なぜか20m FT8では交信できませんでした。コールバックは3回帰ってきたのですが、RR73まで到達できませんでした。レポートはhis/my = -18/-25dBで、室内LW×10Wの微弱電波はノイズに埋もれてしまったようです。ちなみに、30mのレポートはhis/my = -10/-16dBで、ストレートにシーケンスを終了できました。翌日は20m FT8の入感はなく、翌々日にようやく20m FT8も交信できました。この時のレポートもhis/my = -16/-25dBで、室内LW×10Wの微弱電波はノイズすれすれのため2回のコールバックを要しました。室内LW×10Wによる20mバンドは北極圏に対して鬼門のようです。

FT8によって複数バンドで交信できると、CWによる交信も欲が出てきます。CWは17mで2回入感がありました。1回目は高速QSBがあり、コールバックの文字が歯抜けになってしまい、ミスコピーの判断ができないため諦めました。現地は強風との報告があったため、アンテナが揺れていたのかもしれません。後日、ペディションQRT間近の2回目は低速QSBになっており、「JK1?」とプリフィックスを取ってもらった後の再送1回でフルコピーを取ってもらえました。

17m CWで交信した日時のグレイマップを示します。距離はオーストラリアのノーザンテリトリーと同じぐらいですが、電波が減衰する大陸反射のパスになります。赤道異常のようなダクトパスの恩恵も期待できそうにない位置関係です。17m CWで交信できた日時は日本がグレイラインに入る直前でした。特殊なコンディションは想定できません。なお、この季節のフランツ・ヨーゼフ諸島は白夜で、日は沈みませんが、フランツ・ヨーゼフ諸島と日本が同時にグレイラインに入る時間帯もあったようです。

RI1FJLに続いてRI1FJZのペディションがありました。CWの積み増しを狙っていたのですが、FT8で稀にデコードするだけで終わりました。何が違ったのでしょうか。後者のRI1FJZの運用はSSNの極少期でした。現地の発電機に制限があり、白熊の徘徊でアンテナの保守にも制限があったようです。前者のRI1FJLのQTHは、フランツ・ヨーゼフ諸島の中央南寄りに位置するヘイス島でした。この島は無人島ではなく、地球物理学観測ステーションが置かれています。一方、後者のRI1FJZのQTHは、フランツ・ヨーゼフ諸島の西端に位置するアレクサンドラ島でした。この島も無人島ではなく、空軍基地が置かれています。後者のRI1FJZの方が前者のRI1FJLより日本から遠いことは確かですが、誤差のような気もします。

春と初秋の FT8 DX

室内LW×10Wでは、FT8の交信も楽ではありません。見えていても、平均-10dBダウンとなるため、コールを続けても交信できないことの方が多くなります。それでも、この初秋は季節チェンジのコンディションにより、アフリカや中東のATNOと交信できました。

アフリカ

初秋に以下のアフリカのエンティティと交信できました。

  1. CN8NS (Morocco)12m FT8
  2. V51PJ(Namibia)17m FT8
  3. V51WH(Namibia)12m FT8
  4. C91RU(Mozambique)12m FT8
  5. S79/DL2SBY(Seychelles)17m FT8
  6. FR5ZE(Reunion Island)10m FT8

この中、Morocco、Namibia、Mozambiquの3つのエンティティがATNOでした。インド洋の島嶼部であるSeychellesとReunion Islandはバンド・ニューもしくはモード・ニューです。大圏パスでEUの延長線上にあるMoroccoを除いては、日本から見て西の方向のアフリカ南側に集中しています。

強く印象に残っているのはナミブ砂漠で有名なNamibiaです。12m FT8でV51WHが室内LWにも強力に入感しました。最初の入感ではパイルが大きいために交信に至りませんでしたが、2回目の入感で交信できました。同じNamibiaのV51PJと17m FT8でも交信できたため、初秋にNamibiaとの特別なパスが出来ていたようです。

Zone 34のSudanも一度だけ強力に入感しましたが、パイルが大きいために交信に至りませんでした。残念ながら、Sudanの2度目の入感はありませんでした。一期一会のチャンスを掴まないと、室内LW×10WではZone 34との交信は難しいようです。

中東

中東(西アジア)はアフリカ以上に難関です。強く入感しても、室内LW×10Wの電波は砂漠に吸収されてしまうのか届きません。しかし、サイクル25の下降期になって中東のいくつかのエンティティと交信できました。

下図に、今春にQSOした中東のエンティティを青で、今秋にQSOした中東のエンティティを赤でそれぞれ示します。今春は地中海寄りのエンティティが多く、今秋は少し日本に寄ってコーカサスの局が増えました。最難関のアラビア半島の局は各季節に1局ずつでした。

まず、今春に以下の中東のエンティティの局(図中の青ピン)と交信できました。全てATNOです。

  1. 4Z5PT(Israel)17m FT8
  2. 9K2HN(Kuwait)12m FT8
  3. 5B4AMM(Cyprus)15m FT8
  4. JY5IB(Jordan)15m FT8

思い出深いのはCyprusです。過去にCWでもFT8でもコールバックを得ていたのに交信は不成立でした。今回、ようやくRR73を得てコンファームできました。

つづいて、今秋に以下の中東のエンティティの局(図中の赤ピン)と交信できました。こちらも全てATNOです。

  1. A61BG(United Arab Emirates)15m FT8
  2. 4L7T(Georgia)15m FT8
  3. 4K6FO(Azerbaijan)10m FT8
  4. OD5ZZ(Lebanon)15m FT8

Georgiaも過去にCWやFT8で複数回トライして、今回、ようやくコンファームできました。

中東は15mバンドが中心ですが、サイクル25の下降期の今はFT8のDX銀座になっています。ATNOのDX局を見つけるのは容易ですが、室内LW×10Wの電波はQRMの海に埋もれてしまい、RR73までのシーケンスの完遂が難しくなっています。

IC-705用APFの検討(8)Spresenseオーディオ信号入力インターフェース

IC-7110がハムフェアで発表されました。動画内の液晶パネルのアップに「APF」の表示が見て取れました。IC-7300MK2に続いて、同じコードをベースにしていると思われるIC-7110にもAPFが搭載されたようです。ここにきてICOMリグへのAPF搭載が進むことは、従来はAPF機能が欠けていたことの裏返しであり、IC-705用APFのニーズがますます高くなります。

最初の回(IC-705用APFの検討(1)Spresenseの調査)で紹介した通り、Spresenseはセンシングプロセッサ(CXD5602)とパワーマネージメント&オーディオアナログIC(CXD5247)(以下オーディオアナログIC)の2つのチップから構成されています。オーディオアナログIC(CXD5247)の入力はマイク信号を想定しています。IC-705の[SP]ジャックとの接続方法を調査し、インターフェースボードを作成しました。

Spresenseマイク入力インターフェースの調査

Spresense拡張ボードの写真と、マイクI/F回路図を下記に示します。

回路図の赤文字の「X」および「-」はこちらで記入しました。多くのコンデンサーや抵抗が見えますが、大部分は「XX」(未実装)やゼロΩとなっており、実装されている部品はC37、C38、C40、C41の4系統のカップリングコンデンサと、JP10のマイク信号入力ピンヘッダだけです。未実装の部品は、入力ラインにノイズが重畳した場合にCRフィルタを構成するためのものや、アナログコンデンサーマイクにバイアス電圧をボード上で供給するためのものです。バイアス電圧自体はピンヘッダに供給されているため、必要に応じて外部I/F上で印加する方式に変更になっています。IC-705の[SP]ジャックにはバイアス電圧を印加する必要がないため、好都合です。

JP14は下記に示すようにはんだジャンパです。はんだをブリッジすれば、1uFのカップリングコンデンサを飛び越して信号を直結できます。外部I/F上でカップリング(DC遮断)する場合はブリッジしますが、今回は拡張基板上のカップリングをそのまま利用する方針にしました。

カップリングコンデンサを通過したAC信号は、SpresenseのBtoBコネクタを経由して、メインボードのオーディオアナログIC(CXD5247)のマイク入力端子に直行します。

IC-705 to Spresense I/Fボードの作成

秋月で販売のある「Mic&LCD KIT for SPRESENSE」のマイクI/F基板を使用して、マイクのコツコツ音テストを行ってきました。

このマイクI/F基板には、前述のバイアス電圧を印加するための外出ししたチップ抵抗が実装されています。そのまま、IC-705の[SP]ジャックと接続することはできません。

また、IC-705の[SP]ジャック出力とSpresenseマイク入力の間のI/Fには、電圧レベルを調整する可変抵抗回路だけを挟めば良いと当初は考えていました。しかし、IC-705のマニュアルを再読したところ、「コネクター情報」の章にPCのマイク入力に接続する際の回路例が掲載されており、絶縁トランスを挟む回路例が紹介されていました。IC-705はPCとUSB接続しているため、SpresenseのUSB接続とループを構成する危険が確かにあります。コモンモード電流によるIC-705の送信暴走を抑えることには苦労したため、ここは大事を取って絶縁トランスを挟むことにしました。

製作した「IC-705 to Spresense I/Fボード」の回路図を下記に示します。

トランジスタ用小型絶縁トランス(橋本電気 SANSUI ST-23)は、秋月では取り扱いがありませんでした。小型トランスはオーディオ用途のインピーダンス変換で需要があるようですが、1:1の絶縁トランスの需要は少ないのかもしれません。探したところ、マルツに在庫がありました。

試作したI/Fボードの写真を下記に示します。試作を繰り返すことを想定して、絶縁トランスのリードは詰めていません。

ユニバーサル基板の寸法は20×80mmです。秋月にありましたが、特に型番はないようです。当初は機械的な強度を考えて、Arduino用ユニバーサル基板を使用する構想でしたが、SpresenseのJP10接続コネクタとユニバーサル基板のスルーホールの位置が合いませんでした(穴の列が足りませんでした)。

絶縁トランスST23は、2つのツメで機械的に取り付ける方式を取っています。当初は、ツメを外側に開いてスルーホールにはんだ付けすることで機械的取付強度を確保しようと考えていました。しかし、偶然にもユニバーサル基板の横幅20mmとST23の横幅が一致していたため、下記写真に示すようにST23が基板を抱きかかえるようにして、ツメを内側に折り曲げてスルーホールにはんだ付けすることができました。


IC-705 to Spresense I/Fボードの単体テスト

USBオシロスコープの Analog Discovery 2 を用いて、配線チェックも兼ねたI/Fボードの単体テストを行いました。

波形観測

Analog Discovery 2 には信号波形生成機能(Wavegen)があります。下記の構成でI/Fボードの単体テストを行いました。

Wavegenで1kHzの正弦波を発生させて、1次側のチェック端子に入力し、2次側チェック端子の出力波形をオシロ機能で観察しました。

振幅1Vの正弦波入力に対して、振幅0.21Vの出力波形が観察されました。絶縁トランスの損失を無視すると、抵抗分圧から出力振幅は0~0.5Vになります。可変抵抗器のノブが中央で0.25Vになるため、おおよそ正しい値が出ていると判断でき、配線間違いは無いようです。

ネットワーク解析

USBオシロのAnalog Discovery 2 をPCから操作するWaveFormsソフトウェアにはネットワーク解析機能があります。RF信号は難しくてもAF信号なら周波数応答を解析できそうです。Analog Discovery 2 操作のリハビリも兼ねて、下記の構成でI/Fボードの単体テストを行いました。

まず、波形観測を行い、VRの調整と実験設定の確認を行いました。一次側入力は上記と同じく1Vとし、二次側出力は0.25Vに設定しました。

次に、ネットワーク解析のウィンドウを立ち上げて、周波数掃引を実行しました。

ゲインは0.25Vの設定通りに-12dB(= 20log(0.25V/1.0V))になっています。絶縁トランスとVRがLRフィルタを構成するためか、20kHzからゲインが持ち上がり、67kHzでピークを打って減衰します。この周辺帯域のノイズはサンプリングによるナイキスト周波数を超えていますが、今回はエイリアス防止フィルタを設けていないため、折り返して混入する可能性があります。
Spresense拡張ボードのマイクI/F回路図で言及した未実装のCRフィルタを実装する手がありますが、配線パターンが想定するチップ部品が小さ過ぎて無理そうです。IC-705のAF出力段にローパスフィルタがあることを期待することにします。

IC-705用APFの検討(7)SpresenseへのIIR型APFの実装Ⅱ

IIRフィルタ仕様のオンデマンド変更方法の実装(つづき)

前回(IC-705用APFの検討(6)SpresenseへのIIR型APFの実装)、キー入力リセットによってsetup関数を再起動することで、任意のフィルタ仕様をオンデマンドで再設定可能にしました。しかし、リセット方式では音途切れが発生し、キーボードからの設定も煩雑です。そこで、loop関数内でフィルタ仕様を変更可能にしました。

オンデマンド変更方法のloop関数への実装

オーディオ信号のマルチコア分散処理シーケンス

Spresense上に実装するオーディオ信号処理のブロック図を下記に再録します。

マルチコアの処理シーケンスを下記に示します。

Spresenseのオーディオ信号の入出力処理はMainCoreでのみ実行可能な制限があるとのこと。そこで、オーディオ信号の入出力処理他をMainCoreで処理し、フィルタ演算処理をSubCoreで処理する実装としています。これにより処理シーケンスは、MainCoreのloop関数内の処理シーケンス(左列)、MainCoreのDMA割り込みコールバック関数内の処理シーケンス(中列)、SubCoreのloop関数内のフィルタ演算処理シーケンス(右列)に分かれています。ブロッキングを行うコア間通信で各シーケンスの同期を取ります。Spresense Arduino ExampleのLPF実装例を踏襲していますが、背景を着色した処理ブロックはフィルタ仕様のオンデマンド変更のために追加した処理ブロックです。

左列のMainCoreのloop関数では、最初にWDT(Watch Dog Timer)をキックし、制限時間エラーのResetが発動しないようにします。
次に、コマンドシェル入力(USBシリアル通信のキーボード入力)をスキャンしてフィルタ仕様(中心周波数fcと周波数帯域幅fb)の変更イベントがあるかどうかを調べます。スキャンはノンブロッキングです。もしキーボード入力でオーディオ信号処理サイクルをブロックすると、WDTが発動するからです。変更イベントがある場合は、イベント種別に応じてフィルタ仕様をステップ更新します(詳細は次節参照)。
その後、SubCoreからのフィルタ演算結果の受信待機状態に進みます。コア間通信のMP.Recv関数はディフォルトでは受信するまで処理をブロックします。オーディオ信号処理サイクルに収まる時間内に受信できれば、WDTは発動しません。

中列のMainCoreの割り込みコールバック関数は、ADCでサンプリングしたPCMデータの所定の配列数のDMA転送が完了すると、割り込みによって自動的に処理を開始します。つまり、オーディオ信号のサイクル処理の起点になります。
DMA転送されたPCMデータ配列のサイズをチェックし、Request構造体にPCMデータ配列とサイズを格納します。Request構造体は、SubCoreにフィルタ演算処理をリクエストする際に送付するデータ構造体であり、以下の構造を定義しました。

// MainCoreからSubCoreへ送信するデータ構造体
struct Request {
  void *buffer;  // PCMデータ配列へのポインタ
  int  sample;   // サンプル数
  float fc;      // APFの中心周波数
  float fb;      // APFの周波数帯域幅 
};

ここで、PCMデータ配列と同時に、フィルタ仕様(中心周波数fcと周波数帯域幅fb)もSubCoreに送信するようにしました。これにより、フィルタ仕様のオンデマンド変更に対応できます。
MP.Send関数でRequest構造体を送信して割り込みコールバック関数を終了します。MP.Send関数はキューにRequest構造体を登録して即座に終了するため、コールバック関数の終了をブロックしません。
なお、コールバック関数の中でフィルタ演算結果のDAC出力まで実行した方が、一連のオーディオ信号処理の見通しが良い気がします。しかし、それではSubCoreからのフィルタ演算結果の受信待機状態になってしまい、コールバック関数の終了、すなわちをMainCoreの処理をブロックしてしまうため、SubCoreでフィルタ演算を並列に実行する意味が無くなってしまいます。前述の通りMainCoreのLoop関数の中で受信待機状態になるようにすれば、MainCoreのLoop関数の中の他の処理を実行する機会がオーディオ信号処理サイクル(16msec)毎に訪れます。

右列のSubCoreのloop関数では、MainCoreからのRequest構造体を受信すると、まず、フィルタ仕様に変更があるかどうかを調べます。変更がある場合は、IIRフィルタ係数更新関数によってフィルタを再設計します。これにより、最速16msecサイクルの頻度でフィルタをオンデマンド変更可能になります。
次に、PCMデータ配列をリングバッファに複写して、CMSIS-DSPライブラリのIIRフィルタ演算関数arm_biquad_cascade_df2T_f32をコールします。PCMデータ配列をリングバッファに都度複写するのは無駄な気がしますが、フィルタ更新の有無等によってサイクル毎の処理時間には増減があるため、そのばらつきを吸収するためかと思われます。
最後に、フィルタ演算結果をResult構造体に格納してMainCoreに返送し、Loopサイクルを終了します。その後、最初のRequest構造体の受信待機状態に遷移します。MP.Recv関数のブロッキングにより、SubCoreのLoop関数はオーディオ信号処理サイクル(16msec)に同期しています。

左列のMainCoreのLoop関数に戻り、受信したSubCoreからのフィルタ演算結果Result構造体をOutputMixerに出力してDACから再生し、Loopサイクルを終了します。その後、最初のコマンドシェル入力のスキャンに戻って実行し、Result構造体の受信待機状態に遷移します。

本システムは、プロセッサコアを2つだけ使用する単純な構成です。しかし、そこに割り込み処理を伴う信号処理サイクルが絡むと、どの関数がノンブロッキングでどの関数が処理をブロックするかを把握していないと、コア間の同期の追跡が難しくなることが分かります。

オンデマンド変更用コマンドシェル

フィルタ特性をオンデマンドで変更するために、loop関数内に以下のコマンドシェルを実装しました。

void disp_command_in_loop(){
  printf("MainAPF> ***********************\n");
  printf("MainAPF> **  command_in_loop  **\n");
  printf("MainAPF> ***********************\n");
  printf("MainAPF> (h) help \n");
  printf("MainAPF> (i)(d) inclease or declease fc = %f \n", g_fc);
  printf("MainAPF> (w)(n) widen or narrow fb = %f \n", g_fb);
  printf("MainAPF> (r) reset board \n");
}

loop関数内でキーボードからの数値入力待ちにすると、オーディオ信号出力処理をブロックしてしまいます。キーボードからノンブロッキングでシリアル通信を受け付けるために、受信するのは1文字コマンドのみとしました。

コマンド「i/d」は、中心周波数fcの増減を指示します。当座の増減幅は10Hzとしました。100Hz、・・・、590Hz、600Hz、610Hz、・・・、1kHzの刻みで変更できます。

コマンド「w/n」は、周波数帯域幅fbの拡大縮小を指示します。当座の拡大倍率は2、縮小倍率は0.5としました。3Hz、・・・、50Hz、100Hz、200Hz、・・・、800Hzの刻みで変更できます。

キーボードからのコマンド入力方式では増減幅常数と倍率常数でフィルタ仕様を変更することになりますが、これら常数をsetup関数で変更できるようにすれば任意のフィルタ仕様への変更が実現できます。

実験結果

前回と同様に、マイクとアンプスピーカをSpresenseボードにつなげ、マイクからコツコツ音を拾う実験を行いました。

キーボードからのフィルタ仕様のオンデマンド変更によって、コツコツ音の音調が大きく変化することが確認できました。

次は、音調変化の官能検査実験を可視化することが課題です。SGから信号を入力して、ネットワーク解析を行いたいところです。