diff --git a/IIR_filter.py b/IIR_filter.py new file mode 100644 index 0000000..0c3b2c7 --- /dev/null +++ b/IIR_filter.py @@ -0,0 +1,23 @@ +import numpy as np + +import matplotlib.pyplot as plt +import fft + +class IIR_1_Order_LP(object): + """docstring for fft_meas""" + def __init__(self, fs, fc=200): + self.fs = fs + self.fc = fc + self.dt = 1/self.fs + self.RC = 1 / (2*np.pi*self.fc) + self.alpha = self.dt / (self.RC + self.dt) + + self.y = 0 + + def step(self, x): + self.y = self.y + self.alpha * (x - self.y) + return self.y + + + + diff --git a/__pycache__/IIR_filter.cpython-312.pyc b/__pycache__/IIR_filter.cpython-312.pyc new file mode 100644 index 0000000..acd0854 Binary files /dev/null and b/__pycache__/IIR_filter.cpython-312.pyc differ diff --git a/__pycache__/fft.cpython-312.pyc b/__pycache__/fft.cpython-312.pyc new file mode 100644 index 0000000..7352838 Binary files /dev/null and b/__pycache__/fft.cpython-312.pyc differ diff --git a/__pycache__/fft_meas.cpython-312.pyc b/__pycache__/fft_meas.cpython-312.pyc new file mode 100644 index 0000000..1168680 Binary files /dev/null and b/__pycache__/fft_meas.cpython-312.pyc differ diff --git a/__pycache__/ftt.cpython-312.pyc b/__pycache__/ftt.cpython-312.pyc new file mode 100644 index 0000000..f26874b Binary files /dev/null and b/__pycache__/ftt.cpython-312.pyc differ diff --git a/__pycache__/ftt_meas.cpython-312.pyc b/__pycache__/ftt_meas.cpython-312.pyc new file mode 100644 index 0000000..cac1a69 Binary files /dev/null and b/__pycache__/ftt_meas.cpython-312.pyc differ diff --git a/__pycache__/zerocross_meas.cpython-312.pyc b/__pycache__/zerocross_meas.cpython-312.pyc new file mode 100644 index 0000000..46bbb66 Binary files /dev/null and b/__pycache__/zerocross_meas.cpython-312.pyc differ diff --git a/fft.py b/fft.py new file mode 100644 index 0000000..1007dfb --- /dev/null +++ b/fft.py @@ -0,0 +1,58 @@ +import numpy as np + + +def fft(x, fs, X_nom=None, epsilon=None): + + # Implementation from IEC 61000-4-7:2002/AMD1:2008 + + + # Data length + N = len(x) + # Number of positive-frequency bins + K = int(np.floor(N/2)) + # Frequency axis (0 .. fs/2) + #freq = np.linspace(start=0, stop=fs/2, num=K+1, endpoint=True) + freq = np.arange(K + 1) * fs / N + + # allocate + a = np.zeros(K + 1) + b = np.zeros(K + 1) + c = np.zeros(K + 1) + Y_C = np.zeros(K + 1) + phi = np.zeros(K + 1) + + n = np.arange(N) + + # DC + c[0] = np.mean(x) # c0 per IEC + a[0] = 2 * c[0] # not really used; just for completeness + b[0] = 0.0 + + # k = 1..K + for k in range(1, K+1): + angle = 2 * np.pi * k * n / N + a[k] = (2/N) * np.sum(x * np.cos(angle)) + b[k] = (2/N) * np.sum(x * np.sin(angle)) + + # Nyquist (if N even): do NOT apply the 2/N doubling + if (N % 2 == 0) and (k == K): + a[k] *= 0.5 + b[k] *= 0.5 + c[k] = np.sqrt(a[k]*a[k] + b[k]*b[k]) + + # RMS value calculated in Eq.2. + Y_C[k] = c[k] / np.sqrt(2) + + + # Phase: apply dead-band if provided, else always compute + if (X_nom is not None) and (epsilon is not None): + if (np.abs(a[k]) <= epsilon * X_nom) and (np.abs(b[k]) <= epsilon * X_nom): + phi[k] = 0.0 + continue + + # IEC quadrant handling (equivalent to the piecewise definition) + phi[k] = np.arctan2(a[k], b[k]) + + + + return freq, a, b, c, Y_C, phi \ No newline at end of file diff --git a/fft_meas.py b/fft_meas.py new file mode 100644 index 0000000..3bf0318 --- /dev/null +++ b/fft_meas.py @@ -0,0 +1,49 @@ +import numpy as np + +import matplotlib.pyplot as plt +import fft + +class fft_meas(object): + """docstring for fft_meas""" + def __init__(self, fs=50e3, tn=0.2): + self.fs = fs + self.ts = 1/fs + self.tn = tn + self.N = int(int(fs*tn)) + self.data = np.zeros(self.N) + self.idx = -1 + self.time = 0 + + self.freq = 0 + self.a = 0 + self.b = 0 + self.c = 0 + self.Y_C = 0 + self.phi = 0 + + def step(self, data, time, f_H1, unit): + + if time - self.time > self.ts: + self.time = time + self.idx += 1 + + self.data[self.idx] = data + + if self.idx == self.N-1: + + + self.freq , self.a, self.b , self.c, self.Y_C, self.phi = fft.fft(x=self.data, fs=self.fs) + + self.idx = -1 + + + + def plot(self): + #fs, a, b, c, YC, phi = ftt.fft(Ph1, sample_freq) + + plt.plot(self.freq, self.c) + plt.xlabel("x") + plt.ylabel("y") + plt.title("Simple plot") + plt.show() + diff --git a/main.py b/main.py new file mode 100644 index 0000000..baac427 --- /dev/null +++ b/main.py @@ -0,0 +1,46 @@ + +import numpy as np +import fft_meas +import zerocross_meas + + +# to do +# Need a zero-cross algorithem to comply with 61000-4-30 + + + +def main(): + time = 0 + delta_t = 1e-6 + t_stop = 1.1 + + + + amplitude = 1 + base_freq = 50 + + + fft_measurements = fft_meas.fft_meas(fs=50e3, tn=0.2) + zerocross = zerocross_meas.zerocross_meas(fs=50e3, tn=1) + + for i in range(0, int(t_stop/delta_t)+1): + Ph1 = np.sin(2*np.pi*time*base_freq) + + + zerocross.step(x=Ph1, time=time) + + fft_measurements.step(data=Ph1, time=time, f_H1=zerocross.freq, unit="I") + + + + time += delta_t + + + zerocross.print() + #fft_measurements.plot() + + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/zerocross_meas.py b/zerocross_meas.py new file mode 100644 index 0000000..9c8e432 --- /dev/null +++ b/zerocross_meas.py @@ -0,0 +1,41 @@ +import numpy as np + +import matplotlib.pyplot as plt +import fft +import IIR_filter + +class zerocross_meas(object): + """docstring for fft_meas""" + def __init__(self, fs=50e3, tn=1): + self.fs = fs + self.ts = 1/self.fs + self.tn = tn + self.time = 0 + self.freq = 0 + self.freq_ts = 0 + self.y = 0 + self.y_old = 0 + self.idx = 0 + self.LPfilter = IIR_filter.IIR_1_Order_LP(fs=self.fs, fc=200) + + def step(self, x, time): + + if time - self.time > self.ts: + self.time = time + self.y = self.LPfilter.step(x) + + if self.y_old < 0 and self.y > 0: + self.idx += 1 + + + if time - self.freq_ts > self.tn: + self.freq = self.idx / self.tn + self.freq_ts = time + self.idx = 0 + + + self.y_old = self.y + + + def print(self): + print(self.freq)