1 /*******************************************************************************
2 Copyright(c) 2017 Jasem Mutlaq. All rights reserved.
3
4 Televue FocusMaster Driver.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License version 2 as published by the Free Software Foundation.
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 Library General Public License for more details.
14 .
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19 *******************************************************************************/
20
21 #include "focusmaster.h"
22
23 #include <cmath>
24 #include <cstring>
25 #include <memory>
26 #include <unistd.h>
27
28 #define POLLMS_OVERRIDE 1000 /* 1000 ms */
29 #define FOCUSMASTER_TIMEOUT 1000 /* 1000 ms */
30 #define MAX_FM_BUF 16
31
32 #define FOCUS_SETTINGS_TAB "Settings"
33
34 // We declare an auto pointer to FocusMaster.
35 std::unique_ptr<FocusMaster> focusMaster(new FocusMaster());
36
FocusMaster()37 FocusMaster::FocusMaster()
38 {
39 FI::SetCapability(FOCUSER_CAN_ABORT);
40 setConnection(CONNECTION_NONE);
41 }
42
Connect()43 bool FocusMaster::Connect()
44 {
45 handle = hid_open(0x134A, 0x9030, nullptr);
46
47 if (handle == nullptr)
48 {
49 LOG_ERROR("No FocusMaster focuser found.");
50 return false;
51 }
52 else
53 {
54 // N.B. Check here if we have the digital readout gadget.
55
56 // if digital readout
57 //FI::SetCapability(GetFocuserCapability() | FOCUSER_CAN_REL_MOVE | FOCUSER_CAN_ABS_MOVE);
58
59 SetTimer(POLLMS_OVERRIDE);
60
61 }
62
63 return (handle != nullptr);
64 }
65
Disconnect()66 bool FocusMaster::Disconnect()
67 {
68 hid_close(handle);
69 hid_exit();
70 return true;
71 }
72
getDefaultName()73 const char *FocusMaster::getDefaultName()
74 {
75 return (const char *)"FocusMaster";
76 }
77
initProperties()78 bool FocusMaster::initProperties()
79 {
80 INDI::Focuser::initProperties();
81
82 // Sync to a particular position
83 IUFillNumber(&SyncN[0], "Ticks", "", "%.f", 0, 100000, 100., 0.);
84 IUFillNumberVector(&SyncNP, SyncN, 1, getDeviceName(), "Sync", "", MAIN_CONTROL_TAB, IP_RW, 0, IPS_IDLE);
85
86 // Full Forward/Reverse Motion
87 IUFillSwitch(&FullMotionS[FOCUS_INWARD], "FULL_INWARD", "Full Inward", ISS_OFF);
88 IUFillSwitch(&FullMotionS[FOCUS_OUTWARD], "FULL_OUTWARD", "Full Outward", ISS_OFF);
89 IUFillSwitchVector(&FullMotionSP, FullMotionS, 2, getDeviceName(), "FULL_MOTION", "Full Motion", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
90
91 FocusAbsPosN[0].min = SyncN[0].min = 0;
92 FocusAbsPosN[0].max = SyncN[0].max;
93 FocusAbsPosN[0].step = SyncN[0].step;
94 FocusAbsPosN[0].value = 0;
95
96 FocusRelPosN[0].max = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 2;
97 FocusRelPosN[0].step = FocusRelPosN[0].max / 100.0;
98 FocusRelPosN[0].value = 100;
99
100 addSimulationControl();
101
102 return true;
103 }
104
updateProperties()105 bool FocusMaster::updateProperties()
106 {
107 INDI::Focuser::updateProperties();
108
109 if (isConnected())
110 {
111 defineProperty(&FullMotionSP);
112 //defineProperty(&SyncNP);
113 }
114 else
115 {
116 deleteProperty(FullMotionSP.name);
117 //deleteProperty(SyncNP.name);
118 }
119
120 return true;
121 }
122
TimerHit()123 void FocusMaster::TimerHit()
124 {
125 if (!isConnected())
126 return;
127
128 //uint32_t currentTicks = 0;
129
130 if (FocusTimerNP.s == IPS_BUSY)
131 {
132 float remaining = CalcTimeLeft(focusMoveStart, focusMoveRequest);
133
134 if (remaining <= 0)
135 {
136 FocusTimerNP.s = IPS_OK;
137 FocusTimerN[0].value = 0;
138 AbortFocuser();
139 }
140 else
141 FocusTimerN[0].value = remaining * 1000.0;
142
143 IDSetNumber(&FocusTimerNP, nullptr);
144 }
145
146 #if 0
147 bool rc = getPosition(¤tTicks);
148
149 if (rc)
150 FocusAbsPosN[0].value = currentTicks;
151
152 if (FocusAbsPosNP.s == IPS_BUSY || FocusRelPosNP.s == IPS_BUSY)
153 {
154 if (targetPosition == FocusAbsPosN[0].value)
155 {
156 if (FocusRelPosNP.s == IPS_BUSY)
157 {
158 FocusRelPosNP.s = IPS_OK;
159 IDSetNumber(&FocusRelPosNP, nullptr);
160 }
161
162 FocusAbsPosNP.s = IPS_OK;
163 LOG_DEBUG("Focuser reached target position.");
164 }
165 }
166
167 IDSetNumber(&FocusAbsPosNP, nullptr);
168 #endif
169
170 SetTimer(POLLMS_OVERRIDE);
171 }
172
ISNewSwitch(const char * dev,const char * name,ISState * states,char * names[],int n)173 bool FocusMaster::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
174 {
175 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
176 {
177 // Full Motion
178 if (!strcmp(FullMotionSP.name, name))
179 {
180 IUUpdateSwitch(&FullMotionSP, states, names, n);
181 FocusDirection targetDirection = static_cast<FocusDirection>(IUFindOnSwitchIndex(&FullMotionSP));
182
183 FullMotionSP.s = MoveFocuser(targetDirection, 0, 0);
184 return true;
185 }
186 }
187
188 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
189
190 }
ISNewNumber(const char * dev,const char * name,double values[],char * names[],int n)191 bool FocusMaster::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
192 {
193 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
194 {
195 // Sync
196 if (strcmp(SyncNP.name, name) == 0)
197 {
198 IUUpdateNumber(&SyncNP, values, names, n);
199 if (!sync(SyncN[0].value))
200 SyncNP.s = IPS_ALERT;
201 else
202 SyncNP.s = IPS_OK;
203
204 IDSetNumber(&SyncNP, nullptr);
205 return true;
206 }
207 }
208
209 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
210 }
211
MoveFocuser(FocusDirection dir,int speed,uint16_t duration)212 IPState FocusMaster::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
213 {
214 INDI_UNUSED(speed);
215
216 uint8_t command[MAX_FM_BUF] = {0};
217
218 if (dir == FOCUS_INWARD)
219 {
220 command[0] = 0x31;
221 command[1] = 0x21;
222 }
223 else
224 {
225 command[0] = 0x32;
226 command[1] = 0x22;
227 }
228
229 sendCommand(command);
230
231 gettimeofday(&focusMoveStart, nullptr);
232 focusMoveRequest = duration / 1000.0;
233
234 if (duration > 0 && duration <= POLLMS_OVERRIDE)
235 {
236 usleep(duration * 1000);
237 AbortFocuser();
238 return IPS_OK;
239 }
240
241 return IPS_BUSY;
242 }
243
MoveAbsFocuser(uint32_t targetTicks)244 IPState FocusMaster::MoveAbsFocuser(uint32_t targetTicks)
245 {
246 INDI_UNUSED(targetTicks);
247
248 FocusAbsPosNP.s = IPS_BUSY;
249
250 return IPS_BUSY;
251 }
252
MoveRelFocuser(FocusDirection dir,uint32_t ticks)253 IPState FocusMaster::MoveRelFocuser(FocusDirection dir, uint32_t ticks)
254 {
255 uint32_t finalTicks = FocusAbsPosN[0].value + (ticks * (dir == FOCUS_INWARD ? -1 : 1));
256
257 return MoveAbsFocuser(finalTicks);
258 }
259
sendCommand(const uint8_t * command,char * response)260 bool FocusMaster::sendCommand(const uint8_t *command, char *response)
261 {
262 INDI_UNUSED(response);
263
264 int rc = hid_write(handle, command, 2);
265
266 LOGF_DEBUG("CMD <%#02X %#02X>", command[0], command[1]);
267
268 if (rc < 0)
269 {
270 LOGF_ERROR("<%#02X %#02X>: Error writing to device %s", command[0], command[1], hid_error(handle));
271 return false;
272 }
273
274 return true;
275 }
276
setPosition(uint32_t ticks)277 bool FocusMaster::setPosition(uint32_t ticks)
278 {
279 INDI_UNUSED(ticks);
280 return false;
281 }
282
getPosition(uint32_t * ticks)283 bool FocusMaster::getPosition(uint32_t *ticks)
284 {
285 INDI_UNUSED(ticks);
286 return false;
287 }
288
AbortFocuser()289 bool FocusMaster::AbortFocuser()
290 {
291 uint8_t command[MAX_FM_BUF] = {0};
292
293 command[0] = 0x30;
294 command[1] = 0x30;
295
296 LOG_DEBUG("Aborting Focuser...");
297
298 bool rc = sendCommand(command);
299
300 if (rc)
301 {
302 if (FullMotionSP.s == IPS_BUSY)
303 {
304 IUResetSwitch(&FullMotionSP);
305 FullMotionSP.s = IPS_IDLE;
306 IDSetSwitch(&FullMotionSP, nullptr);
307 }
308
309 if (FocusMotionSP.s == IPS_BUSY)
310 {
311 IUResetSwitch(&FocusMotionSP);
312 FocusMotionSP.s = IPS_IDLE;
313 IDSetSwitch(&FocusMotionSP, nullptr);
314 }
315 }
316
317 return rc;
318 }
319
sync(uint32_t ticks)320 bool FocusMaster::sync(uint32_t ticks)
321 {
322 INDI_UNUSED(ticks);
323 return false;
324 }
325
CalcTimeLeft(timeval start,float req)326 float FocusMaster::CalcTimeLeft(timeval start, float req)
327 {
328 double timesince;
329 double timeleft;
330 struct timeval now { 0, 0 };
331 gettimeofday(&now, nullptr);
332
333 timesince = (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
334 timesince = timesince / 1000;
335 timeleft = req - timesince;
336 return timeleft;
337 }
338