SDR using dsPIC for SSB MODE

FM,AMモードはまだ納得できる状態ではないが,SSBに関しては問題なさそうなので,ファームウェア(ソースコード)などを公開した.

Source code,  Circuit diagram

IIR, FIR フィルタの特性を変えるためのツールも置いてある.

dsPICの内部処理は以下のとおり.

SSB_demod.jpg

原理的にはこれまで通り,複素係数フィルタで正(または負)の周波数成分をカットするフィルタ式.
コードを書いていて思ったのだが実際の処理内容はPSNと酷似している.なので,複素係数フィルタをAF PSN とみなしてもよいと思う.
hilbert 変換自体が負の周波数をカットするハイパスフィルタだし,一方,複素係数フィルタの虚数部は hilbert 変換器と同じものになっていることを考えれば.本質的に PSN方式 もフィルタ方式も同じということだろう(もちろん実現手法の違いはある).

FM demodulation (SDR using dsPIC)

FMの復調機能の追加を行った.
AMの復調ができているので,それを少しモディファイするだけでOK.
つまりAMは振幅を求めるがFMは位相を求めて微分すればよいので,下図のような構成になる.

FM_demod.jpg

tan の逆関数は安直にテーブル参照にした.math ライブラリのatanやatan2関数は処理が重くて使えなかった.
今のところSGからのFM信号はうまく復調できている.リミッタを深く効かせた通常のFM復調と違ってノイズは多め.この方式はI,Q信号の瞬時値から位相を計算するため,リミッタが使えないから仕方ない.

もう少しテストが必要かもしれないが,とりあえず SSB(USB/LSB),CW,AM,FM モードの受信が可能になった.

AM demodulation (SDR using dsPIC) (2)

Direct Conversion 方式でAM復調がうまくいった… 確かにほぼうまくいったのだが,問題があることがわかった.
ローカル信号のリークが周波数によっては予想以上に大きくなるところがあり,アンプをDC結合したためにリークの直流成分でアンプが飽和してしまい,その周波数では受信不能となる.飽和しないレベルであれば動作はするが,ダイナミックレンジが狭くなる.
直前の記事で,「直交ミキサでベースバンドに変換するDC方式はなかなか難しい」と書いた通りになった.

まず,アンプをAC結合に戻し直流による飽和が起こらないようにした.

次に,ローカル発振(VFO)周波数 ωc を受信周波数 ωm より ωi  低くして,受信したAM信号の周波数を ωi に変換し,これを増幅した後AD変換してdsPIC に取り込むようにした.
今回 ωi = 2 π 12.5kHz としている.

AM_demod2.jpg

その後信号処理で,上図に示すようにベースバンドに変換している.
(乗算器が4個必要なのは,複素数同士の掛け算のため)
LPF以降は前回と同じ.

dsPIC33FJの能力の限界に近い処理量のようだが,AM受信自体は前回と同様問題なくできている様子.受信周波数を500kHzから54MHzまで変えてみたが,受信できなくなる周波数は無いようので,しばらくこれで動作確認をしてみる.

AM demodulation (SDR using dsPIC)

dsPICによるSDRでSSBやCWはうまく受信できている.これに加えてAMモードも実装したい.AM受信はSSBモードでもできるが,キャリア周波数を完全に合わせないと受信信号のピッチが変わってしまう.会話なら問題も少ないが音楽は不協和音になってしまって聞きづらいので,やはりAMモードが必要だと思う.

AMの復調をするには,下図のように,IchとQchをそれぞれ2乗して加算したあと平方根をとればよい.

AM_demod

理屈は簡単だが,直交ミキサでベースバンドに変換するDC方式だと,キャリア周波数が0Hz(直流)に変換されるので実際にはなかなか難しい.なので多くは,キャリア周波数を0Hzではなく10kHzなどに変換し,それを信号処理してAM復調をする.

ただ今回使用しているdsPICの性能にあまり余裕がないので,上図のとおりDC方式にする.

LPFの出力は次式となる.eq1_2

AMの場合,A + m(t) > 0 なので,

eq3

これの直流分 A/2 をカットすれば復調完了で,式(3)にはキャリア周波数とローカル周波数の差の要素は含まれないので,チューニングのずれで復調される信号ピッチが変わるということはない.

