Instrument Neutral Distributed Interface INDI 2.2.5
steeldrive.cpp
Go to the documentation of this file.
1/*
2 Baader Steeldrive Focuser
3 Copyright (C) 2014 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
21#include "steeldrive.h"
22
23#include "indicom.h"
24
25#include <cmath>
26#include <memory>
27#include <cstring>
28#include <termios.h>
29#include <unistd.h>
30
31#define STEELDRIVE_MAX_RETRIES 3
32#define STEELDRIVE_TIMEOUT 2
33#define STEELDRIVE_MAXBUF 16
34#define STEELDRIVE_CMD 9
35#define STEELDRIVE_CMD_LONG 11
36#define STEELDRIVE_TEMPERATURE_FREQ 20 /* Update every 20 POLLMS cycles. For POLLMS 500ms = 10 seconds freq */
37#define STEELDIVE_POSITION_THRESHOLD 5 /* Only send position updates to client if the diff exceeds 5 steps */
38
39#define FOCUS_SETTINGS_TAB "Settings"
40
41std::unique_ptr<SteelDrive> steelDrive(new SteelDrive());
42
44{
45 setVersion(1, 1);
46 // Can move in Absolute & Relative motions, can AbortFocuser motion, and has variable speed.
48}
49
51{
53
54 FocusSpeedNP[0].setMin(350);
55 FocusSpeedNP[0].setMax(1000);
56 FocusSpeedNP[0].setValue(500);
57 FocusSpeedNP[0].setStep(50);
58
59 // Focuser temperature
60 IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
61 IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
63
64 // Temperature Settings
65 IUFillNumber(&TemperatureSettingN[FOCUS_T_COEFF], "Coefficient", "", "%.3f", 0, 0.999, 0.1, 0.1);
66 IUFillNumber(&TemperatureSettingN[FOCUS_T_SAMPLES], "# of Samples", "", "%3.0f", 16, 128, 16, 16);
67 IUFillNumberVector(&TemperatureSettingNP, TemperatureSettingN, 2, getDeviceName(), "Temperature Settings", "",
69
70 // Compensate for temperature
71 IUFillSwitch(&TemperatureCompensateS[0], "Enable", "", ISS_OFF);
72 IUFillSwitch(&TemperatureCompensateS[1], "Disable", "", ISS_ON);
73 IUFillSwitchVector(&TemperatureCompensateSP, TemperatureCompensateS, 2, getDeviceName(), "Temperature Compensate",
75
76 // Focuser Models
77 IUFillSwitch(&ModelS[0], "NT2", "", ISS_OFF);
78 fSettings[0].maxTrip = 30;
79 fSettings[0].gearRatio = 0.25040;
80 IUFillSwitch(&ModelS[1], "SC2", "", ISS_OFF);
81 fSettings[1].maxTrip = 30;
82 fSettings[1].gearRatio = 0.25040;
83 IUFillSwitch(&ModelS[2], "RT2", "", ISS_OFF);
84 fSettings[2].maxTrip = 80;
85 fSettings[2].gearRatio = 0.25040;
86 IUFillSwitch(&ModelS[3], "RT3", "", ISS_OFF);
87 fSettings[3].maxTrip = 115;
88 fSettings[3].gearRatio = 0.25040;
89 IUFillSwitch(&ModelS[4], "Custom", "", ISS_ON);
90 fSettings[4].maxTrip = 30;
91 fSettings[4].gearRatio = 0.25040;
92 IUFillSwitchVector(&ModelSP, ModelS, 5, getDeviceName(), "Model", "", FOCUS_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 0,
93 IPS_IDLE);
94
95 // Custom Settings
96 IUFillNumber(&CustomSettingN[FOCUS_MAX_TRIP], "Max Trip (mm)", "", "%6.2f", 20, 150, 0, 30);
97 IUFillNumber(&CustomSettingN[FOCUS_GEAR_RATIO], "Gear Ratio", "", "%.5f", 0.1, 1, 0, .25040);
98 IUFillNumberVector(&CustomSettingNP, CustomSettingN, 2, getDeviceName(), "Custom Settings", "", FOCUS_SETTINGS_TAB,
99 IP_RW, 0, IPS_IDLE);
100
101 // Focuser Acceleration
102 IUFillNumber(&AccelerationN[0], "Ramp", "", "%3.0f", 1500., 3000., 100., 2000.);
103 IUFillNumberVector(&AccelerationNP, AccelerationN, 1, getDeviceName(), "FOCUS_ACCELERATION", "Acceleration",
105
106 // Focus Sync
107 IUFillNumber(&SyncN[0], "Position (steps)", "", "%3.0f", 0., 200000., 100., 0.);
108 IUFillNumberVector(&SyncNP, SyncN, 1, getDeviceName(), "FOCUS_SYNC", "Sync", MAIN_CONTROL_TAB, IP_RW, 0, IPS_IDLE);
109
110 // Version
111 IUFillText(&VersionT[0], "HW Rev.", "", nullptr);
112 IUFillText(&VersionT[1], "FW Rev.", "", nullptr);
113 IUFillTextVector(&VersionTP, VersionT, 2, getDeviceName(), "FOCUS_VERSION", "Version", MAIN_CONTROL_TAB, IP_RO, 0,
114 IPS_IDLE);
115
116 FocusRelPosNP[0].setValue(0);
117 FocusAbsPosNP[0].setValue(0);
118 simPosition = FocusAbsPosNP[0].getValue();
119
120 updateFocusMaxRange(fSettings[4].maxTrip, fSettings[4].gearRatio);
121
123
124 return true;
125}
126
128{
130
131 if (isConnected())
132 {
133 defineProperty(&TemperatureNP);
134 defineProperty(&TemperatureSettingNP);
135 defineProperty(&TemperatureCompensateSP);
136
137 defineProperty(&ModelSP);
138 defineProperty(&CustomSettingNP);
139 defineProperty(&AccelerationNP);
140 defineProperty(&SyncNP);
141
142 defineProperty(&VersionTP);
143
144 GetFocusParams();
145
146 //loadConfig(true);
147
148 LOG_INFO("SteelDrive parameters updated, focuser ready for use.");
149 }
150 else
151 {
152 deleteProperty(TemperatureNP.name);
153 deleteProperty(TemperatureSettingNP.name);
154 deleteProperty(TemperatureCompensateSP.name);
155
156 deleteProperty(ModelSP.name);
157 deleteProperty(CustomSettingNP.name);
158 deleteProperty(AccelerationNP.name);
159 deleteProperty(SyncNP.name);
160
161 deleteProperty(VersionTP.name);
162 }
163
164 return true;
165}
166
168{
169 if (isSimulation())
170 return true;
171
172 if (Ack())
173 {
174 LOG_INFO("SteelDrive is online. Getting focus parameters...");
175 temperatureUpdateCounter = 0;
176 return true;
177 }
178
179 LOG_INFO("Error retrieving data from SteelDrive, please ensure SteelDrive controller is "
180 "powered and the port is correct.");
181 return false;
182}
183
185{
186 return "Baader SteelDrive";
187}
188
189/************************************************************************************
190 *
191* ***********************************************************************************/
192bool SteelDrive::Ack()
193{
194 int nbytes_written = 0, nbytes_read = 0, rc = -1;
195 char errstr[MAXRBUF] = {0};
196 char resp[STEELDRIVE_MAXBUF] = {0};
197 char hwVer[STEELDRIVE_MAXBUF];
198
199 tcflush(PortFD, TCIOFLUSH);
200
201 if (!sim && (rc = tty_write(PortFD, ":FVERSIO#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
202 {
203 tty_error_msg(rc, errstr, MAXRBUF);
204 LOGF_ERROR(":FVERSIO# getHWVersion error: %s.", errstr);
205 return false;
206 }
207
208 LOG_DEBUG("CMD (:FVERSIO#)");
209
210 if (sim)
211 {
212 strncpy(resp, ":FV2.00812#", STEELDRIVE_CMD_LONG + 1);
213 nbytes_read = STEELDRIVE_CMD_LONG;
214 }
215 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
216 {
217 tty_error_msg(rc, errstr, MAXRBUF);
218 LOGF_ERROR("getHWVersion error: %s.", errstr);
219 return false;
220 }
221
222 resp[nbytes_read] = '\0';
223
224 LOGF_DEBUG("RES (%s)", resp);
225
226 rc = sscanf(resp, ":FV%s#", hwVer);
227
228 return rc > 0;
229}
230
231/************************************************************************************
232 *
233* ***********************************************************************************/
234bool SteelDrive::updateVersion()
235{
236 int nbytes_written = 0, nbytes_read = 0, rc = -1;
237 char errstr[MAXRBUF] = {0};
238 char resp[STEELDRIVE_MAXBUF] = {0};
239 char hardware_string[MAXRBUF];
240 char firmware_string[MAXRBUF];
241
242 char hwdate[STEELDRIVE_MAXBUF / 2];
243 char hwrev[STEELDRIVE_MAXBUF / 2];
244 char fwdate[STEELDRIVE_MAXBUF / 2];
245 char fwrev[STEELDRIVE_MAXBUF / 2];
246
247 memset(hwdate, 0, sizeof(hwdate));
248 memset(hwrev, 0, sizeof(hwrev));
249 memset(fwdate, 0, sizeof(fwdate));
250 memset(fwrev, 0, sizeof(fwrev));
251
252 tcflush(PortFD, TCIOFLUSH);
253
254 if (!sim && (rc = tty_write(PortFD, ":FVERSIO#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
255 {
256 tty_error_msg(rc, errstr, MAXRBUF);
257 LOGF_ERROR(":FVERSIO# getHWVersion write error: %s.", errstr);
258 return false;
259 }
260
261 LOG_DEBUG("CMD (:FVERSIO#)");
262
263 if (sim)
264 {
265 strncpy(resp, ":FV2.00812#", STEELDRIVE_CMD_LONG + 1);
266 nbytes_read = STEELDRIVE_CMD_LONG;
267 }
268 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
269 {
270 tty_error_msg(rc, errstr, MAXRBUF);
271 LOGF_ERROR("FVERSIO# getHWVersion read error: %s.", errstr);
272 return false;
273 }
274
275 resp[nbytes_read] = '\0';
276
277 LOGF_DEBUG("RES (%s)", resp);
278
279 rc = sscanf(resp, ":FV%s#", hardware_string);
280
281 if (rc > 0)
282 {
283 snprintf(hwrev, sizeof(hwrev), "%.3s", hardware_string);
284 snprintf(hwdate, sizeof(hwdate), "%.4s", hardware_string + 3);
285 char mon[3], year[3];
286 memset(mon, 0, sizeof(mon));
287 memset(year, 0, sizeof(year));
288 snprintf(mon, sizeof(mon), "%.2s", hwdate);
289 snprintf(year, sizeof(year), "%.2s", hwdate + 2);
290 snprintf(hardware_string, MAXRBUF, "Version: %s Date: %s.%s", hwrev, mon, year);
291 IUSaveText(&VersionT[0], hardware_string);
292 }
293 else
294 {
295 LOGF_ERROR("Unknown error: getHWVersion value (%s)", resp);
296 return false;
297 }
298
299 tcflush(PortFD, TCIOFLUSH);
300
301 if (!sim && (rc = tty_write(PortFD, ":FNFIRMW#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
302 {
303 tty_error_msg(rc, errstr, MAXRBUF);
304 LOGF_ERROR(":FNFIRMW# getSWVersion write error: %s.", errstr);
305 return false;
306 }
307
308 LOG_DEBUG("CMD (:FNFIRMW#)");
309
310 if (sim)
311 {
312 snprintf(resp, STEELDRIVE_CMD_LONG + 1, "%.13s", ":FN2.21012#");
313 nbytes_read = STEELDRIVE_CMD_LONG;
314 }
315 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
316 {
317 tty_error_msg(rc, errstr, MAXRBUF);
318 LOGF_ERROR("FNFIRMW# getSWVersion read error: %s.", errstr);
319 return false;
320 }
321
322 resp[nbytes_read] = '\0';
323
324 LOGF_DEBUG("RES (%s)", resp);
325
326 rc = sscanf(resp, ":FN%s#", firmware_string);
327
328 if (rc > 0)
329 {
330 snprintf(fwrev, sizeof(fwrev), "%.3s", firmware_string);
331 snprintf(fwdate, sizeof(fwdate), "%.4s", firmware_string + 3);
332 char mon[3], year[3];
333 memset(mon, 0, sizeof(mon));
334 memset(year, 0, sizeof(year));
335 snprintf(mon, sizeof(mon), "%.2s", fwdate);
336 snprintf(year, sizeof(year), "%.2s", fwdate + 2);
337 snprintf(firmware_string, MAXRBUF, "Version: %s Date: %s.%s", fwrev, mon, year);
338 IUSaveText(&VersionT[1], firmware_string);
339 }
340 else
341 {
342 LOGF_ERROR("Unknown error: getSWVersion value (%s)", resp);
343 return false;
344 }
345
346 return true;
347}
348
349/************************************************************************************
350 *
351* ***********************************************************************************/
352bool SteelDrive::updateTemperature()
353{
354 int nbytes_written = 0, nbytes_read = 0, rc = -1;
355 char errstr[MAXRBUF] = {0};
356 char resp[STEELDRIVE_MAXBUF] = {0};
357 int temperature;
358
359 tcflush(PortFD, TCIOFLUSH);
360
361 if (!sim && (rc = tty_write(PortFD, ":F5ASKT0#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
362 {
363 tty_error_msg(rc, errstr, MAXRBUF);
364 LOGF_ERROR(":F5ASKT0# updateTemperature write error: %s.", errstr);
365 return false;
366 }
367
368 LOG_DEBUG("CMD (:F5ASKT0#)");
369
370 if (sim)
371 {
372 strncpy(resp, ":F5+1810#", STEELDRIVE_CMD + 1);
373 nbytes_read = STEELDRIVE_CMD;
374 }
375 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
376 {
377 tty_error_msg(rc, errstr, MAXRBUF);
378 LOGF_ERROR(":F5ASKT0# updateTemperature read error: %s.", errstr);
379 return false;
380 }
381
382 resp[nbytes_read] = '\0';
383
384 LOGF_DEBUG("RES (%s)", resp);
385
386 rc = sscanf(resp, ":F5%d#", &temperature);
387
388 if (rc > 0)
389 {
390 TemperatureN[0].value = ((double)temperature) / 100.0;
391 TemperatureNP.s = IPS_OK;
392 }
393 else
394 {
395 char junk[STEELDRIVE_MAXBUF];
396 rc = sscanf(resp, ":F5%s#", junk);
397 if (rc > 0)
398 {
399 TemperatureN[0].value = 0;
400 LOG_DEBUG("Temperature probe is not connected.");
401 }
402 else
403 LOGF_ERROR("Unknown error: focuser temperature value (%s)", resp);
404
405 TemperatureNP.s = IPS_ALERT;
406 return false;
407 }
408
409 return true;
410}
411
412/************************************************************************************
413 *
414* ***********************************************************************************/
415bool SteelDrive::updatePosition()
416{
417 int nbytes_written = 0, nbytes_read = 0, rc = -1;
418 char errstr[MAXRBUF] = {0};
419 char resp[STEELDRIVE_MAXBUF] = {0};
420 unsigned short pos = 0;
421 int retries = 0;
422
423 for (retries = 0; retries < STEELDRIVE_MAX_RETRIES; retries++)
424 {
425 tcflush(PortFD, TCIOFLUSH);
426
427 if (!sim && (rc = tty_write(PortFD, ":F8ASKS0#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
428 {
429 tty_error_msg(rc, errstr, MAXRBUF);
430 LOGF_ERROR(":F8ASKS0# updatePosition write error: %s.", errstr);
431 return false;
432 }
433
434 LOG_DEBUG("CMD (:F8ASKS0#)");
435
436 if (sim)
437 {
438 snprintf(resp, STEELDRIVE_CMD_LONG + 1, ":F8%07u#", (int)simPosition);
439 nbytes_read = STEELDRIVE_CMD_LONG;
440 break;
441 }
442 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
443 {
444 tty_error_msg(rc, errstr, MAXRBUF);
445 resp[nbytes_read] = '\0';
446 LOGF_DEBUG(":F8ASKS0# updatePosition read error: %s. Retry: %d. Bytes: %d. Buffer (%s)", errstr, retries,
447 nbytes_read, resp);
448 }
449 else
450 break;
451 }
452
453 if (retries == STEELDRIVE_MAX_RETRIES)
454 {
455 LOG_ERROR("UpdatePosition: failed to read.");
456 return false;
457 }
458
459 resp[nbytes_read] = '\0';
460
461 LOGF_DEBUG("RES (%s)", resp);
462
463 rc = sscanf(resp, ":F8%hu#", &pos);
464
465 if (rc > 0)
466 {
467 FocusAbsPosNP[0].setValue(pos);
468 }
469 else
470 {
471 LOGF_ERROR("Unknown error: focuser position value (%s)", resp);
472 return false;
473 }
474
475 return true;
476}
477
478/************************************************************************************
479 *
480* ***********************************************************************************/
481bool SteelDrive::updateSpeed()
482{
483 int nbytes_written = 0, nbytes_read = 0, rc = -1;
484 char errstr[MAXRBUF] = {0};
485 char resp[STEELDRIVE_MAXBUF] = {0};
486 unsigned short speed;
487
488 tcflush(PortFD, TCIOFLUSH);
489
490 if (!sim && (rc = tty_write(PortFD, ":FGSPMAX#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
491 {
492 tty_error_msg(rc, errstr, MAXRBUF);
493 LOGF_ERROR(":FGSPMAX# updateSpeed write error: %s.", errstr);
494 return false;
495 }
496
497 LOG_DEBUG("CMD (:FGSPMAX#)");
498
499 if (sim)
500 {
501 strncpy(resp, ":FG00350#", STEELDRIVE_CMD + 1);
502 nbytes_read = STEELDRIVE_CMD;
503 }
504 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
505 {
506 tty_error_msg(rc, errstr, MAXRBUF);
507 LOGF_ERROR(":FGSPMAX# updateSpeed read error: %s.", errstr);
508 return false;
509 }
510
511 resp[nbytes_read] = '\0';
512
513 LOGF_DEBUG("RES (%s)", resp);
514
515 rc = sscanf(resp, ":FG%hu#", &speed);
516
517 if (rc > 0)
518 {
519 FocusSpeedNP[0].setValue(speed);
520 }
521 else
522 {
523 LOGF_ERROR("Unknown error: focuser speed value (%s)", resp);
524 return false;
525 }
526
527 return true;
528}
529
530/************************************************************************************
531 *
532* ***********************************************************************************/
533bool SteelDrive::updateAcceleration()
534{
535 int nbytes_written = 0, nbytes_read = 0, rc = -1;
536 char errstr[MAXRBUF] = {0};
537 char resp[STEELDRIVE_MAXBUF] = {0};
538 unsigned short accel;
539
540 tcflush(PortFD, TCIOFLUSH);
541
542 if (!sim && (rc = tty_write(PortFD, ":FHSPMIN#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
543 {
544 tty_error_msg(rc, errstr, MAXRBUF);
545 LOGF_ERROR(":FHSPMIN# updateAcceleration write error: %s.", errstr);
546 return false;
547 }
548
549 LOG_DEBUG("CMD (:FHSPMIN#)");
550
551 if (sim)
552 {
553 strncpy(resp, ":FH01800#", STEELDRIVE_CMD + 1);
554 nbytes_read = STEELDRIVE_CMD;
555 }
556 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
557 {
558 tty_error_msg(rc, errstr, MAXRBUF);
559 LOGF_ERROR(":FHSPMIN# updateAcceleration read error: %s.", errstr);
560 return false;
561 }
562
563 resp[nbytes_read] = '\0';
564
565 LOGF_DEBUG("RES (%s)", resp);
566
567 rc = sscanf(resp, ":FH%hu#", &accel);
568
569 if (rc > 0)
570 {
571 AccelerationN[0].value = accel;
572 }
573 else
574 {
575 LOGF_ERROR("Unknown error: updateAcceleration value (%s)", resp);
576 return false;
577 }
578 return true;
579}
580
581/************************************************************************************
582 *
583* ***********************************************************************************/
584bool SteelDrive::updateTemperatureSettings()
585{
586 int nbytes_written = 0, nbytes_read = 0, rc = -1;
587 char errstr[MAXRBUF] = {0};
588 char resp[STEELDRIVE_MAXBUF] = {0};
589
590 char selectedFocuser[1], coeff[3], enabled[1], tResp[STEELDRIVE_MAXBUF];
591
592 tcflush(PortFD, TCIOFLUSH);
593
594 if (!sim && (rc = tty_write(PortFD, ":F7ASKC0#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
595 {
596 tty_error_msg(rc, errstr, MAXRBUF);
597 LOGF_ERROR(":F7ASKC0# updateTemperatureSettings write error: %s.", errstr);
598 return false;
599 }
600
601 LOG_DEBUG("CMD (:F7ASKC0#)");
602
603 if (sim)
604 {
605 strncpy(resp, ":F710004#", STEELDRIVE_CMD + 1);
606 nbytes_read = STEELDRIVE_CMD;
607 }
608 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
609 {
610 tty_error_msg(rc, errstr, MAXRBUF);
611 LOGF_ERROR(":F7ASKC0# updateTemperatureSettings read error: %s.", errstr);
612 return false;
613 }
614
615 resp[nbytes_read] = '\0';
616
617 LOGF_DEBUG("RES (%s)", resp);
618
619 rc = sscanf(resp, ":F7%s#", tResp);
620
621 if (rc > 0)
622 {
623 snprintf(coeff, sizeof(coeff), "%.3s", tResp);
624 snprintf(enabled, sizeof(enabled), "%.1s", tResp + 3);
625 snprintf(selectedFocuser, sizeof(selectedFocuser), "%.1s", tResp + 4);
626
627 TemperatureSettingN[FOCUS_T_COEFF].value = atof(coeff) / 1000.0;
628
629 IUResetSwitch(&TemperatureCompensateSP);
630 if (enabled[0] == '0')
631 TemperatureCompensateS[1].s = ISS_ON;
632 else
633 TemperatureCompensateS[0].s = ISS_ON;
634 }
635 else
636 {
637 LOGF_ERROR("Unknown error: updateTemperatureSettings value (%s)", resp);
638 return false;
639 }
640
641 return true;
642}
643
644/************************************************************************************
645 *
646* ***********************************************************************************/
647bool SteelDrive::updateCustomSettings()
648{
649 int nbytes_written = 0, nbytes_read = 0, rc = -1;
650 char errstr[MAXRBUF] = {0};
651 char resp[STEELDRIVE_MAXBUF] = {0};
652
653 char selectedFocuser[2], maxTrip[8], tResp[STEELDRIVE_MAXBUF];
654 int gearR;
655 double gearRatio;
656
657 tcflush(PortFD, TCIOFLUSH);
658
659 // Get Gear Ratio
660 if (!sim && (rc = tty_write(PortFD, ":FEASKGR#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
661 {
662 tty_error_msg(rc, errstr, MAXRBUF);
663 LOGF_ERROR(":FEASKGR# updateCustomSettings write error: %s.", errstr);
664 return false;
665 }
666
667 LOG_DEBUG("CMD (:FEASKGR#)");
668
669 if (sim)
670 {
671 strncpy(resp, ":FE25040#", STEELDRIVE_CMD + 1);
672 nbytes_read = STEELDRIVE_CMD;
673 }
674 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
675 {
676 tty_error_msg(rc, errstr, MAXRBUF);
677 LOGF_ERROR(":FEASKGR# updateCustomSettings read error: %s.", errstr);
678 return false;
679 }
680
681 resp[nbytes_read] = '\0';
682
683 LOGF_DEBUG("RES (%s)", resp);
684
685 rc = sscanf(resp, ":FE%d#", &gearR);
686
687 if (rc > 0)
688 {
689 gearRatio = ((double)gearR) / 100000.0;
690 }
691 else
692 {
693 LOGF_ERROR("Unknown error: updateCustomSettings value (%s)", resp);
694 return false;
695 }
696
697 tcflush(PortFD, TCIOFLUSH);
698
699 // Get Max Trip
700 if (!sim && (rc = tty_write(PortFD, ":F8ASKS1#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
701 {
702 tty_error_msg(rc, errstr, MAXRBUF);
703 LOGF_ERROR(":F8ASKS1# updateCustomSettings write error: %s.", errstr);
704 return false;
705 }
706
707 LOG_DEBUG("CMD (:F8ASKS1#)");
708
709 if (sim)
710 {
711 strncpy(resp, ":F40011577#", STEELDRIVE_CMD_LONG + 1);
712 nbytes_read = STEELDRIVE_CMD_LONG;
713 }
714 else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
715 {
716 tty_error_msg(rc, errstr, MAXRBUF);
717 LOGF_ERROR(":F8ASKS1# updateCustomSettings read error: %s.", errstr);
718 return false;
719 }
720
721 resp[nbytes_read] = '\0';
722
723 LOGF_DEBUG("RES (%s)", resp);
724
725 rc = sscanf(resp, ":F%s#", tResp);
726
727 if (rc > 0)
728 {
729 snprintf(selectedFocuser, sizeof(selectedFocuser), "%.1s", tResp);
730 snprintf(maxTrip, sizeof(maxTrip), "%.7s", tResp + 1);
731
732 int sFocuser = atoi(selectedFocuser);
733
734 IUResetSwitch(&ModelSP);
735 ModelS[sFocuser].s = ISS_ON;
736
737 fSettings[sFocuser].maxTrip = (atof(maxTrip) * gearRatio) / 100.0;
738 fSettings[sFocuser].gearRatio = gearRatio;
739
740 CustomSettingN[FOCUS_MAX_TRIP].value = fSettings[sFocuser].maxTrip;
741 CustomSettingN[FOCUS_GEAR_RATIO].value = fSettings[sFocuser].gearRatio;
742
743 LOGF_DEBUG("Updated max trip: %g gear ratio: %g", fSettings[sFocuser].maxTrip,
744 fSettings[sFocuser].gearRatio);
745 }
746 else
747 {
748 LOGF_ERROR("Unknown error: updateCustomSettings value (%s)", resp);
749 return false;
750 }
751
752 return true;
753}
754
755/************************************************************************************
756 *
757* ***********************************************************************************/
758bool SteelDrive::setTemperatureSamples(unsigned int targetSamples, unsigned int *finalSample)
759{
760 int nbytes_written = 0, rc = -1;
761 char errstr[MAXRBUF] = {0};
762 char cmd[STEELDRIVE_MAXBUF] = {0};
763
764 int maxSample = TemperatureSettingN[FOCUS_T_SAMPLES].max;
765 int sample = 0;
766
767 for (int i = maxSample; i > 0;)
768 {
769 if (targetSamples & maxSample)
770 {
771 sample = maxSample;
772 break;
773 }
774
775 maxSample >>= 1;
776 }
777
778 int value = 0;
779 if (sample == 16)
780 value = 5000;
781 else if (sample == 32)
782 value = 15000;
783 else if (sample == 64)
784 value = 25000;
785 else
786 value = 35000;
787
788 snprintf(cmd, STEELDRIVE_CMD + 1, ":FI%05d#", value);
789
790 tcflush(PortFD, TCIOFLUSH);
791
792 LOGF_DEBUG("CMD (%s)", cmd);
793
794 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
795 {
796 tty_error_msg(rc, errstr, MAXRBUF);
797 LOGF_ERROR("%s setTemperatureSamples write error: %s.", cmd, errstr);
798 return false;
799 }
800
801 TemperatureSettingN[FOCUS_T_SAMPLES].value = sample;
802 *finalSample = sample;
803
804 return true;
805}
806
807/************************************************************************************
808 *
809* ***********************************************************************************/
810bool SteelDrive::setTemperatureCompensation()
811{
812 int nbytes_written = 0, rc = -1;
813 char errstr[MAXRBUF] = {0};
814 char cmd[STEELDRIVE_MAXBUF] = {0};
815
816 double coeff = TemperatureSettingN[FOCUS_T_COEFF].value;
817 bool enable = TemperatureCompensateS[0].s == ISS_ON;
818 int selectedFocus = IUFindOnSwitchIndex(&ModelSP);
819
820 snprintf(cmd, STEELDRIVE_CMD + 1, ":F%02d%03d%d#", selectedFocus, (int)(coeff * 1000), enable ? 2 : 0);
821
822 tcflush(PortFD, TCIOFLUSH);
823
824 LOGF_DEBUG("CMD (%s)", cmd);
825
826 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
827 {
828 tty_error_msg(rc, errstr, MAXRBUF);
829 LOGF_ERROR("setTemperatureCoefficient error: %s.", errstr);
830 return false;
831 }
832
833 return true;
834}
835
836/************************************************************************************
837 *
838* ***********************************************************************************/
839bool SteelDrive::setCustomSettings(double maxTrip, double gearRatio)
840{
841 int nbytes_written = 0, rc = -1;
842 char errstr[MAXRBUF] = {0};
843 char cmd[STEELDRIVE_MAXBUF] = {0};
844
845 unsigned short mmTrip = (unsigned short int)(maxTrip / gearRatio * 100.0);
846
847 snprintf(cmd, STEELDRIVE_CMD_LONG + 1, ":FC%07d#", mmTrip);
848
849 tcflush(PortFD, TCIOFLUSH);
850
851 LOGF_DEBUG("CMD (%s)", cmd);
852
853 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD_LONG, &nbytes_written)) != TTY_OK)
854 {
855 tty_error_msg(rc, errstr, MAXRBUF);
856 LOGF_ERROR("setCustomSettings error: %s.", errstr);
857 return false;
858 }
859
860 snprintf(cmd, STEELDRIVE_CMD + 1, ":FD%05d#", (int)(gearRatio * 100000));
861
862 tcflush(PortFD, TCIOFLUSH);
863
864 LOGF_DEBUG("CMD (%s)", cmd);
865
866 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
867 {
868 tty_error_msg(rc, errstr, MAXRBUF);
869 LOGF_ERROR("setCustomSettings error: %s.", errstr);
870 return false;
871 }
872
873 return true;
874}
875
876/************************************************************************************
877 *
878* ***********************************************************************************/
879bool SteelDrive::Sync(unsigned int position)
880{
881 int nbytes_written = 0, rc = -1;
882 char errstr[MAXRBUF] = {0};
883 char cmd[STEELDRIVE_MAXBUF] = {0};
884
885 snprintf(cmd, STEELDRIVE_CMD_LONG + 1, ":FB%07d#", position);
886
887 tcflush(PortFD, TCIOFLUSH);
888
889 LOGF_DEBUG("CMD (%s)", cmd);
890
891 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD_LONG, &nbytes_written)) != TTY_OK)
892 {
893 tty_error_msg(rc, errstr, MAXRBUF);
894 LOGF_ERROR("Sync error: %s.", errstr);
895 return false;
896 }
897
898 simPosition = position;
899
900 return true;
901}
902
903/************************************************************************************
904 *
905* ***********************************************************************************/
906bool SteelDrive::moveFocuser(unsigned int position)
907{
908 int nbytes_written = 0, rc = -1;
909 char errstr[MAXRBUF] = {0};
910 char cmd[STEELDRIVE_MAXBUF] = {0};
911
912 if (position < FocusAbsPosNP[0].getMin() || position > FocusAbsPosNP[0].getMax())
913 {
914 LOGF_ERROR("Requested position value out of bound: %d", position);
915 return false;
916 }
917
919 AbortFocuser();
920
921 snprintf(cmd, STEELDRIVE_CMD_LONG + 1, ":F9%07d#", position);
922
923 tcflush(PortFD, TCIOFLUSH);
924
925 LOGF_DEBUG("CMD (%s)", cmd);
926
927 // Goto absolute step
928 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD_LONG, &nbytes_written)) != TTY_OK)
929 {
930 tty_error_msg(rc, errstr, MAXRBUF);
931 LOGF_ERROR("setPosition error: %s.", errstr);
932 return false;
933 }
934
935 targetPos = position;
936
937 return true;
938}
939
940/************************************************************************************
941 *
942* ***********************************************************************************/
943bool SteelDrive::startMotion(FocusDirection dir)
944{
945 int nbytes_written = 0, rc = -1;
946 char errstr[MAXRBUF] = {0};
947 char cmd[STEELDRIVE_MAXBUF] = {0};
948
949 // inward --> decreasing value --> DOWN
950 // outward --> increasing value --> UP
951 strncpy(cmd, (dir == FOCUS_INWARD) ? ":F2MDOW0#" : ":F1MUP00#", STEELDRIVE_CMD + 1);
952
953 tcflush(PortFD, TCIOFLUSH);
954
955 LOGF_DEBUG("CMD (%s)", cmd);
956
957 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
958 {
959 tty_error_msg(rc, errstr, MAXRBUF);
960 LOGF_ERROR("StartMotion error: %s.", errstr);
961 return false;
962 }
963
964 return true;
965}
966
967/************************************************************************************
968 *
969* ***********************************************************************************/
970bool SteelDrive::setSpeed(unsigned short speed)
971{
972 int nbytes_written = 0, rc = -1;
973 char errstr[MAXRBUF] = {0};
974 char cmd[STEELDRIVE_MAXBUF] = {0};
975
976 snprintf(cmd, STEELDRIVE_CMD + 1, ":Fg%05d#", speed);
977
978 tcflush(PortFD, TCIOFLUSH);
979
980 LOGF_DEBUG("CMD (%s)", cmd);
981
982 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
983 {
984 tty_error_msg(rc, errstr, MAXRBUF);
985 LOGF_ERROR("setSpeed error: %s.", errstr);
986 return false;
987 }
988
989 currentSpeed = speed;
990
991 return true;
992}
993
994/************************************************************************************
995 *
996* ***********************************************************************************/
997bool SteelDrive::setAcceleration(unsigned short accel)
998{
999 int nbytes_written = 0, rc = -1;
1000 char errstr[MAXRBUF] = {0};
1001 char cmd[STEELDRIVE_MAXBUF] = {0};
1002
1003 snprintf(cmd, STEELDRIVE_CMD + 1, ":Fh%05d#", accel);
1004
1005 tcflush(PortFD, TCIOFLUSH);
1006
1007 LOGF_DEBUG("CMD (%s)", cmd);
1008
1009 if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
1010 {
1011 tty_error_msg(rc, errstr, MAXRBUF);
1012 LOGF_ERROR("setAcceleration error: %s.", errstr);
1013 return false;
1014 }
1015
1016 return true;
1017}
1018
1019/************************************************************************************
1020 *
1021* ***********************************************************************************/
1022bool SteelDrive::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
1023{
1024 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1025 {
1026 if (strcmp(TemperatureCompensateSP.name, name) == 0)
1027 {
1028 int last_index = IUFindOnSwitchIndex(&TemperatureCompensateSP);
1029 IUUpdateSwitch(&TemperatureCompensateSP, states, names, n);
1030
1031 bool rc = setTemperatureCompensation();
1032
1033 if (!rc)
1034 {
1035 TemperatureCompensateSP.s = IPS_ALERT;
1036 IUResetSwitch(&TemperatureCompensateSP);
1037 TemperatureCompensateS[last_index].s = ISS_ON;
1038 IDSetSwitch(&TemperatureCompensateSP, nullptr);
1039 return false;
1040 }
1041
1042 TemperatureCompensateSP.s = IPS_OK;
1043 IDSetSwitch(&TemperatureCompensateSP, nullptr);
1044 return true;
1045 }
1046
1047 if (strcmp(ModelSP.name, name) == 0)
1048 {
1049 IUUpdateSwitch(&ModelSP, states, names, n);
1050
1051 int i = IUFindOnSwitchIndex(&ModelSP);
1052
1053 double focusMaxPos = floor(fSettings[i].maxTrip / fSettings[i].gearRatio) * 100;
1054 FocusAbsPosNP[0].setMax(focusMaxPos);
1056
1057 CustomSettingN[FOCUS_MAX_TRIP].value = fSettings[i].maxTrip;
1058 CustomSettingN[FOCUS_GEAR_RATIO].value = fSettings[i].gearRatio;
1059
1060 IDSetNumber(&CustomSettingNP, nullptr);
1061
1062 ModelSP.s = IPS_OK;
1063 IDSetSwitch(&ModelSP, nullptr);
1064
1065 return true;
1066 }
1067 }
1068
1069 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
1070}
1071
1072/************************************************************************************
1073 *
1074* ***********************************************************************************/
1075bool SteelDrive::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
1076{
1077 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1078 {
1079 // Set Accelration
1080 if (strcmp(name, AccelerationNP.name) == 0)
1081 {
1082 if (setAcceleration((int)values[0]))
1083 {
1084 IUUpdateNumber(&AccelerationNP, values, names, n);
1085 AccelerationNP.s = IPS_OK;
1086 IDSetNumber(&AccelerationNP, nullptr);
1087 return true;
1088 }
1089 else
1090 {
1091 AccelerationNP.s = IPS_ALERT;
1092 IDSetNumber(&AccelerationNP, nullptr);
1093 return false;
1094 }
1095 }
1096
1097 // Set Temperature Settings
1098 if (strcmp(name, TemperatureSettingNP.name) == 0)
1099 {
1100 // Coeff is only needed when we enable or disable the temperature compensation. Here we only set the # of samples
1101 unsigned int targetSamples;
1102
1103 if (strcmp(names[0], TemperatureSettingN[FOCUS_T_SAMPLES].name) == 0)
1104 targetSamples = (int)values[0];
1105 else
1106 targetSamples = (int)values[1];
1107
1108 unsigned int finalSample = targetSamples;
1109
1110 if (setTemperatureSamples(targetSamples, &finalSample))
1111 {
1112 IUUpdateNumber(&TemperatureSettingNP, values, names, n);
1113 TemperatureSettingN[FOCUS_T_SAMPLES].value = finalSample;
1114
1115 if (TemperatureSettingN[FOCUS_T_COEFF].value > TemperatureSettingN[FOCUS_T_COEFF].max)
1116 TemperatureSettingN[FOCUS_T_COEFF].value = TemperatureSettingN[FOCUS_T_COEFF].max;
1117
1118 TemperatureSettingNP.s = IPS_OK;
1119 IDSetNumber(&TemperatureSettingNP, nullptr);
1120 return true;
1121 }
1122 else
1123 {
1124 TemperatureSettingNP.s = IPS_ALERT;
1125 IDSetNumber(&TemperatureSettingNP, nullptr);
1126 return true;
1127 }
1128 }
1129
1130 // Set Custom Settings
1131 if (strcmp(name, CustomSettingNP.name) == 0)
1132 {
1133 int i = IUFindOnSwitchIndex(&ModelSP);
1134
1135 // If the model is NOT set to custom, then the values cannot be updated
1136 if (i != 4)
1137 {
1138 CustomSettingNP.s = IPS_IDLE;
1139 LOG_WARN("You can not set custom values for a non-custom focuser.");
1140 IDSetNumber(&CustomSettingNP, nullptr);
1141 return false;
1142 }
1143
1144 double maxTrip, gearRatio;
1145
1146 if (strcmp(names[0], CustomSettingN[FOCUS_MAX_TRIP].name) == 0)
1147 {
1148 maxTrip = values[0];
1149 gearRatio = values[1];
1150 }
1151 else
1152 {
1153 maxTrip = values[1];
1154 gearRatio = values[0];
1155 }
1156
1157 if (setCustomSettings(maxTrip, gearRatio))
1158 {
1159 IUUpdateNumber(&CustomSettingNP, values, names, n);
1160 CustomSettingNP.s = IPS_OK;
1161 IDSetNumber(&CustomSettingNP, nullptr);
1162
1163 updateFocusMaxRange(maxTrip, gearRatio);
1166 }
1167 else
1168 {
1169 CustomSettingNP.s = IPS_ALERT;
1170 IDSetNumber(&CustomSettingNP, nullptr);
1171 }
1172 }
1173
1174 // Set Sync Position
1175 if (strcmp(name, SyncNP.name) == 0)
1176 {
1177 if (Sync((unsigned int)values[0]))
1178 {
1179 IUUpdateNumber(&SyncNP, values, names, n);
1180 SyncNP.s = IPS_OK;
1181 IDSetNumber(&SyncNP, nullptr);
1182
1183 if (updatePosition())
1185
1186 return true;
1187 }
1188 else
1189 {
1190 SyncNP.s = IPS_ALERT;
1191 IDSetNumber(&SyncNP, nullptr);
1192
1193 return false;
1194 }
1195 }
1196 }
1197
1198 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
1199}
1200
1201/************************************************************************************
1202 *
1203* ***********************************************************************************/
1204void SteelDrive::GetFocusParams()
1205{
1206 if (updateVersion())
1207 IDSetText(&VersionTP, nullptr);
1208
1209 if (updateTemperature())
1210 IDSetNumber(&TemperatureNP, nullptr);
1211
1212 if (updateTemperatureSettings())
1213 IDSetNumber(&TemperatureSettingNP, nullptr);
1214
1215 if (updatePosition())
1217
1218 if (updateSpeed())
1220
1221 if (updateAcceleration())
1222 IDSetNumber(&AccelerationNP, nullptr);
1223
1224 if (updateCustomSettings())
1225 {
1226 IDSetNumber(&CustomSettingNP, nullptr);
1227 IDSetSwitch(&ModelSP, nullptr);
1228 }
1229}
1230
1232{
1233 bool rc = false;
1234
1235 rc = setSpeed(speed);
1236
1237 if (!rc)
1238 return false;
1239
1240 currentSpeed = speed;
1241
1244
1245 return true;
1246}
1247
1248IPState SteelDrive::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
1249{
1250 if (speed != (int)currentSpeed)
1251 {
1252 bool rc = setSpeed(speed);
1253
1254 if (!rc)
1255 return IPS_ALERT;
1256 }
1257
1258 gettimeofday(&focusMoveStart, nullptr);
1259 focusMoveRequest = duration / 1000.0;
1260
1261 startMotion(dir);
1262
1263 if (duration <= getCurrentPollingPeriod())
1264 {
1265 usleep(getCurrentPollingPeriod() * 1000);
1266 AbortFocuser();
1267 return IPS_OK;
1268 }
1269
1270 return IPS_BUSY;
1271}
1272
1274{
1275 targetPos = targetTicks;
1276
1277 bool rc = false;
1278
1279 rc = moveFocuser(targetPos);
1280
1281 if (!rc)
1282 return IPS_ALERT;
1283
1285
1286 return IPS_BUSY;
1287}
1288
1290{
1291 double newPosition = 0;
1292 bool rc = false;
1293
1294 if (dir == FOCUS_INWARD)
1295 newPosition = FocusAbsPosNP[0].getValue() - ticks;
1296 else
1297 newPosition = FocusAbsPosNP[0].getValue() + ticks;
1298
1299 rc = moveFocuser(newPosition);
1300
1301 if (!rc)
1302 return IPS_ALERT;
1303
1304 FocusRelPosNP[0].setValue(ticks);
1307
1308 return IPS_BUSY;
1309}
1310
1312{
1313 if (!isConnected())
1314 return;
1315
1316 bool rc = updatePosition();
1317 if (rc)
1318 {
1319 if (fabs(lastPos - FocusAbsPosNP[0].getValue()) > STEELDIVE_POSITION_THRESHOLD)
1320 {
1322 lastPos = FocusAbsPosNP[0].getValue();
1323 }
1324 }
1326 {
1327 // During motion, hardware may be busy and timeout errors are expected
1328 // Only log as debug/warning, don't treat as critical error
1329 LOG_DEBUG("Position read timeout during motion (hardware busy) - will retry next cycle.");
1330 }
1331
1332 if (temperatureUpdateCounter++ > STEELDRIVE_TEMPERATURE_FREQ)
1333 {
1334 temperatureUpdateCounter = 0;
1335 rc = updateTemperature();
1336 if (rc)
1337 {
1338 if (fabs(lastTemperature - TemperatureN[0].value) >= 0.5)
1339 lastTemperature = TemperatureN[0].value;
1340 }
1341
1342 IDSetNumber(&TemperatureNP, nullptr);
1343 }
1344
1346 {
1347 float remaining = CalcTimeLeft(focusMoveStart, focusMoveRequest);
1348
1349 if (sim)
1350 {
1351 if (FocusMotionSP[FOCUS_INWARD].getState() == ISS_ON)
1352 {
1353 FocusAbsPosNP[0].setValue(FocusAbsPosNP[0].getValue() - FocusSpeedNP[0].getValue());
1354
1355 if (FocusAbsPosNP[0].getValue() <= 0)
1356 FocusAbsPosNP[0].setValue(0);
1357 simPosition = FocusAbsPosNP[0].getValue();
1358 }
1359 else
1360 {
1361 FocusAbsPosNP[0].setValue(FocusAbsPosNP[0].getValue() + FocusSpeedNP[0].getValue());
1362 if (FocusAbsPosNP[0].getValue() >= FocusAbsPosNP[0].getMax())
1363 FocusAbsPosNP[0].setValue(FocusAbsPosNP[0].getMax());
1364 simPosition = FocusAbsPosNP[0].getValue();
1365 }
1366 }
1367
1368 // If we exceed focus abs values, stop timer and motion
1369 if (FocusAbsPosNP[0].getValue() <= 0 || FocusAbsPosNP[0].getValue() >= FocusAbsPosNP[0].getMax())
1370 {
1371 AbortFocuser();
1372
1373 if (FocusAbsPosNP[0].getValue() <= 0)
1374 FocusAbsPosNP[0].setValue(0);
1375 else
1376 FocusAbsPosNP[0].setValue(FocusAbsPosNP[0].getMax());
1377
1378 FocusTimerNP[0].setValue(0);
1380 }
1381 else if (remaining <= 0)
1382 {
1384 FocusTimerNP[0].setValue(0);
1385 AbortFocuser();
1386 }
1387 else
1388 FocusTimerNP[0].setValue(remaining * 1000.0);
1389
1391 }
1392
1394 {
1395 if (sim)
1396 {
1397 if (FocusAbsPosNP[0].getValue() < targetPos)
1398 simPosition += 100;
1399 else
1400 simPosition -= 100;
1401
1402 if (fabs(simPosition - targetPos) < 100)
1403 {
1404 FocusAbsPosNP[0].setValue(targetPos);
1405 simPosition = FocusAbsPosNP[0].getValue();
1406 }
1407 }
1408
1409 // Set it OK to within 5 steps
1410 if (fabs(targetPos - FocusAbsPosNP[0].getValue()) < 5)
1411 {
1416 lastPos = FocusAbsPosNP[0].getValue();
1417 LOG_INFO("Focuser reached requested position.");
1418 }
1419 }
1420
1422}
1423
1424/************************************************************************************
1425 *
1426* ***********************************************************************************/
1428{
1429 int nbytes_written = 0, rc = -1;
1430 char errstr[MAXRBUF] = {0};
1431
1432 tcflush(PortFD, TCIOFLUSH);
1433
1434 LOG_DEBUG("CMD :F3STOP0#");
1435
1436 if (!sim && (rc = tty_write(PortFD, ":F3STOP0#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
1437 {
1438 tty_error_msg(rc, errstr, MAXRBUF);
1439 LOGF_ERROR(":F3STOP0# Stop error: %s.", errstr);
1440 return false;
1441 }
1442
1444 {
1447 }
1448
1453
1454 return true;
1455}
1456
1457/************************************************************************************
1458 *
1459* ***********************************************************************************/
1460float SteelDrive::CalcTimeLeft(timeval start, float req)
1461{
1462 double timesince;
1463 double timeleft;
1464 struct timeval now
1465 {
1466 0, 0
1467 };
1468 gettimeofday(&now, nullptr);
1469
1470 timesince =
1471 (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
1472 timesince = timesince / 1000;
1473 timeleft = req - timesince;
1474 return timeleft;
1475}
1476
1477/************************************************************************************
1478 *
1479* ***********************************************************************************/
1481{
1483
1484 IUSaveConfigNumber(fp, &TemperatureSettingNP);
1485 IUSaveConfigSwitch(fp, &TemperatureCompensateSP);
1486 FocusSpeedNP.save(fp);
1487 IUSaveConfigNumber(fp, &AccelerationNP);
1488 IUSaveConfigNumber(fp, &CustomSettingNP);
1489 IUSaveConfigSwitch(fp, &ModelSP);
1490
1491 return saveFocuserConfig();
1492}
1493
1494/************************************************************************************
1495 *
1496* ***********************************************************************************/
1497bool SteelDrive::saveFocuserConfig()
1498{
1499 int nbytes_written = 0, rc = -1;
1500 char errstr[MAXRBUF] = {0};
1501
1502 tcflush(PortFD, TCIOFLUSH);
1503
1504 LOG_DEBUG("CMD (:FFPOWER#)");
1505
1506 if (!sim && (rc = tty_write(PortFD, ":FFPOWER#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
1507 {
1508 tty_error_msg(rc, errstr, MAXRBUF);
1509 LOGF_ERROR(":FFPOWER# saveFocuserConfig error: %s.", errstr);
1510 return false;
1511 }
1512
1513 return true;
1514}
1515
1516/************************************************************************************
1517 *
1518* ***********************************************************************************/
1520{
1521 tty_set_debug(enable ? 1 : 0);
1522}
1523
1524/************************************************************************************
1525 *
1526* ***********************************************************************************/
1527void SteelDrive::updateFocusMaxRange(double maxTrip, double gearRatio)
1528{
1529 double maxSteps = floor(maxTrip / gearRatio * 100.0);
1530
1531 /* Relative and absolute movement */
1532 FocusRelPosNP[0].setMin(0);
1533 FocusRelPosNP[0].setMax(floor(maxSteps / 2.0));
1534 FocusRelPosNP[0].setStep(100);
1535
1536 FocusAbsPosNP[0].setMin(0);
1537 FocusAbsPosNP[0].setMax(maxSteps);
1538 FocusAbsPosNP[0].setStep(1000);
1539}
double temperature
bool isConnected() const
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertySwitch FocusMotionSP
INDI::PropertyNumber FocusSpeedNP
INDI::PropertyNumber FocusTimerNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool saveConfigItems(FILE *fp) override
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
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.
void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration) override
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
@ FOCUS_T_SAMPLES
Definition steeldrive.h:46
const char * getDefaultName() override
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool saveConfigItems(FILE *fp) override
@ FOCUS_GEAR_RATIO
Definition steeldrive.h:41
virtual IPState MoveRelFocuser(FocusDirection dir, unsigned int ticks) override
virtual bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
#define FOCUS_SETTINGS_TAB
Definition dmfc.cpp:34
double max(void)
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
@ IP_RO
Definition indiapi.h:182
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
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:653
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
Definition indicom.c:349
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:490
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition indidevapi.c:376
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition indidevapi.c:416
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition indidevapi.c:398
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
uint8_t cmd[4]
Definition pwc-ioctl.h:2
#define STEELDRIVE_TIMEOUT
#define STEELDRIVE_MAX_RETRIES
std::unique_ptr< SteelDrive > steelDrive(new SteelDrive())
#define STEELDRIVE_MAXBUF
#define STEELDRIVE_CMD
#define STEELDIVE_POSITION_THRESHOLD
#define STEELDRIVE_TEMPERATURE_FREQ
#define STEELDRIVE_CMD_LONG
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248