Hilbert Transform

From Hamsterworks Wiki!

Revision as of 09:28, 31 July 2019 by User (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

This FPGA Project was finished in July 2019

I learnt a little bit about the Hilbert Transform used in DSP - it 'magically' generates a quadrature signal from a pure real signal, by removing negative frequencies.

This is useful for things like envelope detection and frequency tracking.

The math is quite interesting, well worth learning if you have a chance.

Hilbert.png

Source Files

This is a very small filter - only 15 points in the kernel (8 taps).

It also isn't very efficient - you can easily use half the multipliers.

Also, there is the possibility of exceeding the range of the output, as very large sudden changes in the real_in input can cause a very large change in the imag_out output.

hibert_transformer.vhd

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity hilbert_transformer is
    Port ( clk      : in  STD_LOGIC;
           real_in  : in  STD_LOGIC_VECTOR (9 downto 0);
           real_out : out  STD_LOGIC_VECTOR (10 downto 0) := (others => '0');
           imag_out : out  STD_LOGIC_VECTOR (10 downto 0) := (others => '0'));
end hilbert_transformer;

architecture Behavioral of hilbert_transformer is
   -- Constants are 2/(n * pi) * 512, for n of -7,-5,-3,-1,1,3,5,7
   constant kernel0  : signed(real_in'length-1 downto 0) := to_signed( -47, real_in'length);
   constant kernel2  : signed(real_in'length-1 downto 0) := to_signed( -66, real_in'length);
   constant kernel4  : signed(real_in'length-1 downto 0) := to_signed(-109, real_in'length);
   constant kernel6  : signed(real_in'length-1 downto 0) := to_signed(-326, real_in'length);
   constant kernel8  : signed(real_in'length-1 downto 0) := to_signed( 326, real_in'length);
   constant kernel10 : signed(real_in'length-1 downto 0) := to_signed( 109, real_in'length);
   constant kernel12 : signed(real_in'length-1 downto 0) := to_signed(  66, real_in'length);
   constant kernel14 : signed(real_in'length-1 downto 0) := to_signed(  47, real_in'length);

   type a_delay is array (0 to 14) of signed(real_in'high downto 0);

   signal delay : a_delay := (others => (others => '0'));
   signal tap0  : signed(real_in'length+kernel0'length-1  downto 0) := (others => '0');
   signal tap2  : signed(real_in'length+kernel2'length-1  downto 0) := (others => '0');
   signal tap4  : signed(real_in'length+kernel4'length-1  downto 0) := (others => '0');
   signal tap6  : signed(real_in'length+kernel6'length-1  downto 0) := (others => '0');
   signal tap8  : signed(real_in'length+kernel8'length-1  downto 0) := (others => '0');
   signal tap10 : signed(real_in'length+kernel10'length-1 downto 0) := (others => '0');
   signal tap12 : signed(real_in'length+kernel12'length-1 downto 0) := (others => '0');
   signal tap14 : signed(real_in'length+kernel14'length-1 downto 0) := (others => '0');
   
begin

process(clk) 
   variable imag_tmp : signed(real_in'length*2-1 downto 0);
   begin
      if   rising_edge(clk) then 
         
         real_out <= std_logic_vector(resize(delay(8),real_out'length));  -- deliberatly advanced by one due to latency
         
         imag_tmp := tap0 + tap2  + tap4  + tap6 
                   + tap8 + tap10 + tap12 + tap14;
         imag_out <= std_logic_vector(imag_tmp(imag_tmp'high downto imag_tmp'high-imag_out'high));
         
         tap0  <= delay(0)  * kernel0;
         tap2  <= delay(2)  * kernel2;
         tap4  <= delay(4)  * kernel4;
         tap6  <= delay(6)  * kernel6;
         tap8  <= delay(8)  * kernel8;
         tap10 <= delay(10) * kernel10;
         tap12 <= delay(12) * kernel12;
         tap14 <= delay(14) * kernel14;
         
         -- Update the delay line 
         delay(1 to 14) <= delay(0 to 13) ;
         delay(0)       <= signed(real_in);
      end if;
   end process;
end Behavioral;

tb_hibert_transformer.vhd

Testbench for the above file

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
ENTITY tb_hilbert_transformer IS
END tb_hilbert_transformer;
 
ARCHITECTURE behavior OF tb_hilbert_transformer IS 
 
    -- Component Declaration for the Unit Under Test (UUT)
 
    COMPONENT hilbert_transformer
    PORT(
         clk : IN  std_logic;
         real_in : IN  std_logic_vector(9 downto 0);
         real_out : OUT  std_logic_vector(10 downto 0);
         imag_out : OUT  std_logic_vector(10 downto 0)
        );
    END COMPONENT;
    
   signal clk : std_logic := '0';
   signal real_in : std_logic_vector(9 downto 0) := (others => '0');
   signal real_out : std_logic_vector(10 downto 0);
   signal imag_out : std_logic_vector(10 downto 0);
   constant clk_period : time := 10 ns;
 
BEGIN
 
   uut: hilbert_transformer PORT MAP (
          clk => clk,
          real_in => real_in,
          real_out => real_out,
          imag_out => imag_out
        );

   -- Clock process definitions
   clk_process :process
   begin
      clk <= '0';
      wait for clk_period/2;
      clk <= '1';
      wait for clk_period/2;
   end process;
 

   -- Stimulus process
   stim_proc: process
   begin      
      -- hold reset state for 100 ns.
      wait for 100 ns;   
      wait for clk_period*10;
      
      real_in <= std_logic_vector(to_signed(   1,10));
      wait for 10 ns;    

      real_in <= std_logic_vector(to_signed(   0,10));
      wait for 10 ns;   
      real_in <= std_logic_vector(to_signed( 71,10));
      wait for 10 ns;   
      real_in <= std_logic_vector(to_signed( 100,10));
      wait for 10 ns;   
      real_in <= std_logic_vector(to_signed( 71,10));
      wait for 10 ns;   
      real_in <= std_logic_vector(to_signed(   0,10));
      wait for 10 ns;   
      real_in <= std_logic_vector(to_signed(-71,10));
      wait for 10 ns;   
      real_in <= std_logic_vector(to_signed(-100,10));
      wait for 10 ns;   
      real_in <= std_logic_vector(to_signed(-71,10));
      wait for 10 ns;   

      for i in 1 to 20 loop
         real_in <= std_logic_vector(to_signed(   0,10));
         wait for 10 ns;   
         real_in <= std_logic_vector(to_signed( 141,10));
         wait for 10 ns;   
         real_in <= std_logic_vector(to_signed( 200,10));
         wait for 10 ns;   
         real_in <= std_logic_vector(to_signed( 141,10));
         wait for 10 ns;   
         real_in <= std_logic_vector(to_signed(   0,10));
         wait for 10 ns;   
         real_in <= std_logic_vector(to_signed(-141,10));
         wait for 10 ns;   
         real_in <= std_logic_vector(to_signed(-200,10));
         wait for 10 ns;   
         real_in <= std_logic_vector(to_signed(-141,10));
         wait for 10 ns;   
      end loop;
      real_in <= std_logic_vector(to_unsigned(  0,10));
      wait;
   end process;
END;

Personal tools