Wow, thanks for feedback!
FOCUS_CONFIRMATION is a variable
Thanks a1ex, that gives me a nice overview of the embedded environment.
Algorithmically, the ML implementation would likely want to match the human-level algorithm of DotTune, provided that the behavior FOCUS_CONFIRMATION roughly matches the behavior of confirmation dot delivered in the VF.
Q: Is msleep() only used as a method to measure time and delay, or is it also required to allow background tasks to execute? (non-preemptive yield point for DryOS). If it's the former, do you have an alternate way to measure elapsed time so that the FOCUS_CONFIRMATION loops can sample continuously without yielding? This would eliminate sampling/timing errors of the variable relative to the micro-controller/background task that updates it.
Q: Is FOCUS_CONFIRMATION updated continuously while the camera is powered on or is there an internal trigger needed to initiate the background task sampling, akin to a half-press of the shutter? I see in your source that you call fake_simple_button(). Is that for this purpose?
Q: It might take some trial and error but I think we'd need to establish roughly what the time delta is between changing an AF tune value and when the first PD sensing result in FOCUS_CONFIRMATION reflects that new value. For the purposes of the source provided below I took a swag at 100ms.
Here is source to an implementation that uses an algorithm that matches the user-level steps of DotTune. Some questions remain about how exactly FOCUS_CONFIRMATION works relative to the VF indication, so this logic may need some tweaks after debugging. Unfortunately I sold my 5DM2 some time ago and the only Canon body I have with me is a 5DM3, which seems to still be in alpha for ML.
#define TUNE_VALUE_INVALID (INT_MIN)
#define MIN_TUNE_VALUE (-20)
#define MAX_TUNE_VALUE (20)
#define AF_TUNE_CHANGE_SETTLE_TIME_MS (100) // time it takes for PDAF to resample phase after changing AF tune (swag)
#define MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS (300) // max time we allow before 1st focus confirmation (swag)
#define MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS (5000) // min time that focus confirmation must remain TRUE
int is_focus_confirmed(void) {
return FOCUS_CONFIRMATION;
}
int perform_dot_tune(void) { // returns optimal AF tune value or TUNE_VALUE_INVALID
int tuneValue;
int confirmedTuneRangeLow, confirmedTuneRangeHigh;
int waitTimeMs;
//
// algorithm: with critical focus already established prior to
// this routine being called, we scan through the full +/- 20
// AF tune range to find the range of values which produce
// an immediate and consistent focus confirmation, then use
// the midpoint of that confirmed range as the optimal AF tune value.
//
// when the scan starts we're looking for the first tune value
// that produces an immediate and consistent focus confirmation;
// that represents the low end of the confirmed range. once we
// find that the scan is then looking for the first tune value
// which fails to produce confirmation; that tune value minus 1
// represents the high end of the confirmed range.
//
confirmedTuneRangeLow = confirmedTuneRangeHigh = TUNE_VALUE_INVALID;
for (tuneValue=MIN_TUNE_VALUE; tuneValue<=MAX_TUNE_VALUE; tuneValue++) {
//
// set next AF tune value, allow time for PDAF to resample the
// phase with the new AF TUNE value
//
set_afma(tuneValue, -1);
msleep(AF_TUNE_CHANGE_SETTLE_TIME_MS);
//
// detect how long it takes for PD to sample a successful focus
// confirmation. based on user-level observations, if there is
// any perceptible delay between half-pressing the shutter and
// getting the first focus confirmation then the focus confirmation
// should be disregarded and the AF TUNE value not included in
// the tuning range. since our logic is sampling an internal
// firmware variable to detect focus confirmation, we would
// need some way to "turn off" the camera's PD sensing so that
// we can detect a fresh PD sensing transition akin to a half-press
// of the shutter. if no such mechanism exists then our logic will
// likely only be useful for AF tune changes that transition
// from a non-confirmed to confirmed values (ie, when we're looking
// for the high end of the range). if so, we might consider
// changing the algorithm so that we start scanning from the opposite
// end of the AF tune scale to find the high-end of the range
//
waitTimeMs = 0;
do {
if (is_focus_confirmed())
break;
msleep(10);
waitTimeMs += 10;
} while (waitTimeMs < MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS);
if (waitTimeMs < MAX_TIME_TO_FIRST_FOCUS_CONFIRM_MS) {
//
// detected first focus confirmation within the time period
// allowed. now make sure the focus confirmation stays
// continuously on for our threshold before we consider
// the current tune value part of the confirmed range
//
waitTimeMs = 0;
do {
if (!is_focus_confirmed())
break;
msleep(10);
waitTimeMs += 10;
} while (waitTimeMs < MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS);
if (waitTimeMs >= MIN_CONTINUOUS_FOCUS_CONFIRM_TIME_MS) {
//
// focus remained confirmed for the duration we require, so
// current AF tune value was confirmed to be part of the
// confirmed range
//
if (confirmedTuneRangeLow == TUNE_VALUE_INVALID) {
// we just found the first end of the range (low end)
confirmedTuneRangeLow = tuneValue;
continue; // advance to next tune value to start searching high-end of range
} else
//
// we've previously found the low end of the tune range and
// are trying to find the first non-confirmed tune
// value to establish the high-end of the range
//
continue; // advance to next tune value
} // else focus confirmation failed to sustain for minimum time allowed
} // else first focus confirmation didn't occur within max time allowed
//
// we reach this point if the current AF tune value is not confirmed
//
if (confirmedTuneRangeLow != TUNE_VALUE_INVALID) {
//
// we've previously found the low end of the tune range and now
// found our first tune value which is non-confirmed, meaning
// the high-end of the confirmed range is the current value
// minus 1
confirmedTuneRangeHigh = tuneValue-1;
break; // done finding range; no more tune values to check
}
} // end of AF tune scan loop
if (confirmedTuneRangeLow != TUNE_VALUE_INVALID && confirmedTuneRangeHigh != TUNE_VALUE_INVALID)
//
// DotTune successful. Optimal tune value is midpoint of range.
// if the midpoint is between two whole values then we round down
//
return (confirmedTuneRangeHigh - confirmedTuneRangeLow)/2 + confirmedTuneRangeLow;
if (confirmedTuneRangeLow == TUNE_VALUE_INVALID) {
//
// no AF tune values produced a confirmation, which likely
// means critical focus was not properly achieved prior
// to calling this routine
//
// show appropriate message to user
;
} else {
//
// we found the low end of the range but +20 still produced a focus
// confirmation, so the high end of the range is beyond +20, meaning
// we can't find the precise midpoint
//
// show appropriate message to user
;
}
return TUNE_VALUE_INVALID; // indicate DotTune failed
}