----------------------------------------------
-- MSS copyright 1999-2001
-- VHDL code for Signal to Noise Ratio
-- Primary project: Sirius Satellite Radio
-- Author: Alain Zarembowitch + Nayef Ahmar / MSS
-- Edit date: 1-19-01
-- Revision: 0.002
--
-- Rev 0.002 az
-- Revision: averaged over 4K samples (i.e. 2 frames) for slower MRC response.
----------------------------------------------
-- The SNR is related to the standard deviation of the absolute value of the data samples 
-- taken at the optimum sampling instant. 
-- The standard deviation is averaged (continuous average) over 512 samples.
-- The best SNR (all "1111" or "0000" samples) generates an output of 0.
-- The worst SNR (all "1000" or "0111" samples) generates an output of "1100010".
-- The intrinsic accuracy of the SNR measurement is 0.6 dB when the noise is additive
-- white Gaussian noise. Some sort of calibration is required as the output of this module
-- is not linearly related to the SNR.
----------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;    
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;


entity SIGNAL_NOISE_RATIO is
    port (
		CLK: in std_logic;	
			-- reference clock. Typically 30 MHz. For all synchronous design 
    	RESETN: in  std_logic;-- reset# active low for initialisation to well known state prior to power up
      SAMPLE_CLK: in std_logic; -- Common Aligned CLK for the 3 streams TDM1, TDM2, and COFDM
                               -- 19 clk cycles period per each sample
   	SAMPLE: in std_logic_vector(3 downto 0);
			-- 1 sample data from SDRAM
			-- per one data stream
			-- 4-bit sample format:0000 most negative, 1111 most positive, with no zero representation 
				
      SNR: out std_logic_vector(7 downto 0) 
			--Signal to Noise Ratio (truncated to 8 bits, from 15)
	);
 end SIGNAL_NOISE_RATIO;
  
architecture BEHAVIOR of SIGNAL_NOISE_RATIO is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
-- 8-bit adder
component fcadd8_2 is
	port (
		ci: 	in std_logic;
		a7,a6,a5,a4,a3,a2,a1,a0:	in std_logic;
		b7,b6,b5,b4,b3,b2,b1,b0:	in std_logic;
		sum7,sum6,sum5,sum4,sum3,sum2,sum1,sum0: out std_logic;
		co: out std_logic);
end component;     

-- 16-bit adder
component fcadd16_2 is
	port (
		ci: 	in std_logic;
		a15,a14,a13,a12,a11,a10,a9,a8,a7,a6,a5,a4,a3,a2,a1,a0:	in std_logic;
		b15,b14,b13,b12,b11,b10,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0:	in std_logic;
		sum15,sum14,sum13,sum12,sum11,sum10,sum9,sum8,sum7,sum6,sum5,sum4,sum3,sum2,sum1,sum0: out std_logic;
		co: out std_logic);
end component;

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
signal SAMPLE_VARIANCE: std_logic_vector(6 downto 0);-- variance of sample, square of difference
													-- of the magnitude from the mean 
signal VAR_AVG_DIFF: std_logic_vector(6 downto 0); --store the difference between the
													-- variance of one sample and average sum    
signal VAR_AVG_DIFF_E: std_logic_vector(17 downto 0); -- extended precision prior to adder
signal OLD_SUM: std_logic_vector(17 downto 0); -- old sum 
signal NEW_SUM: std_logic_vector(17 downto 0); -- new sum 
signal SUM_DIV4096: std_logic_vector(6 downto 0);

-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin 

--generate the variance for each sample
--squaring process
VARIANCE_GEN: process (SAMPLE)
begin
	if (SAMPLE = "1111") or (SAMPLE = "0000") then
		SAMPLE_VARIANCE <= "0000000";--0
	elsif (SAMPLE = "1110") or (SAMPLE = "0001") then
		SAMPLE_VARIANCE <= "0000001";--1
	elsif (SAMPLE = "1101") or (SAMPLE = "0010") then
		SAMPLE_VARIANCE <= "0000100";--4
	elsif (SAMPLE = "1100") or (SAMPLE = "0011") then
		SAMPLE_VARIANCE <= "0001001";--9
	elsif (SAMPLE = "1011") or (SAMPLE = "0100") then
		SAMPLE_VARIANCE <= "0010000";--16
	elsif (SAMPLE = "1010") or (SAMPLE = "0101") then
		SAMPLE_VARIANCE <= "0011001";--25
	elsif (SAMPLE = "1001") or (SAMPLE = "0110") then
		SAMPLE_VARIANCE <= "0100100";--36
	else --(SAMPLE = "1000") or (SAMPLE = "0111") then
		SAMPLE_VARIANCE <= "0110001";--49
	end if;		  
end process;      


-- Divide average sum by 4096 (i.e. shift by 12 bits) 
SUM_DIV4096(5 downto 0) <= OLD_SUM(17 downto 12);
SUM_DIV4096(6) <= '0';
       
-- compute the difference Variance - average sum in 2`s complement format 
VAR_AVG_DIFF <= SAMPLE_VARIANCE + not(SUM_DIV4096) + 1;

-- Extend precision for VAR_AVG_DIFF to 18 bits prior to addition
VAR_AVG_DIFF_E(6 downto 0) <= VAR_AVG_DIFF;
VAR_AVG_DIFF_E(7) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(8) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(9) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(10) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(11) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(12) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(13) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(14) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(15) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(16) <= VAR_AVG_DIFF(6);
VAR_AVG_DIFF_E(17) <= VAR_AVG_DIFF(6);

--compute the new sum =  old sum + variance - sum/4096
NEW_SUM <= OLD_SUM + VAR_AVG_DIFF_E;


UPDATE_SUM: process (CLK, RESETN, SAMPLE_CLK, NEW_SUM )
begin
	if (RESETN = '0') then
		OLD_SUM <= "000000000000000000";
	elsif (rising_edge(CLK)) then 
		if  (SAMPLE_CLK = '1') then
			if (NEW_SUM(17 downto 12) = "110001") then--protect from overflow maximum sum is 49*4096
				OLD_SUM <= "110001000000000000";
			elsif(NEW_SUM(17 downto 12) = "111111") then--protect from undererflow minimum sum is 0
				OLD_SUM <= "000000000000000000";
			else 
				OLD_SUM <= NEW_SUM;
			end if;  
		end if;
	end if;
end process;

SNR <= NEW_SUM(17 downto 10);
end  BEHAVIOR;

