Doing THD calc
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
__pycache__/fundamental_freq_meas.cpython-312.pyc
Normal file
BIN
__pycache__/fundamental_freq_meas.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/measurements.cpython-312.pyc
Normal file
BIN
__pycache__/measurements.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
105
fft_meas.py
105
fft_meas.py
@@ -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)
|
def step(self, y, base_freq, unit):
|
||||||
|
|
||||||
self.idx = -1
|
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
51
fundamental_freq_meas.py
Normal 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
26
main.py
@@ -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,32 +11,37 @@ 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()
|
||||||
zerocross.print()
|
Meas_ADC.plot()
|
||||||
#fft_measurements.plot()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
58
measurements.py
Normal file
58
measurements.py
Normal 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()
|
||||||
|
|
||||||
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user