7#include "adaptivefocus.h"
8#include <kstars_debug.h>
15AdaptiveFocus::AdaptiveFocus(Focus *_focus) : m_focus(_focus)
17 if (m_focus ==
nullptr)
18 qCDebug(KSTARS_EKOS_FOCUS) <<
"AdaptiveFocus constructed with null focus ptr";
21AdaptiveFocus::~AdaptiveFocus()
26void AdaptiveFocus::runAdaptiveFocus(
const int currentPosition,
const QString &filter)
30 qCDebug(KSTARS_EKOS_FOCUS) <<
"runAdaptiveFocus called but focus ptr is null. Ignoring.";
31 adaptiveFocusAdmin(currentPosition,
false,
false);
35 if (!m_focus->m_FilterManager)
37 qCDebug(KSTARS_EKOS_FOCUS) <<
"runAdaptiveFocus called but focus filterManager is null. Ignoring.";
38 adaptiveFocusAdmin(currentPosition,
false,
false);
42 if (!m_focus->m_OpsFocusSettings->focusAdaptive->isChecked())
44 qCDebug(KSTARS_EKOS_FOCUS) <<
"runAdaptiveFocus called but focusAdaptive->isChecked is false. Ignoring.";
45 adaptiveFocusAdmin(currentPosition,
false,
false);
49 if (inAdaptiveFocus())
51 qCDebug(KSTARS_EKOS_FOCUS) <<
"runAdaptiveFocus called whilst already inAdaptiveFocus. Ignoring.";
52 adaptiveFocusAdmin(currentPosition,
false,
false);
56 if (m_focus->inAutoFocus || m_focus->inFocusLoop || m_focus->inAdjustFocus || m_focus->inBuildOffsets)
58 qCDebug(KSTARS_EKOS_FOCUS) <<
"adaptiveFocus called whilst other focus activity in progress. Ignoring.";
59 adaptiveFocusAdmin(currentPosition,
false,
false);
63 setInAdaptiveFocus(
true);
64 m_ThisAdaptiveFocusStartPos = currentPosition;
68 QString adaptiveFilter = getAdaptiveFilter(filter);
71 if (filter != m_LastAdaptiveFilter)
73 resetAdaptiveFocusCounters();
74 m_LastAdaptiveFilter =
filter;
76 if (!m_focus->m_FilterManager->getFilterAbsoluteFocusDetails(adaptiveFilter, refPosition,
77 m_focus->m_LastSourceAutofocusTemperature,
78 m_focus->m_LastSourceAutofocusAlt))
80 qCDebug(KSTARS_EKOS_FOCUS) <<
"runAdaptiveFocus unable to get last Autofocus details. Ignoring.";
81 adaptiveFocusAdmin(currentPosition,
false,
false);
86 refPosition += getAdaptiveFilterOffset(filter, adaptiveFilter);
89 double currentTemp = INVALID_VALUE;
90 double tempTicksDelta = 0.0, tempTicksDeltaLast = 0.0;
91 if (m_focus->currentTemperatureSourceElement && m_focus->m_LastSourceAutofocusTemperature != INVALID_VALUE)
93 if (m_LastAdaptiveFocusTemperature == INVALID_VALUE)
95 m_LastAdaptiveFocusTemperature = m_focus->m_LastSourceAutofocusTemperature;
97 currentTemp = m_focus->currentTemperatureSourceElement->value;
100 const double tempDelta = currentTemp - m_focus->m_LastSourceAutofocusTemperature;
101 const double tempDeltaLast = m_LastAdaptiveFocusTemperature - m_focus->m_LastSourceAutofocusTemperature;
104 const double ticksPerTemp = m_focus->m_FilterManager->getFilterTicksPerTemp(adaptiveFilter);
105 tempTicksDelta = ticksPerTemp * tempDelta;
106 tempTicksDeltaLast = ticksPerTemp * tempDeltaLast;
110 double currentAlt = INVALID_VALUE;
111 double altTicksDelta = 0.0, altTicksDeltaLast = 0.0;
112 bool altDimension =
false;
113 if (m_focus->m_LastSourceAutofocusAlt != INVALID_VALUE)
115 if (m_LastAdaptiveFocusAlt == INVALID_VALUE)
117 m_LastAdaptiveFocusAlt = m_focus->m_LastSourceAutofocusAlt;
119 currentAlt = m_focus->mountAlt;
121 const double altDelta = currentAlt - m_focus->m_LastSourceAutofocusAlt;
122 const double altDeltaLast = m_LastAdaptiveFocusAlt - m_focus->m_LastSourceAutofocusAlt;
125 const double ticksPerAlt = m_focus->m_FilterManager->getFilterTicksPerAlt(adaptiveFilter);
126 altDimension = (abs(ticksPerAlt) > 0.001);
127 altTicksDelta = ticksPerAlt * altDelta;
128 altTicksDeltaLast = ticksPerAlt * altDeltaLast;
132 int proposedPosition = refPosition +
static_cast<int>(round(tempTicksDelta + altTicksDelta));
133 int proposedPositionLast = refPosition +
static_cast<int>(round(tempTicksDeltaLast + altTicksDeltaLast));
134 int proposedMove = proposedPosition - currentPosition;
137 m_ThisAdaptiveFocusTempTicks = tempTicksDelta - tempTicksDeltaLast;
138 m_ThisAdaptiveFocusAltTicks = altTicksDelta - altTicksDeltaLast;
143 m_LastAdaptiveFocusPosErrorReversal = proposedPositionLast - currentPosition;
146 m_ThisAdaptiveFocusRoundingError = proposedMove - m_LastAdaptiveFocusPosErrorReversal -
147 static_cast<int>(round(m_ThisAdaptiveFocusTempTicks + m_ThisAdaptiveFocusAltTicks));
150 if (abs(proposedMove) < m_focus->m_OpsFocusSettings->focusAdaptiveMinMove->value())
152 m_focus->appendLogText(
i18n(
"Adaptive Focus: No movement (below threshold)"));
153 adaptiveFocusAdmin(currentPosition,
true,
false);
158 if (abs(m_focus->initialFocuserAbsPosition - proposedPosition) > m_focus->m_OpsFocusMechanics->focusMaxTravel->value())
162 m_focus->m_OpsFocusSettings->focusAdaptive->setChecked(
false);
163 m_focus->appendLogText(
i18n(
"Adaptive Focus suspended. Total movement would exceed Max Travel limit"));
164 adaptiveFocusAdmin(currentPosition,
false,
false);
166 else if (abs(m_AdaptiveTotalMove + proposedMove) > m_focus->m_OpsFocusSettings->focusAdaptiveMaxMove->value())
170 m_focus->m_OpsFocusSettings->focusAdaptive->setChecked(
false);
171 m_focus->appendLogText(
i18n(
"Adaptive Focus suspended. Total movement would exceed adaptive limit"));
172 adaptiveFocusAdmin(currentPosition,
false,
false);
179 QString text =
i18n(
"Adaptive Focus: Moving from %1 to %2 (TempΔ %3", currentPosition, proposedPosition, tempStr);
181 text +=
i18n(
"; AltΔ %1", altStr);
182 text += (m_LastAdaptiveFocusPosErrorReversal == 0) ?
")" :
i18n(
"; Pos Error %1)", m_LastAdaptiveFocusPosErrorReversal);
183 m_focus->appendLogText(text);
186 if (m_focus->changeFocus(proposedMove))
189 m_ThisAdaptiveFocusTemperature = currentTemp;
190 m_ThisAdaptiveFocusAlt = currentAlt;
191 m_AdaptiveFocusPositionReq = proposedPosition;
192 m_AdaptiveTotalMove += proposedMove;
197 m_focus->appendLogText(i18n(
"Adaptive Focus unable to move focuser"));
198 adaptiveFocusAdmin(currentPosition, false, false);
205void AdaptiveFocus::adaptiveFocusAdmin(
const int currentPosition,
const bool success,
const bool focuserMoved)
208 setInAdaptiveFocus(
false);
210 int thisPosError = 0;
214 QTimer::singleShot(m_focus->m_OpsFocusMechanics->focusSettleTime->value() * 1000, m_focus, [ &, success]()
216 emit m_focus->focusAdaptiveComplete(success, m_focus->opticalTrain());
221 if (m_AdaptiveFocusPositionReq != INVALID_VALUE)
223 thisPosError = currentPosition - m_AdaptiveFocusPositionReq;
224 m_AdaptiveTotalMove += thisPosError;
228 emit m_focus->focusAdaptiveComplete(success, m_focus->opticalTrain());
235 int totalTicks =
static_cast<int>(round(m_ThisAdaptiveFocusTempTicks + m_ThisAdaptiveFocusAltTicks)) +
236 m_LastAdaptiveFocusPosErrorReversal + m_ThisAdaptiveFocusRoundingError + thisPosError;
238 emit m_focus->adaptiveFocusComplete(m_focus->filter(), m_ThisAdaptiveFocusTemperature, m_ThisAdaptiveFocusTempTicks,
239 m_ThisAdaptiveFocusAlt, m_ThisAdaptiveFocusAltTicks, m_LastAdaptiveFocusPosErrorReversal,
240 thisPosError, totalTicks, currentPosition, focuserMoved);
243 if (totalTicks < m_focus->m_OpsFocusSettings->focusAdaptiveMinMove->value())
246 check = (m_ThisAdaptiveFocusStartPos + totalTicks == currentPosition);
249 if (success && focuserMoved)
252 m_LastAdaptiveFocusTemperature = m_ThisAdaptiveFocusTemperature;
253 m_LastAdaptiveFocusAlt = m_ThisAdaptiveFocusAlt;
257 qCDebug(KSTARS_EKOS_FOCUS) <<
"Adaptive Focus Stats: Filter:" << m_LastAdaptiveFilter
258 <<
", Adaptive Filter:" << getAdaptiveFilter(m_LastAdaptiveFilter)
259 <<
", Min Move:" << m_focus->m_OpsFocusSettings->focusAdaptiveMinMove->value()
260 <<
", success:" << (success ?
"Yes" :
"No")
261 <<
", focuserMoved:" << (focuserMoved ?
"Yes" :
"No")
262 <<
", Temp:" << m_ThisAdaptiveFocusTemperature
263 <<
", Temp ticks:" << m_ThisAdaptiveFocusTempTicks
264 <<
", Alt:" << m_ThisAdaptiveFocusAlt
265 <<
", Alt ticks:" << m_ThisAdaptiveFocusAltTicks
266 <<
", Last Pos Error reversal:" << m_LastAdaptiveFocusPosErrorReversal
267 <<
", Rounding Error:" << m_ThisAdaptiveFocusRoundingError
268 <<
", New Pos Error:" << thisPosError
269 <<
", Starting Pos:" << m_ThisAdaptiveFocusStartPos
270 <<
", Current Position:" << currentPosition
271 <<
", Accounting Check:" << (check ?
"Passed" :
"Failed");
277 if (!m_focus->m_FilterManager)
282 QString lockFilter = m_focus->m_FilterManager->getFilterLock(filter);
283 if (lockFilter != NULL_FILTER)
284 adaptiveFilter = lockFilter;
286 return adaptiveFilter;
290int AdaptiveFocus::getAdaptiveFilterOffset(
const QString &activeFilter,
const QString &adaptiveFilter)
293 if (!m_focus->m_FilterManager)
296 if (activeFilter != adaptiveFilter)
298 int activeOffset = m_focus->m_FilterManager->getFilterOffset(activeFilter);
299 int adaptiveOffset = m_focus->m_FilterManager->getFilterOffset(adaptiveFilter);
300 if (activeOffset != INVALID_VALUE && adaptiveOffset != INVALID_VALUE)
301 offset = activeOffset - adaptiveOffset;
303 qCDebug(KSTARS_EKOS_FOCUS) <<
"getAdaptiveFilterOffset unable to calculate filter offsets";
309void AdaptiveFocus::resetAdaptiveFocusCounters()
311 m_LastAdaptiveFilter = NULL_FILTER;
312 m_LastAdaptiveFocusTemperature = INVALID_VALUE;
313 m_LastAdaptiveFocusAlt = INVALID_VALUE;
314 m_AdaptiveTotalMove = 0;
318void AdaptiveFocus::setInAdaptiveFocus(
bool value)
320 m_inAdaptiveFocus = value;
326int AdaptiveFocus::adaptStartPosition(
int currentPosition,
QString &AFfilter)
331 if (!m_focus->m_FilterManager)
332 return currentPosition;
335 QString lockFilter = m_focus->m_FilterManager->getFilterLock(AFfilter);
336 if (m_focus->inBuildOffsets || lockFilter == NULL_FILTER || lockFilter == AFfilter)
337 filterText = AFfilter;
340 filterText = AFfilter +
" locked to " + lockFilter;
341 AFfilter = lockFilter;
344 if (!m_focus->m_OpsFocusSettings->focusAdaptStart->isChecked())
346 return currentPosition;
348 if (m_focus->m_FocusAlgorithm != Focus::FOCUS_LINEAR1PASS)
350 return currentPosition;
354 double lastTemp, lastAlt;
355 if(!m_focus->m_FilterManager->getFilterAbsoluteFocusDetails(AFfilter, lastPos, lastTemp, lastAlt))
357 return currentPosition;
361 return currentPosition;
364 int minTravelLimit = qMax(0.0, currentPosition - m_focus->m_OpsFocusMechanics->focusMaxTravel->value());
365 int maxTravelLimit = qMin(m_focus->absMotionMax, currentPosition + m_focus->m_OpsFocusMechanics->focusMaxTravel->value());
366 if (lastPos < minTravelLimit || lastPos > maxTravelLimit)
369 m_focus->appendLogText(
i18n(
"Adaptive start point, last AF solution outside Max Travel, ignoring"));
370 return currentPosition;
374 double ticksTemp = 0.0;
375 double tempDelta = 0.0;
376 if (!m_focus->currentTemperatureSourceElement)
377 m_focus->appendLogText(
i18n(
"Adaptive start point, no temperature source available"));
378 else if (lastTemp == INVALID_VALUE)
379 m_focus->appendLogText(
i18n(
"Adaptive start point, no temperature for last AF solution"));
382 double currentTemp = m_focus->currentTemperatureSourceElement->value;
383 tempDelta = currentTemp - lastTemp;
384 if (abs(tempDelta) > 30)
386 m_focus->appendLogText(
i18n(
"Adaptive start point, very large temperature delta, ignoring"));
388 ticksTemp = tempDelta * m_focus->m_FilterManager->getFilterTicksPerTemp(AFfilter);
392 double ticksAlt = 0.0;
393 double currentAlt = m_focus->mountAlt;
394 double altDelta = currentAlt - lastAlt;
397 if (lastAlt == INVALID_VALUE)
398 m_focus->appendLogText(
i18n(
"Adaptive start point, no alt recorded for last AF solution"));
399 else if (abs(altDelta) > 90.0)
400 m_focus->appendLogText(
i18n(
"Adaptive start point, very large altitude delta, ignoring"));
402 ticksAlt = altDelta * m_focus->m_FilterManager->getFilterTicksPerAlt(AFfilter);
405 const int ticksTotal =
static_cast<int> (round(ticksTemp + ticksAlt));
406 int targetPos = lastPos + ticksTotal;
407 if (targetPos < minTravelLimit || targetPos > maxTravelLimit)
410 m_focus->appendLogText(
i18n(
"Adaptive start point, target position is outside Max Travel, ignoring"));
411 return currentPosition;
414 if (abs(targetPos - currentPosition) > m_focus->m_OpsFocusSettings->focusAdaptiveMaxMove->value())
418 m_focus->appendLogText(
i18n(
"Adaptive start point [%1] excessive move disallowed", filterText));
419 qCDebug(KSTARS_EKOS_FOCUS) <<
"Adaptive start point: " << filterText
420 <<
" startPosition: " << currentPosition
421 <<
" Last filter position: " << lastPos
422 <<
" Temp delta: " << tempDelta <<
" Temp ticks: " << ticksTemp
423 <<
" Alt delta: " << altDelta <<
" Alt ticks: " << ticksAlt
424 <<
" Target position: " << targetPos
425 <<
" Exceeds max allowed move: " << m_focus->m_OpsFocusSettings->focusAdaptiveMaxMove->value()
426 <<
" Using startPosition.";
427 return currentPosition;
432 m_focus->appendLogText(
i18n(
"Adapting start point [%1] from %2 to %3", filterText, currentPosition, targetPos));
433 qCDebug(KSTARS_EKOS_FOCUS) <<
"Adaptive start point: " << filterText
434 <<
" startPosition: " << currentPosition
435 <<
" Last filter position: " << lastPos
436 <<
" Temp delta: " << tempDelta <<
" Temp ticks: " << ticksTemp
437 <<
" Alt delta: " << altDelta <<
" Alt ticks: " << ticksAlt
438 <<
" Target position: " << targetPos;
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
QString arg(Args &&... args) const const
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)