記事中のプログラムは,Arduino IDE 2.1.1, Board Manager esp32 ver. 2.0.9 で動作確認しています.
AM,FMの復調処理を加えて,クラシックなすべてのモードの復調ができるようにした.前の記事と重複する内容があるがあらためて記載する.
まず,デシメーションまでの構成は下図のとおり.

DeMod. のブロックに各モードの復調処理が入る.内容は以下のとおり.

SSBの復調は以下のようなコードとなる.(前の記事と同じ)
// USB
dsps_fir_f32(&sfirRe, Lch_in, Lch_out, BLOCK_SAMPLES/DOWN_SAMPLE);
dsps_fir_f32(&sfirIm, Rch_in, Rch_out, BLOCK_SAMPLES/DOWN_SAMPLE);
for(int i=0; i<BLOCK_SAMPLES/DOWN_SAMPLE; i++){
Lch_in[i] =Lch_out[i] - Rch_out[i];
}
// LSB
dsps_fir_f32(&sfirRe, Lch_in, Lch_out, BLOCK_SAMPLES/DOWN_SAMPLE);
dsps_fir_f32(&sfirIm, Rch_in, Rch_out, BLOCK_SAMPLES/DOWN_SAMPLE);
for(int i=0; i<BLOCK_SAMPLES/DOWN_SAMPLE; i++){
Lch_in[i] = Lch_out[i] + Rch_out[i];
}
複素係数フィルタをとおして,実部と虚部を加算するだけなので非常にシンプル.
AMの復調は以下のとおり.
// AM
dsps_fir_f32(&sfirAMRe, Lch_in, Lch_out, BLOCK_SAMPLES/DOWN_SAMPLE);
dsps_fir_f32(&sfirAMIm, Rch_in, Rch_out, BLOCK_SAMPLES/DOWN_SAMPLE);
for(int i=0; i<BLOCK_SAMPLES/DOWN_SAMPLE; i++){
Re=Lch_out[i];
Im=Rch_out[i];
float Demod_AM = sqrt(Re*Re+Im*Im);
Rch_in[i] = Demod_AM*0.003 + zDC*0.997; // DC component
zDC = Rch_in[i]; // DC component
Lch_in[i] = 3.0f*(Demod_AM - zDC);
}
複素係数フィルタの代わりに実部,虚部を同じ特性のLPFにとおし(デシメーションフィルタがあるのでこれはなくてもよい),その振幅 Demod_AM を求めている. Demod_AM にはキャリアに相当するDC成分が含まれているのでそれをカットし出力としている.
zDC は float のグローバル変数.
FMの復調は以下のとおり.
// FM
for(int i=0; i<BLOCK_SAMPLES/DOWN_SAMPLE; i++){
Re = Lch_in[i]*zRe + Rch_in[i]*zIm;
Im = Rch_in[i]*zRe - Lch_in[i]*zIm;
zRe = Lch_in[i];
zIm = Rch_in[i];
Lch_in[i] = 1e5 * atan2(Im, Re) * (float)(fsample/DOWN_SAMPLE);
}
zRE,zIm は,float のグローバル変数.
一般的には信号の位相を求めてそれを微分する.微分は1サンプル前のデータの位相との差をとればよい.その通りやればいいのだが「微分=ノイズ増長」というイメージがあってやりたくなかったので別の方法でFM復調をすることにした.
具体的には上の図にあるように,1サンプル前のデータの複素共役を掛けてその偏角を求める.複素数同士の乗算ではその偏角は加算されるが,複素共役をとった場合は偏角は差分( ≃ 微分)となる.これによって差分や除算なしにFMの復調処理ができた.一般的な方法と比べてどれほどの効果があるかは確認していない.