Doing THD calc

This commit is contained in:
2026-01-25 17:48:45 +01:00
parent 473eb899a7
commit d1b4e641c3
10 changed files with 233 additions and 49 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -5,7 +5,11 @@ import fft
class fft_meas(object): class fft_meas(object):
"""docstring for fft_meas""" """docstring for fft_meas"""
# 61000-4-7 says 200ms
# 10-cycles for 50Hz
# 12-cycles for 60Hz
def __init__(self, fs=50e3, tn=0.2): def __init__(self, fs=50e3, tn=0.2):
self.fs = fs self.fs = fs
self.ts = 1/fs self.ts = 1/fs
self.tn = tn self.tn = tn
@@ -19,25 +23,94 @@ class fft_meas(object):
self.b = 0 self.b = 0
self.c = 0 self.c = 0
self.Y_C = 0 self.Y_C = 0
# 61000-4-7: 3.2.3
# Upto the 40 Harmonic per 3.3.1 Note 2
# Harmonic order
self.Y_H = np.zeros(40)
# 61000-4-7: 3.2.3
# Upto the 40 Harmonic per 3.3.2 Note 2
# Grouped Harmonic order
self.Y_g = np.zeros(40)
# 61000-4-7: 3.2.2
self.h = np.zeros(40)
self.THD = 0
self.THDG = 0
self.phi = 0 self.phi = 0
self.base_freq = 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 step(self, y, base_freq, unit):
self.idx += 1
self.data[self.idx] = y
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.calc_Y_H(base_freq)
self.calc_THD()
self.calc_Y_g(base_freq)
self.calc_THDG()
self.idx = -1
def calc_Y_H(self, base_freq):
# 61000-4-7: 3.2.3
if np.abs(base_freq-50) < np.abs(base_freq-60):
self.base_freq = 50
else:
self.base_freq = 60
if self.base_freq == 50:
for k in range(len(self.Y_H)):
self.Y_H[k] = self.Y_C[int(10*k)]
self.h[k] = self.freq[int(10*k)]
elif self.base_freq == 60:
for k in range(len(self.Y_H)):
self.Y_H[k] = self.Y_C[int(12*k)]
self.h[k] = self.freq[int(12*k)]
def calc_THD(self):
# 61000-4-7: 3.3.1
self.THD = 0
for k in range(2, len(self.Y_H)):
self.THD += np.power(self.Y_H[k]/self.Y_H[1],2)
self.THD = np.sqrt(self.THD)
def calc_Y_g(self, base_freq):
# 61000-4-7: 3.2.4
if np.abs(base_freq-50) < np.abs(base_freq-60):
self.base_freq = 50
else:
self.base_freq = 60
if self.base_freq == 50:
for k in range(len(self.Y_g)):
self.Y_g[k] = self.Y_C[int(10*k)-1] + self.Y_C[int(10*k)] + self.Y_C[int(10*k)+1]
self.h[k] = self.freq[int(10*k)]
elif self.base_freq == 60:
for k in range(len(self.Y_g)):
self.Y_g[k] = self.Y_C[int(12*k)-1] + self.Y_C[int(12*k)] + self.Y_C[int(12*k)+1]
self.h[k] = self.freq[int(12*k)]
def calc_THDG(self):
# 61000-4-7: 3.3.1
self.THDG = 0
for k in range(2, len(self.Y_H)):
self.THDG += np.power(self.Y_g[k]/self.Y_g[1],2)
self.THDG = np.sqrt(self.THDG)
print(self.THDG)
def plot(self): def plot(self):
#fs, a, b, c, YC, phi = ftt.fft(Ph1, sample_freq) #fs, a, b, c, YC, phi = ftt.fft(Ph1, sample_freq)
@@ -47,3 +120,11 @@ class fft_meas(object):
plt.title("Simple plot") plt.title("Simple plot")
plt.show() plt.show()
def plot_Y_H(self):
plt.stem(self.h, self.Y_H, use_line_collection=True)
plt.xlabel("Harmonic order h")
plt.ylabel("RMS value Y_H,h")
plt.title("Harmonic spectrum (IEC 61000-4-7: 3.2.3)")
plt.grid(True)
plt.show()

51
fundamental_freq_meas.py Normal file
View File

