Wiener Filtrenin VHDL ile Gerçeklenmesi

Wiener filtresi, gözlenen sinyalin içerisindeki istenmeyen gürültüyü azaltmak ve asıl (temiz) sinyali tahmin etmek için kullanılan doğrusal ve istatistiksel bir filtredir. Temel olarak, minimum ortalama kare hata ilkesine göre çalışır ve FIR (Finite Impulse Response) yapısını temel alır.

Bu filtre özellikle şuralarda etkilidir:

  • Sinyal ve gürültünün istatistiksel özellikleri biliniyorsa
  • Gürültü sabitse veya karakterize edilebiliyorsa
  • Eğitim verisiyle referans sinyal tahmin edilebiliyorsa

Gözlenen sinyal şu şekilde ifade edilir:

 x(n) = s(n) + n(n)

Burada:

  • x(n) : Ölçülen (gözlenen) sinyal
  • s(n): Asıl (temiz) sinyal
  • n(n): Gürültü bileşeni

Amaç: x(n)’den yola çıkarak s(n)’yi olabildiğince doğru şekilde tahmin etmektir.

Wiener filtresi FIR yapısındadır. Çıkış sinyali geçmiş giriş örneklerinin ağırlıklı toplamıdır:

 \hat{s}(n) = \sum_{k=0}^{M-1} w_k \cdot x(n-k)

Burada:

  • \hat{s}(n): Tahmin edilen sinyal
  • w_k​: Filtre katsayıları
  • M: Filtrenin derecesi (tap sayısı)

Filtrenin doğruluğu, çıkış ile gerçek sinyal arasındaki ortalama karesel hata (MSE) ile ölçülür:

 J = E\left[ (s(n) - \hat{s}(n))^2 \right]

Bu hata fonksiyonunu minimize eden katsayılar “optimal” olarak kabul edilir.

Optimal katsayılar şu denklemle hesaplanır:

 \mathbf{w}{\text{opt}} = \mathbf{R}{xx}^{-1} \cdot \mathbf{r}_{xd}

Burada:

  • \mathbf{R}_{xx}​: Giriş sinyalinin otokorelasyon matrisi
  • \mathbf{r}_{xd}​​: Giriş sinyali ile referans (temiz) sinyalin çapraz korelasyon vektörü
  • \mathbf{w}_{\text{opt}}​​: Optimal filtre katsayıları

Aşağıda FIR filtre olarak katsayılaı hesaplanmış Wiener Filtrenin VHDL kodlarını görebilirsiniz.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;
use IEEE.MATH_REAL.ALL;
use std.textio.ALL;

library work;
use work.Wiener_Filter_Package.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity Wiener_Filter is
  Generic(
    DATA_LENGTH : integer := 16
  );
  Port ( 
    in_clk : in std_logic;
    in_rst : in std_logic;
    in_data : in std_logic_vector(DATA_LENGTH - 1 downto 0);
    in_data_vld : in std_logic;
    out_data : out std_logic_vector(DATA_LENGTH - 1 downto 0);
    out_data_vld : out std_logic

  );
end Wiener_Filter;

