Instrument Neutral Distributed Interface INDI 2.2.5
indifocuserinterface.cpp
Go to the documentation of this file.
1/*
2 Focuser Interface
3 Copyright (C) 2011 Jasem Mutlaq ([email protected])
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19*/
20
22
23#include "indilogger.h"
24
25#include <cstring>
26#include <cmath>
27
28namespace INDI
29{
30
31FocuserInterface::FocuserInterface(DefaultDevice * defaultDevice) : m_defaultDevice(defaultDevice)
32{
33}
34
35void FocuserInterface::initProperties(const char * groupName)
36{
37 // Focuser Speed
38 FocusSpeedNP[0].fill("FOCUS_SPEED_VALUE", "Focus Speed", "%3.0f", 0.0, 255.0, 1.0, 255.0);
39 FocusSpeedNP.fill(m_defaultDevice->getDeviceName(), "FOCUS_SPEED", "Speed", groupName, IP_RW, 60, IPS_OK);
40
41 // Focus Timer
42 FocusTimerNP[0].fill("FOCUS_TIMER_VALUE", "Focus Timer (ms)", "%4.0f", 0.0, 5000.0, 50.0, 1000.0);
43 FocusTimerNP.fill(m_defaultDevice->getDeviceName(), "FOCUS_TIMER", "Timer", groupName, IP_RW, 60, IPS_OK);
44 lastTimerValue = 1000.0;
45
46 // Focus Motion
47 FocusMotionSP[FOCUS_INWARD].fill("FOCUS_INWARD", "Focus In", ISS_ON);
48 FocusMotionSP[FOCUS_OUTWARD].fill("FOCUS_OUTWARD", "Focus Out", ISS_OFF);
49 FocusMotionSP.fill(m_defaultDevice->getDeviceName(), "FOCUS_MOTION", "Direction", groupName, IP_RW, ISR_1OFMANY, 60,
50 IPS_OK);
51
52 // Absolute Position
53 FocusAbsPosNP[0].fill("FOCUS_ABSOLUTE_POSITION", "Steps", "%.f", 0.0, 100000.0, 1000.0, 0);
54 FocusAbsPosNP.fill(m_defaultDevice->getDeviceName(), "ABS_FOCUS_POSITION", "Absolute Position", groupName, IP_RW, 60,
55 IPS_OK);
56
57 // Relative Position
58 FocusRelPosNP[0].fill("FOCUS_RELATIVE_POSITION", "Steps", "%.f", 0.0, 100000.0, 1000.0, 0);
59 FocusRelPosNP.fill(m_defaultDevice->getDeviceName(), "REL_FOCUS_POSITION", "Relative Position", groupName, IP_RW, 60,
60 IPS_OK);
61
62 // Sync
63 FocusSyncNP[0].fill("FOCUS_SYNC_VALUE", "Steps", "%.f", 0.0, 100000.0, 1000.0, 0);
64 FocusSyncNP.fill(m_defaultDevice->getDeviceName(), "FOCUS_SYNC", "Sync", groupName, IP_RW, 60, IPS_OK);
65
66 // Maximum Position
67 FocusMaxPosNP[0].fill("FOCUS_MAX_VALUE", "Steps", "%.f", 1e3, 1e6, 1e4, 1e5);
68 FocusMaxPosNP.fill(m_defaultDevice->getDeviceName(), "FOCUS_MAX", "Max. Position", groupName, IP_RW, 60, IPS_OK);
69
70 // Abort
71 FocusAbortSP[0].fill("ABORT", "Abort", ISS_OFF);
72 FocusAbortSP.fill(m_defaultDevice->getDeviceName(), "FOCUS_ABORT_MOTION", "Abort Motion", groupName, IP_RW, ISR_ATMOST1, 60,
73 IPS_IDLE);
74
75 // Reverse
76 FocusReverseSP[DefaultDevice::INDI_ENABLED].fill("INDI_ENABLED", "Enabled", ISS_OFF);
77 FocusReverseSP[DefaultDevice::INDI_DISABLED].fill("INDI_DISABLED", "Disabled", ISS_ON);
78 FocusReverseSP.fill(m_defaultDevice->getDeviceName(), "FOCUS_REVERSE_MOTION", "Reverse Motion", groupName, IP_RW,
80
81 // Backlash Compensation
82 FocusBacklashSP[DefaultDevice::INDI_ENABLED].fill("INDI_ENABLED", "Enabled", ISS_OFF);
83 FocusBacklashSP[DefaultDevice::INDI_DISABLED].fill("INDI_DISABLED", "Disabled", ISS_ON);
84 FocusBacklashSP.fill(m_defaultDevice->getDeviceName(), "FOCUS_BACKLASH_TOGGLE", "Backlash", groupName, IP_RW, ISR_1OFMANY,
85 60, IPS_IDLE);
86
87 // Backlash Compensation Value
88 FocusBacklashNP[0].fill("FOCUS_BACKLASH_VALUE", "Steps", "%.f", 0, 1e6, 100, 0);
89 FocusBacklashNP.fill(m_defaultDevice->getDeviceName(), "FOCUS_BACKLASH_STEPS", "Backlash", groupName, IP_RW, 60, IPS_OK);
90}
91
93{
95 {
96 // Now we add our focusser specific stuff
98
99 if (HasVariableSpeed())
100 {
102
103 // We only define Focus Timer if we can not absolute move
104 if (CanAbsMove() == false)
106 }
107 if (CanRelMove())
109 if (CanAbsMove())
110 {
113 }
114 if (CanAbort())
116 if (CanSync())
118 if (CanReverse())
120 if (HasBacklash())
121 {
124 }
125 }
126 else
127 {
129 if (HasVariableSpeed())
130 {
132
133 if (CanAbsMove() == false)
135 }
136 if (CanRelMove())
138 if (CanAbsMove())
139 {
142 }
143 if (CanAbort())
145 if (CanSync())
147 if (CanReverse())
149 if (HasBacklash())
150 {
153 }
154 }
155
156 return true;
157}
158
159bool FocuserInterface::processNumber(const char * dev, const char * name, double values[], char * names[], int n)
160{
161 if (dev && !strcmp(dev, m_defaultDevice->getDeviceName()))
162 {
163 // Move focuser based on requested timeout
164 if (FocusTimerNP.isNameMatch(name))
165 {
166 FocusDirection dir;
167 int speed;
168 int t;
169
170 // first we get all the numbers just sent to us
171 FocusTimerNP.update(values, names, n);
172
173 // Now lets find what we need for this move
174 speed = FocusSpeedNP[0].getValue();
175
176 if (FocusMotionSP[FOCUS_INWARD].getState() == ISS_ON)
177 dir = FOCUS_INWARD;
178 else
179 dir = FOCUS_OUTWARD;
180
181 t = FocusTimerNP[0].getValue();
182 lastTimerValue = t;
183
184 FocusTimerNP.setState(MoveFocuser(dir, speed, t));
186 return true;
187 }
188
189 // Set variable focus speed
190 if (FocusSpeedNP.isNameMatch(name))
191 {
193 int current_speed = FocusSpeedNP[0].getValue();
194 FocusSpeedNP.update(values, names, n);
195
196 if (SetFocuserSpeed(FocusSpeedNP[0].getValue()) == false)
197 {
198 FocusSpeedNP[0].setValue(current_speed);
201 }
202
203 // Update client display
205 return true;
206 }
207
208 // Update Maximum Position allowed
209 if (FocusMaxPosNP.isNameMatch(name))
210 {
211 uint32_t maxTravel = rint(values[0]);
212 if (SetFocuserMaxPosition(maxTravel))
213 {
214 FocusMaxPosNP.update(values, names, n);
215
216 FocusAbsPosNP[0].setMax(FocusMaxPosNP[0].getValue());
217 FocusSyncNP[0].setMax(FocusMaxPosNP[0].getValue());
218
219 FocusAbsPosNP[0].setStep(FocusMaxPosNP[0].getValue() / 50.0);
220 FocusSyncNP[0].setStep(FocusMaxPosNP[0].getValue() / 50.0);
221
222 FocusAbsPosNP[0].setMin(0);
223 FocusSyncNP[0].setMin(0);
224
225 FocusRelPosNP[0].setMax(FocusMaxPosNP[0].getValue() / 2);
226 FocusRelPosNP[0].setStep(FocusMaxPosNP[0].getValue() / 100.0);
227 FocusRelPosNP[0].setMin(0);
228
232
234
236 }
237 else
239
241 return true;
242 }
243
244 // Sync
245 if (FocusSyncNP.isNameMatch(name))
246 {
247 if (SyncFocuser(rint(values[0])))
248 {
249 FocusSyncNP[0].setValue(rint(values[0]));
250 FocusAbsPosNP[0].setValue(rint(values[0]));
254 }
255 else
256 {
259 }
260 return true;
261 }
262
263 // Set backlash value
265 {
267 {
269
270 // Only warn if there is non-zero backlash value.
271 if (values[0] > 0)
272 DEBUGDEVICE(dev, Logger::DBG_WARNING, "Focuser backlash must be enabled first.");
273 }
274 else
275 {
276 uint32_t steps = static_cast<uint32_t>(values[0]);
277 if (SetFocuserBacklash(steps))
278 {
279 FocusBacklashNP[0].setValue(values[0]);
282 }
283 else
285 }
287 return true;
288 }
289
290 // Update Absolute Focuser Position
291 if (FocusAbsPosNP.isNameMatch(name))
292 {
293 int newPos = rint(values[0]);
294
295 if (newPos < FocusAbsPosNP[0].getMin())
296 {
299 DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus minimum position is %g",
300 FocusAbsPosNP[0].getMin());
301 return true;
302 }
303 else if (newPos > FocusAbsPosNP[0].getMax())
304 {
307 DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus maximum position is %g",
308 FocusAbsPosNP[0].getMax());
309 return true;
310 }
311
312 IPState ret;
313
314 if ((ret = MoveAbsFocuser(newPos)) == IPS_OK)
315 {
317 FocusAbsPosNP.update(values, names, n);
318 DEBUGFDEVICE(dev, Logger::DBG_SESSION, "Focuser moved to position %d", newPos);
320 return true;
321 }
322 else if (ret == IPS_BUSY)
323 {
325 DEBUGFDEVICE(dev, Logger::DBG_SESSION, "Focuser is moving to position %d", newPos);
327 return true;
328 }
329
331 DEBUGDEVICE(dev, Logger::DBG_ERROR, "Focuser failed to move to new requested position.");
333 return true;
334 }
335
336 // Update Relative focuser steps. This moves the focuser CW/CCW by this number of steps.
337 if (FocusRelPosNP.isNameMatch(name))
338 {
339 int newPos = rint(values[0]);
340
341 if (newPos <= 0)
342 {
343 DEBUGDEVICE(dev, Logger::DBG_ERROR, "Relative ticks value must be greater than zero.");
346 return true;
347 }
348
349 IPState ret;
350
351 if (CanAbsMove())
352 {
353 if (FocusMotionSP[FOCUS_INWARD].getState() == ISS_ON)
354 {
355 if (FocusAbsPosNP[0].getValue() - newPos < FocusAbsPosNP[0].getMin())
356 {
360 "Requested position out of bound. Focus minimum position is %g", FocusAbsPosNP[0].getMin());
361 return true;
362 }
363 }
364 else
365 {
366 if (FocusAbsPosNP[0].getValue() + newPos > FocusAbsPosNP[0].getMax())
367 {
371 "Requested position out of bound. Focus maximum position is %g", FocusAbsPosNP[0].getMax());
372 return true;
373 }
374 }
375 }
376
378 newPos)) == IPS_OK)
379 {
382 FocusRelPosNP.update(values, names, n);
383 FocusRelPosNP.apply("Focuser moved %d steps %s", newPos,
384 FocusMotionSP[FOCUS_INWARD].getState() == ISS_ON ? "inward" : "outward");
386 return true;
387 }
388 else if (ret == IPS_BUSY)
389 {
390 FocusRelPosNP.update(values, names, n);
393 FocusAbsPosNP.apply("Focuser is moving %d steps %s...", newPos,
394 FocusMotionSP[FOCUS_INWARD].getState() == ISS_ON ? "inward" : "outward");
396 return true;
397 }
398
400 DEBUGDEVICE(dev, Logger::DBG_ERROR, "Focuser failed to move to new requested position.");
402 return true;
403 }
404 }
405
406 return false;
407}
408
409bool FocuserInterface::processSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
410{
411 if (dev && !strcmp(dev, m_defaultDevice->getDeviceName()))
412 {
413 // This one is for focus motion
414 if (FocusMotionSP.isNameMatch(name))
415 {
416 // Record last direction and state.
418 IPState prevState = FocusMotionSP.getState();
419
420 FocusMotionSP.update(states, names, n);
421
423
424 if (CanRelMove() || CanAbsMove() || HasVariableSpeed())
425 {
427 }
428 // If we are dealing with a simple dumb DC focuser, we move in a specific direction in an open-loop fashion until stopped.
429 else
430 {
431 // If we are reversing direction let's issue abort first.
432 if (prevDirection != targetDirection && prevState == IPS_BUSY)
433 AbortFocuser();
434
435 FocusMotionSP.setState(MoveFocuser(targetDirection, 0, 0));
436 }
437
439
440 return true;
441 }
442
443 // Backlash compensation
444 else if (FocusBacklashSP.isNameMatch(name))
445 {
446 int prevIndex = FocusBacklashSP.findOnSwitchIndex();
447 FocusBacklashSP.update(states, names, n);
448
450 {
451 FocusBacklashSP.update(states, names, n);
454 }
455 else
456 {
458 FocusBacklashSP[prevIndex].setState(ISS_ON);
460 }
461
463 return true;
464 }
465
466 // Abort Focuser
467 else if (FocusAbortSP.isNameMatch(name))
468 {
470
471 if (AbortFocuser())
472 {
475 {
478 }
480 {
483 }
484 }
485 else
487
489 return true;
490 }
491
492 // Reverse Motion
493 else if (FocusReverseSP.isNameMatch(name))
494 {
495 int prevIndex = FocusReverseSP.findOnSwitchIndex();
496 FocusReverseSP.update(states, names, n);
497
499 {
502 }
503 else
504 {
506 FocusReverseSP[prevIndex].setState(ISS_ON);
508 }
509
511 return true;
512 }
513 }
514
515 return false;
516}
517
518IPState FocuserInterface::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
519{
520 INDI_UNUSED(dir);
521 INDI_UNUSED(speed);
522 INDI_UNUSED(duration);
523 // Must be implemented by child class
524 return IPS_ALERT;
525}
526
528{
529 INDI_UNUSED(dir);
530 INDI_UNUSED(ticks);
531 // Must be implemented by child class
532 return IPS_ALERT;
533}
534
536{
537 INDI_UNUSED(ticks);
538 // Must be implemented by child class
539 return IPS_ALERT;
540}
541
543{
544 // This should be a virtual function, because the low level hardware class
545 // must override this
546 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support abort motion.");
547 return false;
548}
549
551{
552 INDI_UNUSED(enabled);
553 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support reverse motion.");
554 return false;
555}
556
558{
559 INDI_UNUSED(ticks);
560 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support syncing.");
561 return false;
562}
563
565{
566 INDI_UNUSED(speed);
567 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support variable speed.");
568 return false;
569}
570
572{
573 INDI_UNUSED(ticks);
574 return true;
575}
576
578{
579 INDI_UNUSED(steps);
580 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support backlash compensation.");
581 return false;
582}
583
585{
586 // If disabled, set the focuser backlash to zero.
587 if (enabled)
588 return SetFocuserBacklash(static_cast<int32_t>(FocusBacklashNP[0].getValue()));
589 else
590 return SetFocuserBacklash(0);
591}
592
594{
595 if (CanAbsMove())
597 if (CanReverse())
599 if (HasBacklash())
600 {
603 }
604
605 return true;
606}
607
608}
bool isConnected() const
const char * getDeviceName() const
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
INDI::PropertyNumber FocusRelPosNP
virtual bool SetFocuserBacklash(int32_t steps)
SetFocuserBacklash Set the focuser backlash compensation value.
INDI::PropertySwitch FocusReverseSP
virtual bool SetFocuserBacklashEnabled(bool enabled)
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
virtual bool ReverseFocuser(bool enabled)
ReverseFocuser Reverse focuser motion direction.
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks)
MoveFocuser the focuser to an relative position.
INDI::PropertyNumber FocusSyncNP
INDI::PropertySwitch FocusMotionSP
INDI::PropertyNumber FocusSpeedNP
INDI::PropertySwitch FocusAbortSP
INDI::PropertyNumber FocusTimerNP
void initProperties(const char *groupName)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
virtual bool SetFocuserMaxPosition(uint32_t ticks)
SetFocuserMaxPosition Set Focuser Maximum position limit in the hardware.
INDI::PropertyNumber FocusMaxPosNP
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
virtual bool SetFocuserSpeed(int speed)
SetFocuserSpeed Set Focuser speed.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process focus switch properties.
virtual bool SyncFocuser(uint32_t ticks)
SyncFocuser Set current position to ticks without moving the focuser.
FocuserInterface(DefaultDevice *defaultDevice)
INDI::PropertySwitch FocusBacklashSP
INDI::PropertyNumber FocusBacklashNP
virtual bool AbortFocuser()
AbortFocuser all focus motion.
INDI::PropertyNumber FocusAbsPosNP
virtual IPState MoveAbsFocuser(uint32_t targetTicks)
MoveFocuser the focuser to an absolute position.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
save Save property to configuration file.
const char * getName() const
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_ATMOST1
Definition indiapi.h:172
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define DEBUGDEVICE(device, priority, msg)
Definition indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition indilogger.h:61
Namespace to encapsulate INDI client, drivers, and mediator classes.