]> www.fi.muni.cz Git - bike-lights.git/blobdiff - firmware/ambient.c
ambient.c: fixup, disable shadow code
[bike-lights.git] / firmware / ambient.c
index f6e8ae5f5be088eba62d2efc6533b495e5a254bd..3ae15d3c0e97080e1ac56957f06b67a883275305 100644 (file)
 #include <avr/io.h>
+#include <avr/eeprom.h>
 
 #include "lights.h"
 
-static uint16_t ambient_val;
-volatile unsigned char ambient_zone;
+#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, ambient_shadow;
+static unsigned char ambient_min, ambient_max, ambient_drop;
+
+/* logging */
+#define AMBIENT_LOG_SIZE 256
+static unsigned char ambient_log_offset_stored EEMEM;
+static unsigned char ambient_log_offset;
+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;
 
 /*
  * Note: these have to be sorted, starting with 0, ending with 0xFFFF
  * and having small overlaps in order to provide a bit of hysteresis.
  */
-static ambient_zone_t ambient_zones[] = {
-       { 0x0000, 0xb000 }, // dark
-       { 0xa800, 0xc700 },
-       { 0xc600, 0xcb00 },
-       { 0xca80, 0xffff }
+static ambient_zone_t ambient_zones[N_AMBIENT_ZONES] = {
+#ifdef PAVLINA
+       { 0x0000, 0x02d0 }, // dark
+       { 0x02b0, 0x0318 }, // evening
+       { 0x0308, 0x032c }, // dawn
+       { 0x0324, 0xffff }, // day
+#else
+       { 0x0000, 0x0250 }, // dark
+       { 0x0230, 0x02e8 }, // evening
+       { 0x02d0, 0x0302 }, // dawn
+       { 0x02fc, 0xffff }, // day
+#endif
 };
-#define N_AMBIENT_ZONES (sizeof(ambient_zones)/sizeof(ambient_zones[0]))
+
+#define SHADOW_DROP_LIMIT      0x20 // in ADC units (0..0x3ff)
 
 void init_ambient()
 {
-       ambient_val = 0;
+       ambient_slow = 0;
+       ambient_fast = 0;
+       ambient_max = 0;
+       ambient_min = 0xFF;
        ambient_zone = 1;
+       ambient_drop = 0;
+       ambient_shadow = 0;
+
+       ambient_log_offset = eeprom_read_byte(&ambient_log_offset_stored);
+
+       if (ambient_log_offset == AMBIENT_LOG_SIZE)
+               ambient_log_offset = 0; // start over
 }
 
-void ambient_zone_changed()
+void susp_ambient()
 {
-#if 1
-       log_byte(0xab);
-       log_byte(ambient_zone);
-       log_word(ambient_val);
-       log_flush();
-#endif
+       unsigned char stored_offset;
+
+       ambient_log_min_max();
 
-       // led_set_pattern(N_PWMLEDS, status_led_pattern_select());
-       // led_set_pattern(N_PWMLEDS+1, illumination_led_pattern_select());
-       // pattern_reload();
+       stored_offset = eeprom_read_byte(&ambient_log_offset_stored);
+       if (stored_offset != ambient_log_offset)
+               eeprom_write_byte(&ambient_log_offset_stored,
+                       ambient_log_offset);
+}
+
+void ambient_log_min_max()
+{
+       if (ambient_log_offset >= AMBIENT_LOG_SIZE - 1)
+               ambient_log_offset = 0; // start over
+
+       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_drop = 0;
+}
+
+static inline void ambient_zone_changed()
+{
+       pwmled_select_brightness();
+       pattern_reload();
+}
+
+static unsigned char val_to_zone(uint16_t ambient_val)
+{
+       unsigned char new_zone = ambient_zone;
+
+       if (new_zone >= N_AMBIENT_ZONES)
+               new_zone = N_AMBIENT_ZONES-1;
+
+       while (ambient_zones[new_zone].lo > ambient_val)
+               new_zone--;
+
+       while (ambient_zones[new_zone].hi < ambient_val)
+               new_zone++;
+
+       return new_zone;
 }
 
 void ambient_adc(uint16_t adcval)
 {
-       unsigned char old_zone = ambient_zone;
+       unsigned char new_zone, user_zone, new_shadow = 0;
+       unsigned char byte_fast, byte_slow, drop;
+       uint16_t fast_10bit, slow_10bit;
+
+       // running avg - shorter timespan
+       ambient_fast += adcval - (ambient_fast >> AMBIENT_FAST_SHIFT);
+
+       // running avg - longer timespan
+       fast_10bit = ambient_fast >> (AMBIENT_FAST_SHIFT + AMBIENT_ADC_SHIFT);
+       ambient_slow += fast_10bit - (ambient_slow >> AMBIENT_SLOW_SHIFT);
+
+       // ambient zones are governed by shorter timespan by default
+       new_zone = val_to_zone(fast_10bit);
+
+       slow_10bit = ambient_slow >> AMBIENT_SLOW_SHIFT;
+
+       if ((new_zone > ambient_zone)
+               || (new_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(slow_10bit);
+       }
 
-       ambient_val += adcval - (ambient_val >> 3);
+       // user_param ambient zone override
+       if ((user_zone = get_user_param(0)) > 0)
+               new_zone = user_zone - 1;
 
-       while (ambient_zones[ambient_zone].lo > ambient_val)
-               ambient_zone--;
+#if 0  // ignore shadow-entering code altogether for now
+       // are we entering the shadow?
+       if (!user_zone && new_zone < ambient_zone
+               && ambient_zone >= 2 && slow_10bit > fast_10bit
+               && slow_10bit - fast_10bit >= SHADOW_DROP_LIMIT) {
+               // we are entering the shadow
+               new_shadow = 0x30;
+       }
+
+       if (ambient_shadow) {
+               if (new_shadow) {
+                       new_zone = ambient_zone; // don't change while entering shadow
+                       ambient_shadow = new_shadow; // update the timeout
+               } else {
+                       ambient_shadow--;
+                       if (!ambient_shadow) { // leaving the shadow
+                               ambient_zone_changed();
+                       }
+               }
+       } else if (new_shadow) {
+               ambient_shadow = new_shadow; // set up the timeout
+               new_zone = ambient_zone; // don't change while entering shadow
+               ambient_zone_changed();  // notify others the first time
+       }
+#endif
+
+       // TODO: maybe use these values instead of 10-bit?
+       byte_fast = fast_10bit >> 2;
+       byte_slow = slow_10bit >> 2;
 
-       while (ambient_zones[ambient_zone].hi < ambient_val)
-               ambient_zone++;
+       if (byte_slow > byte_fast) {
+               drop = byte_slow - byte_fast;
+               if (drop > ambient_drop)
+                       ambient_drop = drop;
+       }
+
+       if (ambient_min > byte_slow)
+               ambient_min = byte_slow;
+
+       if (ambient_max < byte_slow)
+               ambient_max = byte_slow;
 
+       if (new_zone != ambient_zone) {
+               ambient_zone = new_zone;
 #if 0
-       if (old_zone != ambient_zone) {
                log_byte(0xab);
                log_byte(ambient_zone);
                log_word(adcval);
                log_flush();
-       }
-               // ambient_zone_changed();
 #endif
+               ambient_zone_changed();
+       }
 }