« ホワイトノイズ発生器 | メイン | dsPICでSSBトランシーバー(製作開始) »

2020年6月13日 (土)

dsPICでSSBトランシーバー(SSBミキサー)

カテゴリ<SDR>

dsPIC33FJにて、AD変換、デジタルLPF、DA変換の基本動作が出来るようになりましたので、このデジタルLPFを音声源信号のBPFに変え、その後ろにサブキャリアによるミキサーを追加する事にします。

サブキャリアの周波数は自由に決められる訳ではなく、ADCのサンプリング周波数に依存します。 サブキャリアの最高周波数は、サンプリング周波数の1/4で、これより1/2づつ低い周波数、1/8とか、1/16の周波数になります。

今回のADCのサンプリング周波数は39.0625KHzですから、サブキャリアは、その1/4の9.765625KHzとなります。 サブキャリアとしては、10KHzとか12KHzのようなキリの良い周波数にしたくても、dsPICのクロック条件などにより、このような半端な周波数になってしまいます。 しかし、今では、Hz単位で任意の周波数を発振させられるDDSがありますので、中途半端なサブキャリアでも、高周波IFに変換する際、キリの良い周波数に変換できますから、支障は生じません。

まず、ミキサーの前に、SSB信号として必要な音声帯域のフィルターを用意します。 今回は61TAPの200-2800HzのBPFを用意しました。

Bpf2002800

上は、ホワイトノイズを入力した時の、BPFの様子です。低域のキレが良くありませんが、最終的に、SSB信号を取り出せるようになったら、TAP数や遮断周波数をトリミングしてみるつもりです。

この後に、ミキサーを繋ぎますが、まず、サブキャリアの信号を作らねばなりません。 サブキャリアはADCのサンプリング周波数の1/4としますので、Timer3のタイミングでADCが出力するたびに、1/4サイクル分のサイン信号をADC出力に掛け算してやれば良い訳です。

一番簡単な4分割のサイン信号は{sin0度、sin90度、 sin180度、sin270度}で、これを順番に掛け算する事になります。 この数値を実数で表すと{0, 1, 0, -1}であり、HEXで表すと{0x0000, 0x7FFF、0x0000, 0x8000}となります。 この4つのデータをリング状にして、ADCがデータ出力する都度、順番に掛け算をさせる為に、リングメモリーが必要になります。 dsPIC33Fの中には、リングメモリーを2つ作る事ができますが、これは、ミキサーの前のBPFと、ミキサー後にUSBもしくはLSBを切り取るBPFで使いますので、ミキサーでは使えません。 そこで、このリングメモリはアセンブラで直接作っています。

1khz_mixing_2

1khz_mixing_wave

上のスペクトルは、1KHzの音声信号とサブキャリアをミキシングした時の、ものです。 本来はサブキャリのレベルはゼロにならなければなりませんが、ADCに直流成分が含まれており、これが為に、キャリア漏れの現象が生じているものです。 ADC入力に与える直流バイアスを調整する、多回転可変抵抗で、これをキャンセルするポイントを見つける事はできますが、かなりクリチカルです。 デジタル処理だから、調整箇所は無いと思っていましたが、これは誤算でした。 DSB信号から一方のサイドバンドだけを切り取るとき、サブキャリアは、問題ないレベルまで減少することを期待する事にします。

左上の波形は、上が、ADCの入力である1KHzです。下はサブキャリアで変調されたDSB信号です。

10khz_mixing

上のスペクトルは、ホワイトノイズを変調した時のDSB信号です。 ミキサーの前にある61TAPのBPF(ファイル名はLPFですが、実体はBPFです)の特性が、サブキャリアを中心に両サイドに広がっています。

ここまでのソフトは以下です。

SSB_generator_1.cをダウンロード

float_Tap2800LPF.hをダウンロード

FIRフィルターのDSPアセンブラの意味を以下に示します。

//FIR LPF処理
asm("mov _repeat3k_Num,W11"); //リピート回数をW11にセット
asm("mov _Tap_coef3k_adr,W10");//TAP係数格納の先頭番地をW10に格納
asm("mov W0,[W8++]");//W0のデータをW8が示すアドレスにコピーした後W8を+2する。

