ネットワークスペクトラムアナライザー NSA−1

おじさん工房 TOP へ


2008/11/23
2009/01/12 スペクトラム測定例
2009/02/14 RBW=8Hz実現とAM復調追加


2008/11/23

アナログ部分を極力減らしたネットワークスペクトラムアナライザーをつくっています。 この計画は前からあり、プリント基板(写真の左下部分)もすでにつくってありました。 ずーっとほうってありましたが、やっと開始しました。 とはいっても前に作った基板を使わずデザインウェーブの付録FPGA基板です。 USB付きAVR(AT90USB162)を使ってみたかったからです。

ネットワークスペクトラムアナライザーとしては入ってきた信号を周波数変換する部分とそれをAD変換してDDCしPCに送る部分の2枚構成になります。

周波数変換する部分は、入力信号(0〜100MHz)をLO1(150〜250MHz)で周波数変換しIF(150MHz)にします。 AD変換(Fs=66.7MHz)でアンダーサンプリングし16.7MHzになり、さらにデジタル周波数変換したあとCICでサンプリング周波数を落としていきます(この辺はAPM−3と同じですね)。 その後PCでガウス窓関数をかけることでRBW帯域幅を決め、FFTしてスペクトラムを求めます。 普通のスペアナではRBW用のフィルター群を作るのが大変ですが、すべてデジタル処理ですませてしまおうというのが今回の計画です。 今回は0〜100MHzのネットワークスペクトラムアナライザーですが、150MHzのIFをつくるような高周波処理部分をつくればいくらでも周波数範囲は拡大できます。 ただあまり高い周波数になるとLOとしてYIGを使うなど難易度が上がります。

まず作ったのはAD変換以降の部分です。 この基板だけでも30MHzぐらいまでのスペアナになります。


■つくった基板

NSA-1 基板写真

基板の大きさは95mmx72mmです。 使っているのはXC3S250E(デザインウェーブ2007年7月号付録基板)+AT90USB162(右上のQFP32)+ADC12L080(右下のQFP32)です。 ADCは12ビット80MHzで、入力は差動なのでFB801でシングルエンド・差動変換をしています。


■閑話休題1■
AT90USB162のCDCを使いましたが、まず最初にループバック試験をしたところ文字落ちが激しく使い物になりませんでした。 そこで以下のように cdc_task.c の cdc_task 関数を変更しています。

      if (Uart_tx_ready())    //USART free ?
      {
         if (uart_usb_test_hit())   // Something received from the USB ?
         {
            uart_putchar(uart_usb_getchar());   // loop back USB to USART
            Led0_toggle();
         }
      }
オリジナルではUSBで受信したデータをすべてUARTに送り終わるまでUARTから受信しないようになっているのを1回に1文字だけUART送信するようにしています。 これでループバックしても文字落ちはなくなりました。


■閑話休題2■
AT90USB162からJTAGでFPGA(XC3S250E)をコンフィグするようにしています。 ただこの部分についてまとまった説明が見つからずちょっと試行錯誤しました。
まず見つけたXAPP188 (「スパルタン2をJTAGでコンフィグする方法」)の p.11 に示される手順ををそのままインプリしましたが全く動きません。 まさかスパルタン3でインストラクションが5ビットから6ビットに変更されているとは思ってもみませんでした。 あと、XC3S250Eでは CFG_IN の前に JPROGRAM が必要なようです。 これらはSVFファイルを作り中を調べてみてやっとわかりました。 ただSVFにかいてあるBYPASSとか、CFG_INを2回入れるとかは必要ないようです。
この部分で悩んでいる人がいるかもしれないのでJTAGでコンフィグするときの手順を書いておきます。

    1.RESETステート(TMS=1でTCKを5回)へ移動
    2.IDLEステートへ移動
    3.IR=JPROGRAM(001011)→IDLEステート
    4.IR=CFG_IN(000101)→IDLEステート
    5.SHIFT−DRステートへ移動
    6.bitファイルのMSBから順に最後まで送る(一番最後のビットを送るときはTMS=1にする)
    7.IDLEステートへ移動
    8.IR=JSTART(001100)→IDLEステート
    9.TMS=0でTCKを12回(IDLEステートのまま)
JTAGではIRとかDRはLSBから入出力しますが、bitファイルを送るときはMSBからと逆になることに気をつけないといけません。
JTAGの制御プログラムをテストする際は、まずRESETステートからSHIFT−DRステートに移動しIDCODEを読み出してみる(RESETするとIRはIDCODEになるので)。 次はIRにIDCODEを設定しIDCODEを読み出してみる。 この2つが正常にできればJTAG制御プログラムとして正常に動作していると言えます。


2009/01/12 スペクトラム測定例

試作している基板では66.7MHzの水晶発振器が手に入らなかったので暫定的に50MHzの水晶発振器をつけています。 この状態でスペクトラム測定ができるようになりました。 通常A/DしたものをそのままFFTするとそれほどRBWを小さくすることはできません。 今回の場合4KW分サンプリングしますので、ガウス窓を使った場合は、RBW=50MHz/4096*3.5=43KHzとなりますが、試作している基板では内部にDDCを持っていますのでサンプリング周波数をさげていくことによりRBWをいくらでも小さくすることができます。


■スペクトラム測定

NSA-1 スペアナ測定

