]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/ambient.c
ambient.c: "entering shadow" detection
[bike-lights.git] / firmware / ambient.c
1 #include <avr/io.h>
2 #include <avr/eeprom.h>
3
4 #include "lights.h"
5
6 #define AMBIENT_FAST_SHIFT 2    // running avg of ADC vals
7 #define AMBIENT_SLOW_SHIFT 6    // 1 << AMBIENT_SLOW_SHIFT times slower
8
9 static uint16_t ambient_fast, ambient_slow;
10 volatile unsigned char ambient_zone, ambient_shadow;
11 static unsigned char ambient_min, ambient_max, ambient_drop;
12
13 /* logging */
14 #define AMBIENT_LOG_SIZE 128
15 static unsigned char ambient_log_offset_stored EEMEM;
16 static unsigned char ambient_log_offset;
17 static unsigned char ambient_log[AMBIENT_LOG_SIZE] EEMEM;
18
19 /* My photodiode reads 0x00C5 .. 0x033B */
20 typedef struct {
21         uint16_t lo, hi; // TODO: consider changing this to bytes
22 } ambient_zone_t;
23
24 /*
25  * Note: these have to be sorted, starting with 0, ending with 0xFFFF
26  * and having small overlaps in order to provide a bit of hysteresis.
27  */
28 static ambient_zone_t ambient_zones[N_AMBIENT_ZONES] = {
29         { 0x0000, 0x0250 }, // dark
30         { 0x0240, 0x02e0 }, // evening
31         { 0x02c0, 0x0300 }, // dawn
32         { 0x02f8, 0xffff }, // day
33 };
34
35 #define SHADOW_DROP_LIMIT       0x20 // in ADC units (0..0x3ff)
36
37 void init_ambient()
38 {
39         ambient_slow = 0;
40         ambient_fast = 0;
41         ambient_max = 0;
42         ambient_min = 0xFF;
43         ambient_zone = 1;
44         ambient_drop = 0;
45         ambient_shadow = 0;
46
47         ambient_log_offset = eeprom_read_byte(&ambient_log_offset_stored);
48
49         if (ambient_log_offset == AMBIENT_LOG_SIZE)
50                 ambient_log_offset = 0; // start over
51 }
52
53 void susp_ambient()
54 {
55         unsigned char stored_offset;
56
57         ambient_log_min_max();
58
59         stored_offset = eeprom_read_byte(&ambient_log_offset_stored);
60         if (stored_offset != ambient_log_offset)
61                 eeprom_write_byte(&ambient_log_offset_stored,
62                         ambient_log_offset);
63 }
64
65 void ambient_log_min_max()
66 {
67         if (ambient_log_offset >= AMBIENT_LOG_SIZE - 1)
68                 return;
69
70         ambient_max -= ambient_min;
71         // ambient_max >>= 2;
72         if (ambient_max > 15)
73                 ambient_max = 15;
74
75         ambient_drop >>= 2;
76         if (ambient_drop > 15)
77                 ambient_drop = 15;
78
79         eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_min);
80         eeprom_write_byte(&ambient_log[ambient_log_offset++],
81                 (ambient_max << 4) | ambient_drop);
82
83         ambient_min = 0xFF;
84         ambient_max = 0;
85         ambient_drop = 0;
86 }
87
88 static inline void ambient_zone_changed()
89 {
90         pwmled_select_brightness();
91         pattern_reload();
92 }
93
94 static unsigned char val_to_zone(uint16_t ambient_val)
95 {
96         unsigned char new_zone = ambient_zone;
97
98         while (ambient_zones[new_zone].lo > ambient_val)
99                 new_zone--;
100
101         while (ambient_zones[new_zone].hi < ambient_val)
102                 new_zone++;
103
104         return new_zone;
105 }
106
107 void ambient_adc(uint16_t adcval)
108 {
109         unsigned char new_zone, user_zone, new_shadow = 0;
110         unsigned char byte_fast, byte_slow, drop;
111         uint16_t fast_10bit, slow_10bit;
112
113         // running avg - shorter timespan
114         ambient_fast += adcval - (ambient_fast >> AMBIENT_FAST_SHIFT);
115
116         // running avg - longer timespan
117         fast_10bit = ambient_fast >> (AMBIENT_FAST_SHIFT + AMBIENT_ADC_SHIFT);
118         ambient_slow += fast_10bit - (ambient_slow >> AMBIENT_SLOW_SHIFT);
119
120         // ambient zones are governed by shorter timespan by default
121         new_zone = val_to_zone(fast_10bit);
122
123         slow_10bit = ambient_slow >> AMBIENT_SLOW_SHIFT;
124
125         if (new_zone > 1 && (
126                 new_zone == ambient_zone-1 || new_zone == ambient_zone+1)) {
127                 // but change to the neighbouring zone is governed by _slow,
128                 // except to the darkest zone, where we want fast reaction.
129                 new_zone = val_to_zone(slow_10bit);
130         }
131
132         // user_param ambient zone override
133         if ((user_zone = get_user_param(0)) > 0)
134                 new_zone = user_zone - 1;
135
136         // are we entering the shadow?
137         if (!user_zone && new_zone < ambient_zone
138                 && ambient_zone >= 2 && slow_10bit > fast_10bit
139                 && slow_10bit - fast_10bit >= SHADOW_DROP_LIMIT) {
140                 // we are entering the shadow
141                 new_shadow = 0x30;
142         }
143
144         if (ambient_shadow) {
145                 if (new_shadow) {
146                         new_zone = ambient_zone; // don't change while entering shadow
147                         ambient_shadow = new_shadow; // update the timeout
148                 } else {
149                         ambient_shadow--;
150                         if (!ambient_shadow) { // leaving the shadow
151                                 ambient_zone_changed();
152                         }
153                 }
154         } else if (new_shadow) {
155                 ambient_shadow = new_shadow; // set up the timeout
156                 new_zone = ambient_zone; // don't change while entering shadow
157                 ambient_zone_changed();  // notify others the first time
158         }
159
160         // TODO: maybe use these values instead of 10-bit?
161         byte_fast = fast_10bit >> 2;
162         byte_slow = slow_10bit >> 2;
163
164         if (byte_slow > byte_fast) {
165                 drop = byte_slow - byte_fast;
166                 if (drop > ambient_drop)
167                         ambient_drop = drop;
168         }
169
170         if (ambient_min > byte_slow)
171                 ambient_min = byte_slow;
172
173         if (ambient_max < byte_slow)
174                 ambient_max = byte_slow;
175
176         if (new_zone != ambient_zone) {
177                 ambient_zone = new_zone;
178 #if 0
179                 log_byte(0xab);
180                 log_byte(ambient_zone);
181                 log_word(adcval);
182                 log_flush();
183 #endif
184                 ambient_zone_changed();
185         }
186 }
187