Instrument Neutral Distributed Interface INDI 2.2.5
lakeside.cpp
Go to the documentation of this file.
1/*
2 Lakeside Focuser
3 Copyright (C) 2017 Phil Shepherd ([email protected])
4 Technical Information kindly supplied by Peter Chance at LakesideAstro ([email protected])
5
6 Code template from original Moonlite code by Jasem Mutlaq ([email protected])
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22*/
23
24/*
25Modifications
260.1 psjshep xx-xxx-xxxx - 1st version
27..
28..
290.11 psjshep 17-Mar-2017 - changed PortT[0].text to serialConnection->port()
30
311.1 JM 29-11-2018: Misc fixes and improvements.
32 */
33
34#define LAKESIDE_VERSION_MAJOR 1
35#define LAKESIDE_VERSION_MINOR 1
36
37#include "lakeside.h"
38#include <config.h>
39#include "indicom.h"
41
42#include <stdio.h>
43#include <termios.h>
44#include <string.h>
45#include <sys/time.h>
46#include <unistd.h>
47#include <math.h>
48#include <memory>
49
50// tty_read_section timeout in seconds
51#define LAKESIDE_TIMEOUT 2
52#define LAKESIDE_LEN 7
53
54// Max number of Timeouts for a tty_read_section
55// This is in case a buffer read is too fast
56// or nothing in the buffer during GetLakesideStatus()
57#define LAKESIDE_TIMEOUT_RETRIES 2
58
59static std::unique_ptr<Lakeside> lakeside(new Lakeside());
60
71
72// Initialise
74{
76
77 // Current Direction
78 // IUFillSwitch(&MoveDirectionS[0], "Normal", "", ISS_ON);
79 // IUFillSwitch(&MoveDirectionS[1], "Reverse", "", ISS_OFF);
80 // IUFillSwitchVector(&MoveDirectionSP, MoveDirectionS, 2, getDeviceName(), "","Move Direction", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
81
82 // Focuser temperature (degrees C) - read only
83 IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%3.2f", -50, 70., 0., 0.);
84 IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature (C)",
86
87 // Focuser temperature (Kelvin)- read only & only read once at connect
88 IUFillNumber(&TemperatureKN[0], "TEMPERATUREK", "Kelvin", "%3.2f", 0., 373.15, 0., 0.);
89 IUFillNumberVector(&TemperatureKNP, TemperatureKN, 1, getDeviceName(), "FOCUS_TEMPERATUREK", "Temperature (K)",
91
92 // Compensate for temperature
93 IUFillSwitch(&TemperatureTrackingS[0], "Enable", "", ISS_OFF);
94 IUFillSwitch(&TemperatureTrackingS[1], "Disable", "", ISS_ON);
95 IUFillSwitchVector(&TemperatureTrackingSP, TemperatureTrackingS, 2, getDeviceName(), "Temperature Track", "",
97
98 // Backlash 0-255
99 // IUFillNumber(&FocusBacklashN[0], "BACKLASH", "(0-255)", "%.f", 0, 255, 0, 0);
100 // IUFillNumberVector(&FocusBacklashNP, FocusBacklashN, 1, getDeviceName(), "BACKLASH", "Backlash", SETTINGS_TAB, IP_RW, 0, IPS_IDLE );
101 FocusBacklashNP[0].setMin(0);
102 FocusBacklashNP[0].setMax(255);
103 FocusBacklashNP[0].setStep(10);
104 FocusBacklashNP[0].setValue(0);
105
106 // Maximum Travel - read only
107 // IUFillNumber(&MaxTravelN[0], "MAXTRAVEL", "No. Steps", "%.f", 1, 65536, 0, 10000);
108 // IUFillNumberVector(&MaxTravelNP, MaxTravelN, 1, getDeviceName(), "MAXTRAVEL", "Max travel(Via Ctrlr)", SETTINGS_TAB, IP_RO, 0, IPS_IDLE );
110
111 // Step Size - read only
112 IUFillNumber(&StepSizeN[0], "STEPSIZE", "No. Steps", "%.f", 1, 65536, 0, 1);
113 IUFillNumberVector(&StepSizeNP, StepSizeN, 1, getDeviceName(), "STEPSIZE", "Step Size(Via Ctrlr)", SETTINGS_TAB, IP_RO, 0,
114 IPS_IDLE);
115
116 // Active Temperature Slope - select 1 or 2
117 IUFillSwitch(&ActiveTemperatureSlopeS[0], "Slope 1", "", ISS_ON);
118 IUFillSwitch(&ActiveTemperatureSlopeS[1], "Slope 2", "", ISS_OFF);
119 IUFillSwitchVector(&ActiveTemperatureSlopeSP, ActiveTemperatureSlopeS, 2, getDeviceName(), "Active Slope", "Active Slope",
120 SETTINGS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
121
122 // Slope 1 : Directions
123 IUFillSwitch(&Slope1DirS[0], "0", "", ISS_ON);
124 IUFillSwitch(&Slope1DirS[1], "1", "", ISS_OFF);
125 IUFillSwitchVector(&Slope1DirSP, Slope1DirS, 2, getDeviceName(), "Slope 1 Direction", "Slope 1 Direction", SETTINGS_TAB,
127
128 // Slope 1 : Slope Increments (counts per degree, 0.1 step increments
129 IUFillNumber(&Slope1IncN[0], "SLOPE1INC", "No. Steps (0-655356", "%.f", 0, 65536, 0, 0);
130 IUFillNumberVector(&Slope1IncNP, Slope1IncN, 1, getDeviceName(), "SLOPE1INC", "Slope1 Increments", SETTINGS_TAB, IP_RW, 0,
131 IPS_IDLE );
132
133 // slope 1 : Deadband - value between 0 and 255
134 IUFillNumber(&Slope1DeadbandN[0], "SLOPE1DEADBAND", "(0-255)", "%.f", 0, 255, 0, 0);
135 IUFillNumberVector(&Slope1DeadbandNP, Slope1DeadbandN, 1, getDeviceName(), "SLOPE1DEADBAND", "Slope 1 Deadband",
136 SETTINGS_TAB, IP_RW, 0, IPS_IDLE );
137
138 // Slope 1 : Time Period (Minutes, 0.1 step increments
139 IUFillNumber(&Slope1PeriodN[0], "SLOPE1PERIOD", "Minutes (0-99)", "%.f", 0, 99, 0, 0);
140 IUFillNumberVector(&Slope1PeriodNP, Slope1PeriodN, 1, getDeviceName(), "SLOPE1PERIOD", "Slope 1 Period", SETTINGS_TAB,
141 IP_RW, 0, IPS_IDLE );
142
143 // Slope 2 : Direction
144 IUFillSwitch(&Slope2DirS[0], "0", "", ISS_ON);
145 IUFillSwitch(&Slope2DirS[1], "1", "", ISS_OFF);
146 IUFillSwitchVector(&Slope2DirSP, Slope2DirS, 2, getDeviceName(), "Slope 2 Direction", "", SETTINGS_TAB, IP_RW, ISR_1OFMANY,
147 0, IPS_IDLE);
148
149 // slope 2 : Slope Increments (counts per degree, 0.1 step increments
150 IUFillNumber(&Slope2IncN[0], "SLOPE2INC", "No. Steps (0-65536)", "%.f", 0, 65536, 0, 0);
151 IUFillNumberVector(&Slope2IncNP, Slope2IncN, 1, getDeviceName(), "SLOPE2INC", "Slope 2 Increments", SETTINGS_TAB, IP_RW, 0,
152 IPS_IDLE );
153
154 // slope 2 : Deadband - value between 0 and 255
155 IUFillNumber(&Slope2DeadbandN[0], "SLOPE2DEADBAND", "Steps (0-255)", "%.f", 0, 255, 0, 0);
156 IUFillNumberVector(&Slope2DeadbandNP, Slope2DeadbandN, 1, getDeviceName(), "SLOPE2DEADBAND", "Slope 2 Deadband",
157 SETTINGS_TAB, IP_RW, 0, IPS_IDLE );
158
159 // slope 2 : Time Period (Minutes, 0.1 step increments)
160 IUFillNumber(&Slope2PeriodN[0], "SLOPE2PERIOD", "Minutes (0-99)", "%.f", 0, 99, 0, 0);
161 IUFillNumberVector(&Slope2PeriodNP, Slope2PeriodN, 1, getDeviceName(), "SLOPE2PERIOD", "Slope 2 Period", SETTINGS_TAB,
162 IP_RW, 0, IPS_IDLE );
163
164 FocusAbsPosNP[0].setMin(0.);
165
166 // shephpj - not used
167 //FocusAbsPosNP[0].setMax(65536.);
168
170
172
173 return true;
174
175}
176
178{
180
181 if (isConnected())
182 {
183 //defineProperty(&FocusBacklashNP);
184 //defineProperty(&MaxTravelNP);
185 defineProperty(&StepSizeNP);
186 defineProperty(&TemperatureNP);
187 defineProperty(&TemperatureKNP);
188 //defineProperty(&MoveDirectionSP);
189 defineProperty(&TemperatureTrackingSP);
190 defineProperty(&ActiveTemperatureSlopeSP);
191 defineProperty(&Slope1DirSP);
192 defineProperty(&Slope1IncNP);
193 defineProperty(&Slope1DeadbandNP);
194 defineProperty(&Slope1PeriodNP);
195 defineProperty(&Slope2DirSP);
196 defineProperty(&Slope2IncNP);
197 defineProperty(&Slope2DeadbandNP);
198 defineProperty(&Slope2PeriodNP);
199
200 GetFocusParams();
201
202 LOG_INFO("Lakeside parameters updated, focuser ready for use.");
203 }
204 else
205 {
206 //deleteProperty(FocusBacklashNP.name);
207 //deleteProperty(MaxTravelNP.name);
208 deleteProperty(StepSizeNP.name);
209 //deleteProperty(MoveDirectionSP.name);
210 deleteProperty(TemperatureNP.name);
211 deleteProperty(TemperatureKNP.name);
212 deleteProperty(TemperatureTrackingSP.name);
213 deleteProperty(ActiveTemperatureSlopeSP.name);
214 deleteProperty(Slope1DirSP.name);
215 deleteProperty(Slope1IncNP.name);
216 deleteProperty(Slope1DeadbandNP.name);
217 deleteProperty(Slope1PeriodNP.name);
218 deleteProperty(Slope2DirSP.name);
219 deleteProperty(Slope2IncNP.name);
220 deleteProperty(Slope2DeadbandNP.name);
221 deleteProperty(Slope2PeriodNP.name);
222 }
223
224 return true;
225
226}
227
228#if 0
229// connect to focuser port
230//
231// 9600 baud
232// 8 bits
233// 0 parity
234// 1 stop bit
235//
237{
238 int rc = 0;
239 char errorMsg[MAXRBUF];
240
241 // if ( (rc = tty_connect(PortT[0].text, 9600, 8, 0, 1, &PortFD)) != TTY_OK)
242 if ( (rc = tty_connect(serialConnection->port(), 9600, 8, 0, 1, &PortFD)) != TTY_OK)
243 {
244 tty_error_msg(rc, errorMsg, MAXRBUF);
245 LOGF_INFO("Failed to connect to port %s, with Error %s", serialConnection->port(), errorMsg);
246 return false;
247 }
248
249 LOGF_INFO("Connected to port %s", serialConnection->port());
250
251 if (LakesideOnline())
252 {
253 LOGF_INFO("Lakeside is online on port %s", serialConnection->port());
255 return true;
256 }
257 else
258 {
259 LOGF_INFO("Unable to connect to Lakeside Focuser. Please ensure the controller is powered on and the port (%s) is correct.",
261 return false;
262 }
263}
264
265
266// Disconnect from focuser
268{
269 LOG_INFO("Lakeside is offline.");
271}
272#endif
273
275{
276 return LakesideOnline();
277}
278
280{
281 return "Lakeside";
282}
283
284//
285// Send Lakeside a command
286//
287// In :
288// in_cmd : command to send to the focuser
289//
290// Returns true for successful write
291// false for failed write
292//
293bool Lakeside::SendCmd(const char * in_cmd)
294{
295 int nbytes_written = 0, rc = -1;
296 char errstr[MAXRBUF];
297
298 LOGF_DEBUG("CMD <%s>", in_cmd);
299
300 if ( (rc = tty_write_string(PortFD, in_cmd, &nbytes_written)) != TTY_OK)
301 {
302 tty_error_msg(rc, errstr, MAXRBUF);
303 LOGF_ERROR("SendCmd: Write for command (%s) failed - %s", in_cmd, errstr);
304 return false;
305 }
306
307 return true;
308
309}
310
311//
312// Read the Lakeside buffer, setting response to the contents
313//
314// Returns
315// true : something to read in the buffer
316// false : error reading the buffer
317//
318bool Lakeside::ReadBuffer(char * response)
319{
320 int nbytes_read = 0, rc = -1;
321 char resp[LAKESIDE_LEN] = {0};
322
323 //strcpy(resp," ");
324 // read until 0x23 (#) received
325 if ( (rc = tty_read_section(PortFD, resp, 0x23, LAKESIDE_TIMEOUT, &nbytes_read)) != TTY_OK)
326 {
327 char errstr[MAXRBUF];
328 tty_error_msg(rc, errstr, MAXRBUF);
329 LOGF_ERROR("ReadBuffer: Read failed - %s", errstr);
330 strncpy(response, "ERROR", LAKESIDE_LEN);
331 return false;
332 }
333
334 // char hex_cmd[LAKESIDE_LEN * 3] = {0};
335 // hexDump(hex_cmd, resp, LAKESIDE_LEN * 3);
336 // LOGF_DEBUG("RES <%s>", hex_cmd);
337
338 resp[nbytes_read] = 0;
339 LOGF_DEBUG("RES <%s>", resp);
340
341 strncpy(response, resp, LAKESIDE_LEN);
342 return true;
343}
344
345//
346// check for OK# from Lakeside - i.e. it is responding
347//
348bool Lakeside::LakesideOnline()
349{
350 char resp[LAKESIDE_LEN] = {0};
351 const char * cmd = "??#";
352
353 //strcpy(resp," ");
354
355 if (!SendCmd(cmd))
356 {
357 return false;
358 }
359
360 LOGF_DEBUG("LakesideOnline: Successfully sent (%s)", cmd);
361
362 if (!ReadBuffer(resp))
363 {
364 return false;
365 }
366
367 // if SendCmd succeeded, resp contains response from the command
368 LOGF_DEBUG("LakesideOnline: Received (%s)", resp);
369
370 if (!strncmp(resp, "OK#", 3))
371 {
372 LOG_DEBUG("LakesideOnline: Received OK# - Lakeside responded");
373 return true;
374 }
375 else
376 {
377 LOGF_ERROR("LakesideOnline: OK# not found. Instead, received (%s)", resp);
378 return false;
379 }
380
381}
382// get current movement direction
383//
384// 0 = Normal
385// 1 = Reversed
386bool Lakeside::updateMoveDirection()
387{
388 int temp = -1;
389 char resp[LAKESIDE_LEN] = {0};
390 char cmd[] = "?D#";
391
392 if (!SendCmd(cmd))
393 {
394 return false;
395 }
396
397 if (!ReadBuffer(resp))
398 {
399 return false;
400 }
401
402 //IUResetSwitch(&MoveDirectionSP);
403
404 // direction is in form Dnnnnn#
405 // where nnnnn is 0 for normal or 1 for reversed
406 sscanf(resp, "D%5d#", &temp);
407
408 if ( temp == 0)
409 {
411 LOGF_DEBUG("updateMoveDirection: Move Direction is (%d)", temp);
412 }
413 else if ( temp == 1)
414 {
416 LOGF_DEBUG("updateMoveDirection: Move Direction is (%d)", temp);
417 }
418 else
419 {
420 LOGF_ERROR("updateMoveDirection: Unknown move Direction response (%s)", resp);
421 return false;
422 }
423
424 return true;
425}
426
427// Decode contents of buffer
428// Returns:
429// P : Position update found - FocusAbsPosNP[0].getValue() updated
430// T : Temperature update found - TemperatureN[0].value
431// K : Temperature in Kelvin update found - TemperatureKN[0].value
432// D : DONE# received
433// O : OK# received
434// E : Error due to unknown/malformed command having been sent
435// ? : unknown response received
436char Lakeside::DecodeBuffer(char * in_response)
437{
438 int temp = 0, pos = 0, rc = -1;
439
440 LOGF_DEBUG("DecodeBuffer: in_response (%s)", in_response);
441
442 // if focuser finished moving, DONE# received
443 if (!strncmp(in_response, "DONE#", 5))
444 {
445 return 'D';
446 }
447
448 // if focuser returned OK#
449 if (!strncmp(in_response, "OK#", 3))
450 {
451 return 'O';
452 }
453
454 // if focuser returns an error for unknown command
455 if (!strncmp(in_response, "!#", 2))
456 {
457 return 'E';
458 }
459
460 // Temperature update is Tnnnnnn# where nnnnn is left space padded
461 if (!strcmp("TN/A#", in_response))
462 {
463 TemperatureNP.s = IPS_IDLE;
464 return 'T';
465 }
466 else
467 {
468 rc = sscanf(in_response, "T%5d#", &temp);
469 if (rc > 0)
470 {
471 // need to divide result by 2
472 TemperatureN[0].value = ((int) temp) / 2.0;
473 LOGF_DEBUG("DecodeBuffer: Result (%3.1f)", TemperatureN[0].value);
474
475 return 'T';
476 }
477 }
478
479 // Temperature update is Knnnnnn# where nnnnn is left space padded
480 rc = sscanf(in_response, "K%5d#", &temp);
481 if (rc > 0)
482 {
483 // need to divide result by 2
484 TemperatureKN[0].value = ((int) temp) / 2.00;
485 LOGF_DEBUG("DecodeBuffer: Result (%3.2f)", TemperatureKN[0].value);
486
487 return 'K';
488 }
489
490 // look for step info Pnnnnn#
491 rc = sscanf(in_response, "P%5d#", &pos);
492 // focuser position returned Pnnnnn#
493 if (rc > 0)
494 {
495 FocusAbsPosNP[0].setValue(pos);
497
498 LOGF_DEBUG("DecodeBuffer: Returned position (%d)", pos);
499 return 'P';
500 }
501 else
502 {
503 LOGF_ERROR("DecodeBuffer: Unknown response : (%s)", in_response);
504 return '?';
505 }
506}
507
508// Get Temperature in C from focuser
509//
510// Return :
511// true : successfully got Temperature & updated INDI
512// false : Unable to get & update Temperature (timeout or other)
513//
514bool Lakeside::updateTemperature()
515{
516 char resp[LAKESIDE_LEN] = {0};
517 char cmd[] = "?T#";
518 char buffer_response = '?';
519
520 if (!SendCmd(cmd))
521 {
522 return false;
523 }
524
525 if (!ReadBuffer(resp))
526 {
527 return false;
528 }
529
530 LOGF_DEBUG("updateTemperature: Read response (%s)", resp);
531
532 // ascertain contents of buffer & update temp if necessary
533 buffer_response = DecodeBuffer(resp);
534
535 // if temperature updated, then return true
536 if ( buffer_response == 'T' )
537 {
538 return true;
539 }
540 else
541 {
542 return false;
543 }
544}
545
546// Get Temperature in K from focuser
547//
548// Return :
549// true : successfully got Temperature in K & updated INDI
550// false : Unable to get & update Temperature in K (timeout or other)
551//
552bool Lakeside::updateTemperatureK()
553{
554 char resp[LAKESIDE_LEN] = {0};
555 char cmd[] = "?K#";
556 char buffer_response = '?';
557
558 if (!SendCmd(cmd))
559 {
560 return false;
561 }
562
563 if (!ReadBuffer(resp))
564 {
565 return false;
566 }
567
568 LOGF_DEBUG("updateTemperatureK: Read response (%s)", resp);
569
570 // ascertain contents of buffer & update temp in K if necessary
571 buffer_response = DecodeBuffer(resp);
572
573 // if temperature updated, then return true
574 if ( buffer_response == 'K' )
575 {
576 return true;
577 }
578 else
579 {
580 return false;
581 }
582}
583
584// Get position of focuser
585//
586// Return :
587// true : successfully got focus position & updated INDI
588// false : Unable to get & update position (timeout or other)
589//
590bool Lakeside::updatePosition()
591{
592 char resp[LAKESIDE_LEN] = {0};
593 char cmd[] = "?P#";
594 char buffer_response = '?';
595
596 if (!SendCmd(cmd))
597 {
598 return false;
599 }
600
601 LOGF_DEBUG("updatePosition: Successfully sent (%s)", cmd);
602
603 if (!ReadBuffer(resp))
604 {
605 return false;
606 }
607
608 LOGF_DEBUG("updatePosition: Fetched (%s)", resp);
609
610 // ascertain contents of buffer & update position if necessary
611 buffer_response = DecodeBuffer(resp);
612
613 if ( buffer_response == 'P' )
614 {
615 return true;
616 }
617 else
618 {
619 return false;
620 }
621}
622
623// Get Backlash compensation
624bool Lakeside::updateBacklash()
625{
626 int temp = -1;
627 char resp[LAKESIDE_LEN] = {0};
628 char cmd[] = "?B#";
629
630 if (!SendCmd(cmd))
631 {
632 return false;
633 }
634
635 if (!ReadBuffer(resp))
636 {
637 return false;
638 }
639
640 // Backlash is in form Bnnnnn#
641 // where nnnnn is 0 - 255, space left padded
642 sscanf(resp, "B%5d#", &temp);
643
644 if ( temp >= 0)
645 {
646 FocusBacklashNP[0].setValue(temp);
647 LOGF_DEBUG("updateBacklash: Backlash is (%d)", temp);
648 }
649 else
650 {
651 LOGF_ERROR("updateBacklash: Backlash request error (%s)", resp);
652 return false;
653 }
654
655 return true;
656}
657
658// get Slope 1 Increments
659bool Lakeside::updateSlope1Inc()
660{
661 int temp = -1;
662 char resp[LAKESIDE_LEN];
663 char cmd[] = "?1#";
664
665 if (!SendCmd(cmd))
666 {
667 return false;
668 }
669
670 if (!ReadBuffer(resp))
671 {
672 return false;
673 }
674
675 // Slope 1 Increment is in form 1nnnnn#
676 // where nnnnn is number of 0.1 step increments, space left padded
677 sscanf(resp, "1%5d#", &temp);
678
679 if ( temp >= 0)
680 {
681 Slope1IncN[0].value = temp;
682 LOGF_DEBUG("updateSlope1Inc: Slope 1 Increments is (%d)", temp);
683 }
684 else
685 {
686 LOGF_ERROR("updateSlope1Inc: Slope 1 Increment request error (%s)", resp);
687 return false;
688 }
689
690 return true;
691}
692
693// get Slope 2 Increments
694bool Lakeside::updateSlope2Inc()
695{
696 int temp = -1;
697 char resp[LAKESIDE_LEN] = {0};
698 char cmd[] = "?2#";
699
700 if (!SendCmd(cmd))
701 {
702 return false;
703 }
704
705 if (!ReadBuffer(resp))
706 {
707 return false;
708 }
709
710 // Slope 1 Increment is in form 1nnnnn#
711 // where nnnnn is number of 0.1 step increments, space left padded
712 sscanf(resp, "2%5d#", &temp);
713
714 if ( temp >= 0)
715 {
716 Slope2IncN[0].value = temp;
717 LOGF_DEBUG("updateSlope2Inc: Slope 2 Increments is (%d)", temp);
718 }
719 else
720 {
721 LOGF_ERROR("updateSlope2Inc: Slope 2 Increment request error (%s)", resp);
722 return false;
723 }
724
725 return true;
726}
727
728// get Slope 1 direction : 0 or 1
729bool Lakeside::updateSlope1Dir()
730{
731 int temp = -1;
732 char resp[LAKESIDE_LEN] = {0};
733 char cmd[] = "?a#";
734
735 if (!SendCmd(cmd))
736 {
737 return false;
738 }
739
740 if (!ReadBuffer(resp))
741 {
742 return false;
743 }
744
745 // Slope 1 Direction is in form annnnn#
746 // where nnnnn is either 0 or 1, space left padded
747 sscanf(resp, "a%5d#", &temp);
748
749 if ( temp == 0)
750 {
751 Slope1DirS[0].s = ISS_ON;
752 LOGF_DEBUG("updateSlope1Dir: Slope 1 Direction is (%d)", temp);
753 }
754 else if ( temp == 1)
755 {
756 Slope1DirS[1].s = ISS_ON;
757 }
758 else
759 {
760 LOGF_ERROR("updateSlope1Dir: Unknown Slope 1 Direction response (%s)", resp);
761 return false;
762 }
763
764 return true;
765}
766
767// get Slope 2 direction : 0 or 1
768bool Lakeside::updateSlope2Dir()
769{
770 int temp = -1;
771 char resp[LAKESIDE_LEN] = {0};
772 char cmd[] = "?b#";
773
774 if (!SendCmd(cmd))
775 {
776 return false;
777 }
778
779 if (!ReadBuffer(resp))
780 {
781 return false;
782 }
783
784 // Slope 2 Direction is in form annnnn#
785 // where nnnnn is either 0 or 1, space left padded
786 sscanf(resp, "b%5d#", &temp);
787
788 if ( temp == 0)
789 {
790 Slope2DirS[0].s = ISS_ON;
791 LOGF_DEBUG("updateSlope2Dir: Slope 2 Direction is (%d)", temp);
792 }
793 else if ( temp == 1)
794 {
795 Slope2DirS[1].s = ISS_ON;
796 }
797 else
798 {
799 LOGF_ERROR("updateSlope2Dir: Unknown Slope 2 Direction response (%s)", resp);
800 return false;
801 }
802
803 return true;
804}
805
806// Get slope 1 deadband
807bool Lakeside::updateSlope1Deadband()
808{
809 int temp = -1;
810 char resp[LAKESIDE_LEN] = {0};
811 char cmd[] = "?c#";
812
813 if (!SendCmd(cmd))
814 {
815 return false;
816 }
817
818 if (!ReadBuffer(resp))
819 {
820 return false;
821 }
822
823 // Deadband is in form cnnnnn#
824 // where nnnnn is 0 - 255, space left padded
825 sscanf(resp, "c%5d#", &temp);
826
827 if ( temp >= 0)
828 {
829 Slope1DeadbandN[0].value = temp;
830 LOGF_DEBUG("updateSlope1Deadband: Slope 1 Deadband is (%d)", temp);
831 }
832 else
833 {
834 LOGF_ERROR("updateSlope1Deadband: Slope 1 Deadband request error (%s)", resp);
835 return false;
836 }
837
838 return true;
839}
840
841// Get slope 2 deadband
842bool Lakeside::updateSlope2Deadband()
843{
844 int temp = -1;
845 char resp[LAKESIDE_LEN] = {0};
846 char cmd[] = "?d#";
847
848 if (!SendCmd(cmd))
849 {
850 return false;
851 }
852
853 if (!ReadBuffer(resp))
854 {
855 return false;
856 }
857
858 // Deadband is in form dnnnnn#
859 // where nnnnn is 0 - 255, space left padded
860 sscanf(resp, "d%5d#", &temp);
861
862 if ( temp >= 0)
863 {
864 Slope2DeadbandN[0].value = temp;
865 LOGF_DEBUG("updateSlope2Deadband: Slope 2 Deadband is (%d)", temp);
866 }
867 else
868 {
869 LOGF_ERROR("updateSlope2Deadband: Slope 2 Deadband request error (%s)", resp);
870 return false;
871 }
872
873 return true;
874}
875
876// get Slope 1 time period
877bool Lakeside::updateSlope1Period()
878{
879 int temp = -1;
880 char resp[LAKESIDE_LEN] = {0};
881 char cmd[] = "?e#";
882
883 if (!SendCmd(cmd))
884 {
885 return false;
886 }
887
888 if (!ReadBuffer(resp))
889 {
890 return false;
891 }
892
893 // Slope 1 Period is in form ennnnn#
894 // where nnnnn is number of 0.1 step increments, space left padded
895 sscanf(resp, "e%5d#", &temp);
896
897 if ( temp >= 0)
898 {
899 Slope1PeriodN[0].value = temp;
900 LOGF_DEBUG("updateSlope1Period: Slope 1 Period is (%d)", temp);
901 }
902 else
903 {
904 LOGF_ERROR("updateSlope1Period: Slope 1 Period request error (%s)", resp);
905 return false;
906 }
907
908 return true;
909}
910
911// get Slope 2 time period
912bool Lakeside::updateSlope2Period()
913{
914 int temp = -1;
915 char resp[LAKESIDE_LEN] = {0};
916 char cmd[] = "?f#";
917
918 if (!SendCmd(cmd))
919 {
920 return false;
921 }
922
923 if (!ReadBuffer(resp))
924 {
925 return false;
926 }
927
928 // Slope 2 Period is in form ennnnn#
929 // where nnnnn is number of 0.1 step increments, space left padded
930 sscanf(resp, "f%5d#", &temp);
931
932 if ( temp >= 0)
933 {
934 Slope2PeriodN[0].value = temp;
935 LOGF_DEBUG("updateSlope2Period: Slope 2 Period is (%d)", temp);
936 }
937 else
938 {
939 LOGF_ERROR("updateSlope2Period: Slope 2 Period request error (%s)", resp);
940 return false;
941 }
942
943 return true;
944}
945
946// Get Max travel
947bool Lakeside::updateMaxTravel()
948{
949 int temp = -1;
950 char resp[LAKESIDE_LEN] = {0};
951 char cmd[] = "?I#";
952
953 if (!SendCmd(cmd))
954 {
955 return false;
956 }
957
958 if (!ReadBuffer(resp))
959 {
960 return false;
961 }
962
963 // MaxTravel is in form Innnnn#
964 // where nnnnn is 0 - 65536, space left padded
965 sscanf(resp, "I%5d#", &temp);
966
967 if ( temp > 0)
968 {
969 FocusMaxPosNP[0].setValue(temp);
970 LOGF_DEBUG("updateMaxTravel: MaxTravel is (%d)", temp);
971 }
972 else
973 {
974 LOGF_ERROR("updateMaxTravel: MaxTravel request error (%s)", resp);
975 return false;
976 }
977
978 return true;
979}
980
981// get step size
982bool Lakeside::updateStepSize()
983{
984 int temp = -1;
985 char resp[LAKESIDE_LEN] = {0};
986 char cmd[] = "?S#";
987
988 if (!SendCmd(cmd))
989 {
990 return false;
991 }
992
993 LOGF_DEBUG("updateStepSize: Sent (%s)", cmd);
994
995 if (!ReadBuffer(resp))
996 {
997 return false;
998 }
999
1000 // StepSize is in form Snnnnn#
1001 // where nnnnn is 0 - ??, space left padded
1002 sscanf(resp, "S%5d#", &temp);
1003
1004 if ( temp > 0)
1005 {
1006 StepSizeN[0].value = temp;
1007 LOGF_DEBUG("updateStepSize: step size is (%d)", temp);
1008 }
1009 else
1010 {
1011 LOGF_ERROR("updateStepSize: StepSize request error (%s)", resp);
1012 return false;
1013 }
1014
1015 return true;
1016}
1017
1018//
1019// NOTE : set via hand controller
1020//
1021bool Lakeside::setCalibration()
1022{
1023 return true;
1024}
1025
1026// Move focuser to "position"
1027bool Lakeside::gotoPosition(uint32_t position)
1028{
1029 int calc_steps = 0;
1030 char cmd[LAKESIDE_LEN] = {0};
1031
1032 // Lakeside only uses move NNNNN steps - goto step not available.
1033 // calculate as steps to move = current position - new position
1034 // if -ve then move out, else +ve moves in
1035 calc_steps = FocusAbsPosNP[0].getValue() - position;
1036
1037 // MaxTravelN[0].value is set by "calibrate" via the control box, & read at connect
1038 if ( position > FocusMaxPosNP[0].getValue() )
1039 {
1040 LOGF_ERROR("Position requested (%ld) is out of bounds between %g and %g", position, FocusAbsPosNP[0].getMin(),
1041 FocusMaxPosNP[0].getValue());
1043 return false;
1044 }
1045
1046 // -ve == Move Out
1047 if ( calc_steps < 0 )
1048 {
1049 snprintf(cmd, LAKESIDE_LEN, "CO%d#", abs(calc_steps));
1050 LOGF_DEBUG("MoveFocuser: move-out cmd to send (%s)", cmd);
1051 }
1052 else
1053 // ve == Move In
1054 if ( calc_steps > 0 )
1055 {
1056 // Move in nnnnn steps = CInnnnn#
1057 snprintf(cmd, LAKESIDE_LEN, "CI%d#", calc_steps);
1058 LOGF_DEBUG("MoveFocuser: move-in cmd to send (%s)", cmd);
1059 }
1060 else
1061 {
1062 // Zero == no steps to move
1063 LOGF_DEBUG("MoveFocuser: No steps to move. calc_steps = %d", calc_steps);
1065 return false;
1066 }
1067
1068 // flush ready to move
1069 tcflush(PortFD, TCIOFLUSH);
1070
1071 if (!SendCmd(cmd))
1072 {
1074 return false;
1075 }
1076 else
1077 LOGF_DEBUG("MoveFocuser: Sent cmd (%s)", cmd);
1078
1079 // At this point, the move command has been sent, so set BUSY & return true
1081 return true;
1082}
1083
1085{
1086 return setBacklash(steps);
1087}
1088
1090{
1091 INDI_UNUSED(enabled);
1092 return true;
1093}
1094
1095//
1096// Set backlash compensation
1097//
1098bool Lakeside::setBacklash(int backlash )
1099{
1100 char cmd[LAKESIDE_LEN] = {0};
1101 char resp[LAKESIDE_LEN] = {0};
1102
1103 tcflush(PortFD, TCIOFLUSH);
1104
1105 //CRBnnn#
1106 snprintf(cmd, LAKESIDE_LEN, "CRB%d#", backlash);
1107
1108 if (!SendCmd(cmd))
1109 {
1110 return false;
1111 }
1112
1113 if (!ReadBuffer(resp))
1114 {
1115 return false;
1116 }
1117
1118 if (!strncmp(resp, "OK#", 3))
1119 {
1120 LOGF_INFO("Backlash steps set to %d", backlash);
1121 }
1122 else
1123 {
1124 LOGF_ERROR("setBacklash: Unknown result (%s)", resp);
1125 return false;
1126 }
1127
1128 return true;
1129}
1130
1131//
1132// NOTE : set via hand controller
1133// Here for example
1134//
1135bool Lakeside::setStepSize(int stepsize )
1136{
1137 char cmd[LAKESIDE_LEN] = {0};
1138 char resp[LAKESIDE_LEN] = {0};
1139
1140 tcflush(PortFD, TCIOFLUSH);
1141
1142 // CRSnnnnn#
1143 snprintf(cmd, LAKESIDE_LEN, "CRS%d#", stepsize);
1144
1145 if (!SendCmd(cmd))
1146 {
1147 return false;
1148 }
1149
1150 if (!ReadBuffer(resp))
1151 {
1152 return false;
1153 }
1154
1155 if (!strncmp(resp, "OK#", 3))
1156 {
1157 LOGF_DEBUG("setStepSize: cmd (%s) - %s", cmd, resp);
1158 }
1159 else
1160 {
1161 LOGF_ERROR("setStepSize: Unknown result (%s)", resp);
1162 return false;
1163 }
1164
1165 return true;
1166}
1167
1168//
1169// NOTE : set via hand controller
1170// Use calibrate routine on controller box
1171//
1172bool Lakeside::setMaxTravel(int /*maxtravel*/ )
1173{
1174 return true;
1175}
1176
1177// Change Move Direction
1178// 0 = Normal direction
1179// 1 = Reverse direction
1180// In case motor connection is on reverse side of the focus shaft
1181// NOTE : This just reverses the voltage sent to the motor
1182// & does NOT reverse the CI / CO commands
1183//bool Lakeside::setMoveDirection(int direction)
1185{
1186 char cmd[LAKESIDE_LEN] = {0};
1187 char resp[LAKESIDE_LEN] = {0};
1188
1189 tcflush(PortFD, TCIOFLUSH);
1190
1191 strncpy(cmd, enabled ? "CRD1#" : "CRD0#", LAKESIDE_LEN);
1192
1193 // if (direction == 0)
1194 // strncpy(cmd, "CRD0#", LAKESIDE_LEN);
1195 // else
1196 // if (direction == 1)
1197 // strncpy(cmd, "CRD1#", LAKESIDE_LEN);
1198 // else
1199 // {
1200 // LOGF_ERROR("setMoveDirection: Unknown direction (%d)", direction);
1201 // return false;
1202 // }
1203
1204 if (!SendCmd(cmd))
1205 {
1206 return false;
1207 }
1208
1209 if (!ReadBuffer(resp))
1210 {
1211 return false;
1212 }
1213
1214 if (!strncmp(resp, "OK#", 3))
1215 {
1216 LOGF_DEBUG("setMoveDirection: Completed cmd (%s). Result - %s", cmd, resp);
1217 if (!enabled)
1218 LOG_INFO("Move Direction : Normal");
1219 else
1220 LOG_INFO("Move Direction : Reversed");
1221 }
1222 else
1223 {
1224 LOGF_ERROR("setMoveDirection: Unknown result (%s)", resp);
1225 return false;
1226 }
1227
1228 return true;
1229}
1230
1231// Enable/disable Temperature Tracking functionality
1232bool Lakeside::setTemperatureTracking(bool enable)
1233{
1234 int nbytes_written = 0, rc = -1;
1235 char errstr[MAXRBUF];
1236 char cmd[LAKESIDE_LEN] = {0};
1237
1238 // flush all
1239 tcflush(PortFD, TCIOFLUSH);
1240
1241 if (enable)
1242 strncpy(cmd, "CTN#", LAKESIDE_LEN);
1243 else
1244 strncpy(cmd, "CTF#", LAKESIDE_LEN);
1245
1246 if ( (rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
1247 {
1248 tty_error_msg(rc, errstr, MAXRBUF);
1249 LOGF_ERROR("setTemperatureTracking: Write for command (%s) failed - %s", cmd, errstr);
1250 return false;
1251 }
1252 else
1253 {
1254 LOGF_DEBUG("setTemperatureTracking: Sent (%s)", cmd);
1255 if (enable)
1256 LOG_INFO("Temperature Tracking : Enabled");
1257 else
1258 LOG_INFO("Temperature Tracking : Disabled");
1259 }
1260
1261 // NOTE: NO reply string is sent back
1262
1263 return true;
1264
1265}
1266
1267// Set which Active Temperature slope to use : 1 or 2
1268bool Lakeside::setActiveTemperatureSlope(uint32_t active_slope)
1269{
1270 char cmd[LAKESIDE_LEN] = {0};
1271 char resp[LAKESIDE_LEN] = {0};
1272
1273 // flush all
1274 tcflush(PortFD, TCIOFLUSH);
1275
1276 // slope in is either 1 or 2
1277 // CRg1# : Slope 1
1278 // CRg2# : Slope 2
1279
1280 snprintf(cmd, LAKESIDE_LEN, "CRg%d#", active_slope);
1281
1282 if (!SendCmd(cmd))
1283 {
1284 return false;
1285 }
1286
1287 LOGF_DEBUG("setActiveTemperatureSlope: Sent (%s)", cmd);
1288
1289 if (!ReadBuffer(resp))
1290 {
1291 return false;
1292 }
1293
1294 if (!strncmp(resp, "OK#", 3))
1295 {
1296 LOGF_INFO("Selected Active Temperature Slope is %d", active_slope);
1297 }
1298 else
1299 {
1300 LOGF_ERROR("setActiveTemperatureSlope: Unknown result (%s)", resp);
1301 return false;
1302 }
1303
1304 return true;
1305
1306}
1307
1308//
1309// Set Slope 1 0.1 step increments
1310//
1311bool Lakeside::setSlope1Inc(uint32_t slope1_inc)
1312{
1313 char cmd[LAKESIDE_LEN] = {0};
1314 char resp[LAKESIDE_LEN] = {0};
1315
1316 tcflush(PortFD, TCIOFLUSH);
1317
1318 //CR1nnn#
1319 snprintf(cmd, LAKESIDE_LEN, "CR1%d#", slope1_inc);
1320
1321 if (!SendCmd(cmd))
1322 {
1323 return false;
1324 }
1325
1326 if (!ReadBuffer(resp))
1327 {
1328 return false;
1329 }
1330
1331 if (!strncmp(resp, "OK#", 3))
1332 {
1333 LOGF_INFO("Slope 1 0.1 counts per degree set to %d", slope1_inc);
1334 }
1335 else
1336 {
1337 LOGF_ERROR("setSlope1Inc: Unknown result (%s)", resp);
1338 return false;
1339 }
1340
1341 return true;
1342}
1343
1344//
1345// Set Slope 2 0.1 step increments
1346//
1347bool Lakeside::setSlope2Inc(uint32_t slope2_inc)
1348{
1349 char cmd[LAKESIDE_LEN] = {0};
1350 char resp[LAKESIDE_LEN] = {0};
1351
1352 tcflush(PortFD, TCIOFLUSH);
1353
1354 //CR2nnn#
1355 snprintf(cmd, LAKESIDE_LEN, "CR2%d#", slope2_inc);
1356
1357 if (!SendCmd(cmd))
1358 {
1359 return false;
1360 }
1361
1362 if (!ReadBuffer(resp))
1363 {
1364 return false;
1365 }
1366
1367 if (!strncmp(resp, "OK#", 3))
1368 {
1369 LOGF_INFO("Slope 2 0.1 counts per degree set to %d", slope2_inc);
1370 }
1371 else
1372 {
1373 LOGF_ERROR("setSlope2Inc: Unknown result (%s)", resp);
1374 return false;
1375 }
1376
1377 return true;
1378}
1379
1380//
1381// Set slope 1 direction 0 or 1
1382//
1383bool Lakeside::setSlope1Dir(uint32_t slope1_direction)
1384{
1385 char cmd[LAKESIDE_LEN] = {0};
1386 char resp[LAKESIDE_LEN] = {0};
1387
1388 tcflush(PortFD, TCIOFLUSH);
1389
1390 //CRannn#
1391 snprintf(cmd, LAKESIDE_LEN, "CRa%d#", slope1_direction);
1392
1393 if (!SendCmd(cmd))
1394 {
1395 return false;
1396 }
1397
1398 if (!ReadBuffer(resp))
1399 {
1400 return false;
1401 }
1402
1403 if (!strncmp(resp, "OK#", 3))
1404 {
1405 LOGF_INFO("Slope 1 Direction set to %d", slope1_direction);
1406 }
1407 else
1408 {
1409 LOGF_ERROR("setSlope1Dir: Unknown result (%s)", resp);
1410 return false;
1411 }
1412
1413 return true;
1414}
1415
1416//
1417// Set Slope 2 Direction 0 or 1
1418//
1419bool Lakeside::setSlope2Dir(uint32_t slope2_direction)
1420{
1421 char cmd[LAKESIDE_LEN] = {0};
1422 char resp[LAKESIDE_LEN] = {0};
1423
1424 tcflush(PortFD, TCIOFLUSH);
1425
1426 //CRannn#
1427 snprintf(cmd, LAKESIDE_LEN, "CRb%d#", slope2_direction);
1428
1429 if (!SendCmd(cmd))
1430 {
1431 return false;
1432 }
1433
1434 if (!ReadBuffer(resp))
1435 {
1436 return false;
1437 }
1438
1439 if (!strncmp(resp, "OK#", 3))
1440 {
1441 LOGF_INFO("Slope 2 Direction set to %d", slope2_direction);
1442 }
1443 else
1444 {
1445 LOGF_ERROR("setSlope2Dir: Unknown result (%s)", resp);
1446 return false;
1447 }
1448
1449 return true;
1450}
1451
1452//
1453// Set Slope 1 Deadband 0 - 255
1454//
1455bool Lakeside::setSlope1Deadband(uint32_t slope1_deadband)
1456{
1457 char cmd[LAKESIDE_LEN] = {0};
1458 char resp[LAKESIDE_LEN] = {0};
1459
1460 tcflush(PortFD, TCIOFLUSH);
1461
1462 //CRcnnn#
1463 snprintf(cmd, LAKESIDE_LEN, "CRc%d#", slope1_deadband);
1464
1465 if (!SendCmd(cmd))
1466 {
1467 return false;
1468 }
1469
1470 if (!ReadBuffer(resp))
1471 {
1472 return false;
1473 }
1474
1475 if (!strncmp(resp, "OK#", 3))
1476 {
1477 LOGF_INFO("Slope 1 deadband set to %d", slope1_deadband);
1478 }
1479 else
1480 {
1481 LOGF_ERROR("setSlope1Deadband: Unknown result (%s)", resp);
1482 return false;
1483 }
1484
1485 return true;
1486}
1487
1488//
1489// Set Slope 1 Deadband 0 - 255
1490//
1491bool Lakeside::setSlope2Deadband(uint32_t slope2_deadband)
1492{
1493 char cmd[LAKESIDE_LEN] = {0};
1494 char resp[LAKESIDE_LEN] = {0};
1495
1496 tcflush(PortFD, TCIOFLUSH);
1497
1498 //CRdnnn#
1499 snprintf(cmd, LAKESIDE_LEN, "CRd%d#", slope2_deadband);
1500
1501 if (!SendCmd(cmd))
1502 {
1503 return false;
1504 }
1505
1506 if (!ReadBuffer(resp))
1507 {
1508 return false;
1509 }
1510
1511 if (!strncmp(resp, "OK#", 3))
1512 {
1513 LOGF_INFO("Slope 2 deadband set to %d", slope2_deadband);
1514 }
1515 else
1516 {
1517 LOGF_ERROR("setSlope2Deadband: Unknown result (%s)", resp);
1518 return false;
1519 }
1520
1521 return true;
1522}
1523
1524//
1525// Set Slope 1 Period in minutes
1526//
1527bool Lakeside::setSlope1Period(uint32_t slope1_period)
1528{
1529 char cmd[LAKESIDE_LEN] = {0};
1530 char resp[LAKESIDE_LEN] = {0};
1531
1532 tcflush(PortFD, TCIOFLUSH);
1533
1534 //CRennn#
1535 snprintf(cmd, LAKESIDE_LEN, "CRe%d#", slope1_period);
1536
1537 if (!SendCmd(cmd))
1538 {
1539 return false;
1540 }
1541
1542 if (!ReadBuffer(resp))
1543 {
1544 return false;
1545 }
1546
1547 if (!strncmp(resp, "OK#", 3))
1548 {
1549 LOGF_INFO("Slope 1 Period set to %d", slope1_period);
1550 }
1551 else
1552 {
1553 LOGF_ERROR("setSlope1Period: Unknown result (%s)", resp);
1554 return false;
1555 }
1556
1557 return true;
1558}
1559
1560//
1561// Set Slope 2 Period in minutes
1562//
1563bool Lakeside::setSlope2Period(uint32_t slope2_period)
1564{
1565 char cmd[LAKESIDE_LEN] = {0};
1566 char resp[LAKESIDE_LEN] = {0};
1567
1568 tcflush(PortFD, TCIOFLUSH);
1569
1570 //CRfnnn#
1571 snprintf(cmd, LAKESIDE_LEN, "CRf%d#", slope2_period);
1572
1573 if (!SendCmd(cmd))
1574 {
1575 return false;
1576 }
1577
1578 if (!ReadBuffer(resp))
1579 {
1580 return false;
1581 }
1582
1583 if (!strncmp(resp, "OK#", 3))
1584 {
1585 LOGF_INFO("Slope 2 Period set to %d", slope2_period);
1586 }
1587 else
1588 {
1589 LOGF_ERROR("setSlope2Period: Unknown result (%s)", resp);
1590 return false;
1591 }
1592
1593 return true;
1594}
1595
1596//
1597// Process client new switch
1598//
1599bool Lakeside::ISNewSwitch (const char * dev, const char * name, ISState * states, char * names[], int n)
1600{
1601 if(strcmp(dev, getDeviceName()) == 0)
1602 {
1603 // Move Direction
1604 // if (!strcmp(MoveDirectionSP.name, name))
1605 // {
1606 // bool rc=false;
1607 // int current_mode = IUFindOnSwitchIndex(&MoveDirectionSP);
1608 // IUUpdateSwitch(&MoveDirectionSP, states, names, n);
1609 // int target_mode = IUFindOnSwitchIndex(&MoveDirectionSP);
1610 // if (current_mode == target_mode)
1611 // {
1612 // MoveDirectionSP.s = IPS_OK;
1613 // IDSetSwitch(&MoveDirectionSP, nullptr);
1614 // }
1615 // // switch will be either 0 for normal or 1 for reverse
1616 // rc = setMoveDirection(target_mode);
1617
1618 // if (rc == false)
1619 // {
1620 // IUResetSwitch(&MoveDirectionSP);
1621 // MoveDirectionS[current_mode].s = ISS_ON;
1622 // MoveDirectionSP.s = IPS_ALERT;
1623 // IDSetSwitch(&MoveDirectionSP, nullptr);
1624 // return false;
1625 // }
1626
1627 // MoveDirectionSP.s = IPS_OK;
1628 // IDSetSwitch(&MoveDirectionSP, nullptr);
1629 // return true;
1630 // }
1631
1632 // Temperature Tracking
1633 if (!strcmp(TemperatureTrackingSP.name, name))
1634 {
1635 int last_index = IUFindOnSwitchIndex(&TemperatureTrackingSP);
1636 IUUpdateSwitch(&TemperatureTrackingSP, states, names, n);
1637
1638 bool rc = setTemperatureTracking((TemperatureTrackingS[0].s == ISS_ON));
1639
1640 if (rc == false)
1641 {
1642 TemperatureTrackingSP.s = IPS_ALERT;
1643 IUResetSwitch(&TemperatureTrackingSP);
1644 TemperatureTrackingS[last_index].s = ISS_ON;
1645 IDSetSwitch(&TemperatureTrackingSP, nullptr);
1646 return false;
1647 }
1648
1649 TemperatureTrackingSP.s = IPS_OK;
1650 IDSetSwitch(&TemperatureTrackingSP, nullptr);
1651
1652 return true;
1653 }
1654
1655 // Active Temperature Slope
1656 if (!strcmp(ActiveTemperatureSlopeSP.name, name))
1657 {
1658 bool rc = false;
1659 int current_slope = IUFindOnSwitchIndex(&ActiveTemperatureSlopeSP);
1660 // current slope Selection will be either 1 or 2
1661 // Need to add 1 to array index, as it starts at 0
1662 current_slope++;
1663 IUUpdateSwitch(&ActiveTemperatureSlopeSP, states, names, n);
1664 int target_slope = IUFindOnSwitchIndex(&ActiveTemperatureSlopeSP);
1665 // target slope Selection will be either 1 or 2
1666 // Need to add 1 to array index, as it starts at 0
1667 target_slope++;
1668 if (current_slope == target_slope)
1669 {
1670 ActiveTemperatureSlopeSP.s = IPS_OK;
1671 IDSetSwitch(&ActiveTemperatureSlopeSP, nullptr);
1672 }
1673
1674 rc = setActiveTemperatureSlope(target_slope);
1675
1676 if (rc == false)
1677 {
1678 current_slope--;
1679 IUResetSwitch(&ActiveTemperatureSlopeSP);
1680 ActiveTemperatureSlopeS[current_slope].s = ISS_ON;
1681 ActiveTemperatureSlopeSP.s = IPS_ALERT;
1682 IDSetSwitch(&ActiveTemperatureSlopeSP, nullptr);
1683 return false;
1684 }
1685
1686 ActiveTemperatureSlopeSP.s = IPS_OK;
1687 IDSetSwitch(&ActiveTemperatureSlopeSP, nullptr);
1688 return true;
1689 }
1690
1691 // Slope 1 direction - either 0 or 1
1692 if (!strcmp(Slope1DirSP.name, name))
1693 {
1694 bool rc = false;
1695 int current_slope_dir1 = IUFindOnSwitchIndex(&Slope1DirSP);
1696 // current slope 1 Direction will be either 0 or 1
1697
1698 IUUpdateSwitch(&Slope1DirSP, states, names, n);
1699 int target_slope_dir1 = IUFindOnSwitchIndex(&Slope1DirSP);
1700 // target slope Selection will be either 0 or 1
1701
1702 if (current_slope_dir1 == target_slope_dir1)
1703 {
1704 Slope1DirSP.s = IPS_OK;
1705 IDSetSwitch(&Slope1DirSP, nullptr);
1706 }
1707
1708 rc = setSlope1Dir(target_slope_dir1);
1709
1710 if (rc == false)
1711 {
1712 IUResetSwitch(&Slope1DirSP);
1713 Slope1DirS[current_slope_dir1].s = ISS_ON;
1714 Slope1DirSP.s = IPS_ALERT;
1715 IDSetSwitch(&Slope1DirSP, nullptr);
1716 return false;
1717 }
1718
1719 Slope1DirSP.s = IPS_OK;
1720 IDSetSwitch(&Slope1DirSP, nullptr);
1721 return true;
1722 }
1723 }
1724
1725 // Slope 2 direction - either 0 or 1
1726 if (!strcmp(Slope2DirSP.name, name))
1727 {
1728 bool rc = false;
1729 int current_slope_dir2 = IUFindOnSwitchIndex(&Slope2DirSP);
1730 // current slope 2 Direction will be either 0 or 1
1731
1732 IUUpdateSwitch(&Slope2DirSP, states, names, n);
1733 int target_slope_dir2 = IUFindOnSwitchIndex(&Slope2DirSP);
1734 // target slope 2 Selection will be either 0 or 1
1735
1736 if (current_slope_dir2 == target_slope_dir2)
1737 {
1738 Slope2DirSP.s = IPS_OK;
1739 IDSetSwitch(&Slope2DirSP, nullptr);
1740 }
1741
1742 rc = setSlope2Dir(target_slope_dir2);
1743
1744 if (rc == false)
1745 {
1746 IUResetSwitch(&Slope2DirSP);
1747 Slope2DirS[current_slope_dir2].s = ISS_ON;
1748 Slope2DirSP.s = IPS_ALERT;
1749 IDSetSwitch(&Slope2DirSP, nullptr);
1750 return false;
1751 }
1752
1753 Slope2DirSP.s = IPS_OK;
1754 IDSetSwitch(&Slope2DirSP, nullptr);
1755 return true;
1756 }
1757
1758 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
1759}
1760
1761//
1762// Process client new number
1763//
1764bool Lakeside::ISNewNumber (const char * dev, const char * name, double values[], char * names[], int n)
1765{
1766 int i = 0;
1767
1768 if(strcmp(dev, getDeviceName()) == 0)
1769 {
1770 // // max travel - read only
1771 // if (!strcmp (name, MaxTravelNP.name))
1772 // {
1773 // IUUpdateNumber(&MaxTravelNP, values, names, n);
1774 // MaxTravelNP.s = IPS_OK;
1775 // IDSetNumber(&MaxTravelNP, nullptr);
1776 // return true;
1777 // }
1778
1779 // Backlash compensation
1780 // if (!strcmp (name, FocusBacklashNP.name))
1781 // {
1782 // int new_back = 0 ;
1783 // int nset = 0;
1784
1785 // for (nset = i = 0; i < n; i++)
1786 // {
1787 // //Find numbers with the passed names in SetFocusBacklashNP property
1788 // INumber * eqp = IUFindNumber (&FocusBacklashNP, names[i]);
1789
1790 // //If the number found is Backlash (FocusBacklashN[0]) then process it
1791 // if (eqp == &FocusBacklashN[0])
1792 // {
1793
1794 // new_back = (values[i]);
1795
1796 // // limits
1797 // nset += new_back >= -0xff && new_back <= 0xff;
1798 // }
1799 // if (nset == 1)
1800 // {
1801
1802 // // Set the Lakeside state to BUSY
1803 // FocusBacklashNP.s = IPS_BUSY;
1804 // IDSetNumber(&FocusBacklashNP, nullptr);
1805
1806 // if( !setBacklash(new_back))
1807 // {
1808
1809 // FocusBacklashNP.s = IPS_IDLE;
1810 // IDSetNumber(&FocusBacklashNP, "Setting new backlash failed.");
1811
1812 // return false ;
1813 // }
1814
1815 // FocusBacklashNP.s = IPS_OK;
1816 // FocusBacklashNP[0].setValue(new_back);
1817 // IDSetNumber(&FocusBacklashNP, nullptr);
1818
1819 // return true;
1820 // }
1821 // else
1822 // {
1823
1824 // FocusBacklashNP.s = IPS_IDLE;
1825 // IDSetNumber(&FocusBacklashNP, "Need exactly one parameter.");
1826
1827 // return false ;
1828 // }
1829
1830 // }
1831 // }
1832
1833 // Step size - read only
1834 if (!strcmp (name, StepSizeNP.name))
1835 {
1836 IUUpdateNumber(&StepSizeNP, values, names, n);
1837 StepSizeNP.s = IPS_OK;
1838 IDSetNumber(&StepSizeNP, nullptr);
1839 return true;
1840 }
1841
1842 // Slope 1 Increments
1843 if (!strcmp (name, Slope1IncNP.name))
1844 {
1845 int new_Slope1Inc = 0 ;
1846 int nset = 0;
1847
1848 for (nset = i = 0; i < n; i++)
1849 {
1850 //Find numbers with the passed names in SetSlope1IncNP property
1851 INumber * eqp = IUFindNumber (&Slope1IncNP, names[i]);
1852
1853 //If the number found is Slope1Inc (Slope1IncN[0]) then process it
1854 if (eqp == &Slope1IncN[0])
1855 {
1856
1857 new_Slope1Inc = (values[i]);
1858
1859 // limits
1860 nset += new_Slope1Inc >= -0xff && new_Slope1Inc <= 0xff;
1861 }
1862 if (nset == 1)
1863 {
1864
1865 // Set the Lakeside state to BUSY
1866 Slope1IncNP.s = IPS_BUSY;
1867 IDSetNumber(&Slope1IncNP, nullptr);
1868
1869 if( !setSlope1Inc(new_Slope1Inc))
1870 {
1871
1872 Slope1IncNP.s = IPS_IDLE;
1873 IDSetNumber(&Slope1IncNP, "Setting new Slope1 increment failed.");
1874
1875 return false ;
1876 }
1877
1878 Slope1IncNP.s = IPS_OK;
1879 Slope1IncN[0].value = new_Slope1Inc;
1880 IDSetNumber(&Slope1IncNP, nullptr) ;
1881
1882 return true;
1883 }
1884 else
1885 {
1886
1887 Slope1IncNP.s = IPS_IDLE;
1888 IDSetNumber(&Slope1IncNP, "Need exactly one parameter.");
1889
1890 return false ;
1891 }
1892
1893 }
1894 }
1895
1896 // Slope 2 Increments
1897 if (!strcmp (name, Slope2IncNP.name))
1898 {
1899 int new_Slope2Inc = 0 ;
1900 int nset = 0;
1901
1902 for (nset = i = 0; i < n; i++)
1903 {
1904 //Find numbers with the passed names in SetSlope2IncNP property
1905 INumber * eqp = IUFindNumber (&Slope2IncNP, names[i]);
1906
1907 //If the number found is Slope2Inc (Slope2IncN[0]) then process it
1908 if (eqp == &Slope2IncN[0])
1909 {
1910
1911 new_Slope2Inc = (values[i]);
1912
1913 // limits
1914 nset += new_Slope2Inc >= -0xff && new_Slope2Inc <= 0xff;
1915 }
1916 if (nset == 1)
1917 {
1918
1919 // Set the Lakeside state to BUSY
1920 Slope2IncNP.s = IPS_BUSY;
1921 IDSetNumber(&Slope2IncNP, nullptr);
1922
1923 if( !setSlope2Inc(new_Slope2Inc))
1924 {
1925
1926 Slope2IncNP.s = IPS_IDLE;
1927 IDSetNumber(&Slope2IncNP, "Setting new Slope2 increment failed.");
1928
1929 return false ;
1930 }
1931
1932 Slope2IncNP.s = IPS_OK;
1933 Slope2IncN[0].value = new_Slope2Inc;
1934 IDSetNumber(&Slope2IncNP, nullptr);
1935
1936 return true;
1937 }
1938 else
1939 {
1940
1941 Slope2IncNP.s = IPS_IDLE;
1942 IDSetNumber(&Slope2IncNP, "Need exactly one parameter.");
1943
1944 return false ;
1945 }
1946
1947 }
1948 }
1949
1950 // Slope 1 Deadband
1951 if (!strcmp (name, Slope1DeadbandNP.name))
1952 {
1953 int new_Slope1Deadband = 0 ;
1954 int nset = 0;
1955
1956 for (nset = i = 0; i < n; i++)
1957 {
1958 //Find numbers with the passed names in SetSlope1DeadbandNP property
1959 INumber * eqp = IUFindNumber (&Slope1DeadbandNP, names[i]);
1960
1961 //If the number found is Slope1Deadband (Slope1DeadbandN[0]) then process it
1962 if (eqp == &Slope1DeadbandN[0])
1963 {
1964
1965 new_Slope1Deadband = (values[i]);
1966
1967 // limits
1968 nset += new_Slope1Deadband >= -0xff && new_Slope1Deadband <= 0xff;
1969 }
1970 if (nset == 1)
1971 {
1972
1973 // Set the Lakeside state to BUSY
1974 Slope1DeadbandNP.s = IPS_BUSY;
1975 IDSetNumber(&Slope1DeadbandNP, nullptr);
1976
1977 if( !setSlope1Deadband(new_Slope1Deadband))
1978 {
1979
1980 Slope1DeadbandNP.s = IPS_IDLE;
1981 IDSetNumber(&Slope1DeadbandNP, "Setting new Slope 1 Deadband failed.");
1982
1983 return false ;
1984 }
1985
1986 Slope1DeadbandNP.s = IPS_OK;
1987 Slope1DeadbandN[0].value = new_Slope1Deadband;
1988 IDSetNumber(&Slope1DeadbandNP, nullptr) ;
1989
1990 return true;
1991 }
1992 else
1993 {
1994
1995 Slope1DeadbandNP.s = IPS_IDLE;
1996 IDSetNumber(&Slope1DeadbandNP, "Need exactly one parameter.");
1997
1998 return false ;
1999 }
2000
2001 }
2002 }
2003
2004 // Slope 2 Deadband
2005 if (!strcmp (name, Slope2DeadbandNP.name))
2006 {
2007 int new_Slope2Deadband = 0 ;
2008 int nset = 0;
2009
2010 for (nset = i = 0; i < n; i++)
2011 {
2012 //Find numbers with the passed names in SetSlope2DeadbandNP property
2013 INumber * eqp = IUFindNumber (&Slope2DeadbandNP, names[i]);
2014
2015 //If the number found is Slope2Deadband (Slope2DeadbandN[0]) then process it
2016 if (eqp == &Slope2DeadbandN[0])
2017 {
2018
2019 new_Slope2Deadband = (values[i]);
2020
2021 // limits
2022 nset += new_Slope2Deadband >= -0xff && new_Slope2Deadband <= 0xff;
2023 }
2024 if (nset == 1)
2025 {
2026
2027 // Set the Lakeside state to BUSY
2028 Slope2DeadbandNP.s = IPS_BUSY;
2029 IDSetNumber(&Slope2DeadbandNP, nullptr);
2030
2031 if( !setSlope2Deadband(new_Slope2Deadband))
2032 {
2033
2034 Slope2DeadbandNP.s = IPS_IDLE;
2035 IDSetNumber(&Slope2DeadbandNP, "Setting new Slope 2 Deadband failed.");
2036
2037 return false ;
2038 }
2039
2040 Slope2DeadbandNP.s = IPS_OK;
2041 Slope2DeadbandN[0].value = new_Slope2Deadband;
2042 IDSetNumber(&Slope2DeadbandNP, nullptr) ;
2043
2044 return true;
2045 }
2046 else
2047 {
2048
2049 Slope2DeadbandNP.s = IPS_IDLE;
2050 IDSetNumber(&Slope2DeadbandNP, "Need exactly one parameter.");
2051
2052 return false ;
2053 }
2054
2055 }
2056 }
2057
2058 // Slope 1 Period Minutes
2059 if (!strcmp (name, Slope1PeriodNP.name))
2060 {
2061 int new_Slope1Period = 0 ;
2062 int nset = 0;
2063
2064 for (nset = i = 0; i < n; i++)
2065 {
2066 //Find numbers with the passed names in SetSlope1PeriodNP property
2067 INumber * eqp = IUFindNumber (&Slope1PeriodNP, names[i]);
2068
2069 //If the number found is Slope1Period (Slope1PeriodN[0]) then process it
2070 if (eqp == &Slope1PeriodN[0])
2071 {
2072
2073 new_Slope1Period = (values[i]);
2074
2075 // limits
2076 nset += new_Slope1Period >= -0xff && new_Slope1Period <= 0xff;
2077 }
2078 if (nset == 1)
2079 {
2080
2081 // Set the Lakeside state to BUSY
2082 Slope1PeriodNP.s = IPS_BUSY;
2083 IDSetNumber(&Slope1PeriodNP, nullptr);
2084
2085 if( !setSlope1Period(new_Slope1Period))
2086 {
2087
2088 Slope1PeriodNP.s = IPS_IDLE;
2089 IDSetNumber(&Slope1PeriodNP, "Setting new Slope 1 Period failed.");
2090
2091 return false ;
2092 }
2093
2094 Slope1PeriodNP.s = IPS_OK;
2095 Slope1PeriodN[0].value = new_Slope1Period;
2096 IDSetNumber(&Slope1PeriodNP, nullptr);
2097
2098 return true;
2099 }
2100 else
2101 {
2102
2103 Slope1PeriodNP.s = IPS_IDLE;
2104 IDSetNumber(&Slope1PeriodNP, "Need exactly one parameter.");
2105
2106 return false ;
2107 }
2108
2109 }
2110 }
2111
2112 // Slope 2 Period Minutes
2113 if (!strcmp (name, Slope2PeriodNP.name))
2114 {
2115 int new_Slope2Period = 0 ;
2116 int nset = 0;
2117
2118 for (nset = i = 0; i < n; i++)
2119 {
2120 //Find numbers with the passed names in SetSlope2PeriodNP property
2121 INumber * eqp = IUFindNumber (&Slope2PeriodNP, names[i]);
2122
2123 //If the number found is Slope2Period (Slope2PeriodN[0]) then process it
2124 if (eqp == &Slope2PeriodN[0])
2125 {
2126
2127 new_Slope2Period = (values[i]);
2128
2129 // limits
2130 nset += new_Slope2Period >= -0xff && new_Slope2Period <= 0xff;
2131 }
2132 if (nset == 1)
2133 {
2134
2135 // Set the Lakeside state to BUSY
2136 Slope2PeriodNP.s = IPS_BUSY;
2137 IDSetNumber(&Slope2PeriodNP, nullptr);
2138
2139 if( !setSlope2Period(new_Slope2Period))
2140 {
2141
2142 Slope2PeriodNP.s = IPS_IDLE;
2143 IDSetNumber(&Slope2PeriodNP, "Setting new Slope 2 Period failed.");
2144
2145 return false ;
2146 }
2147
2148 Slope2PeriodNP.s = IPS_OK;
2149 Slope2PeriodN[0].value = new_Slope2Period;
2150 IDSetNumber(&Slope2PeriodNP, nullptr);
2151
2152 return true;
2153 }
2154 else
2155 {
2156
2157 Slope2PeriodNP.s = IPS_IDLE;
2158 IDSetNumber(&Slope2PeriodNP, "Need exactly one parameter.");
2159
2160 return false ;
2161 }
2162
2163 }
2164 }
2165 }
2166
2167 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
2168}
2169
2170//
2171// Get focus paraameters
2172//
2173void Lakeside::GetFocusParams ()
2174{
2175 if (updatePosition())
2177
2178 if (updateTemperature())
2179 IDSetNumber(&TemperatureNP, nullptr);
2180
2181 // This is currently the only time Kelvin is read - just a nice to have
2182 if (updateTemperatureK())
2183 IDSetNumber(&TemperatureKNP, nullptr);
2184
2185 if (updateBacklash())
2187
2188 if (updateMaxTravel())
2190
2191 if (updateStepSize())
2192 IDSetNumber(&StepSizeNP, nullptr);
2193
2194 if (updateMoveDirection())
2196
2197 if (updateSlope1Inc())
2198 IDSetNumber(&Slope1IncNP, nullptr);
2199
2200 if (updateSlope2Inc())
2201 IDSetNumber(&Slope2IncNP, nullptr);
2202
2203 if (updateSlope1Dir())
2204 IDSetSwitch(&Slope1DirSP, nullptr);
2205
2206 if (updateSlope2Dir())
2207 IDSetSwitch(&Slope2DirSP, nullptr);
2208
2209 if (updateSlope1Deadband())
2210 IDSetNumber(&Slope1DeadbandNP, nullptr);
2211
2212 if (updateSlope2Deadband())
2213 IDSetNumber(&Slope2DeadbandNP, nullptr);
2214
2215 if (updateSlope1Period())
2216 IDSetNumber(&Slope1PeriodNP, nullptr);
2217
2218 if (updateSlope1Period())
2219 IDSetNumber(&Slope2PeriodNP, nullptr);
2220
2221}
2222
2224{
2225 return MoveAbsFocuser(dir == FOCUS_INWARD ? FocusAbsPosNP[0].getValue() - ticks : FocusAbsPosNP[0].getValue() + ticks);
2226}
2227
2228//
2229// Main Lakeside Absolute movement routine
2230//
2232{
2233 targetPos = targetTicks;
2234 bool rc = false;
2235
2236 rc = gotoPosition(targetPos);
2237
2238 return (rc ? IPS_BUSY : IPS_ALERT);
2239}
2240
2241//
2242// Main timer hit routine
2243//
2245{
2246 bool IsMoving = false;
2247 int rc = -1;
2248
2249 if (isConnected() == false)
2250 {
2252 return;
2253 }
2254
2255 // focuser supposedly moving...
2257 {
2258 // Get actual status from focuser
2259 // Note: GetLakesideStatus sends position count when moving.
2260 // Status returns IMoving if moving
2261 IsMoving = GetLakesideStatus();
2262 if ( IsMoving )
2263 {
2264 // GetLakesideStatus() shows position as it is moving
2265 LOG_DEBUG("Focuser is in motion...");
2266 }
2267 else
2268 {
2269 // no longer moving, so reset state to IPS_OK or IDLE?
2270 // IPS_OK turns light green
2272 // update position
2273 // This is necessary in case user clicks short step moves in quick succession
2274 // Lakeside will abort move if command received during move
2275 rc = updatePosition();
2277 LOGF_INFO("Focuser reached requested position %.f", FocusAbsPosNP[0].getValue());
2278 }
2279 }
2280
2281 // focuser not moving, get temperature updates instead
2283 {
2284 // Get a temperature
2285 rc = updateTemperature();
2286 if (rc && fabs(lastTemperature - TemperatureN[0].value) > TEMPERATURE_THRESHOLD)
2287 {
2288 IDSetNumber(&TemperatureNP, nullptr);
2289 lastTemperature = TemperatureN[0].value;
2290 }
2291 }
2292
2293 // IPS_ALERT - any alert situation generated
2294 // if ( FocusAbsPosNP.getState() == IPS_ALERT )
2295 // {
2296 // LOG_DEBUG("TimerHit: Focuser state = IPS_ALERT");
2297 // }
2298
2300
2301}
2302
2303//
2304// This will check the status is the focuser - used to check if moving
2305//
2306// Returns Pnnnnn# : Focuser Moving : return true
2307// empty (time out) : Focuser Idle - NOT moving : return false
2308// Returns DONE# : Focuser Finished moving : return false
2309// Returns OK# : Focuser NOT moving (catchall) : return false
2310bool Lakeside::GetLakesideStatus()
2311{
2312 int rc = -1, nbytes_read = 0, count_timeouts = 1, pos = 0;
2313 char errstr[MAXRBUF];
2314 char resp[LAKESIDE_LEN] = {0};
2315 bool read_buffer = true;
2316 char buffer_response = '?';
2317
2318 // read buffer up to LAKESIDE_TIMEOUT_RETRIES times
2319 while (read_buffer)
2320 {
2321 //strcpy(resp," ");
2322 memset(resp, 0, sizeof(resp));
2323 // read until 0x23 (#) received
2324 if ( (rc = tty_read_section(PortFD, resp, 0x23, LAKESIDE_TIMEOUT, &nbytes_read)) != TTY_OK)
2325 {
2326 // Retry LAKESIDE_TIMEOUT_RETRIES times to make sure focuser
2327 // is not in between status returns
2328 count_timeouts++;
2329 LOGF_DEBUG("GetLakesideStatus: read buffer retry attempts : %d, error=%s", count_timeouts, errstr);
2330
2331 if (count_timeouts > LAKESIDE_TIMEOUT_RETRIES)
2332 {
2333 tty_error_msg(rc, errstr, MAXRBUF);
2334 LOGF_DEBUG("GetLakesideStatus: Timeout limit (%d) reached reading buffer. Error - %s", LAKESIDE_TIMEOUT_RETRIES, errstr);
2335
2336 // force a get focuser position update
2337 rc = updatePosition();
2338
2339 // return false as focuser is NOT known to be moving
2340 return false;
2341 } // if (count_timeouts > LAKESIDE_TIMEOUT_RETRIES)
2342 }
2343 else
2344 read_buffer = false; // break out of loop as buffer has been read
2345 } // end while
2346
2347 // At this point, something has been returned from the buffer
2348 // Therefore, decode response
2349
2350 LOGF_DEBUG("GetLakesideStatus: Read buffer contains : %s", resp);
2351
2352 // decode the contents of the buffer (Temp & Pos are also updated)
2353 buffer_response = DecodeBuffer(resp);
2354
2355 // If DONE# then focuser has finished a move, so get position
2356 if ( buffer_response == 'D' )
2357 {
2358 LOG_DEBUG("GetLakesideStatus: Found DONE# after move request");
2359
2360 // update the current position
2361 rc = updatePosition();
2362
2363 // IPS_IDLE turns off light, IPS_OK turns light green
2365
2366 // return false as focuser is not known to be moving
2367 return false;
2368 }
2369
2370 // If focuser moving > 200 steps, DecodeBuffer returns 'P'
2371 // & updates position
2372 if ( buffer_response == 'P' )
2373 {
2374 // get step position for update message
2375 rc = sscanf(resp, "P%5d#", &pos);
2376 LOGF_INFO("Focuser Moving... position : %d", pos);
2377 // Update current position
2378 FocusAbsPosNP[0].setValue(pos);
2380
2381 // return true as focuser IS moving
2382 return true;
2383 }
2384
2385 // Possible that Temperature response still in the buffer?
2386 if ( buffer_response == 'T' )
2387 {
2388 LOGF_DEBUG("GetLakesideStatus: Temperature status response found - %s", resp);
2389 // return false as focuser is not known to be moving
2390
2391 // IPS_IDLE turns off light, IPS_OK turns light green
2393
2394 return false;
2395 }
2396
2397 // Possible that Temperature in K response still in the buffer?
2398 if ( buffer_response == 'K' )
2399 {
2400 LOGF_DEBUG("GetLakesideStatus: Temperature in K status response found - %s", resp);
2401 // return false as focuser is not known to be moving
2402
2403 // IPS_IDLE turns off light, IPS_OK turns light green
2405
2406 return false;
2407 }
2408
2409 // At this point, something else is returned
2410 LOGF_DEBUG("GetLakesideStatus: Unknown response from buffer read : (%s)", resp);
2412
2413 // return false as focuser is not known to be moving
2414 return false;
2415
2416}
2417
2418//
2419// send abort command
2420//
2422{
2423 int rc = -1;
2424 char errstr[MAXRBUF];
2425 char cmd[] = "CH#";
2426
2427 if (SendCmd(cmd))
2428 {
2429 // IPS_IDLE turns off light, IPS_OK turns light green
2432 LOG_INFO("Focuser Abort Sent");
2433 return true;
2434 }
2435 else
2436 {
2437 tty_error_msg(rc, errstr, MAXRBUF);
2438 LOGF_ERROR("AbortFocuser: Write command (%s) failed - %s", cmd, errstr);
2439 return false;
2440 }
2441}
2442
2446void Lakeside::hexDump(char * buf, const char * data, int size)
2447{
2448 for (int i = 0; i < size; i++)
2449 sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
2450
2451 if (size > 0)
2452 buf[3 * size - 1] = '\0';
2453}
2454
2455
2456// End Lakeside Focuser
virtual const char * port()
bool isConnected() const
virtual bool Disconnect()
Disconnect from device.
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
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.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
void addDebugControl()
Add Debug control to the driver.
INDI::PropertySwitch FocusReverseSP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusMaxPosNP
INDI::PropertyNumber FocusBacklashNP
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 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...
Connection::Serial * serialConnection
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void setPermission(IPerm permission)
virtual IPState MoveAbsFocuser(uint32_t ticks) override
MoveFocuser the focuser to an absolute position.
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,...
Definition lakeside.cpp:73
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
const char * getDefaultName() override
Definition lakeside.cpp:279
virtual bool SetFocuserBacklashEnabled(bool enabled) override
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
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 updateProperties() override
Updates the state and definition of properties based on the current connection status....
Definition lakeside.cpp:177
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
Definition lakeside.cpp:274
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 SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
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
int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
Establishes a tty connection to a terminal device.
Definition indicom.c:1022
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
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 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
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition indidevapi.c:275
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 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
#define INDI_UNUSED(x)
Definition indidevapi.h:132
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.
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
#define LAKESIDE_TIMEOUT
Definition lakeside.cpp:51
#define LAKESIDE_LEN
Definition lakeside.cpp:52
#define LAKESIDE_VERSION_MAJOR
Definition lakeside.cpp:34
#define LAKESIDE_VERSION_MINOR
Definition lakeside.cpp:35
#define LAKESIDE_TIMEOUT_RETRIES
Definition lakeside.cpp:57
uint8_t cmd[4]
Definition pwc-ioctl.h:2
One number descriptor.
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369