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(&currentTicks);
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