asm("clr A, [W8]+=2,W4,[W10]+=2,W5");//アキュムレーターAをクリアし、
          //W8が示すアドレスのデータをW4にコピー後、W8を+2する。
          //W10が示すアドレスのデータをW5にコピー後、W10を+2する。
asm("repeat W11");//次の行をW11の回数分繰り返す。
asm("mac W4 * W5, A, [W8]+=2, W4,[W10]+=2,W5");//W4とW5 を掛け算し、結果をアキュムレータ―Aに加算
           //W8が示すアドレスのデータをW4にコピー後、W8を+2する。
           //W10が示すアドレスのデータをW5にコピー後、W10を+2する。
asm("mac W4 * W5,A");//W4とW5 を掛け算し、結果をアキュムレータ―Aに加算
asm("sac A,W0");//アキュムレーターAのデータをまるめ処理してW0にコピー

asm("mov W0,W4");//W0のデータをW4にコピー

 

プログラム上は、このミキサーの後に、281 TAPのBPFを挿入し、新スプリアス対応可能なSSB信号を取り出します。 追加するBPFはPre BPFのTAP数とTAP係数が異なるだけで、そのプログラムの構成は同じですから、Pre BPFの部分をコピペして、必要な修正を行うだけで、楽勝と思っていましたが、落とし穴にはまり、一日棒に振った後、なんとか解決して出てきた出力のスペクトルは以下です。

10khlsbspectra1

約9.8KHzのキャリア周波数を持つ、LSB信号のみを取り出し成功です。 後は、Pre BPFとPost BPFの設定値を見直し、送信に耐える特性にした後、不動在庫の24MHzクリスタルを使い、クリスタルフィルターを作り、24MHzの中間周波数を作ります。

ここで、今回遭遇した落とし穴を紹介して置きます。  結論は、リングメモリーを制御するワーキングレジスタを指定した後は、そのレジスタを、他の用途に使ってはいけないという事でした。 今回、Xメモリー域とYメモリー域に、各1組、合計2組のリングメモリーを設定し、Xリングメモリーのコントロール用にW8を指定しました。 この時、Yメモリー域に配置した、TAP係数の読み出しの為にW10を使いました。 ここまでは良かったのですが、 Yメモリー域に配置したリングメモリーのコントロール用にW10を指定したら、Post BPFが動かなくなってしまいました。 結局、YリングメモリーのコントロールレジスタはW11とし、かつ、Xメモリーに配置したTAP係数の読み出しはW9を当てる事で解決しました。 (参考:W8,9はXメモリーのアドレス用、W10,11はYメモリーのアドレス用として設定されている)

今やろうとしている、この方法は、昔、リング変調器と455KHzのメカニカルフィルターでSSB信号を取り出していた時の構成を、最新のデジタル技術で出来るようにしたもので、この基本形は1999年くらいから提唱されていたようです。 「おじさん工房」が提案した、第4の方法のSSBジェネレーターの考え方も、これと同じですが、IFに変換した時のもう一方のイメージを、ラフなクリスタルフィルターでカットするというアイデアが振るっています。 そこで、次はこのクリスタルフィルターの検討になります。

LSB信号を取り出すまでのソフトは以下です。

SSB_generator_2.cをダウンロード

float_Tap-151_2800BPF.hをダウンロード

float_Tap8000BPF.hをダウンロード

このプログラムで、DSP処理にかかるFcYは463サイクルでした。 ただし、このインラインアセンブラの前後で、Cによる割り込み処理と、復帰処理がありますので、実際はこのサイクルより多くなっているはずです。 許容可能な最大サイクルは1024ですが、どのくらい余裕が有るかわ判りません。 従い、PREとPOSTのFIRフィルターのTAP数は合計で500くらいを目途に進める事にします。

 

中間周波数は25MHz付近とする事は決まっていますが、あいにく、25MHzのXtalが有りません。そこで、手持ちのXtalを使って、特性を取ってみる事にしました。

下は、特性測定用に用意したテスト回路図と、テスト治具です。

