Disturbances in measured signals for voltage or current are a bit annoying. This file contains a Second Order Generalized Integrator to filter a sine-wave at the input to a disturbance-free output. As replacement for the regular integrator it uses a special third order integrator implementation suggested by Theodorescu et. al. It reduces the ripple on the estimated amplitude and frequency compared to Second order integrator or trapezoidal methods.

Source: Ciobotarum, M.; Theodorescu, R.; Blaabjerg, F.: “A New Single-Phase PLL Structure Based on Second Order Generalized Integrator”, 37th IEEE Power Electronics Specialists Conference, PESC ’06, 2006, 1-6

VHDL
-- Second-order Generalized Integrator (SOGI)
-- Christian Noeding, christian@noeding-online.de
-- https://chrisdevblog.com | https://github.com/xn--nding-jua
--
-- Released under GNU General Public License v3

LIBRARY ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity SOGI is
	PORT
	(
		clk		    : in std_logic;
		v_in		  : in signed(31 downto 0);		-- input voltage
		omega		  : in signed(31 downto 0);		-- Q15.16
		Ts			  : in signed(31 downto 0); 		-- Q0.31
		sync_in	  : in std_logic;

		v_out		  : out signed(31 downto 0);		-- in phase voltage signal (filtered)
		qv_out	  : out signed(31 downto 0);		-- in quadature voltage signal (filtered)
		epsilon	  : out signed(31 downto 0);		-- Q15.16
		sync_out	: out std_logic
	);    
end SOGI;

architecture behavioral of SOGI is   
	-- constants
	constant k       				  : signed(11 downto 0) := to_signed(205, 12); 	-- Q3.8: k = between 0.5 and 1.0. Theodorescu suggests 0.8
	constant one_by_twelve	  : signed(15 downto 0) := to_signed(2731, 16); 	-- Q0.15
	constant c0      				  : signed(7 downto 0) := to_signed(23, 8);			-- Q7.0: c0 = 23 (constant for third order integrator)
	constant c1      				  : signed(7 downto 0) := to_signed(-16, 8);		-- Q7.0: c1 = -16 (constant for third order integrator)
	constant c2      				  : signed(7 downto 0) := to_signed(5, 8);			-- Q7.0: c2 = 5 (constant for third order integrator)

	-- internal signals and constants
	signal state					    : natural range 0 to 18 := 0;

	signal v_int					    : signed(31 downto 0) := (others => '0'); -- Q15.16
	signal v_o, qv_o       	  : signed(31 downto 0) := (others => '0'); -- Q15.16
	signal omega_ts  				  : signed(23 downto 0) := (others => '0'); -- Q0.23

	signal MUL1,MUL2,MUL3		  : signed(31 downto 0) := (others => '0'); -- Q15.16
	signal SUMa,SUMb       	  : signed(31 downto 0) := (others => '0'); -- Q15.16
	signal DLYa0,DLYa1,DLYa2  : signed(31 downto 0) := (others => '0'); -- Q15.16
	signal DLYb0,DLYb1,DLYb2  : signed(31 downto 0) := (others => '0'); -- Q15.16

	signal MULa4,MULa5,MULa6 	: signed(31 downto 0) := (others => '0'); -- Q15.16
	signal MULb4,MULb5,MULb6 	: signed(31 downto 0) := (others => '0'); -- Q15.16

	--signals for multiplier
	signal mult_in_a				  :	signed(31 downto 0) := (others=>'0');
	signal mult_in_b				  :	signed(31 downto 0) := (others=>'0');
	signal mult_out				    :	signed(63 downto 0) := (others=>'0');
