Instrument Neutral Distributed Interface INDI 2.2.5
integra.cpp
Go to the documentation of this file.
1/*
2 Gemini Telescope Design Integra85 Focusing Rotator.
3 Copyright (C) 2017-2021 Hans Lambermont
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#include "integra.h"
21
22#include "indicom.h"
24
25#include <cmath>
26#include <cstring>
27#include <memory>
28#include <termios.h>
29
30#define INTEGRA_TIMEOUT_IN_S 5
31#define INTEGRA_TEMPERATURE_LOOP_SKIPS 60
32#define INTEGRA_TEMPERATURE_TRESHOLD_IN_C 0.1
33#define INTEGRA_ROUNDING_FUDGE 0.001
34
35#define ROTATOR_TAB "Rotator"
36#define SETTINGS_TAB "Settings"
37
38std::unique_ptr<Integra> integra(new Integra());
39
40typedef struct COMMANDDESC
41{
42 const char cmd[12];
43 char ret[2][3];
45
46static const COMMANDDESC IntegraProtocol[] =
47{
48 { "@SW%d,0\r\n", { "S", "SW"}},
49 { "@CS%d,0\r\n", { "C", "CS"}},
50 { "@CE%d,0\r\n", { "CE", "CE"}},
51 { "@CR%d,0\r\n", { "CR", "CR"}},
52 { "@TR\r\n", { "T", "TR"}},
53 { "@PW%d,0\r\n", { "P", "PW"}},
54 { "@PR%d,0\r\n", { "P", "PR"}},
55 { "@MI%d,%d\r\n", { "M", "MI"}},
56 { "@MO%d,%d\r\n", { "M", "MO"}},
57 { "@RR%d,0\r\n", { "R", "RR"}},
58 { "X\r\n", { "", "X"}},
59 { "@IW%d,0\r\n", { "I", "IW"}},
60 { "@ZW\r\n", { "", "ZW"}}
61};
62
63enum
64{
78};
79
88
90{
92
93 MaxPositionNP[FOCUSER].fill("FOCUSER", "Focuser", "%.f",
95 MaxPositionNP[ROTATOR].fill("ROTATOR", "Rotator", "%.f",
97 MaxPositionNP.fill(getDeviceName(), "MAX_POSITION", "Max position",
99
100 FocusSpeedNP[0].setMin(1);
101 FocusSpeedNP[0].setMax(1);
102 FocusSpeedNP[0].setValue(1);
103
104 // Temperature Sensor
105 SensorNP[SENSOR_TEMPERATURE].fill("TEMPERATURE", "Temperature (C)", "%.2f", -100, 100., 1., 0.);
106 SensorNP.fill(getDeviceName(), "SENSORS", "Sensors", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE );
107
108 // Home Find
109 FindHomeSP[HOMING_IDLE].fill("HOMING_IDLE", "Idle", ISS_ON);
110 FindHomeSP[HOMING_START].fill("HOMING_START", "Start", ISS_OFF);
111 FindHomeSP[HOMING_ABORT].fill("HOMING_ABORT", "Abort", ISS_OFF);
112 FindHomeSP.fill(getDeviceName(), "HOMING", "Home at Center", SETTINGS_TAB, IP_RW,
113 ISR_1OFMANY, 60, IPS_IDLE);
114
115 // Relative and absolute movement
116 FocusAbsPosNP[0].setMin(0);
117 FocusAbsPosNP[0].setMax(MaxPositionNP[FOCUSER].getValue());
118 FocusAbsPosNP[0].setStep(MaxPositionNP[ROTATOR].getValue() / 50.0);
119 FocusAbsPosNP[0].setValue(0);
120
121 FocusRelPosNP[0].setMin(0);
122 FocusRelPosNP[0].setMax((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 2);
123 FocusRelPosNP[0].setStep(FocusRelPosNP[0].getMax() / 100.0);
124 FocusRelPosNP[0].setValue(100);
125
127
128 // Rotator Ticks
129 RotatorAbsPosNP[0].fill("ROTATOR_ABSOLUTE_POSITION", "Ticks", "%.f", 0., 61802., 1., 0.);
130 RotatorAbsPosNP.fill(getDeviceName(), "ABS_ROTATOR_POSITION", "Goto", ROTATOR_TAB, IP_RW,
131 0, IPS_IDLE );
132 RotatorAbsPosNP[0].setMin(0);
133
135
136 // The device uses an Arduino which shows up as /dev/ttyACM0 on Linux
137 // An udev rule example is provided that can create a more logical name like /dev/integra_focusing_rotator0
138 serialConnection->setDefaultPort("/dev/ttyACM0");
139 // Set mandatory baud speed. The device does not work with anything else.
141
143
144 return true;
145}
146
148{
150
151 if (isConnected())
152 {
153 defineProperty(MaxPositionNP);
154 // Focus
155 defineProperty(SensorNP);
156 defineProperty(FindHomeSP);
157
158 // Rotator
160 defineProperty(RotatorAbsPosNP);
161 }
162 else
163 {
164 deleteProperty(MaxPositionNP);
165
166 // Focus
167 deleteProperty(SensorNP);
168 deleteProperty(FindHomeSP);
169
170 // Rotator
172 deleteProperty(RotatorAbsPosNP);
173 }
174
175 return true;
176}
177
178// This is called from Serial::processHandshake
180{
181 bool rcFirmware = getFirmware();
182 bool rcMaxPositionMotorFocus = getMaxPosition(MOTOR_FOCUS);
183 bool rcMaxPositionMotorRotator = getMaxPosition(MOTOR_ROTATOR);
184 bool rcType = getFocuserType(); // keep this after the getMaxPositions
185 if (rcFirmware && rcMaxPositionMotorFocus && rcMaxPositionMotorRotator && rcType)
186 {
187 return true;
188 }
189
190 LOG_ERROR("Error retrieving data from Integra, please ensure Integra controller is powered, port choice is correct and baud rate is 115200.");
191 return false;
192}
193
195{
196 return "Integra85";
197}
198
199void Integra::cleanPrint(const char *cmd, char *cleancmd)
200{
201 size_t len = strlen(cmd);
202 int j = 0;
203 for (size_t i = 0; i <= len; i++)
204 {
205 if (cmd[i] == 0xA)
206 {
207 cleancmd[j++] = '\\';
208 cleancmd[j++] = 'n';
209 }
210 else if (cmd[i] == 0xD)
211 {
212 cleancmd[j++] = '\\';
213 cleancmd[j++] = 'r';
214 }
215 else
216 {
217 cleancmd[j++] = cmd[i];
218 }
219 }
220}
221
222// Called from our ::Handshake
223bool Integra::getFirmware()
224{
225 // two firmware versions (in ISO date format) : 2017-01-25 and 2017-12-20
226 // still no direct command to retrieve the version but the protocol
227 // has changed. the later version is returning the full command prefix as response prefix
228 // version 2017-01-25 : cmd RR1,0 => R188600#
229 // version 2017-12-20 : cmd RR1.0 => RR188600#
230
231 // to identify the version we try both protocols, newest first.
232 if ( genericIntegraCommand(__FUNCTION__, "@RR1,0\r\n", "RR", nullptr))
233 {
234 LOGF_INFO("Firmware version is %s", "2017-12-20");
235 this->firmwareVersion = VERSION_20171220;
236 }
237 else if ( genericIntegraCommand(__FUNCTION__, "@RR1,0\r\n", "R", nullptr))
238 {
239 LOGF_INFO("Firmware version is %s, note: there is a firmware upgrade available.", "2017-01-25");
240 this->firmwareVersion = VERSION_20170125;
241 }
242 else
243 {
244 LOG_ERROR("Cannot determine firmware version, there may be a firmware upgrade available.");
245 return false; // cannot retrieve firmware session.
246 }
247
248 return true;
249}
250
251// Called from our ::Handshake
252bool Integra::getFocuserType()
253{
254 int focus_max = int(FocusAbsPosNP[0].getMax());
255 int rotator_max = int(RotatorAbsPosNP[0].getMax());
256 if (focus_max != wellKnownIntegra85FocusMax)
257 {
258 LOGF_ERROR("This is no Integra85 because focus max position %d != %d, trying to continue still", focus_max,
260 // return false;
261 }
262 if (rotator_max != wellKnownIntegra85RotateMax)
263 {
264 LOGF_ERROR("This is no Integra85 because rotator max position %d != %d, trying to continue still", rotator_max,
266 // return false;
267 }
268
269 char resp[64] = "Integra85"; // TODO this is an assumption until the device can report its type
270 LOGF_INFO("Focuser Type %s", resp);
271 if (strcmp(resp, "Integra85") == 0)
272 {
273 // RotatorAbsPosN[0].max is already set by getMaxPosition
274 rotatorTicksPerDegree = RotatorAbsPosNP[0].getMax() / 360.0;
275 rotatorDegreesPerTick = 360.0 / RotatorAbsPosNP[0].getMax();
276 }
277
278 return true;
279}
280
281bool Integra::relativeGotoMotor(MotorType type, int32_t relativePosition)
282{
283 int motorMoveCommand;
284
285 LOGF_DEBUG("Start relativeGotoMotor to %d ...", relativePosition);
286 if (relativePosition > 0)
287 motorMoveCommand = move_mot_out;
288 else
289 motorMoveCommand = move_mot_in;
290
291 if (type == MOTOR_FOCUS)
292 {
293 if (relativePosition > 0)
294 {
295 if (lastFocuserPosition + relativePosition > MaxPositionNP[MOTOR_FOCUS].getValue())
296 {
297 int newRelativePosition = (int32_t)floor(MaxPositionNP[MOTOR_FOCUS].getValue()) - lastFocuserPosition;
298 LOGF_INFO("Focus position change %d clipped to %d to stay at MAX %d",
299 relativePosition, newRelativePosition, MaxPositionNP[MOTOR_FOCUS].getValue());
300 relativePosition = newRelativePosition;
301 }
302 }
303 else
304 {
305 if ((int32_t )lastFocuserPosition + relativePosition < 0)
306 {
307 int newRelativePosition = -lastFocuserPosition;
308 LOGF_INFO("Focus position change %d clipped to %d to stay at MIN 0",
309 relativePosition, newRelativePosition);
310 relativePosition = newRelativePosition;
311 }
312 }
313 }
314 else if (type == MOTOR_ROTATOR)
315 {
316 if (relativePosition > 0)
317 {
318 if (lastRotatorPosition + relativePosition > MaxPositionNP[MOTOR_ROTATOR].getValue())
319 {
320 int newRelativePosition = (int32_t)floor(MaxPositionNP[MOTOR_ROTATOR].getValue()) - lastRotatorPosition;
321 LOGF_INFO("Rotator position change %d clipped to %d to stay at MAX %d",
322 relativePosition, newRelativePosition, MaxPositionNP[MOTOR_ROTATOR].getValue());
323 relativePosition = newRelativePosition;
324 }
325 }
326 else
327 {
328 if (lastRotatorPosition + relativePosition < - MaxPositionNP[MOTOR_ROTATOR].getValue())
329 {
330 int newRelativePosition = - (int32_t)floor(MaxPositionNP[MOTOR_ROTATOR].getValue()) - lastRotatorPosition;
331 LOGF_INFO("Rotator position change %d clipped to %d to stay at MIN %d",
332 relativePosition, newRelativePosition, - MaxPositionNP[MOTOR_ROTATOR].getValue());
333 relativePosition = newRelativePosition;
334 }
335 }
336 }
337
338 return integraMotorSetCommand( __FUNCTION__, motorMoveCommand, type, abs(relativePosition), nullptr);
339}
340
341bool Integra::gotoMotor(MotorType type, int32_t position)
342{
343 LOGF_DEBUG("Start gotoMotor to %d", position);
344 if (type == MOTOR_FOCUS)
345 {
346 return relativeGotoMotor(type, position - lastFocuserPosition);
347 }
348 else if (type == MOTOR_ROTATOR)
349 {
350 return relativeGotoMotor(type, position - lastRotatorPosition);
351 }
352 else
353 {
354 LOGF_ERROR("%s error: MotorType %d is unknown.", __FUNCTION__, type);
355 }
356 return false;
357}
358
359bool Integra::getPosition(MotorType type)
360{
361 char res[16] = {0};
362 if ( !integraMotorGetCommand(__FUNCTION__, get_motstep, type, res ))
363 {
364 return false;
365 }
366 int position = -1e6;
367 position = atoi(res);
368 if (position != -1e6)
369 {
370 if (type == MOTOR_FOCUS)
371 {
372 if (FocusAbsPosNP[0].getValue() != position)
373 {
374 auto position_from = (int) FocusAbsPosNP[0].getValue();
375 int position_to = position;
376 if (haveReadFocusPositionAtLeastOnce)
377 {
378 LOGF_DEBUG("Focus position changed from %d to %d", position_from, position_to);
379 }
380 else
381 {
382 LOGF_DEBUG("Focus position is %d", position_to);
383 }
384 FocusAbsPosNP[0].setValue(position);
385 }
386 }
387 else if (type == MOTOR_ROTATOR)
388 {
389 if (RotatorAbsPosNP[0].getValue() != position)
390 {
391 auto position_from = (int) RotatorAbsPosNP[0].getValue();
392 int position_to = position;
393 if (haveReadRotatorPositionAtLeastOnce)
394 {
395 LOGF_DEBUG("Rotator changed angle from %.2f to %.2f, position from %d to %d",
396 rotatorTicksToDegrees(position_from), rotatorTicksToDegrees(position_to), position_from, position_to);
397 }
398 else
399 {
400 LOGF_DEBUG("Rotator angle is %.2f, position is %d",
401 rotatorTicksToDegrees(position_to), position_to);
402 }
403 RotatorAbsPosNP[0].setValue(position);
404 }
405 }
406 else
407 {
408 LOGF_ERROR("%s error: motor type %d is unknown", __FUNCTION__, type);
409 }
410
411 return true;
412 }
413
414 LOGF_DEBUG("Invalid Position! %d", position);
415 return false;
416}
417
418bool Integra::ISNewSwitch (const char * dev, const char * name, ISState * states, char * names[], int n)
419{
420 if(strcmp(dev, getDeviceName()) == 0)
421 {
422 if (FindHomeSP.isNameMatch(name))
423 {
424 FindHomeSP.update(states, names, n);
425 int index = FindHomeSP.findOnSwitchIndex();
426 switch (index)
427 {
428 case HOMING_IDLE:
429 LOG_INFO("Homing state is IDLE");
430 FindHomeSP[HOMING_IDLE].setState(ISS_ON);
431 FindHomeSP.setState(IPS_OK);
432 break;
433 case HOMING_START:
434 if (findHome())
435 {
436 FindHomeSP.setState(IPS_BUSY);
437 FindHomeSP[HOMING_START].setState(ISS_ON);
439 "Homing process can take up to 2 minutes. You cannot control the unit until the process is fully complete.");
440 }
441 else
442 {
443 FindHomeSP.setState(IPS_ALERT);
444 FindHomeSP[HOMING_START].setState(ISS_OFF);
445 LOG_ERROR("Failed to start homing process.");
446 }
447 break;
448 case HOMING_ABORT:
449 if (abortHome())
450 {
451 FindHomeSP.setState(IPS_IDLE);
452 FindHomeSP[HOMING_ABORT].setState(ISS_ON);
453 LOG_WARN("Homing aborted");
454 }
455 else
456 {
457 FindHomeSP.setState(IPS_ALERT);
458 FindHomeSP[HOMING_ABORT].setState(ISS_OFF);
459 LOG_ERROR("Failed to abort homing process.");
460 }
461 break;
462 default:
463 FindHomeSP.setState(IPS_ALERT);
464 LOG_INFO("Unknown homing index %d");
465 FindHomeSP.apply();
466 return false;
467 }
468 FindHomeSP.apply();
469 return true;
470 }
471 else if (strstr(name, "ROTATOR"))
472 {
473 if (INDI::RotatorInterface::processSwitch(dev, name, states, names, n))
474 return true;
475 }
476 }
477
478 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
479}
480
481bool Integra::ISNewNumber (const char * dev, const char * name, double values[], char * names[], int n)
482{
483 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
484 {
485 if (RotatorAbsPosNP.isNameMatch(name))
486 {
487 RotatorAbsPosNP.setState((gotoMotor(MOTOR_ROTATOR, static_cast<int32_t>(values[0])) ? IPS_BUSY : IPS_ALERT));
488 RotatorAbsPosNP.apply();
489 if (RotatorAbsPosNP.getState() == IPS_BUSY)
490 LOGF_DEBUG("Rotator moving from %d to %.f ticks...", lastRotatorPosition, values[0]);
491 return true;
492 }
493 else if (strstr(name, "ROTATOR"))
494 {
495 if (INDI::RotatorInterface::processNumber(dev, name, values, names, n))
496 return true;
497 }
498 }
499
500 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
501}
502
503IPState Integra::MoveAbsFocuser(uint32_t targetTicks)
504{
505 targetPosition = targetTicks;
506 LOGF_DEBUG("Focuser will move absolute from %d to %d ...", lastFocuserPosition, targetTicks);
507
508 bool rc = false;
509 rc = gotoMotor(MOTOR_FOCUS, targetPosition);
510 if (!rc)
511 return IPS_ALERT;
512
514
515 return IPS_BUSY;
516}
517
519{
520 double newPosition = 0;
521 bool rc = false;
522 LOGF_DEBUG("Focuser will move in direction %d relative %d ticks...", dir, ticks);
523
524 if (dir == FOCUS_INWARD)
525 newPosition = FocusAbsPosNP[0].getValue() - ticks;
526 else
527 newPosition = FocusAbsPosNP[0].getValue() + ticks;
528
529 rc = gotoMotor(MOTOR_FOCUS, newPosition);
530 if (!rc)
531 return IPS_ALERT;
532
533 FocusRelPosNP[0].setValue(ticks);
535
536 return IPS_BUSY;
537}
538
540{
541 if (!isConnected())
542 {
544 return;
545 }
546
547 bool rc = false;
548 bool savePositionsToEEPROM = false;
549
550 // sanity check warning ...
551 if (int(FocusAbsPosNP[0].getMax()) != wellKnownIntegra85FocusMax
552 || int(RotatorAbsPosNP[0].getMax()) != wellKnownIntegra85RotateMax)
553 {
554 LOGF_WARN("Warning: Focus motor max position %d should be %d and Rotator motor max position %d should be %d",
556 int(RotatorAbsPosNP[0].getMax()), wellKnownIntegra85RotateMax);
557 }
558
559 // #1 If we're homing, we check if homing is complete as we cannot check for anything else
560 if (FindHomeSP.getState() == IPS_BUSY)
561 {
562 if (isHomingComplete())
563 {
564 FindHomeSP[HOMING_IDLE].setState(ISS_OFF);
565 FindHomeSP.setState(IPS_OK);
566 FindHomeSP.apply();
567
568 LOG_INFO("Homing is complete");
569 // Next read positions and save to EEPROM :
570 haveReadFocusPositionAtLeastOnce = false;
571 haveReadRotatorPositionAtLeastOnce = false;
572 }
573 else
574 {
575 LOG_DEBUG("Homing");
576 }
577
579 return;
580 }
581
582 // #2 Get Temperature, only read this when no motors are active, and about once per minute
584 && RotatorAbsPosNP.getState() != IPS_BUSY
585 && timeToReadTemperature <= 0)
586 {
587 rc = getTemperature();
588 if ( ! rc)
589 rc = getTemperature();
590 if (rc)
591 {
592 timeToReadTemperature = INTEGRA_TEMPERATURE_LOOP_SKIPS;
593 if (fabs(SensorNP[SENSOR_TEMPERATURE].getValue() - lastTemperature) > INTEGRA_TEMPERATURE_TRESHOLD_IN_C)
594 {
595 lastTemperature = SensorNP[SENSOR_TEMPERATURE].getValue();
596 SensorNP.apply();
597 }
598 }
599 }
600 else
601 {
602 timeToReadTemperature--;
603 }
604
605 // #3 is not used on Integra85
606 // #4 is not used on Integra85
607
608 // #5 Focus Position & Status
609 if (!haveReadFocusPositionAtLeastOnce || FocusAbsPosNP.getState() == IPS_BUSY || FocusRelPosNP.getState() == IPS_BUSY)
610 {
611 if ( ! isMotorMoving(MOTOR_FOCUS))
612 {
613 LOG_DEBUG("Focuser stopped");
616 rc = getPosition(MOTOR_FOCUS);
617 if (rc)
618 {
619 if (FocusAbsPosNP[0].getValue() != lastFocuserPosition)
620 {
621 lastFocuserPosition = FocusAbsPosNP[0].getValue();
624 if (haveReadFocusPositionAtLeastOnce)
625 {
626 LOGF_INFO("Focuser reached requested position %d", lastFocuserPosition);
627 }
628 else
629 {
630 LOGF_INFO("Focuser position is %d", lastFocuserPosition);
631 haveReadFocusPositionAtLeastOnce = true;
632 }
633 savePositionsToEEPROM = true;
634 }
635 }
636 }
637 else
638 {
639 LOG_DEBUG("Focusing");
640 }
641 }
642
643 // #6 Rotator Position & Status
644 if (!haveReadRotatorPositionAtLeastOnce || RotatorAbsPosNP.getState() == IPS_BUSY)
645 {
646 if ( ! isMotorMoving(MOTOR_ROTATOR))
647 {
648 LOG_DEBUG("Rotator stopped");
649 RotatorAbsPosNP.setState(IPS_OK);
651 rc = getPosition(MOTOR_ROTATOR);
652 if (rc)
653 {
654 if (RotatorAbsPosNP[0].getValue() != lastRotatorPosition)
655 {
656 lastRotatorPosition = RotatorAbsPosNP[0].getValue();
657 GotoRotatorNP[0].setValue(rotatorTicksToDegrees(
658 lastRotatorPosition)); //range360(RotatorAbsPosN[0].value / rotatorTicksPerDegree);
659 RotatorAbsPosNP.apply( );
661 if (haveReadRotatorPositionAtLeastOnce)
662 LOGF_INFO("Rotator reached requested angle %.2f, position %d",
663 rotatorTicksToDegrees(lastRotatorPosition), lastRotatorPosition);
664 else
665 {
666 LOGF_INFO("Rotator is at angle %.2f, position %d",
667 rotatorTicksToDegrees(lastRotatorPosition), lastRotatorPosition);
668 haveReadRotatorPositionAtLeastOnce = true;
669 }
670 savePositionsToEEPROM = true;
671 }
672 }
673 }
674 else
675 {
676 LOG_DEBUG("Rotating");
677 }
678 }
679
680 // #7 is not used on Integra85
681
682 if (savePositionsToEEPROM)
683 {
684 saveToEEPROM();
685 }
687}
688
690{
691 return stopMotor(MOTOR_FOCUS);
692}
693
694bool Integra::stopMotor(MotorType type)
695{
696 // TODO (if focuser?) handle CR 2
697 if (integraMotorGetCommand(__FUNCTION__, stop_motor, type, nullptr))
698 {
699 if (type == MOTOR_FOCUS)
700 {
701 haveReadFocusPositionAtLeastOnce = false;
702 }
703 else
704 {
705 haveReadRotatorPositionAtLeastOnce = false;
706 }
707 return true;
708 }
709
710 return false;
711}
712
713bool Integra::isMotorMoving(MotorType type)
714{
715 char res[16] = {0};
716 if ( ! integraGetCommand( __FUNCTION__, is_moving, res))
717 {
718 return false;
719 }
720 if (type == MOTOR_FOCUS)
721 {
722 if (res[0] == '1')
723 {
724 LOG_DEBUG("Focus motor is running");
725 return true;
726 }
727 else
728 {
729 LOG_DEBUG("Focus motor is not running");
730 return false;
731 }
732 }
733 else
734 {
735 // bug, both motors return 1 at res[0] when running
736 // return (res[0] == '2');
737 if (res[0] == '1')
738 {
739 LOG_DEBUG("Rotator motor is running");
740 return true;
741 }
742 else
743 {
744 LOG_DEBUG("Rotator motor is not running");
745 return false;
746 }
747 }
748}
749
750// Called from our ::Handshake
751bool Integra::getMaxPosition(MotorType type)
752{
753 char res[16] = {0};
754 if ( ! integraMotorGetCommand(__FUNCTION__, get_motrange, type, res))
755 {
756 return false;
757 }
758 int position = atoi(res);
759 if (MaxPositionNP[type].getValue() == position)
760 {
761 LOGF_INFO("%s motor max position is %d", (type == MOTOR_FOCUS) ? "Focuser" : "Rotator", position);
762 }
763 else
764 {
765 LOGF_WARN("Updated %s motor max position from %d to %d",
766 (type == MOTOR_FOCUS) ? "Focuser" : "Rotator", MaxPositionNP[type].getValue(), position);
767 MaxPositionNP[type].setValue(position);
768 if (type == MOTOR_FOCUS)
769 {
770 FocusAbsPosNP[0].setMax(MaxPositionNP[type].getValue());
771 }
772 else if (type == MOTOR_ROTATOR)
773 {
774 RotatorAbsPosNP[0].setMax(MaxPositionNP[type].getValue());
775 }
776 else
777 {
778 LOG_ERROR("Unknown Motor type");
779 }
780 }
781 return position > 0; // cannot consider a max position == 0 as a valid max.
782}
783
784bool Integra::saveToEEPROM()
785{
786 return integraGetCommand(__FUNCTION__, EEPROMwrite, nullptr);
787}
788
789bool Integra::getTemperature()
790{
791 char res[16] = {0};
792 if (integraGetCommand(__FUNCTION__, get_temperature, res ) )
793 {
794 SensorNP[SENSOR_TEMPERATURE].setValue(strtod(res, nullptr));
795 return true;
796 }
797 return false;
798}
799
800bool Integra::findHome()
801{
802 return integraMotorGetCommand(__FUNCTION__, calibrate, MOTOR_FOCUS, nullptr);
803}
804
805bool Integra::abortHome()
806{
807 return integraMotorGetCommand(__FUNCTION__, calibrate_interrupt, MOTOR_FOCUS, nullptr);
808}
809
810bool Integra::isHomingComplete()
811{
812 char res[16] = {0};
813 if (integraMotorGetCommand(__FUNCTION__, calibration_state, MOTOR_FOCUS, res))
814 {
815 return (res[0] == '1');
816 }
817 return false;
818}
819
821{
822 Focuser::saveConfigItems(fp);
823 return true;
824}
825
826// Integra position 0..61802 ticks , angle 0..360 deg, position 0 corresponds to 180 deg
827// We need to map the Integra frame to that of the IndiRotatorInterface.
828// INDI rotatorInterface: Only absolute position Rotators are supported.
829// Angle is ranged from 0 to 360 increasing clockwise when looking at the back of the camera.
831{
832 uint32_t p1 = lastRotatorPosition;
833 uint32_t p2 = rotatorDegreesToTicks(angle);
834
835 LOGF_INFO("MoveRotator from %.2f to %.2f degrees, from position %d to %d ...",
836 rotatorTicksToDegrees(lastRotatorPosition), angle, p1, p2);
837 bool rc = relativeGotoMotor(MOTOR_ROTATOR, p2 - p1);
838 if (rc)
839 {
840 RotatorAbsPosNP.setState(IPS_BUSY);
841 RotatorAbsPosNP.apply();
842 return IPS_BUSY;
843 }
844
845 return IPS_ALERT;
846}
847
849{
850 bool rc = stopMotor(MOTOR_ROTATOR);
851 if (rc && RotatorAbsPosNP.getState() != IPS_OK)
852 {
853 RotatorAbsPosNP.setState(IPS_OK);
854 RotatorAbsPosNP.apply();
855 }
856
857 return rc;
858}
859
860uint32_t Integra::rotatorDegreesToTicks(double angle)
861{
862 uint32_t position = 61802 / 2;
863 if (angle >= 0.0 && angle <= 180.0)
864 {
865 position = (uint32_t) lround(61802.0 - (180.0 - angle) / rotatorDegreesPerTick);
866 }
867 else if (angle > 180 && angle <= 360)
868 {
869 position = (uint32_t) lround(61802.0 - (540.0 - angle) / rotatorDegreesPerTick);
870 }
871 else
872 {
873 LOGF_ERROR("%s error: %.2f is out of range", __FUNCTION__, angle);
874 }
875 return position;
876}
877
878double Integra::rotatorTicksToDegrees(uint32_t ticks)
879{
880 double degrees = range360(180.0 + ticks * rotatorDegreesPerTick + INTEGRA_ROUNDING_FUDGE);
881 return degrees;
882}
883
884bool Integra::SyncRotator(double angle)
885{
886 uint32_t position = rotatorDegreesToTicks(angle);
887 if ( integraMotorSetCommand(__FUNCTION__, set_motstep, MOTOR_ROTATOR, position, nullptr ))
888 {
889 haveReadRotatorPositionAtLeastOnce = false;
890 return true;
891 }
892 return false;
893}
894
896{
897 return integraMotorGetCommand(__FUNCTION__, invert_dir, MOTOR_ROTATOR, nullptr);
898}
899
900bool Integra::integraGetCommand( const char *name, int commandIdx, char *returnValueString )
901{
902 char cmd[16] = {0};
903 snprintf(cmd, 16, "%s", IntegraProtocol[commandIdx].cmd);
904 return genericIntegraCommand(name, cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
905}
906
907bool Integra::integraMotorGetCommand( const char *name, int commandIdx, MotorType motor, char *returnValueString )
908{
909 char cmd[16] = {0};
910 snprintf(cmd, 16, IntegraProtocol[commandIdx].cmd, motor + 1);
911 return genericIntegraCommand(name, cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
912}
913
914bool Integra::integraMotorSetCommand(const char *name, int commandIdx, MotorType motor, int value,
915 char *returnValueString )
916{
917 char cmd[16] = {0};
918 snprintf(cmd, 16, IntegraProtocol[commandIdx].cmd, motor + 1, value);
919 return genericIntegraCommand(name, cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
920}
921
922bool Integra::genericIntegraCommand(const char *name, const char *cmd, const char *expectStart, char *returnValueString)
923{
924 char cmdnocrlf[16] = {0};
925 int nbytes_written = 0, nbytes_read = 0, rc = -1;
926 char res[16] = {0};
927 char *correctRes = nullptr;
928 char errstr[MAXRBUF];
929
930 cleanPrint(cmd, cmdnocrlf);
931 LOGF_DEBUG("CMD %s (%s)", name, cmdnocrlf);
932
933 tcflush(PortFD, TCIOFLUSH);
934 if ( (rc = tty_write(PortFD, cmd, (int)strlen(cmd), &nbytes_written)) != TTY_OK)
935 {
936 tty_error_msg(rc, errstr, MAXRBUF);
937 LOGF_ERROR("%s: %s.", name, errstr);
938 return false;
939 }
940
941 if ( (rc = tty_read_section(PortFD, res, '#', INTEGRA_TIMEOUT_IN_S, &nbytes_read)) != TTY_OK)
942 {
943 tty_error_msg(rc, errstr, MAXRBUF);
944 LOGF_ERROR("%s error: %s.", name, errstr);
945 return false;
946 }
947
948 LOGF_DEBUG("RES %s (%s)", name, res);
949
950 // check begin of result string
951 if (expectStart != nullptr)
952 {
953 correctRes = strstr(res, expectStart); // the hw sometimes returns /r or /n at the beginning of the response
954 if (correctRes == nullptr)
955 {
956 LOGF_ERROR("%s error: invalid response (%s)", name, res);
957 return false;
958 }
959 }
960 // check end of result string
961 if (res[nbytes_read - 1] != '#')
962 {
963 LOGF_ERROR("%s error: invalid response 2 (%s)", name, res);
964 return false;
965 }
966 res[nbytes_read - 1] = '\0'; // wipe the #
967
968 if (returnValueString != nullptr && expectStart != nullptr)
969 {
970 size_t expectStrlen = strlen(expectStart);
971 strcpy(returnValueString, correctRes + expectStrlen);
972 }
973
974 return true;
975}
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
void setDefaultPort(const char *port)
setDefaultPort Set default port. Call this function in initProperties() of your driver if you want to...
bool isConnected() const
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
uint32_t getDriverInterface() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusSpeedNP
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 setSupportedConnections(const uint8_t &value)
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
void initProperties(const char *groupName)
Initialize Rotator properties. It is recommended to call this function within initProperties() of you...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
INDI::PropertyNumber GotoRotatorNP
Integra()
Definition integra.cpp:80
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
Definition integra.cpp:89
const char * getDefaultName() override
Definition integra.cpp:194
virtual bool AbortRotator() override
AbortRotator Abort all motion.
Definition integra.cpp:848
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
Definition integra.cpp:895
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...
Definition integra.cpp:481
@ HOMING_START
Definition integra.h:40
@ HOMING_ABORT
Definition integra.h:41
@ HOMING_IDLE
Definition integra.h:39
@ VERSION_20171220
Definition integra.h:31
@ VERSION_20170125
Definition integra.h:31
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
Definition integra.cpp:830
const int wellKnownIntegra85RotateMax
Definition integra.h:35
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...
Definition integra.cpp:418
virtual bool SyncRotator(double angle) override
SyncRotator Set current angle as the supplied angle without moving the rotator.
Definition integra.cpp:884
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition integra.cpp:539
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition integra.cpp:503
virtual bool saveConfigItems(FILE *fp) override
Definition integra.cpp:820
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition integra.cpp:518
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
Definition integra.cpp:179
@ MOTOR_FOCUS
Definition integra.h:29
@ MOTOR_ROTATOR
Definition integra.h:29
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
Definition integra.cpp:147
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition integra.cpp:689
const int wellKnownIntegra85FocusMax
Definition integra.h:34
Provides interface to implement Rotator functionality.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
#define ROTATOR_TAB
Definition gemini.cpp:39
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_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition indicom.c:1321
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
#define MAXRBUF
Definition indidriver.c:52
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
Definition indilogger.h:56
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#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
@ EEPROMwrite
Definition integra.cpp:77
@ get_temperature
Definition integra.cpp:69
@ get_motstep
Definition integra.cpp:71
@ set_motstep
Definition integra.cpp:70
@ move_mot_in
Definition integra.cpp:72
@ stop_motor
Definition integra.cpp:65
@ calibrate_interrupt
Definition integra.cpp:67
@ is_moving
Definition integra.cpp:75
@ move_mot_out
Definition integra.cpp:73
@ get_motrange
Definition integra.cpp:74
@ calibration_state
Definition integra.cpp:68
@ invert_dir
Definition integra.cpp:76
@ calibrate
Definition integra.cpp:66
#define INTEGRA_TEMPERATURE_TRESHOLD_IN_C
Definition integra.cpp:32
std::unique_ptr< Integra > integra(new Integra())
#define INTEGRA_TEMPERATURE_LOOP_SKIPS
Definition integra.cpp:31
#define INTEGRA_TIMEOUT_IN_S
Definition integra.cpp:30
#define INTEGRA_ROUNDING_FUDGE
Definition integra.cpp:33
uint16_t type
Definition pwc-ioctl.h:0
uint8_t cmd[4]
Definition pwc-ioctl.h:2
#define SETTINGS_TAB
Definition robofocus.cpp:44
const char cmd[12]
Definition integra.cpp:42
char ret[2][3]
Definition integra.cpp:43