1 /*******************************************************************************
2 Copyright(c) 2019 Jasem Mutlaq. All rights reserved.
3
4 Starlight Instruments EFS Focuser
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 "si_efs.h"
22
23 #include <cmath>
24 #include <cstring>
25 #include <memory>
26
27 #define FOCUS_SETTINGS_TAB "Settings"
28
29 static std::unique_ptr<SIEFS> siefs(new SIEFS());
30
31 const std::map<SIEFS::SI_COMMANDS, std::string> SIEFS::CommandsMap =
32 {
33 {SIEFS::SI_NOOP, "No Operation"},
34 {SIEFS::SI_IN, "Moving Inwards"},
35 {SIEFS::SI_OUT, "Moving Outwards"},
36 {SIEFS::SI_GOTO, "Goto"},
37 {SIEFS::SI_SET_POS, "Set Position"},
38 {SIEFS::SI_MAX_POS, "Set Max Position"},
39 {SIEFS::SI_FAST_IN, "Fast In"},
40 {SIEFS::SI_FAST_OUT, "Fast Out"},
41 {SIEFS::SI_HALT, "Halt"},
42 };
43
44 const std::map<SIEFS::SI_MOTOR, std::string> SIEFS::MotorMap =
45 {
46 {SIEFS::SI_NOT_MOVING, "Idle"},
47 {SIEFS::SI_MOVING_IN, "Moving Inwards"},
48 {SIEFS::SI_MOVING_OUT, "Moving Outwards"},
49 {SIEFS::SI_LOCKED, "Locked"},
50 };
51
SIEFS()52 SIEFS::SIEFS()
53 {
54 setVersion(0, 1);
55
56 FI::SetCapability(FOCUSER_CAN_ABS_MOVE | FOCUSER_CAN_REL_MOVE | FOCUSER_CAN_ABORT | FOCUSER_CAN_SYNC);
57 setSupportedConnections(CONNECTION_NONE);
58 }
59
Connect()60 bool SIEFS::Connect()
61 {
62 if (isSimulation())
63 {
64 SetTimer(getCurrentPollingPeriod());
65 return true;
66 }
67
68 handle = hid_open(0x04D8, 0xF056, nullptr);
69
70 if (handle == nullptr)
71 {
72 LOG_ERROR("No SIEFS focuser found.");
73 return false;
74 }
75 else
76 {
77 uint32_t maximumPosition = 0;
78 bool rc = getMaxPosition(&maximumPosition);
79 if (rc)
80 {
81 FocusMaxPosN[0].value = maximumPosition;
82
83 FocusAbsPosN[0].min = 0;
84 FocusAbsPosN[0].max = FocusMaxPosN[0].value;
85 FocusAbsPosN[0].step = FocusMaxPosN[0].value / 50.0;
86
87 FocusSyncN[0].min = 0;
88 FocusSyncN[0].max = FocusMaxPosN[0].value;
89 FocusSyncN[0].step = FocusMaxPosN[0].value / 50.0;
90
91 FocusRelPosN[0].max = FocusMaxPosN[0].value / 2;
92 FocusRelPosN[0].step = FocusMaxPosN[0].value / 100.0;
93 FocusRelPosN[0].min = 0;
94 }
95
96 SetTimer(getCurrentPollingPeriod());
97 }
98
99 return (handle != nullptr);
100 }
101
Disconnect()102 bool SIEFS::Disconnect()
103 {
104 if (isSimulation() == false)
105 {
106 hid_close(handle);
107 hid_exit();
108 }
109
110 return true;
111 }
112
getDefaultName()113 const char *SIEFS::getDefaultName()
114 {
115 return "SI EFS";
116 }
117
initProperties()118 bool SIEFS::initProperties()
119 {
120 INDI::Focuser::initProperties();
121
122 addSimulationControl();
123
124 return true;
125 }
126
TimerHit()127 void SIEFS::TimerHit()
128 {
129 if (!isConnected())
130 return;
131
132 uint32_t currentTicks = 0;
133
134 bool rc = getAbsPosition(¤tTicks);
135
136 if (rc)
137 FocusAbsPosN[0].value = currentTicks;
138
139 getStatus();
140
141 if (FocusAbsPosNP.s == IPS_BUSY || FocusRelPosNP.s == IPS_BUSY)
142 {
143 if (isSimulation())
144 {
145 if (FocusAbsPosN[0].value < targetPosition)
146 simPosition += 500;
147 else
148 simPosition -= 500;
149
150 if (std::abs(simPosition - static_cast<int32_t>(targetPosition)) < 500)
151 {
152 FocusAbsPosN[0].value = targetPosition;
153 simPosition = FocusAbsPosN[0].value;
154 m_Motor = SI_NOT_MOVING;
155 }
156
157 FocusAbsPosN[0].value = simPosition;
158 }
159
160 if (m_Motor == SI_NOT_MOVING && targetPosition == FocusAbsPosN[0].value)
161 {
162 if (FocusRelPosNP.s == IPS_BUSY)
163 {
164 FocusRelPosNP.s = IPS_OK;
165 IDSetNumber(&FocusRelPosNP, nullptr);
166 }
167
168 FocusAbsPosNP.s = IPS_OK;
169 LOG_DEBUG("Focuser reached target position.");
170 }
171 }
172
173 IDSetNumber(&FocusAbsPosNP, nullptr);
174
175 SetTimer(getCurrentPollingPeriod());
176 }
177
MoveAbsFocuser(uint32_t targetTicks)178 IPState SIEFS::MoveAbsFocuser(uint32_t targetTicks)
179 {
180 bool rc = setAbsPosition(targetTicks);
181
182 if (!rc)
183 return IPS_ALERT;
184
185 targetPosition = targetTicks;
186
187 rc = sendCommand(SI_GOTO);
188
189 if (!rc)
190 return IPS_ALERT;
191
192 FocusAbsPosNP.s = IPS_BUSY;
193
194 return IPS_BUSY;
195 }
196
MoveRelFocuser(FocusDirection dir,uint32_t ticks)197 IPState SIEFS::MoveRelFocuser(FocusDirection dir, uint32_t ticks)
198 {
199 int direction = (dir == FOCUS_INWARD) ? -1 : 1;
200 int reversed = (FocusReverseS[INDI_ENABLED].s == ISS_ON) ? -1 : 1;
201 int relative = static_cast<int>(ticks);
202
203 int targetAbsPosition = FocusAbsPosN[0].value + (relative * direction * reversed);
204
205 targetAbsPosition = std::min(static_cast<uint32_t>(FocusMaxPosN[0].value)
206 , static_cast<uint32_t>(std::max(static_cast<int>(FocusAbsPosN[0].min), targetAbsPosition)));
207
208 return MoveAbsFocuser(targetAbsPosition);
209 }
210
setPosition(uint32_t ticks,uint8_t cmdCode)211 bool SIEFS::setPosition(uint32_t ticks, uint8_t cmdCode)
212 {
213 int rc = 0;
214 uint8_t command[3];
215 uint8_t response[3];
216
217 // 20 bit resolution position. 4 high bits + 16 lower bits
218
219 // Send 4 high bits first
220 command[0] = cmdCode + 8;
221 command[1] = (ticks & 0x40000) >> 16;
222
223 LOGF_DEBUG("Set %s Position (%ld)", cmdCode == 0x20 ? "Absolute" : "Maximum", ticks);
224 LOGF_DEBUG("CMD <%02X %02X>", command[0], command[1]);
225
226 if (isSimulation())
227 rc = 2;
228 else
229 rc = hid_write(handle, command, 2);
230
231 if (rc < 0)
232 {
233 LOGF_ERROR("setPosition: Error writing to device (%s)", hid_error(handle));
234 return false;
235 }
236
237 if (isSimulation())
238 {
239 rc = 2;
240 response[0] = cmdCode + 8;
241 response[1] = command[1];
242 }
243 else
244 rc = hid_read_timeout(handle, response, 2, SI_TIMEOUT);
245
246 if (rc < 0)
247 {
248 LOGF_ERROR("setPosition: Error reading from device (%s)", hid_error(handle));
249 return false;
250 }
251
252 LOGF_DEBUG("RES <%02X %02X>", response[0], response[1]);
253
254 // Send lower 16 bit
255 command[0] = cmdCode;
256 // Low Byte
257 command[1] = ticks & 0xFF;
258 // High Byte
259 command[2] = (ticks & 0xFF00) >> 8;
260
261 LOGF_DEBUG("CMD <%02X %02X %02X>", command[0], command[1], command[2]);
262
263 if (isSimulation())
264 rc = 3;
265 else
266 rc = hid_write(handle, command, 3);
267
268 if (rc < 0)
269 {
270 LOGF_ERROR("setPosition: Error writing to device (%s)", hid_error(handle));
271 return false;
272 }
273
274 if (isSimulation())
275 {
276 rc = 3;
277 response[0] = command[0];
278 response[1] = command[1];
279 response[2] = command[2];
280 }
281 else
282 rc = hid_read_timeout(handle, response, 3, SI_TIMEOUT);
283
284 if (rc < 0)
285 {
286 LOGF_ERROR("setPosition: Error reading from device (%s)", hid_error(handle));
287 return false;
288 }
289
290 LOGF_DEBUG("RES <%02X %02X %02X>", response[0], response[1], response[2]);
291
292 targetPosition = ticks;
293
294 // TODO add checking later
295 return true;
296 }
297
getPosition(uint32_t * ticks,uint8_t cmdCode)298 bool SIEFS::getPosition(uint32_t *ticks, uint8_t cmdCode)
299 {
300 int rc = 0;
301 uint32_t pos = 0;
302 uint8_t command[1] = {0};
303 uint8_t response[3] = {0};
304
305 // 20 bit resolution position. 4 high bits + 16 lower bits
306
307 // Get 4 high bits first
308 command[0] = cmdCode + 8;
309
310 LOGF_DEBUG("Get %s Position (High 4 bits)", cmdCode == 0x21 ? "Absolute" : "Maximum");
311 LOGF_DEBUG("CMD <%02X>", command[0]);
312
313 if (isSimulation())
314 rc = 2;
315 else
316 rc = hid_write(handle, command, 1);
317
318 if (rc < 0)
319 {
320 LOGF_ERROR("getPosition: Error writing to device (%s)", hid_error(handle));
321 return false;
322 }
323
324 if (isSimulation())
325 {
326 rc = 2;
327 response[0] = command[0];
328 response[1] = simPosition >> 16;
329 }
330 else
331 rc = hid_read_timeout(handle, response, 2, SI_TIMEOUT);
332
333 if (rc < 0)
334 {
335 LOGF_ERROR("getPosition: Error reading from device (%s)", hid_error(handle));
336 return false;
337 }
338
339 LOGF_DEBUG("RES <%02X %02X>", response[0], response[1]);
340
341 // Store 4 high bits part of a 20 bit number
342 pos = response[1] << 16;
343
344 // Get 16 lower bits
345 command[0] = cmdCode;
346
347 LOGF_DEBUG("Get %s Position (Lower 16 bits)", cmdCode == 0x21 ? "Absolute" : "Maximum");
348 LOGF_DEBUG("CMD <%02X>", command[0]);
349
350 if (isSimulation())
351 rc = 1;
352 else
353 rc = hid_write(handle, command, 1);
354
355 if (rc < 0)
356 {
357 LOGF_ERROR("getPosition: Error writing to device (%s)", hid_error(handle));
358 return false;
359 }
360
361 if (isSimulation())
362 {
363 rc = 3;
364 response[0] = command[0];
365 response[1] = simPosition & 0xFF;
366 response[2] = (simPosition & 0xFF00) >> 8;
367 }
368 else
369 rc = hid_read_timeout(handle, response, 3, SI_TIMEOUT);
370
371 if (rc < 0)
372 {
373 LOGF_ERROR("getPosition: Error reading from device (%s)", hid_error(handle));
374 return false;
375 }
376
377 LOGF_DEBUG("RES <%02X %02X %02X>", response[0], response[1], response[2]);
378
379 // Res[1] is lower byte and Res[2] is high byte. Combine them and add them to ticks.
380 pos |= response[1] | response[2] << 8;
381
382 *ticks = pos;
383
384 LOGF_DEBUG("%s Position: %ld", cmdCode == 0x21 ? "Absolute" : "Maximum", pos);
385
386 return true;
387 }
388
setAbsPosition(uint32_t ticks)389 bool SIEFS::setAbsPosition(uint32_t ticks)
390 {
391 return setPosition(ticks, 0x20);
392 }
393
getAbsPosition(uint32_t * ticks)394 bool SIEFS::getAbsPosition(uint32_t *ticks)
395 {
396 return getPosition(ticks, 0x21);
397 }
398
setMaxPosition(uint32_t ticks)399 bool SIEFS::setMaxPosition(uint32_t ticks)
400 {
401 return setPosition(ticks, 0x22);
402 }
403
getMaxPosition(uint32_t * ticks)404 bool SIEFS::getMaxPosition(uint32_t *ticks)
405 {
406 return getPosition(ticks, 0x23);
407 }
408
sendCommand(SI_COMMANDS targetCommand)409 bool SIEFS::sendCommand(SI_COMMANDS targetCommand)
410 {
411 int rc = 0;
412 uint8_t command[2] = {0};
413 uint8_t response[3] = {0};
414
415 command[0] = 0x10;
416 command[1] = targetCommand;
417
418 LOGF_DEBUG("CMD <%02X %02X>", command[0], command[1]);
419
420 if (isSimulation())
421 rc = 2;
422 else
423 rc = hid_write(handle, command, 2);
424
425 if (rc < 0)
426 {
427 LOGF_ERROR("setStatus: Error writing to device (%s)", hid_error(handle));
428 return false;
429 }
430
431 if (isSimulation())
432 {
433 rc = 3;
434 response[0] = command[0];
435 response[1] = 0;
436 response[2] = command[1];
437 // m_Motor = targetCommand;
438 }
439 else
440 rc = hid_read_timeout(handle, response, 3, SI_TIMEOUT);
441
442 if (rc < 0)
443 {
444 LOGF_ERROR("setStatus: Error reading from device (%s)", hid_error(handle));
445 return false;
446 }
447
448 LOGF_DEBUG("RES <%02X %02X %02X>", response[0], response[1], response[2]);
449
450 if (response[1] == 0xFF)
451 {
452 LOG_ERROR("setStatus: Invalid state change.");
453 return false;
454 }
455
456 return true;
457 }
458
getStatus()459 bool SIEFS::getStatus()
460 {
461 int rc = 0;
462 uint8_t command[1] = {0};
463 uint8_t response[2] = {0};
464
465 command[0] = 0x11;
466
467 LOGF_DEBUG("CMD <%02X>", command[0]);
468
469 if (isSimulation())
470 rc = 1;
471 else
472 rc = hid_write(handle, command, 1);
473
474 if (rc < 0)
475 {
476 LOGF_ERROR("getStatus: Error writing to device (%s)", hid_error(handle));
477 return false;
478 }
479
480 if (isSimulation())
481 {
482 rc = 2;
483 response[0] = command[0];
484 response[1] = m_Motor;
485 // Halt/SetPos is state = 0 "not moving".
486 // if (response[1] == SI_HALT || response[1] == SI_SET_POS)
487 // response[1] = 0;
488 }
489 else
490 rc = hid_read_timeout(handle, response, 2, SI_TIMEOUT);
491
492 if (rc < 0)
493 {
494 LOGF_ERROR("getStatus: Error reading from device (%s)", hid_error(handle));
495 return false;
496 }
497
498 LOGF_DEBUG("RES <%02X %02X>", response[0], response[1]);
499
500 m_Motor = static_cast<SI_MOTOR>(response[1]);
501 if (MotorMap.count(m_Motor) > 0)
502 LOGF_DEBUG("State: %s", MotorMap.at(m_Motor).c_str());
503 else
504 {
505 LOGF_WARN("Warning: Unknown status (%d)", response[1]);
506 return false;
507 }
508
509 return true;
510 }
511
AbortFocuser()512 bool SIEFS::AbortFocuser()
513 {
514 return sendCommand(SI_HALT);
515 }
516
SyncFocuser(uint32_t ticks)517 bool SIEFS::SyncFocuser(uint32_t ticks)
518 {
519 bool rc = setAbsPosition(ticks);
520
521 if (!rc)
522 return false;
523
524 simPosition = ticks;
525
526 rc = sendCommand(SI_SET_POS);
527
528 return rc;
529 }
530
SetFocuserMaxPosition(uint32_t ticks)531 bool SIEFS::SetFocuserMaxPosition(uint32_t ticks)
532 {
533 bool rc = setAbsPosition(ticks);
534
535 if (!rc)
536 return false;
537
538 rc = sendCommand(SI_MAX_POS);
539
540 return rc;
541 }
542