architecture Behavioral of Wiener_Filter is

  type t_Signal_Buf_vec is array(0 to c_TIME_samp - 1) of std_logic_vector(DATA_LENGTH - 1 downto 0);

  type t_X_Shift_Ctrl is (IDLE, SHIFT_DATA);
	signal r_X_Shift_Ctrl : t_X_Shift_Ctrl := IDLE;

  signal r_calc_fltr_out_strt : std_logic := '0';
	signal r_data : std_logic_vector(DATA_LENGTH - 1 downto 0) := (others => '0');
	signal r_filter_out_vld : std_logic := '0';
	signal n_i : integer := 0;

  type t_data_X_buffer is array (0 to c_FILT_LEN - 1 ) of std_logic_vector(DATA_LENGTH - 1 downto 0);
  signal r_data_X_buffer : t_data_X_buffer := (others => (others => '0')); 

  function f_Shift_X_Buf(r_data_X_buffer : t_data_X_buffer; in_data : std_logic_vector(DATA_LENGTH - 1 downto 0)) return t_data_X_buffer is
     variable v_data_X_buffer : t_data_X_buffer;
  begin
    v_data_X_buffer := r_data_X_buffer;
    for n_i in c_FILT_LEN - 2 downto 0 loop
      v_data_X_buffer(n_i + 1) := v_data_X_buffer(n_i); 
    end loop;
    v_data_X_buffer(0) := in_data; 
    return v_data_X_buffer;        
  end f_Shift_X_Buf;  

  signal r_filter_sum_out : std_logic_vector(COEF_LENGTH + COEF_ADD + DATA_LENGTH + 4 - 1 downto 0) := (others => '0');
	signal r_filter_out : std_logic_vector(DATA_LENGTH - 1 downto 0) := (others => '0');
	
  type t_Calc_Filter_out is (IDLE, ADD_X_BUF_PARAM , SUB_Y_BUF_PARAM, ROUND_PROCESS, DONE);
	signal r_Calc_Filter_out : t_Calc_Filter_out := IDLE;
  