それはいいのだが,実際はミキサの出力にはローカル信号のリークに起因する直流成分が加算される.DBMにしてもリークを0にすることは現実的には困難で,また厄介なことにリークの量は一定ではなく,周波数によって変化する.

リークは直流として生じるので問題なさそうに思えるが,そうではない.
リーク成分を vi, vq として,それらを考慮した場合のLPFの出力を I’ , Q’ とすれば,

eq4_5

このとき,出力は,

eq6

となる.面倒なのでこれ以上の式の展開はしないが,明らかに出力に歪が生じてしまう.チューニングがあっている(ωc=ωm)のときは歪だけだが,チューニングがずれると式(6)の第3,4 項によってビート成分とその高調波も出てくる.

結局,リーク成分vi, vq を除去することが必須であることがわかる.ただ厳密にはそれは不可能なので,条件をつけて譲歩するしかない.

その条件は,「厳密にチューニングがあう(ωc=ωm)ことはない」ということ.

もし ωc=ωm だったら,

eq7_8

となるが,残念なことに第1項と第3項はどちらも直流で分離不可能.

仮に式(7),(8)から直流分を除去すると次式となり,

eq9_10

出力は次式となる.

eq11

元の信号を全波整流したものとなってしまって復調できない
(  m(t) が正負の値をとることに注意).
AMの場合,A+m(t)が常に>0.そうなるようにAが足されていることに大きな意味がある.

 

…ということで,ωc=ωmのときはあきらめて,厳密にそうなることは稀だということに期待する.

ωc≠ωmならば,式(1), (2)をみればわかるように,I, Q は,周波数差|ωc – ωm|に相当する周期をもつ交流となる.リーク込みのLPF出力は式(4), (5) だが,これの時間平均をとれば,I,Qはともに0に収束し最終的に vi, vq のみとなって,リークによる直流分が得られる.得られた値を信号処理時にI, Q信号から引けばよい.

今回,平均処理を2~3秒間の移動平均としてみる.例えば,平均時間2秒なら,1/2=0.5Hz以上周波数がずれていればよい.AMで0.5Hz以内にゼロインするのは難しいと思うので実用的にはOKだろう.たまたま合ってしまう可能性はゼロではないけど,受信信号とローカル信号は全く無相関なのでその状態が長く続くとも思えない…(と希望的観測しておこう).最終的に約2.6秒とした.

ここまで書いて気付いたが,これってカットオフ周波数の非常に低いHPFで直流をカットしているのと同じことだった.ただカットオフ周波数が0.5Hzとかになるので,容量がものすごく大きなCが要る(10000uFとか)し,カットオフ周波数の調整も面倒そうなので,信号処理でやる意味はありそう.

実際の動作の様子はこちら( https://youtu.be/txrccQwB3Pc )

チューニングのずれによるピッチの変化がなくなりAMらしくなった.少しビートが残っている感じがするが,まあいいんじゃないかと思う.むしろチューニングの目安となっていいかもしれない.

ついでにAGC処理も追加しておいた.
ちなみにスピーカのすぐ上に見える回路はアナログでAGCをやろうとした残骸.
信号処理でAGCがうまくいったので使っていない.
また,直流分も通すためにミキサからAD変換までの回路をDC結合に変更した.

そろそろ基板におこせる段階かな.
その前にFMもやってみようか…

SDR using dsPIC33FJ64GP802 (2)

レシーバをテストしていて気づいたのだが,RF入力のレベルが小さくなっていくと,あるレベル(しきい値)でノイズゲートが掛かったようにいきなり無音となる.さらによく調べてみたら,しきい値付近のレベルだと信号やノイズがとぎれとぎれになってブツブツという音がする.どうもAD変換時の量子化が原因のようだ.しきい値はAD変換の量子化レベルであって,dsPICの場合,12bitなので,3.3V/4096=805 uV.
これ以下の変化は検知できず,一定値の信号として処理され結果として無音になる.また,これを超えるか超えないか微妙なレベルだと変なノイズが出る.

量子化レベル以下の変化を取り込む方法として,よく知られているものとしてはディザー信号を重畳させる方法がある.

つまり,量子化レベルより十分大きな振幅を持つ信号(ディザー信号)を目的信号に加え,量子化レベルを超えさせる.ディザー信号は,目的信号と周波数帯域が重ならないスペクトルを持つ信号とする(一般的には,目的信号より高い周波数領域に成分を持つノイズなど).

目的信号+ディザー信号をAD変換で取り込んだら,信号処理(フィルタ)でディザー信号の成分をカットすればよい.

数値計算で確認してみた結果を以下に.

入力信号は振幅0.1,周波数800Hzとする.

nodither
+/-0.5を量子化の区切りとする.
上段のグラフのように入力信号の振幅が0.1の場合,AD変換器で量子化されると中段のグラフのように一定値0となってしまい入力信号の情報は失われる.当然スペクトラムも下段のとおり全域で0となる.

次に,振幅50,周波数6.25kHzの正弦波をディザー信号として加えてみる.

dither

ディザー信号を入力信号に重畳すれば,量子化されても入力信号の情報は失われないことが
スペクトルをみればわかる.
不要な6.25kHzは,デシメーションフィルタがカットしてくれるので問題ない.

 

もう一つの例として,入力信号が量子化レベルをぎりぎり超える程度の振幅(0.55とした)を持つケースを以下に示す.

nodither2
入力信号は取り込めているが高調波成分が生じている.量子化された波形をみれば当然.

 

ディザー信号ありの場合.

dither2

こちらの方が,高調波成分が少なく,低ひずみで信号が取り込めているのがわかる.

 

実回路で確認.
下図のとおり,DACのLポートから6.25kHzを出力し,オペアンプの入力に加えた.

dither_circuit1

とりあえず,空中配線で…

20200620_225906

信号レベルが低くても無音となることはなく,変なノイズも発生しなくなった.

 

SDR using dsPIC33FJ64GP802

dsPICで受信用のAF PSN試作して動作確認まで行っていたが,それを使って実際に受信機を組んでみた.

Source code ,  Circuit diagram

動作の様子 Youtube

今のところ,AGCはもちろん,RFアンプも入力のフィルタもないが,それなりにうまく動作しているようなので,きちんとした受信機に仕上げてみようと思う.

AF PSN for DC Reciever

以前,SSB送信用のAF PSN をdsPICで製作した.
その後,受信用もできないかと気になりつつも,ずいぶん時間が経ってしまった.
もちろん,dsPIC33FJ64GP802にこだわらなければ何とでもなるが,
このdsPIC  1個でできれば面白いと思う.

よく知られているとおり,PSN受信機の構成は以下のようになる.

r01.jpg

AF PSNの部分をdsPIC33FJ64GP802 1個で構成したいのだが,
ADCが12bitであることと、2ch同時サンプルができないことが課題.
特に,12bitでは受信用としてダイナミックレンジが満足できない気がして,
いま一つやる気が起きなかったが,ようやく,「とりあえずやってみるか」
という気分になってきた.

受信用も,送信用のAF PSN同様,複素係数フィルタを使う.
r02.jpg

解析信号でブロック図を書くと以下のとおりで,送信用AF PSNもそうだが,
本質的にはフィルタ式と同じ.r03.jpg

 

結論から言えば,以下のような構成で,受信用AF PSNを実現できた.
r04.jpg

r06.jpg

r07b.jpg

r08b.jpg

一応設計通り動作している様子.
実際に受信機として使い物になるかどうかは,後日評価したい.

 

以下は詳細.

1.AD変換
送信用は,入力信号がほぼ話者の声だけと考えられるので,
アンチエイリアスフィルタが無くてもほぼ問題なかったが
受信用はそういうわけにはいかず,AD変換前にナイキスト周波数以上の成分を
十分に除去しなければならない.
カットオフ周波数がナイキスト周波数(1/2サンプリング周波数)に近い
シャープな切れ味のLPFを使って,
サンプリング周波数をあまり上げることなく十分な帯域をとりたいところだが,
そのようなフィルタは,カットオフ付近の移相量が大きく素子感度が高いため
温度変化など環境変化やその他様々な要因で特性が変化しやすい.
一番の問題は,そのようなフィルタが2個(I, Q)必要なことと,
それらが常に同じ特性でなければならないこと.
したがって,素子感度を下げ2個フィルタの特性のばらつきをできるだけ抑えたい.

そのためには,カットオフ付近で位相がなだらかに変化するフィルタが望ましいので,
ベッセルフィルタがよいと思う.
当然,カットオフ(振幅)特性もなだらかになり,シャープな切れ味にはならないので,
アンチエイリアスの能力は低下する.
そこでサンプリング周波数をできるだけ高くする.今回は100kHzとした.
また,dsPIC33FJ64GP802は48MHzのオーバークロックで動作させる.

I,Q 2ch同時サンプルはできず,2μs程度の遅延がありサンプリングタイミングがずれる.
2μsは,例えば3kHzにおいては,位相で 2.16deg に相当する.
これは無視できるレベルではないので,後段の複素係数FIRフィルタで
この遅延を補正することにした.

 

2.デシメーション用 IIRフィルタ
100kHzサンプリングですべて処理できればいいのだが,
dsPIC33FJ64GP802の性能では難しそうなので,
サンプリング周波数を1/8(12.5kHz)に下げる.
そのためには,後段の複素係数フィルタのナイキスト周波数(6.25kHz)以上の成分を
十分に除去しなければならない.
(このIIRフィルタのサンプリング周波数は100kHz)
I,Q 2chのフィルタが必要だが,ディジタルフィルタなので,
特性に違いが生じたり変化する心配はない.
できるだけシャープなカットオフ特性をもったLPFを使いたいので,
今回は,4次Elliptic LPF にしてみた.
IIRフィルタは設計が面倒だが,動作時はFIRより少ない計算量で済む.

2次IIRフィルタは以下のような構成になる.4次はこれを2段直列に接続する.

r05.jpg
kは本来不要なパラメータだが(k=1でよい),dsPICのように固定小数点演算の場合は,
計算途中で結果がオーバーフローしないようにk>1とする必要がある.
今回は k=4 とする.

IIRフィルタの設計には,GNU Octave (フリーウェア)を使った.
設計用のスクリプト ( IIR_ellip_LPF_design.m ) を実行すれば,
b0/k, b1/k, b2/k, a1/k, a2/k, k を定義するヘッダファイルが生成される.
signal パッケージを使うので,Octaveを起動したらコマンドラインで,

pkg load signal

とタイプしておく必要がある.

次に,dsPICでIIRフィルタを実行するソースコード(dsPIC33,DSPライブラリ使用)を
以下に記載しておく.

//— IIR ———————————–
//4th IIR Elliptic LPF coeff.
//fs=100000[Hz],  fc=3000[Hz]
//Ripple=1[dB],  Att=70[dB]

fractional _XDATA(2) IIR_coef0[] =
//{b0/k, b1/k, b2/k, a1/k, a2/k, log2(k) }
{ 53, -26, 53, 15311, -7199,  2};

fractional _XDATA(2) IIR_coef1[] =
//{b0/k, b1/k, b2/k, a1/k, a2/k, log2(k) }
{720, -1161, 720, 15706, -7794,  2};

fractional _YBSS(2) Z_Re_0[5];
fractional _YBSS(2) Z_Re_1[5];
fractional x, y;

void IIR_Ellip_LPF(void)
{
//– 1st stage——————————————
Z_Re_0[0]=x;
y=VectorDotProduct(5, &Z_Re_0[0], &IIR_coef0[0]);
y<<=IIR_coef0[5];
Z_Re_0[2]=Z_Re_0[1];  Z_Re_0[1]=Z_Re_0[0];
Z_Re_0[4]=Z_Re_0[3];  Z_Re_0[3]=y;

//– 2nd stage——————————————
Z_Re_1[0]=y;
y=VectorDotProduct(5, &Z_Re_1[0], &IIR_coef1[0]);
y<<=IIR_coef1[5];
Z_Re_1[2]=Z_Re_1[1];  Z_Re_1[1]=Z_Re_1[0];
Z_Re_1[4]=Z_Re_1[3];  Z_Re_1[3]=y;
//output:  y;
}

DSPライブラリ関数にはIIRフィルタもあるが,処理速度が遅かったので
VectorDotProduct 関数を使って自作してみた.
これはI chのみなので,もう一組IIRフィルタが必要となる.

伝達特性(設計値)は,以下のとおり
(横軸はナイキスト周波数50kHzで正規化)r09.jpg

 

3.複素係数FIRフィルタ(PSN)
サンプリング周波数とタップ数が違うだけで送信用と同じ.
今回128タップとしている(これが限界).

FIRの係数を求める際,AD変換の遅延を補正するよう考慮している.
具体的には,遅延をTとして Im側にのみ周波数特性にexp(- j2πf T) を掛けている.
 Source code,   Circuit diagram, and  Octave Script for filter design