diff --git a/watch-faces/sensor/light_meter_face.c b/watch-faces/sensor/light_meter_face.c index a6afc8ce..e2042f5c 100644 --- a/watch-faces/sensor/light_meter_face.c +++ b/watch-faces/sensor/light_meter_face.c @@ -24,6 +24,7 @@ #include #include +#include #include "light_meter_face.h" #include "tc.h" #include "eic.h" @@ -33,10 +34,9 @@ #ifdef HAS_IR_SENSOR static void _light_meter_face_update_display(light_meter_state_t *state, uint8_t subsecond) { - watch_clear_display(); - if (state->mode >= LIGHT_METER_MODE_AP_IS_SETTING_ISO) { // Handle ISO settings mode. + watch_clear_display(); // Custom LCD can say "ISO". On Classic, we can't show an S in position 1, so "FI" for FIlm speed will have to suffice. watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "ISO", "FI"); @@ -80,7 +80,7 @@ static void _light_meter_face_update_display(light_meter_state_t *state, uint8_t // we are in aperture or shutter priority mode. light_meter_aperture_t aperture; light_meter_shutter_speed_t shutter; - uint32_t light_level; + uint16_t light_level; if (!adc_is_enabled()) { /// TODO: need to test this more. If a background task takes a reading and disables the ADC, @@ -92,22 +92,36 @@ static void _light_meter_face_update_display(light_meter_state_t *state, uint8_t light_level = adc_get_analog_value(HAL_GPIO_IRSENSE_pin()); + /// FIXME: This curve is garbage, but in theory it was meant to convert the light level to an exposure index at ISO 25. + // Readings were taken with the custom LCD and the standard Casio light spreader in place. + // PLENTY of room for improvement here! + const float L = 63188.86f; + const float k = 0.8654f; + const float x0 = 7.45f; + const float C = 2491.21f; + float exposure_index = x0 + (1.0f / k) * log((L / ((float)light_level - C)) - 1); + if (isnan(exposure_index)) exposure_index = 0; + + int8_t target_exposure_index_at_f1_or_1s = (int8_t) round(exposure_index) + state->iso; + if (state->mode == LIGHT_METER_MODE_APERTURE_PRIORITY) { aperture = state->aperture_priority; - /// TODO: Calculate actual shutter speed - shutter = light_level * LIGHT_METER_SHUTTER_COUNT / 65535; + shutter = target_exposure_index_at_f1_or_1s - (state->aperture_priority - LIGHT_METER_APERTURE_F1); } else { shutter = state->shutter_priority; - /// TODO: Calculate actual aperture - aperture = light_level * LIGHT_METER_APERTURE_COUNT / 65535; + aperture = target_exposure_index_at_f1_or_1s - (state->shutter_priority - LIGHT_METER_SHUTTER_1_SEC); } + if (shutter < LIGHT_METER_SHUTTER_1_SEC) shutter = LIGHT_METER_SHUTTER_1_SEC; + if (aperture < LIGHT_METER_APERTURE_F1) aperture = LIGHT_METER_APERTURE_F1; // F stop is shown in both AP and SP modes. + watch_clear_display(); watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, " F/", " F"); switch (aperture) { case LIGHT_METER_APERTURE_F1: - watch_display_text(WATCH_POSITION_TOP_RIGHT, "1 "); + watch_display_text(WATCH_POSITION_TOP_RIGHT, " "); + watch_display_text_with_fallback(WATCH_POSITION_TOP, "TooLo", "LO"); break; case LIGHT_METER_APERTURE_F1_4: watch_display_text(WATCH_POSITION_TOP_RIGHT, "14"); @@ -141,8 +155,8 @@ static void _light_meter_face_update_display(light_meter_state_t *state, uint8_t case LIGHT_METER_APERTURE_F32: watch_display_text(WATCH_POSITION_TOP_RIGHT, "32"); break; - case LIGHT_METER_APERTURE_COUNT: - // will not reach here + default: + watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "too HI", "HIGH "); break; } @@ -154,6 +168,10 @@ static void _light_meter_face_update_display(light_meter_state_t *state, uint8_t } switch (shutter) { + case LIGHT_METER_SHUTTER_1_SEC: + case LIGHT_METER_SHUTTER_1_2: + watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "tooLOw", " LO "); + break; case LIGHT_METER_SHUTTER_1_4: watch_display_text(WATCH_POSITION_MINUTES, " 4"); break; @@ -189,8 +207,8 @@ static void _light_meter_face_update_display(light_meter_state_t *state, uint8_t case LIGHT_METER_SHUTTER_1_4000: watch_display_text(WATCH_POSITION_HOURS, "40"); break; - case LIGHT_METER_SHUTTER_COUNT: - // will not reach here + default: + watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "too HI", "HIGH "); break; } } diff --git a/watch-faces/sensor/light_meter_face.h b/watch-faces/sensor/light_meter_face.h index 1484dcfd..107e3559 100644 --- a/watch-faces/sensor/light_meter_face.h +++ b/watch-faces/sensor/light_meter_face.h @@ -32,14 +32,15 @@ * LIGHT METER * * EXTREME WORK IN PROGRESS on a photographic light meter. - * Currently does not display working exposure readings! This commit is just a - * user interface for the light meter, with fake readings until I calibrate it. + * Currently not even remotely calibrated! I did one afternoon of tests with light + * transmitted through the custom LCD, and half-assedly applied a curve that almost + * not not quite entirely fit the data. Pull requests welcome! * */ typedef enum { - LIGHT_METER_APERTURE_F1 = 0, - LIGHT_METER_APERTURE_F1_4, + LIGHT_METER_APERTURE_F1 = -1, // f/1 will not appear as an aperture priority option + LIGHT_METER_APERTURE_F1_4, // numbered 0, f/1.4 is the first aperture priority option LIGHT_METER_APERTURE_F2, LIGHT_METER_APERTURE_F2_8, LIGHT_METER_APERTURE_F4, @@ -53,7 +54,9 @@ typedef enum { } light_meter_aperture_t; typedef enum { - LIGHT_METER_SHUTTER_1_4 = 0, + LIGHT_METER_SHUTTER_1_SEC = -2, // 1 second and 1/2 second will not appear as a shutter priority option + LIGHT_METER_SHUTTER_1_2, + LIGHT_METER_SHUTTER_1_4, // numbered 0, 1/4 second is the first one to appear in AP list LIGHT_METER_SHUTTER_1_8, LIGHT_METER_SHUTTER_1_15, LIGHT_METER_SHUTTER_1_30,