pqopen-lib 0.7.7__tar.gz → 0.7.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/PKG-INFO +1 -1
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pqopen/powersystem.py +40 -11
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pyproject.toml +2 -1
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/test/powersystem-test.py +18 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/.gitignore +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/LICENSE +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/README.md +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pqopen/__init__.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pqopen/eventdetector.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pqopen/goertzel.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pqopen/helper.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pqopen/powerquality.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pqopen/storagecontroller.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/pqopen/zcd.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/test/data_files/event_data_level_low.csv +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/test/eventdetector-test.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/test/goertzel-test.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/test/helper-test.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/test/powerquality-test.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/test/storagecontroller-test.py +0 -0
- {pqopen_lib-0.7.7 → pqopen_lib-0.7.9}/test/zcd-test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pqopen-lib
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.9
|
|
4
4
|
Summary: A power quality processing library for calculating parameters from waveform data
|
|
5
5
|
Project-URL: Homepage, https://github.com/DaqOpen/pqopen-lib
|
|
6
6
|
Project-URL: Issues, https://github.com/DaqOpen/pqopen-lib/issues
|
|
@@ -88,7 +88,8 @@ class PowerSystem(object):
|
|
|
88
88
|
"mains_signaling_tracer": {},
|
|
89
89
|
"debug_channels": False,
|
|
90
90
|
"energy_channels": {},
|
|
91
|
-
"one_period_fundamental":
|
|
91
|
+
"one_period_fundamental": 0,
|
|
92
|
+
"rms_trapz_rule": False}
|
|
92
93
|
self._prepare_calc_channels()
|
|
93
94
|
self.output_channels: Dict[str, DataChannelBuffer] = {}
|
|
94
95
|
self._last_processed_sidx = 0
|
|
@@ -100,6 +101,7 @@ class PowerSystem(object):
|
|
|
100
101
|
self._last_zc_frac = 0.0
|
|
101
102
|
self._calculation_mode = "NORMAL"
|
|
102
103
|
self._last_known_freq = self.nominal_frequency
|
|
104
|
+
self._fund_freq_list = np.zeros(1)
|
|
103
105
|
self._channel_update_needed = False
|
|
104
106
|
|
|
105
107
|
|
|
@@ -214,13 +216,20 @@ class PowerSystem(object):
|
|
|
214
216
|
self._features["energy_channels"] = {"persist_file": persist_file, "energy_counters": energy_counters}
|
|
215
217
|
self._channel_update_needed = True
|
|
216
218
|
|
|
217
|
-
def enable_one_period_fundamental(self):
|
|
219
|
+
def enable_one_period_fundamental(self, freq_agg_cycles: int = 50):
|
|
218
220
|
"""
|
|
219
221
|
Enables the calculation of one (single) period fundamental values
|
|
220
222
|
"""
|
|
221
|
-
self._features["one_period_fundamental"] =
|
|
223
|
+
self._features["one_period_fundamental"] = freq_agg_cycles
|
|
224
|
+
self._fund_freq_list = np.zeros(freq_agg_cycles)
|
|
222
225
|
self._channel_update_needed = True
|
|
223
226
|
|
|
227
|
+
def enable_rms_trapz_rule(self):
|
|
228
|
+
"""
|
|
229
|
+
Enables the trapezoidal integration rule for rms calculation
|
|
230
|
+
"""
|
|
231
|
+
self._features["rms_trapz_rule"] = True
|
|
232
|
+
|
|
224
233
|
def _resync_nper_abs_time(self, zc_idx: int):
|
|
225
234
|
if not self._features["nper_abs_time_sync"]:
|
|
226
235
|
return None
|
|
@@ -320,14 +329,12 @@ class PowerSystem(object):
|
|
|
320
329
|
self._zero_crossings.append(actual_zc)
|
|
321
330
|
if self._zero_cross_counter <= 1:
|
|
322
331
|
continue
|
|
323
|
-
# Calculate Frequency
|
|
324
|
-
frequency = self._samplerate/(self._zero_crossings[-1] + actual_zc_frac - self._zero_crossings[-2] - self._last_zc_frac)
|
|
325
|
-
self._last_zc_frac = actual_zc_frac
|
|
326
332
|
# Add actual zero cross counter to debug channel if enabled
|
|
327
333
|
if "pidx" in self._calc_channels["one_period"]['_debug']:
|
|
328
334
|
self._calc_channels["one_period"]['_debug']['pidx'].put_data_single(self._zero_crossings[-1], self._zero_cross_counter)
|
|
329
335
|
# Process one period calculation, start with second zc
|
|
330
|
-
self._process_one_period(self._zero_crossings[-2], self._zero_crossings[-1],
|
|
336
|
+
self._process_one_period(self._zero_crossings[-2], self._zero_crossings[-1], actual_zc_frac)
|
|
337
|
+
self._last_zc_frac = actual_zc_frac
|
|
331
338
|
if ((self._zero_cross_counter-1) % self.nper) == 0 and (self._zero_cross_counter > self.nper):
|
|
332
339
|
# Process multi-period
|
|
333
340
|
self._process_multi_period(self._zero_crossings[-self.nper - 1], self._zero_crossings[-1])
|
|
@@ -336,14 +343,17 @@ class PowerSystem(object):
|
|
|
336
343
|
|
|
337
344
|
self._last_processed_sidx = stop_acq_sidx
|
|
338
345
|
|
|
339
|
-
def _process_one_period(self, period_start_sidx: int, period_stop_sidx: int,
|
|
346
|
+
def _process_one_period(self, period_start_sidx: int, period_stop_sidx: int, actual_zc_frac: float= 0):
|
|
340
347
|
"""
|
|
341
348
|
Processes data for a single period, calculating voltage, current, and power.
|
|
342
349
|
|
|
343
350
|
Parameters:
|
|
344
351
|
period_start_sidx: Start sample index of the period.
|
|
345
352
|
period_stop_sidx: Stop sample index of the period.
|
|
353
|
+
frequency: Fundamental frequency
|
|
346
354
|
"""
|
|
355
|
+
# Calculate Frequency
|
|
356
|
+
frequency = self._samplerate/(period_stop_sidx + actual_zc_frac - period_start_sidx - self._last_zc_frac)
|
|
347
357
|
self._calc_channels["one_period"]['power']['freq'].put_data_single(period_stop_sidx, frequency)
|
|
348
358
|
if "sidx" in self._calc_channels["one_period"]['_debug']:
|
|
349
359
|
self._calc_channels["one_period"]['_debug']['sidx'].put_data_single(period_stop_sidx, period_stop_sidx)
|
|
@@ -365,7 +375,21 @@ class PowerSystem(object):
|
|
|
365
375
|
msv_edge, msv_value = phase._mains_signaling_tracer.process(u_values[::10])
|
|
366
376
|
for phys_type, output_channel in phase._calc_channels["one_period"]["voltage"].items():
|
|
367
377
|
if phys_type == "trms":
|
|
368
|
-
|
|
378
|
+
if self._features["rms_trapz_rule"]:
|
|
379
|
+
add_start_idx_sample = 1 if self._last_zc_frac < 0 else 0
|
|
380
|
+
add_stop_idx_sample = 1 if actual_zc_frac > 0 else 0
|
|
381
|
+
u_trapz_values = phase._u_channel.read_data_by_index(phase_period_start_sidx-add_start_idx_sample, phase_period_stop_sidx+add_stop_idx_sample)
|
|
382
|
+
sample_points = np.arange(len(u_values), dtype=np.float64)
|
|
383
|
+
if add_start_idx_sample:
|
|
384
|
+
u_trapz_values[0] = (u_trapz_values[1] - u_trapz_values[0])*(1+self._last_zc_frac) + u_trapz_values[0]
|
|
385
|
+
sample_points = np.insert(sample_points,0,self._last_zc_frac)
|
|
386
|
+
if add_stop_idx_sample:
|
|
387
|
+
u_trapz_values[-1] = (u_trapz_values[-1] - u_trapz_values[-2])*self._last_zc_frac + u_trapz_values[-2]
|
|
388
|
+
sample_points = np.insert(sample_points,len(sample_points),sample_points[-1]+actual_zc_frac)
|
|
389
|
+
integral = np.trapezoid(np.power(u_trapz_values,2), sample_points)
|
|
390
|
+
u_rms = np.sqrt(integral * frequency / self._samplerate)
|
|
391
|
+
else:
|
|
392
|
+
u_rms = np.sqrt(np.mean(np.power(u_values, 2)))
|
|
369
393
|
output_channel.put_data_single(phase_period_stop_sidx, u_rms)
|
|
370
394
|
if phys_type == "msv_bit":
|
|
371
395
|
if msv_edge is not None:
|
|
@@ -376,7 +400,10 @@ class PowerSystem(object):
|
|
|
376
400
|
output_channel.put_data_single(phase_period_stop_sidx, np.abs(np.diff(u_values)).max())
|
|
377
401
|
if phys_type == "fund_rms":
|
|
378
402
|
# Use sample-discrete frequency, not the exact one for full cycle
|
|
379
|
-
|
|
403
|
+
self._fund_freq_list = np.roll(self._fund_freq_list,-1)
|
|
404
|
+
self._fund_freq_list[-1] = self._samplerate/len(u_values)
|
|
405
|
+
mean_freq = self._fund_freq_list[self._fund_freq_list>0].mean()
|
|
406
|
+
fund_amp, fund_phase = calc_single_freq(u_values, mean_freq, self._samplerate)
|
|
380
407
|
output_channel.put_data_single(phase_period_stop_sidx, fund_amp)
|
|
381
408
|
for phys_type, output_channel in phase._calc_channels["half_period"]["voltage"].items():
|
|
382
409
|
if phys_type == "trms":
|
|
@@ -653,7 +680,6 @@ class PowerPhase(object):
|
|
|
653
680
|
self._calc_channels["half_period"]["voltage"]["trms"] = DataChannelBuffer('U{:s}_hp_rms'.format(self.name), agg_type='rms', unit="V")
|
|
654
681
|
self._calc_channels["one_period"]["voltage"]["trms"] = DataChannelBuffer('U{:s}_1p_rms'.format(self.name), agg_type='rms', unit="V")
|
|
655
682
|
self._calc_channels["one_period"]["voltage"]["slope"] = DataChannelBuffer('U{:s}_1p_slope'.format(self.name), agg_type='max', unit="V/s")
|
|
656
|
-
self._calc_channels["one_period"]["voltage"]["fund_rms"] = DataChannelBuffer('U{:s}_1p_H1_rms'.format(self.name), agg_type='rms', unit="V")
|
|
657
683
|
self._calc_channels["one_period_ovlp"]["voltage"]["trms"] = DataChannelBuffer('U{:s}_1p_hp_rms'.format(self.name), agg_type='rms', unit="V")
|
|
658
684
|
self._calc_channels["multi_period"]["voltage"]["trms"] = DataChannelBuffer('U{:s}_rms'.format(self.name), agg_type='rms', unit="V")
|
|
659
685
|
|
|
@@ -678,6 +704,9 @@ class PowerPhase(object):
|
|
|
678
704
|
self._calc_channels["one_period"]["voltage"]["msv_mag"] = DataChannelBuffer('U{:s}_1p_msv'.format(self.name), agg_type='max', unit="V")
|
|
679
705
|
self._calc_channels["one_period"]["voltage"]["msv_bit"] = DataChannelBuffer('U{:s}_msv_bit'.format(self.name), unit="")
|
|
680
706
|
|
|
707
|
+
if "one_period_fundamental" in features and features["one_period_fundamental"] > 0:
|
|
708
|
+
self._calc_channels["one_period"]["voltage"]["fund_rms"] = DataChannelBuffer('U{:s}_1p_H1_rms'.format(self.name), agg_type='rms', unit="V")
|
|
709
|
+
|
|
681
710
|
# Create Current Channels
|
|
682
711
|
if self._i_channel:
|
|
683
712
|
self._calc_channels["one_period"]["current"] = {}
|
|
@@ -12,12 +12,13 @@ packages = ["pqopen"]
|
|
|
12
12
|
|
|
13
13
|
[project]
|
|
14
14
|
name = "pqopen-lib"
|
|
15
|
-
version = "0.7.
|
|
15
|
+
version = "0.7.9"
|
|
16
16
|
dependencies = [
|
|
17
17
|
"numpy",
|
|
18
18
|
"daqopen-lib",
|
|
19
19
|
"scipy"
|
|
20
20
|
]
|
|
21
|
+
|
|
21
22
|
authors = [
|
|
22
23
|
{ name="Michael Oberhofer", email="michael@daqopen.com" },
|
|
23
24
|
]
|
|
@@ -229,7 +229,25 @@ class TestPowerSystemCalculation(unittest.TestCase):
|
|
|
229
229
|
self.assertIsNone(np.testing.assert_allclose(u_h_rms[:,3], expected_u_h3_rms, rtol=0.01))
|
|
230
230
|
u_msv_rms, _ = self.power_system.output_channels["U1_msv_rms"].read_data_by_acq_sidx(0, u_values.size)
|
|
231
231
|
self.assertIsNone(np.testing.assert_allclose(u_msv_rms, expected_u_msv_rms, rtol=0.01))
|
|
232
|
+
|
|
233
|
+
def test_one_period_calc_trapz_rule(self):
|
|
234
|
+
t = np.linspace(0, 1, int(self.power_system._samplerate), endpoint=False)
|
|
235
|
+
u_values = np.sqrt(2)*np.sin(2*np.pi*50.02*t)
|
|
232
236
|
|
|
237
|
+
expected_u_rms = np.array(np.zeros(47)) + 1.0
|
|
238
|
+
expected_freq = np.array(np.zeros(47)) + 50.02
|
|
239
|
+
|
|
240
|
+
self.power_system.enable_rms_trapz_rule()
|
|
241
|
+
self.u_channel.put_data(u_values)
|
|
242
|
+
self.i_channel.put_data(u_values)
|
|
243
|
+
self.power_system.process()
|
|
244
|
+
|
|
245
|
+
# Check Voltage
|
|
246
|
+
u_rms, _ = self.power_system.output_channels["U1_1p_rms"].read_data_by_acq_sidx(0, u_values.size)
|
|
247
|
+
self.assertIsNone(np.testing.assert_array_almost_equal(u_rms[1:], expected_u_rms, 4))
|
|
248
|
+
# Check Frequency
|
|
249
|
+
freq, _ = self.power_system.output_channels["Freq"].read_data_by_acq_sidx(0, u_values.size)
|
|
250
|
+
self.assertIsNone(np.testing.assert_array_almost_equal(freq[1:], expected_freq, 3))
|
|
233
251
|
|
|
234
252
|
class TestPowerSystemCalculationThreePhase(unittest.TestCase):
|
|
235
253
|
def setUp(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|