@@ -0,0 +1,51 @@
import numpy as np
import matplotlib.pyplot as plt
class fundamental_freq_meas(object):
"""docstring for fft_meas"""
def __init__(self, fs=50e3, tn=10):
self.fs = fs
self.ts = 1/self.fs
self.tn = tn
self.time = 0
self.y_old = 0
self.tn_start_time = 0
self.tn_end_time = 0
self.zerocross_count = 0
# Calculated Frequency
self.freq = 50
self.t_zc = 0
def step(self, y, zerocross_flag):
# Integrate time
self.time += self.ts
if zerocross_flag:
denom = (y - self.y_old)
if denom != 0.0: # avoid divide-by-zero
# linear interpolation to find zero-crossing time
frac = -self.y_old / denom
self.t_zc = (self.time - self.ts) + frac * self.ts
# store first crossing time
if self.zerocross_count == 0:
self.tn_start_time = self.t_zc
self.zerocross_count += 1
self.tn_end_time = self.t_zc # always keep last crossing time in this window
# window finished (independent of whether a crossing happened exactly here)
if self.time >= self.tn:
if self.zerocross_count >= 2 and self.tn_end_time > self.tn_start_time:
self.freq = (self.zerocross_count - 1) / (self.tn_end_time - self.tn_start_time)
# reset window state
self.zerocross_count = 0
self.tn_start_time = 0.0
self.tn_end_time = 0.0
self.time -= self.tn # avoids drift vs. self.time = 0
self.y_old = y

26
main.py
View File

@@ -1,7 +1,6 @@
import numpy as np import numpy as np
import fft_meas import measurements
import zerocross_meas
# to do # to do
@@ -12,33 +11,38 @@ import zerocross_meas
def main(): def main():
time = 0 time = 0
delta_t = 1e-6 delta_t = 1e-6
t_stop = 1.1 t_stop = 1.2
ADC_sample_frequency = 50e3
ADC_sample_period = 1/ADC_sample_frequency
ADC_timer = 0
amplitude = 1 amplitude = 1
base_freq = 50 base_freq = 60
Meas_ADC = measurements.measurements(fs=ADC_sample_frequency)
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): for i in range(0, int(t_stop/delta_t)+1):
Ph1 = np.sin(2*np.pi*time*base_freq) Ph1 = np.sin(2*np.pi*time*base_freq)
zerocross.step(x=Ph1, time=time) if time - ADC_timer >= ADC_sample_period:
ADC_timer += ADC_sample_period
fft_measurements.step(data=Ph1, time=time, f_H1=zerocross.freq, unit="I") Meas_ADC.Interupt(Ph1)
time += delta_t time += delta_t
Meas_ADC.print()
Meas_ADC.plot()
zerocross.print()
#fft_measurements.plot()

58
measurements.py Normal file
View File

@@ -0,0 +1,58 @@
import numpy as np
import matplotlib.pyplot as plt
import IIR_filter
import zerocross_meas
import fundamental_freq_meas
import fft_meas
class measurements(object):
"""docstring for fft_meas"""
def __init__(self, fs=50e3):
# Sample Frequency
self.fs = fs
# Sample Period
self.ts = 1/self.fs
# Low Pass Filter
# fs = Sample Frequency, fc = LP Cutoff Frequency
self.LPfilter = IIR_filter.IIR_1_Order_LP(fs=self.fs, fc=200)
# Zero Crossing Algorithem
# fs = Sample Frequency, fc = LP Cutoff Frequency
self.zerocross = zerocross_meas.zerocross_meas()
# Fundamental Frequency Algorithem
# fs = Sample Frequency, tn = Time Period for calculations
self.base_freq = fundamental_freq_meas.fundamental_freq_meas(fs=self.fs, tn=1)
# FFT Algorithem
# fs = Sample Frequency, tn = Time Period for calculations
# 61000-4-7 says 200ms
# 10-cycles for 50Hz
# 12-cycles for 60Hz
self.fft = fft_meas.fft_meas(fs=self.fs, tn=0.2)
def Interupt(self, y):
# Filter Input
y_hat = self.LPfilter.step(y)
# Zerocross detection (self.zerocross.flag)
self.zerocross.detect(y_hat)
# Calculate the Fundamental frequency (self.base_freq.freq)
self.base_freq.step(y_hat, self.zerocross.flag)
# Calculate the FFT of the signal
self.fft.step(y, self.base_freq.freq, unit="I")
def print(self):
print(self.base_freq.freq)
def plot(self):
self.fft.plot_Y_H()

View File

@@ -6,35 +6,25 @@ import IIR_filter
class zerocross_meas(object): class zerocross_meas(object):
"""docstring for fft_meas""" """docstring for fft_meas"""
def __init__(self, fs=50e3, tn=1): def __init__(self):
self.fs = fs
self.ts = 1/self.fs # Old value
self.tn = tn
self.time = 0
self.freq = 0
self.freq_ts = 0
self.y = 0
self.y_old = 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): # Zerocross flag
self.flag = False
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: def detect(self, y):
self.freq = self.idx / self.tn
self.freq_ts = time
self.idx = 0
self.flag = 0
# If positive zerocross is detected
if self.y_old < 0 and y > 0:
self.flag = True
self.y_old = y
self.y_old = self.y
def print(self): def print(self):