diff --git a/nilmtools/trainola.py b/nilmtools/trainola.py index 7d90db7..28edcd8 100755 --- a/nilmtools/trainola.py +++ b/nilmtools/trainola.py @@ -106,9 +106,14 @@ class Exemplar(object): def peak_detect(data, delta): """Simple min/max peak detection algorithm, taken from my code - in the disagg.m from the 10-8-5 paper""" - mins = []; - maxs = []; + in the disagg.m from the 10-8-5 paper. + + Returns an array of peaks: each peak is a tuple + (n, p, is_max) + where n is the row number in 'data', and p is 'data[n]', + and is_max is True if this is a maximum, False if it's a minimum, + """ + peaks = []; cur_min = (None, np.inf) cur_max = (None, -np.inf) lookformax = False @@ -119,15 +124,15 @@ def peak_detect(data, delta): cur_min = (n, p) if lookformax: if p < (cur_max[1] - delta): - maxs.append(cur_max) + peaks.append((cur_max[0], cur_max[1], True)) cur_min = (n, p) lookformax = False else: if p > (cur_min[1] + delta): - mins.append(cur_min) + peaks.append((cur_min[0], cur_min[1], False)) cur_max = (n, p) lookformax = True - return (mins, maxs) + return peaks def timestamp_to_short_human(timestamp): dt = datetime_tz.datetime_tz.fromtimestamp(timestamp_to_seconds(timestamp)) @@ -164,11 +169,35 @@ def trainola_matcher(data, interval, args, insert_func, final_chunk): # Find the peaks using the column with the largest amplitude biggest = e.scale.index(max(e.scale)) - peaks_minmax = peak_detect(corrs[biggest], 0.1) - peaks = [ p[0] for p in peaks_minmax[1] ] - - # Now look at every peak - for row in peaks: + peaks = peak_detect(corrs[biggest], 0.1) + + # To try to reduce false positives, discard peaks where + # there's a higher-magnitude peak (either min or max) within + # one exemplar width nearby. + good_peak_locations = [] + for (i, (n, p, is_max)) in enumerate(peaks): + if not is_max: + continue + ok = True + # check up to 'e.count' rows before this one + j = i-1 + while ok and j >= 0 and peaks[j][0] > (n - e.count): + if abs(peaks[j][1]) > abs(p): + ok = False + j -= 1 + + # check up to 'e.count' rows after this one + j = i+1 + while ok and j < len(peaks) and peaks[j][0] < (n + e.count): + if abs(peaks[j][1]) > abs(p): + ok = False + j += 1 + + if ok: + good_peak_locations.append(n) + + # Now look at all good peaks + for row in good_peak_locations: # Correlation for each column must be close enough to 1. for (corr, scale) in zip(corrs, e.scale): # The accepted distance from 1 is based on the relative