|
|
@@ -2,12 +2,18 @@ |
|
|
|
|
|
|
|
# Sine wave fitting. This runs about 5x faster than realtime on raw data. |
|
|
|
|
|
|
|
from nilmdb.utils.printf import * |
|
|
|
import nilmtools.filter |
|
|
|
import nilmdb.client |
|
|
|
from nilmdb.utils.time import (timestamp_to_human, |
|
|
|
timestamp_to_seconds, |
|
|
|
seconds_to_timestamp) |
|
|
|
|
|
|
|
from numpy import * |
|
|
|
from scipy import * |
|
|
|
#import pylab as p |
|
|
|
import operator |
|
|
|
import sys |
|
|
|
|
|
|
|
def main(argv = None): |
|
|
|
f = nilmtools.filter.Filter() |
|
|
@@ -59,12 +65,40 @@ def main(argv = None): |
|
|
|
f.process_numpy(process, args = (args.column, args.frequency, args.min_amp, |
|
|
|
args.min_freq, args.max_freq)) |
|
|
|
|
|
|
|
class SuppressibleWarning(object): |
|
|
|
def __init__(self, maxcount = 10, maxsuppress = 100): |
|
|
|
self.maxcount = maxcount |
|
|
|
self.maxsuppress = maxsuppress |
|
|
|
self.count = 0 |
|
|
|
self.last_msg = "" |
|
|
|
|
|
|
|
def _write(self, sec, msg): |
|
|
|
if sec: |
|
|
|
now = "[" + timestamp_to_human(seconds_to_timestamp(sec)) + "] " |
|
|
|
else: |
|
|
|
now = "" |
|
|
|
sys.stderr.write(now + msg) |
|
|
|
|
|
|
|
def warn(self, msg, seconds = None): |
|
|
|
self.count += 1 |
|
|
|
if self.count <= self.maxcount: |
|
|
|
self._write(seconds, msg) |
|
|
|
if (self.count - self.maxcount) >= self.maxsuppress: |
|
|
|
self.reset(seconds) |
|
|
|
|
|
|
|
def reset(self, seconds = None): |
|
|
|
if self.count > self.maxcount: |
|
|
|
self._write(seconds, sprintf("(%d warnings suppressed)\n", |
|
|
|
self.count - self.maxcount)) |
|
|
|
self.count = 0 |
|
|
|
|
|
|
|
def process(data, interval, args, insert_function, final): |
|
|
|
(column, f_expected, a_min, f_min, f_max) = args |
|
|
|
rows = data.shape[0] |
|
|
|
|
|
|
|
# Estimate sampling frequency from timestamps |
|
|
|
fs = 1e6 * (rows-1) / (data[-1][0] - data[0][0]) |
|
|
|
fs = (rows-1) / (timestamp_to_seconds(data[-1][0]) - |
|
|
|
timestamp_to_seconds(data[0][0])) |
|
|
|
|
|
|
|
# Pull out about 3.5 periods of data at once; |
|
|
|
# we'll expect to match 3 zero crossings in each window |
|
|
@@ -74,26 +108,31 @@ def process(data, interval, args, insert_function, final): |
|
|
|
if rows < N: |
|
|
|
return 0 |
|
|
|
|
|
|
|
warn = SuppressibleWarning(3, 1000) |
|
|
|
|
|
|
|
# Process overlapping windows |
|
|
|
start = 0 |
|
|
|
num_zc = 0 |
|
|
|
last_inserted_timestamp = None |
|
|
|
while start < (rows - N): |
|
|
|
this = data[start:start+N, column] |
|
|
|
t_min = data[start, 0]/1e6 |
|
|
|
t_max = data[start+N-1, 0]/1e6 |
|
|
|
t_min = timestamp_to_seconds(data[start, 0]) |
|
|
|
t_max = timestamp_to_seconds(data[start+N-1, 0]) |
|
|
|
|
|
|
|
# Do 4-parameter sine wave fit |
|
|
|
(A, f0, phi, C) = sfit4(this, fs) |
|
|
|
|
|
|
|
# Check bounds. If frequency is too crazy, ignore this window |
|
|
|
if f0 < f_min or f0 > f_max: |
|
|
|
print "frequency", f0, "outside valid range", f_min, "-", f_max |
|
|
|
warn.warn(sprintf("frequency %s outside valid range %s - %s\n", |
|
|
|
str(f0), str(f_min), str(f_max)), t_min) |
|
|
|
start += N |
|
|
|
continue |
|
|
|
|
|
|
|
# If amplitude is too low, results are probably just noise |
|
|
|
if A < a_min: |
|
|
|
print "amplitude", A, "below minimum threshold", a_min |
|
|
|
warn.warn(sprintf("amplitude %s below minimum threshold %s\n", |
|
|
|
str(A), str(a_min)), t_min) |
|
|
|
start += N |
|
|
|
continue |
|
|
|
|
|
|
@@ -116,7 +155,13 @@ def process(data, interval, args, insert_function, final): |
|
|
|
while zc_n < (N - period_n/2): |
|
|
|
#p.plot(zc_n, C, 'ro') |
|
|
|
t = t_min + zc_n / fs |
|
|
|
insert_function([[t * 1e6, f0, A, C]]) |
|
|
|
if (last_inserted_timestamp is None or |
|
|
|
t > last_inserted_timestamp): |
|
|
|
insert_function([[seconds_to_timestamp(t), f0, A, C]]) |
|
|
|
last_inserted_timestamp = t |
|
|
|
warn.reset(t) |
|
|
|
else: |
|
|
|
warn.warn("timestamp overlap\n", t) |
|
|
|
num_zc += 1 |
|
|
|
last_zc = zc_n |
|
|
|
zc_n += period_n |
|
|
@@ -134,6 +179,7 @@ def process(data, interval, args, insert_function, final): |
|
|
|
start = int(round(start + advance)) |
|
|
|
|
|
|
|
# Return the number of rows we've processed |
|
|
|
warn.reset(last_inserted_timestamp) |
|
|
|
print "Marked", num_zc, "zero-crossings in", start, "rows" |
|
|
|
return start |
|
|
|
|
|
|
|