From 40146343ea055de49ed6cbea7cd5eb38db0bc2e6 Mon Sep 17 00:00:00 2001 From: Thomas Klaehn Date: Sun, 9 Apr 2017 08:24:40 +0200 Subject: [PATCH] gate: Change opened/closed detection to slope calculation Signed-off-by: Thomas Klaehn --- gate_guard/__init__.py | 2 ++ gate_guard/data_buffer.py | 3 ++ gate_guard/gate.py | 76 +++++++++++++++++++++++---------------- 3 files changed, 51 insertions(+), 30 deletions(-) diff --git a/gate_guard/__init__.py b/gate_guard/__init__.py index 532cafe..26d6e17 100644 --- a/gate_guard/__init__.py +++ b/gate_guard/__init__.py @@ -6,8 +6,10 @@ Created on Dec 19, 2016 import time import gate import sys +import logging def main(): + logging.basicConfig(filename='/var/log/gate_guard.log', level=logging.DEBUG) gate_state = gate.Gate() try: while True: diff --git a/gate_guard/data_buffer.py b/gate_guard/data_buffer.py index 26b99f7..da95337 100644 --- a/gate_guard/data_buffer.py +++ b/gate_guard/data_buffer.py @@ -17,6 +17,9 @@ class DataBuffer(object): self.__data.append(element) return True + def get(self): + return self.__data + def average(self): if len(self.__data) is 0: return None diff --git a/gate_guard/gate.py b/gate_guard/gate.py index 6e9d226..edc614b 100644 --- a/gate_guard/gate.py +++ b/gate_guard/gate.py @@ -9,6 +9,9 @@ import data_buffer import light_sensor import engine import power_sensor +import scipy +import scipy.stats +import logging STATE_INIT = "init" STATE_OPENED = "open" @@ -22,19 +25,19 @@ LIGHT_CONSECUTIVE_READS = 10 LIGHT_LX_THRESHOLD = {"open":10, "close":5} MQTT_HOST = "proxy" -MQTT_TOPIC = "outdoor/chickenhouse/newgate" +MQTT_TOPIC = "outdoor/chickenhouse/gate" LIGHT_SENSOR_I2C_BUS = 1 LIGHT_SENSOR_I2C_ADDRESS = 0x23 POWER_SENSOR_I2C_BUS = 1 POWER_SENSOR_I2C_ADDRESS = 0x40 -POWER_CONSECUTIVE_READS = 1000 -MAX_ENGINE_POWER = {"up":330, "down":290} -MAX_GATE_RUNTIME = {"open":300, "close":300} -MIN_GATE_RUNTIME = {"open":10, "close":10} +SLOPE_COUNT = 100 +SLOPE_CNT_MIN = 10 +MAX_GATE_RUNTIME = {"open":60, "close":50} +MAX_POWER_SLOPE = {"up":10, "down":2} class Gate(object): def __init__(self): @@ -54,12 +57,12 @@ class Gate(object): self.__engine = engine.Engine(gpio_1=13, gpio_2=19) self.__power_sensor = power_sensor.PowerSensor(POWER_SENSOR_I2C_BUS, \ POWER_SENSOR_I2C_ADDRESS) - self.__power_data = data_buffer.DataBuffer(POWER_CONSECUTIVE_READS) self.__gate_move_timeout = 0 self.__light_read_timeout = 0 self.__error_count = 0 - self.__runtime_open = 0 - self.__runtime_close = 0 + + self.slope_power = data_buffer.DataBuffer(SLOPE_COUNT) + self.slope_time = data_buffer.DataBuffer(SLOPE_COUNT) def poll(self): current_time = time.time() @@ -75,6 +78,7 @@ class Gate(object): def __is_transition(self): if self.__last_state != self.__next_state: + logging.info('STATE: ' + self.__last_state + ' -> ' + self.__next_state) return True return False @@ -98,10 +102,13 @@ class Gate(object): self.__engine.down() time.sleep(5) self.__engine.stop() + slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(), + self.slope_power.get()) self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \ " Opened " + \ - str(self.__power_data.average()) + " mW") - self.__power_data.clear() + str(slope)) + self.slope_power.clear() + self.slope_time.clear() if (light_avg != None) and (light_avg <= LIGHT_LX_THRESHOLD["close"]): next_state = STATE_CLOSING @@ -115,10 +122,14 @@ class Gate(object): self.__engine.up() time.sleep(5) self.__engine.stop() + slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(), + self.slope_power.get()) self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \ " Closed " + \ - str(self.__power_data.average()) + " mW") + str(slope)) self.__power_data.clear() + self.slope_power.clear() + self.slope_time.clear() if (light_avg != None) and (light_avg > LIGHT_LX_THRESHOLD["open"]): next_state = STATE_OPENING @@ -129,43 +140,48 @@ class Gate(object): def __opening_handler(self, light_avg): next_state = self.__next_state if self.__is_transition(): - self.__runtime_open = time.time() + MIN_GATE_RUNTIME["open"] self.__engine.up() self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["open"] self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \ " Opening " + str(light_avg) + " lx") - - if time.time() > self.__gate_move_timeout: + tm = time.time() + if tm > self.__gate_move_timeout: next_state = STATE_ERROR else: - self.__power_data.push(self.__power_sensor.power_mw()) - current_avg = self.__power_data.average() - if current_avg != None: - if current_avg > MAX_ENGINE_POWER["up"]: - if time.time() > self.__runtime_open: - next_state = STATE_OPENED - + pwr = self.__power_sensor.power_mw() + self.slope_power.push(pwr) + self.slope_time.push(tm) + logging.debug('up: ' + str(tm) + ' ' + str(pwr)) + slope = 0 + if self.slope_power.length() >= SLOPE_CNT_MIN: + slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(), + self.slope_power.get()) + if slope > MAX_POWER_SLOPE["up"]: + next_state = STATE_OPENED self.__update_state(next_state) def __closing_handler(self, light_avg): next_state = self.__next_state if self.__is_transition(): - self.__runtime_close = time.time() + MIN_GATE_RUNTIME["close"] self.__engine.down() self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["close"] self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \ " Closing " + str(light_avg) + " lx") - if time.time() > self.__gate_move_timeout: + tm = time.time() + if tm > self.__gate_move_timeout: next_state = STATE_ERROR else: - self.__power_data.push(self.__power_sensor.power_mw()) - current_avg = self.__power_data.average() - if current_avg != None: - if current_avg > MAX_ENGINE_POWER["down"]: - if time.time() > self.__runtime_close: - next_state = STATE_CLOSED - + pwr = self.__power_sensor.power_mw() + self.slope_power.push(pwr) + self.slope_time.push(pwr) + logging.debug('dw: ' + str(tm) + ' ' + str(pwr)) + slope = 0 + if self.slope_power.length() >= SLOPE_CNT_MIN: + slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(), + self.slope_power.get()) + if slope > MAX_POWER_SLOPE["down"]: + next_state = STATE_OPENED self.__update_state(next_state)