This commit is contained in:
2026-01-24 21:10:50 +01:00
parent 250cdba29b
commit 473eb899a7
11 changed files with 217 additions and 0 deletions

23
IIR_filter.py Normal file
View File

@@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

58
fft.py Normal file
View File

@@ -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

49
fft_meas.py Normal file
View File

@@ -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()

46
main.py Normal file
View File

@@ -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()

41
zerocross_meas.py Normal file
View File

@@ -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)