/** * Radiation counting */ #include "radiation.h" #include "time_base.h" #include "global_state.h" #include "iopins.h" #include #include #include #include #include #define MEAS_BIN_COUNT 120 typedef uint16_t ticks_t; typedef uint8_t binnum_t; #define TICKS_MAX 0xFFFF #define NEEDED_TICKS_FOR_VALUE_DISPLAY 5 #define TICKS_NEEDED_FOR_AUTORANGE 10 uint32_t get_tick_count_in_bins(binnum_t bincount); /// measurements in the last X seconds static volatile ticks_t measurements[MEAS_BIN_COUNT]; /// Currently active measurement bin, increments once per second static volatile binnum_t meas_bin = 0; static volatile binnum_t meas_oldest_bin = 0; static volatile binnum_t num_live_bins = 1; static volatile binnum_t num_observed_bins = 60; // geiger tube pin change interrupt ISR(INT0_vect) { if (measurements[meas_bin] < TICKS_MAX) { measurements[meas_bin]++; } req_update_display = true; disp_show_tick_mark = 1; } void second_callback_irq() { meas_bin++; if (num_live_bins < MEAS_BIN_COUNT) { num_live_bins++; } if (meas_bin >= MEAS_BIN_COUNT) { meas_bin = 0; } if (meas_oldest_bin == meas_bin) { meas_oldest_bin++; if (meas_oldest_bin >= MEAS_BIN_COUNT) { meas_oldest_bin = 0; } } measurements[meas_bin] = 0; req_update_display = true; /* autoranging */ uint16_t count; bool found_range = 0; for (int i = 5; i <= 60; i += 5) { count = get_tick_count_in_bins(i); if (count > TICKS_NEEDED_FOR_AUTORANGE) { num_observed_bins = i * 2; if (num_observed_bins > num_live_bins) { num_observed_bins = num_live_bins; } found_range = true; break; } } if (!found_range) { num_observed_bins = num_live_bins; } } void init_radiation() { // clear the measurement buffer memset((void *) measurements, 0, sizeof(measurements)); as_input(D2); // using INT0 - arduino pin D2 EICRA = _BV(ISC01) | _BV(ISC00); // rising edge EIMSK = _BV(INT0); } uint8_t rad_get_progress() { uint16_t ticks = get_tick_count_in_bins(num_live_bins); if (ticks >= NEEDED_TICKS_FOR_VALUE_DISPLAY) { return 100; } return (((uint16_t) ticks * 100) / (uint16_t) NEEDED_TICKS_FOR_VALUE_DISPLAY); } uint32_t get_tick_count_in_bins(binnum_t bincount) { if (bincount == 0) { return 0; } uint32_t all_ticks = 0; ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { // going backwards binnum_t bin = meas_bin; for (;;) { all_ticks += (uint32_t) measurements[bin]; if (bin == meas_oldest_bin) { break; } if (bincount > 0) { bincount--; if (bincount == 0) { break; } } if (bin > 0) { bin--; } else { bin = MEAS_BIN_COUNT - 1; } } } return all_ticks; } void rad_get_reading(struct rad_results *out) { if (!out) { return; } // bins take 1 second const float count = (float) get_tick_count_in_bins(num_observed_bins); const float cpm = (count / (float) num_observed_bins) * 60.0f; out->cpm_x10 = (uint32_t) roundf(cpm * 10.0f); // https://sites.google.com/site/diygeigercounter/technical/gm-tubes-supported const float usvh = (cpm / 153.8f); if (usvh < 2.0f) { out->danger_level = RAD_LEVEL_0_SAFE; } else if (usvh < 10.0f) { out->danger_level = RAD_LEVEL_1_ELEVATED; } else if (usvh < 20.0f) { out->danger_level = RAD_LEVEL_2_DANGER; } else if (usvh < 1000.0f) { out->danger_level = RAD_LEVEL_3_HIGH_DANGER; } else { out->danger_level = RAD_LEVEL_4_SEVERE; } // // for testing only // if (usvh < 1.0f) { // out->danger_level = RAD_LEVEL_0_SAFE; // } else if (usvh < 3.0f) { // out->danger_level = RAD_LEVEL_1_ELEVATED; // } else if (usvh < 6.0f) { // out->danger_level = RAD_LEVEL_2_DANGER; // } else if (usvh < 9.0f) { // out->danger_level = RAD_LEVEL_3_HIGH_DANGER; // } else { // out->danger_level = RAD_LEVEL_4_SEVERE; // } if (usvh < 99.0f) { out->usvh_decimals = 3; out->usvh_num = (uint32_t) roundf(usvh * 1000.0f); } else if (usvh < 999.0f) { out->usvh_decimals = 2; out->usvh_num = (uint32_t) roundf(usvh * 100.0f); } else if (usvh < 9999.0f) { out->usvh_decimals = 1; out->usvh_num = (uint32_t) roundf(usvh * 10.0f); } else { out->usvh_decimals = 0; out->usvh_num = (uint32_t) roundf(usvh); } }