From 27d3a5c535633680405d05c2353ea77852a729c3 Mon Sep 17 00:00:00 2001 From: jreinholm Date: Tue, 8 Dec 2015 13:18:09 -0500 Subject: [PATCH] New algorithm for Night-to-Day Bulb-Ramping --- src/light.cpp | 68 +++++++++++++++++++++++++++++++++++++++++-------- src/light.h | 20 ++++++++++++--- src/shutter.cpp | 43 ++++++++++++++++++++++++++----- src/shutter.h | 4 +++ 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/src/light.cpp b/src/light.cpp index 9f380d1..2861526 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -30,6 +30,8 @@ Light::Light() // Configure I2C // TWI_Master_Initialise(); lastSeconds = 0; + //Timer for nightEv() updates + nightSeconds = 0; //J.R. 9-30-15 initialized = 0; integrationActive = false; lockedSlope = 0.0; @@ -102,6 +104,8 @@ void Light::stop() TWI_Start_Read_Write(I2C_Buf, 3); lcd.backlight(255); lastSeconds = 0; + //Timer for nightEv() updates + nightSeconds = 0; //J.R. 9-30-15 paused = 0; lockedSlope = 0.0; integrationActive = false; @@ -202,6 +206,16 @@ float Light::readIntegratedSlopeMedian() return value; } +//This function is similar to "light.readIntegratedEv()" except that it calculates the +//midpoint-median light value for the last 3 hours. +//So "nightLight" is set wherever "lightReading" is set in shutter.cpp +float Light::readNightEv() //J.R. 10-11-15 +{ + if(!integrationActive) return 0.0; //J.R. 10-11-15 + float value = arrayMedian50(nightEv, NIGHT_INTEGRATION_COUNT - 1); //J.R. 10-11-15 + return value; //J.R. 10-11-15 +} + void Light::task() { if(!initialized || !integrationActive) return; @@ -232,23 +246,34 @@ void Light::task() } iev[LIGHT_INTEGRATION_COUNT - 1] = readEv(); slope = readIntegratedSlopeMedian(); - - if(iev[LIGHT_INTEGRATION_COUNT - 1] <= NIGHT_THRESHOLD) + + //The following 4 instructions were moved up so the "integrated" variable wold be available + //for the lightThreshold (or NIGHT_THRESHOLD) comparison. + median = arrayMedian50(iev, LIGHT_INTEGRATION_COUNT); //J.R. 10-13-15 + + float sum = 0.0; //J.R. 10-13-15 + for(uint8_t i = 0; i < LIGHT_INTEGRATION_COUNT; i++) sum += iev[i]; //J.R. 10-13-15 + integrated = sum / (float)(LIGHT_INTEGRATION_COUNT); //J.R. 10-13-15 + + //No need to based the comparison on a single value of iev[LIGHT_INTEGRATION_COUNT] + //since all light readings in the TL+ are based on the average of iev[], which is "integrated". + // + //Instead of comparing "integrated" to "NIGHT_THRESHOLD", "conf.lightThreshold" should be used, + //which is the settable threshold value, rather than fixed. + // + //if(iev[LIGHT_INTEGRATION_COUNT - 1] <= NIGHT_THRESHOLD) + if(integrated <= conf.lightThreshold) //J.R. 10-13-15 { underThreshold = true; if(lockedSlope == 0.0 && slope) lockedSlope = slope; } - else if(iev[LIGHT_INTEGRATION_COUNT - 1] > NIGHT_THRESHOLD + NIGHT_THRESHOLD_HYSTERESIS) + //else if(iev[LIGHT_INTEGRATION_COUNT - 1] > NIGHT_THRESHOLD + NIGHT_THRESHOLD_HYSTERESIS) + else if(integrated > conf.lightThreshold + NIGHT_THRESHOLD_HYSTERESIS) //J.R. 10-13-15 { underThreshold = false; lockedSlope = 0.0; } - median = arrayMedian50(iev, LIGHT_INTEGRATION_COUNT); - - float sum = 0.0; - for(uint8_t i = 0; i < LIGHT_INTEGRATION_COUNT; i++) sum += iev[i]; - integrated = sum / (float)(LIGHT_INTEGRATION_COUNT); if(conf.debugEnabled) { @@ -282,6 +307,19 @@ void Light::task() //DEBUG(STR(" #######\r\n")); } } + //The same kind of loop is used here to update nightEv[] that was used for updating iev[]. + //Of course the time between updates is much longer. + if(nightSeconds == 0 || (clock.Seconds() > (nightSeconds + NIGHT_COUNT_DELAY))) //J.R. 9-30-15 + { + nightSeconds = clock.Seconds(); + + for(uint8_t i = 0; i < NIGHT_INTEGRATION_COUNT - 1; i++) //J.R. 9-30-15 + { + nightEv[i] = nightEv[i + 1];//J.R. 9-30-15 + } + //No need to do a readEv() all over again, as for iev[] above. + nightEv[NIGHT_INTEGRATION_COUNT - 1] = iev[LIGHT_INTEGRATION_COUNT - 1]; //J.R. 9-30-15 + } } void Light::integrationStart(uint8_t integration_minutes) @@ -290,15 +328,25 @@ void Light::integrationStart(uint8_t integration_minutes) //DEBUG(STR(" ####### LIGHT INTEGRATION START #######\r\n")); integration = (uint16_t)integration_minutes; lastSeconds = 0; + //Timer for nightEv() updates + nightSeconds = 0; //J.R. 9-30-15 for(uint8_t i = 0; i < LIGHT_INTEGRATION_COUNT; i++) { iev[i] = readEv(); // initialize array with readings // wdt_reset(); } + median = arrayMedian50(iev, LIGHT_INTEGRATION_COUNT); //J.R. 9-30-15 + //This is where nightEv() is loaded with the current darkness level at the start: + for(uint8_t i = 0; i < NIGHT_INTEGRATION_COUNT; i++) //J.R. 9-30-15 + { + nightEv[i] = median; // initialize night array with readings //J.R. 9-30-15; //J.R. 9-30-15 + } integrationActive = true; slope = 0.0; - median = iev[0]; - integrated = iev[0]; + //No need to set median and integrated to iev[0], since better values were obtained. + //median = iev[0]; //J.R. 10-13-15 + //integrated = iev[0]; //J.R. 10-13-15 + integrated = median; //J.R. 9-30-15 lockedSlope = 0.0; task(); } diff --git a/src/light.h b/src/light.h index a2f4759..0f445ad 100644 --- a/src/light.h +++ b/src/light.h @@ -2,9 +2,16 @@ #define I2C_ADDR_WRITE 0b10001000 #define LIGHT_INTEGRATION_COUNT 32 -#define FILTER_LENGTH 3 -#define NIGHT_THRESHOLD 20 +//nightEv() has same array size as iev(LIGHT_INTEGRATION_COUNT) +#define NIGHT_INTEGRATION_COUNT 32 //J.R. 9-30-15 +//338 is the number of seconds between updates to nightEv() +//so the 32 readings in nightEv(NIGHT_INTEGRATION_COUNT) will span the previous 3 hours +#define NIGHT_COUNT_DELAY 338 //J.R. 9-30-15 + +#define FILTER_LENGTH 3 +//This definition isn't needed because it is a settable parameter +//define NIGHT_THRESHOLD 20 //J.R. 10-11-15 #define NIGHT_THRESHOLD_HYSTERESIS 5 #define OFFSET_UNSET 65535 @@ -21,6 +28,8 @@ class Light void setRangeAuto(); float readEv(); float readIntegratedEv(); + //This is the function that determines the best EV value for middle-of-the-night darkness. + float readNightEv(); //J.R. 9-30-15 float readIntegratedSlope(); float readIntegratedSlopeMedian(); void integrationStart(uint8_t integration_minutes); @@ -32,13 +41,18 @@ class Light private: float iev[LIGHT_INTEGRATION_COUNT]; + //This is an array that contains 32 light readings spanned over the last 3 hours + float nightEv[NIGHT_INTEGRATION_COUNT]; //J.R. 9-30-15 float filter[FILTER_LENGTH]; int8_t filterIndex; int8_t wasPaused; uint16_t integration; uint32_t lastSeconds; + //This is the seconds timer value that decides when to put a new value in nightEv(). + //This variable is similar to "lastSeconds" for updating iev(). + uint32_t nightSeconds; //J.R. 9-30-15 uint8_t initialized; uint16_t offset; bool integrationActive; -}; \ No newline at end of file +}; diff --git a/src/shutter.cpp b/src/shutter.cpp index edec3aa..87f1c59 100644 --- a/src/shutter.cpp +++ b/src/shutter.cpp @@ -668,6 +668,8 @@ char shutter::task() status.rampStops_f = 0.0; light.integrationStart(conf.lightIntegrationMinutes); lightReading = status.lightStart_f = light.readIntegratedEv(); + //"nightLight" is set in a similar way as for "lightReading" + nightLight = light.readNightEv(); // J.R. 10-13-15 if(current.nightMode == BRAMP_TARGET_AUTO) { @@ -695,6 +697,17 @@ char shutter::task() status.rampTarget_f = status.lightStart_f - (float)status.nightTarget_i8; } + //When starting the bulb-ramping in the dark, change lightReading and status.lightStart + //to status.nightTarget instead of light.readIntegratedEv(). + if(light.underThreshold && current.nightMode != BRAMP_TARGET_AUTO) //J.R. 10-11-15 + { + //DEBUG(STR(" -----> starting at night target\r\n")); //J.R. 10-11-15 + lightReading = status.lightStart_f = status.nightTarget_i8; + status.rampTarget_f = 0; //J.R. 12-03-15 + //This flag maintains night2day bulb ramping once the light reaches a certain point //J.R. 10-11-15 + Night2Day = false; //J.R. 10-11-15 + } + status.preChecked_u8 = 2; } @@ -972,7 +985,14 @@ char shutter::task() { if(light.underThreshold && current.nightMode != BRAMP_TARGET_AUTO) // holding night exposure { - if(current.nightMode == BRAMP_TARGET_CUSTOM) + //respond during night-to-day once we see light or continue once it's started + if(Night2Day == true || (rampRate ==0 && status.rampStops_f == status.rampTarget_f && lightReading > (nightLight + (conf.lightThreshold/(4 * P_FACTOR))))) //&& lightStart == status.nightTarget) //J.R. 10-1-15 + { + status.rampTarget_f = status.lightStart_f - lightReading; //J.R. 10-1-15 + //This flag maintains night to day algorithm once it starts + Night2Day = true; //J.R. 11-16-15 + } + else if(current.nightMode == BRAMP_TARGET_CUSTOM) { status.rampTarget_f = (float)status.nightTarget_i8 + ((float)current.nightND * 3) / 10.0; } @@ -985,6 +1005,8 @@ char shutter::task() { // using light sensor target status.rampTarget_f = (status.lightStart_f - lightReading); + //No need to maintain night to day algorithm at this point + Night2Day = false; //J.R. 10-16-15 } if(status.rampTarget_f > status.rampMax_i8) status.rampTarget_f = status.rampMax_i8; @@ -1022,12 +1044,15 @@ char shutter::task() } rampRate = (int8_t) delta; - - if(rampRate == 0 && light.underThreshold && current.nightMode != BRAMP_TARGET_AUTO && status.rampStops_f == status.rampTarget_f) - { - // if we've met the night target, switch to guided mode to hold exposure - switchToGuided(); - } + + //Delete the 'switchToGuided' function: + //This is taken care of in the two "if" statements above under "BRAMP_METHOD_AUTO" + + //if(rampRate == 0 && light.underThreshold && current.nightMode != BRAMP_TARGET_AUTO && status.rampStops_f == status.rampTarget_f) + //{ + // // if we've met the night target, switch to guided mode to hold exposure + // switchToGuided(); + //} } //#################################################### @@ -1179,6 +1204,8 @@ char shutter::task() { exps++; lightReading = light.readIntegratedEv(); + //"nightLight" is set in a similar way as for "lightReading" + nightLight = light.readNightEv(); // J.R. 10-13-15 shutter_bulbEndFinish(); @@ -1562,6 +1589,8 @@ void shutter::switchToAuto() { light.integrationStart(conf.lightIntegrationMinutes); lightReading = status.lightStart_f = light.readIntegratedEv(); + //"nightLight" is set in a similar way as for "lightReading" + nightLight = light.readNightEv(); // J.R. 10-13-15 current.brampMethod = BRAMP_METHOD_AUTO; current.nightMode = BRAMP_TARGET_AUTO; } diff --git a/src/shutter.h b/src/shutter.h index 88e9d7b..076d6b2 100644 --- a/src/shutter.h +++ b/src/shutter.h @@ -217,6 +217,10 @@ class shutter int8_t rampRate, apertureEvShift; uint32_t last_photo_ms; float lightReading; + //This variable is the precise median/average EV value for the darkest part of the night + float nightLight; //J.R. 11-30-15 + //This flag maintains night2day bulb ramping once the light reaches a certain point + bool Night2Day; //J.R. 10-11-15 float pastErrors[PAST_ERROR_COUNT]; volatile uint8_t paused, pausing, apertureReady; int8_t evShift;