ambient.c: rewrite
authorJan "Yenya" Kasprzak <kas@fi.muni.cz>
Thu, 18 Jul 2013 21:29:32 +0000 (23:29 +0200)
committerJan "Yenya" Kasprzak <kas@fi.muni.cz>
Thu, 18 Jul 2013 21:29:32 +0000 (23:29 +0200)
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

index aca1257..904f216 100644 (file)
@@ -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_VAL_SHIFT }, // dark
-       { 0x0240<<AMBIENT_VAL_SHIFT, 0x02e0<<AMBIENT_VAL_SHIFT }, // evening
-       { 0x02c0<<AMBIENT_VAL_SHIFT, 0x0306<<AMBIENT_VAL_SHIFT }, // dawn
-       { 0x02f8<<AMBIENT_VAL_SHIFT, 0xffff                    }, // day
+       { 0x0000, 0x0250 }, // dark
+       { 0x0240, 0x02e0 }, // evening
+       { 0x02c0, 0x0306 }, // dawn
+       { 0x02f8, 0xffff }, // day
 };
 
 void init_ambient()
 {
-       ambient_val = 0;
-       ambient_val16 = 0;
+       ambient_slow = 0;
+       ambient_fast = 0;
        ambient_max = 0;
        ambient_min = 0xFF;
        ambient_zone = 1;
-       ambient_16drop = 0;
+       ambient_drop = 0;
 
        ambient_log_offset = eeprom_read_byte(&ambient_log_offset_stored);
 
@@ -62,13 +64,22 @@ void ambient_log_min_max()
        if (ambient_log_offset >= 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);