これは、AT90USB162で4MHzを出力させたものをRBW=670Hzでスペクトラム解析したものです。 ノイズフロアーも−90dBぐらいと十分低く、キャリア近傍の変調成分をはっきり見ることができます。


2008/02/14 RBW=8Hz実現とAM復調追加

最大デシメーション率を4096とし、RBW=8Hzで測定できるようになりました。 またAM検波回路を組み込みましたのでAM放送を聴くことができるようになりました。


■TBSのキャリア近傍を見てみる

NSA-1 TBS測定

RBW=8Hzでキャリア近傍を見るとTBSはAMステレオ放送をしているようで25Hzのパイロット信号を見ることができます。


■水晶発振器のキャリア近傍ノイズ

NSA-1 水晶測定

水晶発振モジュールの近傍ノイズです。 モジュールの電源にはトランジスターで作ったノイズフィルタを入れてありますがまだハム(電源からのノイズ)成分の100Hzが見られます。 もともとこのスペアナを作ろうと思った動機のひとつが発振器のキャリア近傍ノイズの測定にありました。 かなり高級なスペアナでないと測定できない領域です。


■AM復調回路

最初、CoreGenのCORDICを使いましたが、ここだけで333スライス(全体の13%)も使ってしまいました。 どうも角度情報は出力しないようにしているのに内部的には角度計算をしているように見えます。 そこで簡単なCORDICを設計してみました。 これはちょっと手抜きをしたこともあり、124スライス(5%)ですみました。
entity am_det is 
  port (
    CLK     : in  std_logic;                       -- 
    ND      : in  std_logic;                       -- New Data
    I_IN    : in  std_logic_vector( 17 downto 0 ); -- I 信号
    Q_IN    : in  std_logic_vector( 17 downto 0 ); -- Q 信号
    SIG_OUT : out std_logic_vector( 17 downto 0 ); -- 出力データ
    OD      : out std_logic                        -- 出力データイネーブル
  );
end am_det;

architecture RTL of am_det is
  
  signal X : std_logic_vector( 17 downto 0 ); -- 
  signal Y : std_logic_vector( 17 downto 0 ); -- 
  signal N : std_logic_vector(  4 downto 0 ); -- 
  
  signal X1 : std_logic_vector( 17 downto 0 ); -- 1 bit シフト
  signal X2 : std_logic_vector( 17 downto 0 ); -- 2 bit シフト
  signal X4 : std_logic_vector( 17 downto 0 ); -- 4 bit シフト
  signal XN : std_logic_vector( 17 downto 0 ); -- N bit シフト
  
  signal Y1 : std_logic_vector( 17 downto 0 ); -- 1 bit シフト
  signal Y2 : std_logic_vector( 17 downto 0 ); -- 2 bit シフト
  signal Y4 : std_logic_vector( 17 downto 0 ); -- 4 bit シフト
  signal YN : std_logic_vector( 17 downto 0 ); -- N bit シフト
  
  begin
    
    X1 <= X when( N(0)='0' ) else
          X(17) & X(17) & X(16 downto 1);
    
    X2 <= X1 when( N(1)='0' ) else
          X1(17) & X1(17) & X1(17) & X1(16 downto 2);
    
    X4 <= X2 when( N(2)='0' ) else
          X2(17) & X2(17) & X2(17) & X2(17) & X2(17) & X2(16 downto 4);
    
    XN <= X4 when( N(3)='0' ) else
          X4(17) & X4(17) & X4(17) & X4(17) & X4(17) & X4(17) & X4(17) & X4(17) & X4(17) & X4(16 downto 8);
    
    Y1 <= Y when( N(0)='0' ) else
          Y(17) & Y(17) & Y(16 downto 1);
    
    Y2 <= Y1 when( N(1)='0' ) else
          Y1(17) & Y1(17) & Y1(17) & Y1(16 downto 2);
    
    Y4 <= Y2 when( N(2)='0' ) else
          Y2(17) & Y2(17) & Y2(17) & Y2(17) & Y2(17) & Y2(16 downto 4);
    
    YN <= Y4 when( N(3)='0' ) else
          Y4(17) & Y4(17) & Y4(17) & Y4(17) & Y4(17) & Y4(17) & Y4(17) & Y4(17) & Y4(17) & Y4(16 downto 8);
    
    ------------------------------------------------------------------
    --    CORDIC で絶対値を計算(偏角は計算しない)
    ------------------------------------------------------------------
    process( CLK ) begin
      if( rising_edge(CLK) ) then
        if( ND='1' ) then
          if( I_IN(17)='1' ) then X <= not I_IN; else X <= I_IN; end if; -- 本当は+1が必要
          if( Q_IN(17)='1' ) then Y <= not Q_IN; else Y <= Q_IN; end if;
          N <= (others =>'0');
          OD <= '0';
        elsif( N(4)='0' ) then
          if( Y(17)='0' ) then    -- Y >= 0
            X <= X + YN;
            Y <= Y - XN;
          else                    -- Y < 0
            X <= X - YN;
            Y <= Y + XN;
          end if;
          N <= N+1;
        elsif( N="10000" ) then
          SIG_OUT <= X;
          OD <= '1';
          N <= N+1;
        else
          OD <= '0';
        end if;
      end if;
    end process;
  end RTL;



inserted by FC2 system