Instrument Neutral Distributed Interface INDI 2.2.5
indirotatorinterface.cpp
Go to the documentation of this file.
1/*
2 Rotator Interface
3 Copyright (C) 2017 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#include "defaultdevice.h"
23
24#include "indilogger.h"
25
26#include <cstring>
27
28namespace INDI
29{
30
31RotatorInterface::RotatorInterface(DefaultDevice *defaultDevice) : m_defaultDevice(defaultDevice)
32{
33}
34
35void RotatorInterface::initProperties(const char *groupName)
36{
37 // Rotator Angle
38 // @INDI_STANDARD_PROPERTY@
39 GotoRotatorNP[0].fill("ANGLE", "Angle", "%.2f", 0, 360., 10., 0.);
40 GotoRotatorNP.fill(m_defaultDevice->getDeviceName(), "ABS_ROTATOR_ANGLE", "Goto", groupName, IP_RW, 0, IPS_IDLE);
41
42 // Abort Rotator
43 // @INDI_STANDARD_PROPERTY@
44 AbortRotatorSP[0].fill("ABORT", "Abort", ISS_OFF);
45 AbortRotatorSP.fill(m_defaultDevice->getDeviceName(), "ROTATOR_ABORT_MOTION", "Abort Motion", groupName, IP_RW, ISR_ATMOST1,
46 0, IPS_IDLE);
47
48 // Rotator Sync
49 // @INDI_STANDARD_PROPERTY@
50 SyncRotatorNP[0].fill("ANGLE", "Angle", "%.2f", 0, 360., 10., 0.);
51 SyncRotatorNP.fill(m_defaultDevice->getDeviceName(), "SYNC_ROTATOR_ANGLE", "Sync", groupName, IP_RW, 0, IPS_IDLE);
52
53 // Home Rotator
54 // @INDI_STANDARD_PROPERTY@
55 HomeRotatorSP[0].fill("HOME", "Start", ISS_OFF);
56 HomeRotatorSP.fill(m_defaultDevice->getDeviceName(), "ROTATOR_HOME", "Homing", groupName, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
57
58 // Reverse Direction
59 // @INDI_STANDARD_PROPERTY@
60 ReverseRotatorSP[DefaultDevice::INDI_ENABLED].fill("INDI_ENABLED", "Enabled", ISS_OFF);
61 ReverseRotatorSP[DefaultDevice::INDI_DISABLED].fill("INDI_DISABLED", "Disabled", ISS_ON);
62 ReverseRotatorSP.fill(m_defaultDevice->getDeviceName(), "ROTATOR_REVERSE", "Reverse", groupName, IP_RW, ISR_1OFMANY, 0,
63 IPS_IDLE);
64
65 // Backlash Compensation
66 // @INDI_STANDARD_PROPERTY@
67 RotatorBacklashSP[DefaultDevice::INDI_ENABLED].fill("INDI_ENABLED", "Enabled", ISS_OFF);
68 RotatorBacklashSP[DefaultDevice::INDI_DISABLED].fill("INDI_DISABLED", "Disabled", ISS_ON);
69 RotatorBacklashSP.fill(m_defaultDevice->getDeviceName(), "ROTATOR_BACKLASH_TOGGLE", "Backlash", groupName, IP_RW,
71
72 // Backlash Compensation Value
73 // @INDI_STANDARD_PROPERTY@
74 RotatorBacklashNP[0].fill("ROTATOR_BACKLASH_VALUE", "Steps", "%.f", 0, 1e6, 100, 0);
75 RotatorBacklashNP.fill(m_defaultDevice->getDeviceName(), "ROTATOR_BACKLASH_STEPS", "Backlash", groupName, IP_RW, 60,
76 IPS_OK);
77
78 // Rotator Limits
79 // @INDI_STANDARD_PROPERTY@
80 RotatorLimitsNP[0].fill("ROTATOR_LIMITS_VALUE", "Safe Range (degrees)", "%.f", 0, 360, 30, 0);
81 RotatorLimitsNP.fill(m_defaultDevice->getDeviceName(), "ROTATOR_LIMITS", "Limits", groupName, IP_RW, 60, IPS_IDLE);
82}
83
84bool RotatorInterface::processNumber(const char *dev, const char *name, double values[], char *names[], int n)
85{
86 INDI_UNUSED(names);
87 INDI_UNUSED(n);
88
89 if (dev != nullptr && strcmp(dev, m_defaultDevice->getDeviceName()) == 0)
90 {
92 // Move Absolute Angle
94 if (GotoRotatorNP.isNameMatch(name))
95 {
96 if (values[0] == GotoRotatorNP[0].getValue())
97 {
100 return true;
101 }
102
103 // Lazily initialize the cable-neutral reference from the first known hardware
104 // position. By the time the first goto is commanded, GotoRotatorNP[0] will
105 // have been populated (either in the driver's updateProperties or via TimerHit).
106 if (m_RotatorOffset < 0)
107 m_RotatorOffset = GotoRotatorNP[0].getValue();
108
109 // If value is outside safe zone, then prevent motion.
110 // The safe zone is a symmetric window of ±(limit/2) degrees centered on the
111 // cable-neutral reference point (m_RotatorOffset). This prevents cable wrap
112 // regardless of how many incremental moves are commanded.
113 // limit=0 disables the check entirely.
114 if (RotatorLimitsNP[0].getValue() > 0)
115 {
116 double limit = RotatorLimitsNP[0].getValue();
117 // Compute signed angular distance from the reference point, normalized to [-180, +180].
118 double delta = values[0] - m_RotatorOffset;
119 while (delta > 180.0) delta -= 360.0;
120 while (delta < -180.0) delta += 360.0;
121 if (std::abs(delta) > limit / 2.0)
122 {
125 "Rotator target %.2f exceeds safe limits (±%.1f° from reference %.2f°)...",
126 values[0], limit / 2.0, m_RotatorOffset);
128 return true;
129 }
130 }
134 DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_SESSION, "Rotator moving to %.2f degrees...", values[0]);
135 return true;
136 }
138 // Sync
140 else if (SyncRotatorNP.isNameMatch(name))
141 {
142 if (values[0] == GotoRotatorNP[0].getValue())
143 {
146 return true;
147 }
148
149 bool rc = SyncRotator(values[0]);
151 if (rc)
152 {
153 SyncRotatorNP[0].setValue(values[0]);
154 // Sync redefines the cable-neutral reference point.
155 m_RotatorOffset = values[0];
156 }
157
159 return true;
160 }
162 // Backlash value
164 else if (RotatorBacklashNP.isNameMatch(name))
165 {
167 {
169 DEBUGDEVICE(dev, Logger::DBG_WARNING, "Rotatorer backlash must be enabled first.");
171 return true;
172 }
173
174 m_defaultDevice->updateProperty(RotatorBacklashNP, values, names, n, [this, values]()
175 {
176 uint32_t steps = static_cast<uint32_t>(values[0]);
177 return SetRotatorBacklash(steps);
178 }, true);
179 return true;
180 }
182 // Limits
184 else if (RotatorLimitsNP.isNameMatch(name))
185 {
186 m_defaultDevice->updateProperty(RotatorLimitsNP, values, names, n, [this, values, dev]()
187 {
188 if (values[0] == 0)
189 {
190 DEBUGDEVICE(dev, Logger::DBG_SESSION, "Rotator limits are disabled.");
191 }
192 else
193 {
194 // Use the current hardware position as reference if not yet initialized.
195 double ref = (m_RotatorOffset >= 0) ? m_RotatorOffset : GotoRotatorNP[0].getValue();
196 double half = values[0] / 2.0;
197 double startAngle = ref - half;
198 if (startAngle < 0) startAngle += 360.0;
199 double endAngle = ref + half;
200 if (endAngle >= 360) endAngle -= 360.0;
202 "Rotator limits set to %.f degrees. Safe range: %.2f to %.2f degrees (reference: %.2f°).",
203 values[0], startAngle, endAngle, ref);
204 }
205 return true;
206 }, true);
207 return true;
208 }
209 }
210
211 return false;
212}
213
214bool RotatorInterface::processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
215{
216 INDI_UNUSED(states);
217 INDI_UNUSED(names);
218 INDI_UNUSED(n);
219
220 if (dev != nullptr && strcmp(dev, m_defaultDevice->getDeviceName()) == 0)
221 {
223 // Abort
225 if (AbortRotatorSP.isNameMatch(name))
226 {
230 {
232 {
235 }
236 }
237 return true;
238 }
240 // Home
242 else if (HomeRotatorSP.isNameMatch(name))
243 {
247 HomeRotatorSP[0].setState(ISS_ON);
249 return true;
250 }
252 // Reverse Rotator
254 else if (ReverseRotatorSP.isNameMatch(name))
255 {
256 m_defaultDevice->updateProperty(ReverseRotatorSP, states, names, n, [this, names]()
257 {
259
260 if (ReverseRotator(enabled))
261 {
262 DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_SESSION, "Rotator direction is %s.",
263 (enabled ? "reversed" : "normal"));
264 return true;
265 }
266 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_SESSION, "Rotator reverse direction failed.");
267 return false;
268 }, true);
269
270 return true;
271 }
273 // Backlash enable/disable
275 else if (RotatorBacklashSP.isNameMatch(name))
276 {
277 m_defaultDevice->updateProperty(RotatorBacklashSP, states, names, n, [this, names]()
278 {
280
281 if (SetRotatorBacklashEnabled(enabled))
282 {
283 DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_SESSION, "Rotator backlash is %s.",
284 (enabled ? "enabled" : "disabled"));
285 return true;
286 }
287 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Failed to set trigger rotator backlash.");
288 return false;
289 }, true);
290
291 return true;
292 }
293 }
294
295 return false;
296}
297
341
343{
344 INDI_UNUSED(angle);
345 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Rotator does not support syncing.");
346 return false;
347}
348
350{
351 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Rotator does not support homing.");
352 return IPS_ALERT;
353}
354
356{
357 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Rotator does not support abort.");
358 return false;
359}
360
362{
363 INDI_UNUSED(enabled);
364 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Rotator does not support reverse.");
365 return false;
366}
367
369{
370 INDI_UNUSED(steps);
371 DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Rotator does not support backlash compensation.");
372 return false;
373}
374
376{
377 // If disabled, set the Rotatorer backlash to zero.
378 if (enabled)
379 return SetRotatorBacklash(static_cast<int32_t>(RotatorBacklashNP[0].getValue()));
380 else
381 return SetRotatorBacklash(0);
382}
383
385{
386 if (CanReverse())
387 {
389 }
390 if (HasBacklash())
391 {
394 }
396
397 return true;
398}
399
400}
bool isConnected() const
const char * getDeviceName() const
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
bool updateProperty(PropertyType &property, ValueType *values, char *names[], int n, std::function< bool()> updater, bool saveConfig=false)
Generic helper function to update an INDI property based on an external operation.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
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.
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
INDI::PropertySwitch AbortRotatorSP
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
virtual IPState MoveRotator(double angle)=0
MoveRotator Go to specific angle.
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
INDI::PropertySwitch RotatorBacklashSP
virtual bool AbortRotator()
AbortRotator Abort all motion.
RotatorInterface(DefaultDevice *defaultDevice)
INDI::PropertyNumber RotatorLimitsNP
void initProperties(const char *groupName)
Initialize Rotator properties. It is recommended to call this function within initProperties() of you...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
INDI::PropertyNumber SyncRotatorNP
virtual bool SyncRotator(double angle)
SyncRotator Set current angle as the supplied angle without moving the rotator.
virtual bool ReverseRotator(bool enabled)
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
virtual bool SetRotatorBacklashEnabled(bool enabled)
SetRotatorBacklashEnabled Enables or disables the Rotator backlash compensation.
INDI::PropertyNumber RotatorBacklashNP
INDI::PropertySwitch HomeRotatorSP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
virtual IPState HomeRotator()
HomeRotator Go to home position.
INDI::PropertySwitch ReverseRotatorSP
INDI::PropertyNumber GotoRotatorNP
virtual bool SetRotatorBacklash(int32_t steps)
SetRotatorBacklash Set the Rotatorer backlash compensation value.
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.