#include #include #include #include #include #include "stats.h" #include "stm32l_rtc.h" #include "sensor_task.h" // Rounding division (for positive numbers) #define RDIV(a, b) (((a) + (b)/2) / b) struct stats_t { /* Number of milliseconds spent in each of the system states */ uint64_t state_time[3]; /* Number of blinks by day of week and hour of day. */ uint32_t blink_times[7][24]; /* Histogram of movement estimate, by hour of day. */ uint32_t movement_histogram[24][11]; /* Histogram of "at outdoor temperature" estimate, by hour of day. */ uint32_t temperature_histogram[24][11]; /* Histogram of raw movement filter value / 10. */ uint32_t raw_movement_histogram[101]; /* Histogram of raw temperature in celcius + 40. */ uint32_t raw_temperature_histogram[81]; /* Total number of microampere-milliseconds of energy used so far. */ uint64_t total_energy; /* Reboot count */ uint32_t reboot_count; /* Time spent in freefall */ uint64_t freefall_time; }; static struct stats_t g_stats; static uint32_t * const data_eeprom = (uint32_t * const)0x08080000; void stats_load() { memcpy(&g_stats, data_eeprom, sizeof(g_stats)); } void stats_save() { uint32_t *dest = data_eeprom; uint32_t *src = (uint32_t*)&g_stats; int num_words = (sizeof(g_stats) + 3) / 4; FLASH->PEKEYR = 0x89ABCDEF; FLASH->PEKEYR = 0x02030405; while (num_words--) { *dest++ = *src++; } FLASH->PECR |= FLASH_PECR_PELOCK; } void stats_reset() { memset(&g_stats, 0, sizeof(g_stats)); } void stats_add_reboot() { g_stats.reboot_count++; } void stats_add_freefall(int milliseconds) { g_stats.freefall_time += milliseconds; } void stats_add_blink() { time_t time; if (!get_rtc(&time)) return; // RTC not set g_stats.blink_times[time.day_of_week - 1][time.hour] += 1; } static int clamp(int x, int min, int max) { if (x > max) x = max; if (x < min) x = min; return x; } void stats_add_histogram_entry() { time_t time; if (!get_rtc(&time)) return; // RTC not set int movement = sensors_movement(); int raw_movement = sensors_raw_movement(); int temperature = sensors_temperature(); int raw_temperature = sensors_raw_temperature(); movement = clamp(movement / 10, 0, 10); raw_movement = clamp(raw_movement / 10, 0, 100); temperature = clamp(temperature / 10, 0, 10); raw_temperature = clamp(raw_temperature / 1000 + 40, 0, 80); g_stats.movement_histogram[time.hour][movement] += 1; g_stats.temperature_histogram[time.hour][temperature] += 1; g_stats.raw_movement_histogram[raw_movement] += 1; g_stats.raw_temperature_histogram[raw_temperature] += 1; } /* Add time spent in given state. */ void stats_add_time(system_state_t state, int milliseconds) { g_stats.state_time[state] += milliseconds; } /* Add power usage to the statistics. */ void stats_add_power(int uA, int milliseconds) { g_stats.total_energy += (uint64_t)uA * milliseconds; } /* Print out the statistics as text. */ void stats_print(BaseSequentialStream *chp) { { chSysLock(); uint64_t sleep = g_stats.state_time[STATE_DEEPSLEEP]; uint64_t poll = g_stats.state_time[STATE_POLL]; uint64_t active = g_stats.state_time[STATE_ACTIVE]; uint64_t energy = g_stats.total_energy; uint32_t reboots = g_stats.reboot_count; uint64_t freefall = g_stats.freefall_time; chSysUnlock(); uint64_t total = sleep + poll + active; chprintf(chp, "Total time recorded: %d s (%d %% active, %d %% poll, %d %% sleep)\n", (int)(total / 1000), (int)RDIV(active * 100, total), (int)RDIV(poll * 100, total), (int)RDIV(sleep * 100, total)); int uAh = energy / 3600000; chprintf(chp, "Total energy used: %d uAh (%d %% of battery capacity)\n", uAh, uAh * 100 / 900000); chprintf(chp, "Total number of reboots: %d\n", reboots); chprintf(chp, "Total time spent in freefall: %d ms\n", (int)freefall); } chprintf(chp, "\n\n"); { chprintf(chp, "------------ Blinks by day of week and hour of day -----------\n"); chprintf(chp, "Each time the leds blink is recorded. This graph shows the distribution\n" "of the blinks among the hours of the week. Each column is a day and each\n" "row is an hour of that day. The value in each cell is the percentage\n" "of blinks that have occurred during that hour.\n\n"); uint64_t total = 0; for (int d = 0; d < 7; d++) { for (int h = 0; h < 24; h++) { total += g_stats.blink_times[d][h]; } } chprintf(chp, "Total blinks: %d\n\n", (int)total); chprintf(chp, "Hour | Mon Tue Wed Thu Fri Sat Sun\n"); for (int h = 0; h < 24; h++) { chprintf(chp, "%4d |", h); for (int d = 0; d < 7; d++) { chprintf(chp, " %3d", (int)RDIV(100 * (uint64_t)g_stats.blink_times[d][h], total)); } chprintf(chp, "\n"); } } chprintf(chp, "\n\n"); { chprintf(chp, "------------ Movement estimate by hour of day -----------\n"); chprintf(chp, "When the device detects movement, it starts estimating the amount of the\n" "movement over a few seconds window. That estimate is scaled as percentage\n" "of 0-100 %%, and further divided to 10 %% bins. This way the distribution\n" "of the estimate values is recorded for every hour of the day.\n\n" "In the graph below, each row corresponds to an hour of the day, and the\n" "ten columns correspond to the amount of movement. The value in each cell\n" "is the percentage of time, during that hour, that the movement estimate\n" "had the value according to the column.\n\n"); chprintf(chp, "Hour | 0 10 20 30 40 50 60 70 80 90 100\n"); for (int h = 0; h < 24; h++) { chprintf(chp, "%4d |", h); uint64_t total = 0; for (int b = 0; b < 11; b++) { total += g_stats.movement_histogram[h][b]; } if (total == 0) total = 1; for (int b = 0; b < 11; b++) { chprintf(chp, " %3d", (int)RDIV(100 * (uint64_t)g_stats.movement_histogram[h][b], total)); } chprintf(chp, "\n"); } } chprintf(chp, "\n\n"); { chprintf(chp, "------------ Temperature estimate by hour of day -----------\n"); chprintf(chp, "The device senses the temperature inside the case and also the rate of\n" "temperature change. This is used to estimate the ambient temperature,\n" "and furthermore the likelihood of that outdoor temperature at this time\n" "of the year. The results is a value 0-100 %%, where 0 %% is likely indoors\n" "and 100 %% is likely outdoors.\n\n" "The graph below shows the distribution of this estimate with relation\n" "to the hour of the day.\n\n"); chprintf(chp, "Hour | 0 10 20 30 40 50 60 70 80 90 100\n"); for (int h = 0; h < 24; h++) { chprintf(chp, "%4d |", h); uint64_t total = 0; for (int b = 0; b < 11; b++) { total += g_stats.temperature_histogram[h][b]; } if (total == 0) total = 1; for (int b = 0; b < 11; b++) { chprintf(chp, " %3d", (int)RDIV(100 * (uint64_t)g_stats.temperature_histogram[h][b], total)); } chprintf(chp, "\n"); } } chprintf(chp, "\n\n"); { chprintf(chp, "------------ Raw movement value ------------\n"); chprintf(chp, "In forming the movement estimate, some assumptions about the typical\n" "amount of movement are made. The below graph helps to verify these\n" "assumptions. Each row corresponds to a range of raw movement values\n" "and has a bar that displays the number of samples with a value in\n" "that range. The units of the raw movement value are RMS acceleration\n" "in milli-gees.\n\n"); uint64_t total = 0; for (int v = 0; v < 101; v++) { total += g_stats.raw_movement_histogram[v]; } if (total == 0) total = 1; for (int v = 0; v < 101; v++) { chprintf(chp, "%4d ", v * 10); int b = RDIV(100 * g_stats.raw_movement_histogram[v], total); while (b--) chprintf(chp, "#"); chprintf(chp, "\n"); } } chprintf(chp, "\n\n"); { chprintf(chp, "------------ Raw temperature value ------------\n"); chprintf(chp, "The below graph shows the estimated ambient temperature in celcius.\n" "Each row corresponds to a temperature, and the bar graph shows how\n" "commonly that temperature has occurred.\n\n"); uint64_t total = 0; for (int v = 0; v < 81; v++) { total += g_stats.raw_temperature_histogram[v]; } if (total == 0) total = 1; for (int v = 0; v < 81; v++) { chprintf(chp, "%4d C ", v - 40); int b = RDIV(100 * g_stats.raw_temperature_histogram[v], total); while (b--) chprintf(chp, "#"); chprintf(chp, "\n"); } } chprintf(chp, "\n\n"); }