begin
	-- multiplier
	process(mult_in_a, mult_in_b)
	begin
		mult_out <= mult_in_a * mult_in_b;
	end process;

	process(clk)
	begin
		if (rising_edge(clk)) then
			if (sync_in = '1' and state = 0) then
				v_int <= v_in;
				mult_in_a <= omega;
				mult_in_b <= Ts;
				
				state <= 1; -- start of state-machine
			
			elsif (state = 1) then
				omega_ts <= resize(shift_right(mult_out, 24), omega_ts'length); -- Q15.16 * Q0.31 = Qx.47 -> convert to Q0.23
				
				state <= state + 1;
			
			elsif (state = 2) then
				-- divide omega_ts by 12
				mult_in_a <= resize(omega_ts, mult_in_a'length);
				mult_in_b <= resize(one_by_twelve, mult_in_b'length);
				
				state <= state + 1;
				
			elsif (state = 3) then
				omega_ts <= resize(shift_right(mult_out, 15), omega_ts'length); -- Q0.23 * Q0.15 = Q0.38 -> convert to Q0.23
				mult_in_a <= resize(k, mult_in_a'length);
				mult_in_b <= resize(v_int - v_o, mult_in_b'length); -- DIFF1 = v_int - v_o

				epsilon <= v_int - v_o; -- epsilon = DIFF1 = v_int - v_o
			
				state <= state + 1;
				
			elsif (state = 4) then
				MUL1 <= resize(shift_right(mult_out, 8), MUL1'length);
			
				state <= state + 1;

			elsif (state = 5) then
				mult_in_a <= resize(MUL1 - qv_o, mult_in_a'length); -- DIFF2 = MUL1 - qv_o
				mult_in_b <= resize(omega_ts, mult_in_b'length);
			
				state <= state + 1;
				
			elsif (state = 6) then
				MUL2 <= resize(shift_right(mult_out, 23), MUL2'length); -- DIFF2 = Q15.16 | omega = Q0.23 | resulting in Q15.39 -> convert to Q15.16
				mult_in_a <= resize(v_o, mult_in_a'length);
				mult_in_b <= resize(omega_ts, mult_in_b'length);
				
				state <= state + 1;
				
			elsif (state = 7) then
				MUL3 <= resize(shift_right(mult_out, 23), MUL3'length); -- v_o = Q15.16 | omega = Q0.23 | resulting in Q15.39 -> convert to Q15.16
			
				state <= state + 1;
				
			elsif (state = 8) then
				SUMa <= SUMa + MUL2;
				DLYa0 <= SUMa;
				DLYa1 <= DLYa0;
				DLYa2 <= DLYa1;
				SUMb <= SUMb + MUL3;
				DLYb0 <= SUMb;
				DLYb1 <= DLYb0;
				DLYb2 <= DLYb1;
			
				state <= state + 1;
				
			elsif (state = 9) then
				mult_in_a <= resize(DLYa0, mult_in_a'length);
				mult_in_b <= resize(c0, mult_in_b'length);
				
				state <= state + 1;
				
			elsif (state = 10) then
				MULa4 <= resize(mult_out, MULa4'length); -- Q15.16 * Q7.0 = Q23.16 -> just resize to Qx.16
				mult_in_a <= resize(DLYa1, mult_in_a'length);
				mult_in_b <= resize(c1, mult_in_b'length);
				
				state <= state + 1;
				
			elsif (state = 11) then
				MULa5 <= resize(mult_out, MULa5'length); -- Q15.16 * Q7.0 = Q23.16 -> just resize to Qx.16
				mult_in_a <= resize(DLYa2, mult_in_a'length);
				mult_in_b <= resize(c2, mult_in_b'length);
				
				state <= state + 1;
				
			elsif (state = 12) then
				MULa6 <= resize(mult_out, MULa6'length); -- Q15.16 * Q7.0 = Q23.16 -> just resize to Qx.16
				mult_in_a <= resize(DLYb0, mult_in_a'length);
				mult_in_b <= resize(c0, mult_in_b'length);
				
				state <= state + 1;
				
			elsif (state = 13) then
				MULb4 <= resize(mult_out, MULb4'length); -- Q15.16 * Q7.0 = Q23.16 -> just resize to Qx.16
				mult_in_a <= resize(DLYb1, mult_in_a'length);
				mult_in_b <= resize(c1, mult_in_b'length);
				
				state <= state + 1;
				
			elsif (state = 14) then
				MULb5 <= resize(mult_out, MULb5'length); -- Q15.16 * Q7.0 = Q23.16 -> just resize to Qx.16
				mult_in_a <= resize(DLYb2, mult_in_a'length);
				mult_in_b <= resize(c2, mult_in_b'length);
				
				state <= state + 1;
				
			elsif (state = 15) then
				MULb6 <= resize(mult_out, MULb6'length); -- Q15.16 * Q7.0 = Q23.16 -> just resize to Qx.16
				
				state <= state + 1;
				
			elsif (state = 16) then
				v_o <= MULa4 + MULa5 + MULa6;
				qv_o <= MULb4 + MULb5 + MULb6;
			
				state <= state + 1;
				
			elsif (state = 17) then
				v_out <= v_o;
				qv_out <= qv_o;
				sync_out <= '1';
			
				state <= state + 1;
				
			elsif (state = 18) then
				sync_out <= '0';
			
				state <= 0;
			end if;
		end if;
	end process;
end behavioral;

Leave a comment

Your email address will not be published. Required fields are marked *