Xtal_filter_schema

Xtal_filter_testjig

そして、手動でSSGをスイープさせ、それをスペアナでMAX Holdした時のデータが以下の4枚です。 左上から順番に、16MHz, 20MHz,  24MHz  26MHzのデータとなります。

_re16mhz

_re20mhz

_re24mhz

_re26mhz

Act_re_24mhz

4つの周波数で一番適した特性は24MHzです。 そこで、このXtal Filterの特性に合致するように、第2局発(LO2)の周波数を決めたのが、左の画像です。

DSP内で作ったSSB信号のサブキャリアは9.766KHzでしたので、これを中心にして、LSB またはUSBの信号がフィルターの先頭のフラット部分にくるように配置すると、LO2の周波数は、24006.766KHzとなります。 そして、LO2により生じたイメージ信号は、グラフ右側の特性がディップした帯域にはまります。

この特性から、DSPは、LSB及びUSBを選択的に出力して、第1可変局発(LO1)とIFキャリア周波数23997KHzをMIXし、7MHz帯でLSBもしくはUSBのSSB信号を作り出す事ができます。

従い、DSPの機能は以下のブロックのような動作が実現できれば良いのですが、これが、トラブルの連続で、まだ実現出来ていません。 なんとか目途がついたら、紹介します。

Dsp_block

約1週間、格闘した結果、なんとか使えるSSBジェネレーターが出来ました。

PRE BPFもPOST BPFも251TAPとして、送信/受信の切り替え時、同じリングメモリーが使えるようにしました。 この状態で、USB信号を取り出した時のスペクトルは以下です。

Usbnoise251tapx2

Lsbout_tx

Usbout_tx

左上は、送信モードで1KHz変調のLSBを、右上は同じく1KHz変調のUSBを取り出した時のスペクトルです。

Lsbin_rx

Usbin_rx

左上は、サブキャリアに1KHzで変調されたLSBを復調した状態、右上は、同じくUSBを復調した状態です。

これまでで、一番、苦労したところは、送信/受信 および LSB/USBモードの切り替えでした。 切り替えSWを追加し、ソフトを弄り回しましたが、うまく動作しません。 結局、取った手段は、送信/受信とLSB/USBの初期設定をした上で、RESETをかけるという荒業で、なんとか使えるようになりました。 トランシーバーを構成するには、Si5351AのDDSを制御したり、周波数を表示したりするために、dsPICとは別に、メインのマイコンが必要です。 このメインマイコンからdsPICにRESETをかける事にします。  私の技量では、動作中のモード切替は不可と悟るまで1週間かかったというのが実態です。

モード切替以外にAGC電圧の取り出しを仕込みました。 出力されるDC信号は、高入力のとき、3Vが出力され、信号が次第に小さくなると、ゼロVに近づくというもので、DUALゲートのFETを使ったAGCアンプの制御電圧とは逆になります。 この電圧を利用して、Sメーター表示やAGC電圧を作るには、dsPIC側では荷が重すぎますので、これをPWMにてDA変換し、システムコントローラー側に送り、システムコントローラーは再度これをAD変換するという面倒な事を行いますが、Sメーターの振れ具合や、AGCのかかり具合を細かく調整するには、必要な処理になります。

また、システムコントローラーとなるPIC16F1938には、PWMの極性を反転する機能がありますので、処理が簡単です。ただし、以下の回路図にはまだ反映されていません。

以上の回路図 SSB_generator_3.pdfをダウンロード

ここまでのソフトです。

SSB_generator_3.cをダウンロード

float_Tap10kUSB_BPF.hをダウンロード

float_Tap10kLSB_BPF.hをダウンロード

float_Tap2800BPF.hをダウンロード

 

SSBジェネレーターが完成したので、今回のSSBトランシーバーの全体構想を練ってみました。 24MHzのクリスタルフィルターとその前後のミキサーはSDR-3を真似て、双方向回路にしてあります。 今後これをベースに少しづつ、開発を進めていく事にします。

Ssb_trx_dspic_1

 dsPICでSSBトランシーバー(制作開始)へ続く

INDEXに戻る