From 5a5088f09ea11dd3c36f3bb6833a0c5d85715641 Mon Sep 17 00:00:00 2001 From: "Jan \"Yenya\" Kasprzak" Date: Thu, 18 Jul 2013 23:29:32 +0200 Subject: [PATCH] ambient.c: rewrite We now measure two running averages - ambient_slow and ambient_fast. The _fast is computed based on the ADC readings and few bits of averaging. The _slow is computed based on _fast and few bits of averaging. We don't want to change the zone too often, but on the other hand, we want fast reaction to the strong ambient light level changes. So we compute the new ambient zone based on _fast, and when the new one is current one +1 or -1, we retry using the _slow value. Only when even the _slow suggests that the ambient zone has changed, we permit the change to the neighbouring level. Changes to the more distant zone are permitted immediately. Also the logging rewritten to accomodate minimum, a few bits of max-min, and a few bits of level drop. --- firmware/ambient.c | 111 +++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 39 deletions(-) diff --git a/firmware/ambient.c b/firmware/ambient.c index aca1257..904f216 100644 --- a/firmware/ambient.c +++ b/firmware/ambient.c @@ -3,10 +3,12 @@ #include "lights.h" -#define AMBIENT_VAL_SHIFT 2 -static uint16_t ambient_val, ambient_val16; +#define AMBIENT_FAST_SHIFT 2 // running avg of ADC vals +#define AMBIENT_SLOW_SHIFT 6 // 1 << AMBIENT_SLOW_SHIFT times slower + +static uint16_t ambient_fast, ambient_slow; volatile unsigned char ambient_zone; -static unsigned char ambient_min, ambient_max, ambient_16drop; +static unsigned char ambient_min, ambient_max, ambient_drop; /* logging */ #define AMBIENT_LOG_SIZE 128 @@ -16,7 +18,7 @@ static unsigned char ambient_log[AMBIENT_LOG_SIZE] EEMEM; /* My photodiode reads 0x00C5 .. 0x033B */ typedef struct { - uint16_t lo, hi; + uint16_t lo, hi; // TODO: consider changing this to bytes } ambient_zone_t; /* @@ -24,20 +26,20 @@ typedef struct { * and having small overlaps in order to provide a bit of hysteresis. */ static ambient_zone_t ambient_zones[N_AMBIENT_ZONES] = { - { 0x0000 , 0x0250<= AMBIENT_LOG_SIZE - 1) return; - // eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_min); - eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_max); - eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_16drop); + ambient_max -= ambient_min; + ambient_max >>= 2; + if (ambient_max > 15) + ambient_max = 15; + + ambient_drop >>= 2; + if (ambient_drop > 15) + ambient_drop = 15; + + eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_min); + eeprom_write_byte(&ambient_log[ambient_log_offset++], + (ambient_max << 4) | ambient_drop); ambient_min = 0xFF; ambient_max = 0; - ambient_16drop = 0; + ambient_drop = 0; } static inline void ambient_zone_changed() @@ -77,42 +88,64 @@ static inline void ambient_zone_changed() pattern_reload(); } -void ambient_adc(uint16_t adcval) +static unsigned char val_to_zone(uint16_t ambient_val) { - unsigned char old_zone = ambient_zone; - unsigned char byte_val, byte_val16; + unsigned char new_zone = ambient_zone; - ambient_val += adcval - (ambient_val - >> (AMBIENT_VAL_SHIFT - AMBIENT_ADC_SHIFT)); + while (ambient_zones[new_zone].lo > ambient_val) + new_zone--; + + while (ambient_zones[new_zone].hi < ambient_val) + new_zone++; + + return new_zone; +} - while (ambient_zones[ambient_zone].lo > ambient_val) - ambient_zone--; +void ambient_adc(uint16_t adcval) +{ + unsigned char new_zone, user_zone; + unsigned char byte_fast, byte_slow, drop; + uint16_t a_10bit; - while (ambient_zones[ambient_zone].hi < ambient_val) - ambient_zone++; + // running avg - shorter timespan + ambient_fast += adcval - (ambient_fast >> AMBIENT_FAST_SHIFT); - byte_val = ambient_val >> (2 + AMBIENT_VAL_SHIFT - AMBIENT_ADC_SHIFT); + // running avg - longer timespan + a_10bit = ambient_fast >> (AMBIENT_FAST_SHIFT + AMBIENT_ADC_SHIFT); + ambient_slow += a_10bit - (ambient_slow >> AMBIENT_SLOW_SHIFT); - ambient_val16 += byte_val - (ambient_val16 >> 4); - byte_val16 = ambient_val16 >> 4; + // ambient zones are governed by shorter timespan by default + new_zone = val_to_zone(a_10bit); - if (byte_val16 > byte_val) { - byte_val16 -= byte_val; - if (byte_val16 > ambient_16drop) - ambient_16drop = byte_val16; + if (new_zone > 1 && ( + new_zone == ambient_zone-1 || new_zone == ambient_zone+1)) { + // but change to the neighbouring zone is governed by _slow, + // except to the darkest zone, where we want fast reaction. + new_zone = val_to_zone(ambient_slow >> AMBIENT_SLOW_SHIFT); } - if (ambient_min > byte_val) - ambient_min = byte_val; + // user_param ambient zone override + if ((user_zone = get_user_param(0)) > 0) + new_zone = user_zone - 1; - if (ambient_max < byte_val) - ambient_max = byte_val; + // TODO: maybe use these values instead of 10-bit? + byte_fast = a_10bit >> 2; + byte_slow = ambient_slow >> (AMBIENT_SLOW_SHIFT + 2); - // user_param ambient zone override - if ((byte_val = get_user_param(0)) > 0) - ambient_zone = byte_val - 1; + if (byte_slow > byte_fast) { + drop = byte_slow - byte_fast; + if (drop > ambient_drop) + ambient_drop = drop; + } + + if (ambient_min > byte_fast) + ambient_min = byte_fast; + + if (ambient_max < byte_fast) + ambient_max = byte_fast; - if (old_zone != ambient_zone) { + if (new_zone != ambient_zone) { + ambient_zone = new_zone; #if 0 log_byte(0xab); log_byte(ambient_zone); -- 2.43.5