begin
	out_data <= r_filter_out;
  out_data_vld <= r_filter_out_vld; 
   
  process(in_clk, in_rst)
  begin
    if in_rst = '1' then
      r_data_X_buffer <= (others => (others => '0')); 
			r_calc_fltr_out_strt <= '0';
			r_data <= (others => '0');
			
    elsif rising_edge(in_clk) then
			r_calc_fltr_out_strt <= '0';
      case r_X_Shift_Ctrl is
        when IDLE => 
         if in_data_vld = '1' then
           r_data <= in_data;
           r_X_Shift_Ctrl <= SHIFT_DATA;
         end if;
                    
        when SHIFT_DATA => 
          r_data_X_buffer <= f_Shift_X_Buf(r_data_X_buffer, r_data);
          r_X_Shift_Ctrl <= IDLE; 
					r_calc_fltr_out_strt <= '1';
        when others => NULL;    
      end case;
    end if;
  end process;
	
	process(in_clk, in_rst)
	begin
		if in_rst = '1' then
			r_Calc_Filter_out <= IDLE;
			r_filter_sum_out <= (others => '0');
			r_filter_out_vld <= '0';
			r_filter_out <= (others => '0');
			n_i <= 0; 

		elsif rising_edge(in_clk) then
			r_filter_out_vld <= '0';
			case r_Calc_Filter_out is
				when IDLE =>
					if r_calc_fltr_out_strt = '1' then
						r_Calc_Filter_out <= ADD_X_BUF_PARAM; 
					end if;
				
				when ADD_X_BUF_PARAM =>
					r_filter_sum_out <= r_filter_sum_out + sxt((r_data_X_buffer(n_i)) * (r_Coef_vec(n_i)), r_filter_sum_out'length);
					n_i <= n_i + 1;
					if n_i = c_FILT_LEN-1 then
						n_i <= 0;
						r_Calc_Filter_out <= DONE; 
					end if;
					
				when DONE =>		
					r_filter_out_vld <= '1';
					r_filter_sum_out <= (others => '0');
					r_filter_out <= r_filter_sum_out(COEF_LENGTH + DATA_LENGTH - 1 downto COEF_LENGTH );
					r_Calc_Filter_out <= IDLE; 
				when others => NULL;
			end case;
		end if;
	end process;

end Behavioral;

Filtre katsayılarının hesaplandığı ve diğer tanımalamaların yapıldığı paket dosyasını aşağıad görebilirsiniz.

----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 14.06.2025 22:22:15
-- Design Name: 
-- Module Name: Wiener_Filter_Package - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
-- 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;
use IEEE.MATH_REAL.ALL;
use std.textio.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

package Wiener_Filter_Package is

  constant c_FILT_LEN :  integer := 32;
  constant c_FS : integer := 10_000; -- Hz - Örnekleme Frekansı
  constant c_TIME : integer := 100; -- ms - Referasn sinyal süresi
  constant c_FREQ_0 : integer := 2000; -- Hz referans sinyal frekansı
  constant c_TIME_samp : integer := c_FS * c_TIME / 1000; -- Referans sinyal oluşturmak gerekli örnek sayısı
  constant COEF_LENGTH : integer := 12;
  constant COEF_ADD : integer := 4;

  constant PATH_REF : string := "D:\Ref_Signal.txt";
  constant PATH_SUM : string := "D:\Sum_Signal.txt";
  constant PATH_COEF : string := "D:\Wiener_Coef.txt";


  type t_Signal_Buf is array(0 to c_TIME_samp - 1) of real;

  type t_Rxx_Buf is array(0 to c_FILT_LEN - 1, 0 to c_FILT_LEN - 1) of real;
  type t_Rxd_Buf is array(0 to c_FILT_LEN - 1) of real;
  type t_Ext_Rxx_Buf is array(0 to c_FILT_LEN - 1, 0 to c_FILT_LEN) of real;

  function f_Rand_Noise(f_TIME_samp : integer) return t_Signal_Buf;
  function f_Ref_Sin(f_FS, f_TIME_samp, f_FREQ : integer) return t_Signal_Buf; 
  function f_Sum_Signal(Ref_Sig, Rand_Noise : t_Signal_Buf; f_TIME_samp : integer) return t_Signal_Buf;

  signal r_Ref_Signal : t_Signal_Buf := f_Ref_Sin(c_FS, c_TIME_samp, c_FREQ_0);
  signal r_Noise_Signal : t_Signal_Buf := f_Rand_Noise(c_TIME_samp);
  signal r_Sum_Signal : t_Signal_Buf := f_Sum_Signal(r_Ref_Signal, r_Noise_Signal, c_TIME_samp);

  function f_Extend_Rxx(Rxx : t_Rxx_Buf; Rxd : t_Rxd_Buf; Filt_Len : integer) return t_Ext_Rxx_Buf;

  function f_Coef_Calc(Sum_Sig, Ref_Sig : t_Signal_Buf; Filt_Len, Data_Len : integer) return t_Rxd_Buf;
  signal r_Coef : t_Rxd_Buf := f_Coef_Calc(r_Sum_Signal, r_Ref_Signal, c_FILT_LEN, c_TIME_samp);

  type t_Rxd_Buf_vec is array(0 to c_FILT_LEN - 1) of std_logic_vector(COEF_LENGTH + COEF_ADD - 1 downto 0);
  function f_Coef_Conv( Rxd : t_Rxd_Buf; Filt_Len : integer) return t_Rxd_Buf_vec;
  
  signal r_Coef_vec : t_Rxd_Buf_vec := f_Coef_Conv(r_Coef, c_FILT_LEN);

end Wiener_Filter_Package;

package body Wiener_Filter_Package is
  function f_Rand_Noise(f_TIME_samp : integer) return t_Signal_Buf is
    variable v_Noise_Signal : t_Signal_Buf := (others => 0.0);
    variable seed1 : integer := 1;
    variable seed2 : integer := 1;
    variable r : real;
    variable min_val : real := -1.0;
    variable max_val : real :=  1.0;
  begin
    for ni in 0 to f_TIME_samp - 1 loop 
      uniform(seed1, seed2, r);
      v_Noise_Signal(ni) := r * (max_val - min_val) + min_val;
    end loop;
    return v_Noise_Signal;
  end function;

  function f_Ref_Sin(f_FS, f_TIME_samp, f_FREQ : integer) return t_Signal_Buf is 
    variable v_Ref_Signal : t_Signal_Buf := (others => 0.0);
  begin
    for ni in 0 to f_TIME_samp - 1 loop
      v_Ref_Signal(ni) := sin(2.0 * MATH_PI * real(ni) * real(f_FREQ) / real(f_FS));
    end loop;
    return v_Ref_Signal;
  end function;

  function f_Sum_Signal(Ref_Sig, Rand_Noise : t_Signal_Buf; f_TIME_samp : integer) return t_Signal_Buf is 
    variable v_Sum_Signal : t_Signal_Buf := (others => 0.0);
  begin
      for ni in 0 to f_TIME_samp - 1 loop
        v_Sum_Signal(ni) := Ref_Sig(ni) + Rand_Noise(ni);
      end loop;
    return v_Sum_Signal;
  end function;

  function f_Extend_Rxx(Rxx : t_Rxx_Buf; Rxd : t_Rxd_Buf; Filt_Len : integer) return t_Ext_Rxx_Buf is
    variable v_Ext_Rxx_Buf : t_Ext_Rxx_Buf;
  begin

    for ni in 0 to Filt_Len - 1 loop
      for nj in 0 to Filt_Len loop    
        if nj = Filt_Len then
          v_Ext_Rxx_Buf(ni, nj) := Rxd(ni);
        else
          v_Ext_Rxx_Buf(ni, nj) := Rxx(ni, nj);
        end if;
      end loop;
    end loop;
    return v_Ext_Rxx_Buf;
  end function;
  

  function f_Coef_Calc(Sum_Sig, Ref_Sig : t_Signal_Buf; Filt_Len, Data_Len : integer) return t_Rxd_Buf is
    variable v_Rxx : t_Rxx_Buf := (others => (others => 0.0));
    variable v_Rxd : t_Rxd_Buf := (others => 0.0);
    variable v_Ext_Rxx : t_Ext_Rxx_Buf;
    variable v_factor : real;
    variable v_Coef : t_Rxd_Buf := (others => 0.0);
    variable v_sum : real := 0.0;

    
    file file_s : text open write_mode is PATH_SUM;
    file file_c : text open write_mode is PATH_COEF;
    file file_r : text open write_mode is PATH_REF;
    
    variable row_r : line;
    variable row_s : line;
    variable row_c : line;
    variable data : real;

  begin
    for ni in 0 to Filt_Len - 1 loop
      for nj in 0 to Filt_Len - 1 loop
        for nn in Filt_Len - 1 to Data_Len - 1 loop
          v_Rxx(ni, nj) := v_Rxx(ni, nj) + Sum_Sig(nn - ni) * Sum_Sig(nn - nj);         
        end loop;
      end loop;
      for nn in Filt_Len - 1 to Data_Len - 1 loop
         v_Rxd(ni) := v_Rxd(ni) + Sum_Sig(nn - ni) * Ref_Sig(nn);
      end loop;      
    end loop;
    
    for ni in 0 to Filt_Len - 1 loop
      for nj in 0 to Filt_Len - 1 loop    
        v_Rxx(ni, nj) := v_Rxx(ni, nj) / real(Data_Len - Filt_Len + 1);
      end loop;
      v_Rxd(ni) := v_Rxd(ni) / real(Data_Len - Filt_Len + 1);
    end loop;
    
    v_Ext_Rxx := f_Extend_Rxx(v_Rxx, v_Rxd, Filt_Len);
    for ni in 0 to Filt_Len - 1 loop
      for nj in ni + 1 to Filt_Len - 1 loop  
        v_factor := v_Ext_Rxx(nj, ni) / v_Ext_Rxx(ni, ni);
        for nn in 0 to Filt_Len loop
          v_Ext_Rxx(nj, nn) := v_Ext_Rxx(nj, nn) - v_factor * v_Ext_Rxx(ni, nn);
        end loop;
      end loop;
    end loop;

    for ni in Filt_Len-1 downto 0 loop
      v_sum := 0.0;
      for nk in ni + 1 to Filt_Len - 1 loop
         v_sum := v_sum + v_Ext_Rxx(ni, nk) * v_Coef(nk);
      end loop;
      v_Coef(ni) := (v_Ext_Rxx(ni, Filt_Len) - v_sum) / v_Ext_Rxx(ni, ni);
    end loop;

    for ni in 0 to Filt_Len - 1 loop
        data := v_Coef(ni);
        write(row_c, data);
        writeline(file_c, row_c);    
      
    end loop;

    for ni in 0 to Data_Len - 1 loop
        data := Sum_Sig(ni);
        write(row_s, data);
        writeline(file_s, row_s);         
    end loop;
    for ni in 0 to Data_Len - 1 loop
        data := Ref_Sig(ni);
        write(row_r, data);
        writeline(file_r, row_r);         
    end loop;

    return v_Coef;

  end function;
  
  function f_Coef_Conv( Rxd : t_Rxd_Buf; Filt_Len : integer) return t_Rxd_Buf_vec is
      variable v_Rxd_vec : t_Rxd_Buf_vec;
  begin

    for ni in 0 to Filt_Len - 1 loop
      v_Rxd_vec(ni) := conv_std_logic_vector(integer(Rxd(ni) * real(2**COEF_LENGTH)), COEF_LENGTH + COEF_ADD);
    end loop;
    return v_Rxd_vec;
  end function;


end Wiener_Filter_Package;

Aşağıda katsayılarının üretiminde kullanılan sinyalin hesaplanan katsayılar ile filtrelenmesinin benzetim çıktısı ve benzetim kodları verilmiştir.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;
use IEEE.MATH_REAL.ALL;
use std.textio.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity tb_Wiener_Filter is
end tb_Wiener_Filter;

architecture Behavioral of tb_Wiener_Filter is

    component Wiener_Filter
    Generic(
        DATA_LENGTH : integer := 16
    );
    Port ( 
        in_clk : in std_logic;
        in_rst : in std_logic;
        in_data : in std_logic_vector(DATA_LENGTH - 1 downto 0);
        in_data_vld : in std_logic;
        out_data : out std_logic_vector(DATA_LENGTH - 1 downto 0);
        out_data_vld : out std_logic

    );  
    end component;
    
    constant DATA_PATH : string := "D:\Sum_Signal.txt";
    
    constant CLK_PERIOD : time := 100 ns;    
    constant SAMPLING_CNTR : integer := 1000;
    constant DATA_LENGTH : integer := 24;

    signal in_clk : std_logic  := '0';
    signal clk_cnt : integer := 0;

    signal in_data : std_logic_vector(DATA_LENGTH - 1 downto 0) := (others => '0');
    signal in_data_vld : std_logic := '0';

    signal out_data : std_logic_vector(DATA_LENGTH - 1 downto 0) := (others => '0');
    signal out_data_vld : std_logic := '0';

begin

  process
  begin
    in_clk <= '1';
    wait for CLK_PERIOD / 2;
    in_clk <= '0';
    wait for CLK_PERIOD / 2;      
  end process;

  process (in_clk)
    file file_s : text open read_mode is DATA_PATH ;
    variable line_s : line;
    variable data_s : real;
  begin 
    if rising_edge(in_clk) then
        in_data_vld <= '0';
        if clk_cnt = SAMPLING_CNTR - 1 then
            clk_cnt <= 0;
            if not(endfile(file_s)) then
                readline(file_s, line_s);
                read(line_s, data_s);
                in_data <= conv_std_logic_vector(integer(data_s * real(2**(DATA_LENGTH - 2))), 24) ;               
                in_data_vld <= '1';
            end if;             
        else
            clk_cnt <= clk_cnt + 1;
        end if;      
    end if;
  end process;

    Wiener_Filter_map : Wiener_Filter
    Generic map(
        DATA_LENGTH => DATA_LENGTH
    )
    Port map( 
        in_clk => in_clk,
        in_rst => '0',
        in_data => in_data,
        in_data_vld => in_data_vld,
        out_data => out_data,
        out_data_vld => out_data_vld
    );  
    

end Behavioral;

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir