Instrument Neutral Distributed Interface INDI 2.2.5
temmadriver.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2010 Gerry Rozema. All rights reserved.
3
4 Copyright(c) 2017 Jasem Mutlaq. All rights reserved.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License version 2 as published by the Free Software Foundation.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*******************************************************************************/
20
21#include "temmadriver.h"
22
23#include <indicom.h>
25
26#include <bitset>
27#include <unistd.h>
28#include <termios.h>
29
30#define TEMMA_SLEW_RATES 2
31#define TEMMA_TIMEOUT 5
32#define TEMMA_BUFFER 64
33#define TEMMA_SLEWRATE 5 /* slew rate, degrees/s */
34
35// TODO enable Alignment System Later
36#if 0
38using namespace INDI::AlignmentSubsystem;
39#endif
40
41// We declare an auto pointer to temma.
42static std::unique_ptr<TemmaMount> temma(new TemmaMount());
43
45{
55
57
58 // Should be set to invalid first
59 Longitude = std::numeric_limits<double>::quiet_NaN();
60 Latitude = std::numeric_limits<double>::quiet_NaN();
61
62 setVersion(0, 5);
63}
64
66{
67 return "Temma Takahashi";
68}
69
71{
73
75
76 // Temma runs at 19200 8 e 1
79
81
83
84 AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
85 AddTrackMode("TRACK_SOLAR", "Solar");
86
87 // TODO enable later
88#if 0
89 InitAlignmentProperties(this);
90
91 // Force the alignment system to always be on
92 getSwitch("ALIGNMENT_SUBSYSTEM_ACTIVE")->sp[0].s = ISS_ON;
93#endif
94
95 // Get value from config file if it exists.
96 // IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &Longitude);
97 // currentRA = get_local_sidereal_time(Longitude);
98 // IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &Latitude);
99 // currentDEC = Latitude > 0 ? 90 : -90;
100
101 return true;
102}
103
104#if 0
105void TemmaMount::ISGetProperties(const char *dev)
106{
107 /* First we let our parent populate */
109
110 //defineProperty(&GuideNSNP);
111 //defineProperty(&GuideWENP);
112
113 // JM 2016-11-10: This is not used anywhere in the code now. Enable it again when you use it
114 //defineProperty(&GuideRateNP);
115}
116#endif
117
118bool TemmaMount::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
119{
120
121 // Check guider interface
122 if (GI::processNumber(dev, name, values, names, n))
123 return true;
124
125 return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
126}
127
128bool TemmaMount::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
129{
130 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
131 {
132 // Check alignment properties
133 //ProcessAlignmentSwitchProperties(this, name, states, names, n);
134 }
135
136 return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
137}
138
139bool TemmaMount::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
140 char *formats[], char *names[], int n)
141{
142 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
143 {
144 // It is for us
145 //ProcessAlignmentBLOBProperties(this, name, sizes, blobsizes, blobs, formats, names, n);
146 }
147 // Pass it up the chain
148 return INDI::Telescope::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
149}
150
151bool TemmaMount::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
152{
153 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
154 {
155 //ProcessAlignmentTextProperties(this, name, texts, names, n);
156 }
157 // Pass it up the chain
158 return INDI::Telescope::ISNewText(dev, name, texts, names, n);
159}
160
162{
164
165 if (isConnected())
166 {
167 TrackState = motorsEnabled() ? SCOPE_TRACKING : SCOPE_IDLE;
168
169 double lst = get_local_sidereal_time(Longitude);
170
171 if (InitPark())
172 {
173 // If loading parking data is successful, we just set the default parking values.
174 SetAxis1ParkDefault(range24(lst + 3 / 60.0));
175 SetAxis2ParkDefault(Latitude >= 0 ? 90 : -90);
176 }
177 else
178 {
179 // Otherwise, we set all parking data to default in case no parking data is found.
180 SetAxis1Park(range24(lst + 3 / 60.0));
181 SetAxis2Park(Latitude >= 0 ? 90 : -90);
182 SetAxis1ParkDefault(range24(lst + 3 / 60.0));
183 SetAxis2ParkDefault(Latitude >= 0 ? 90 : -90);
184 }
185
186 // Load location so that it could trigger mount initialization
187 loadConfig(true, "GEOGRAPHIC_COORD");
188
189 }
190
192
193 return true;
194}
195
196bool TemmaMount::sendCommand(const char *cmd, char *response)
197{
198 int bytesWritten = 0, bytesRead = 0, rc = 0;
199 char errmsg[MAXRBUF];
200
201 char cmd_temma[TEMMA_BUFFER] = {0};
202 snprintf(cmd_temma, TEMMA_BUFFER, "%s", cmd);
203 int cmd_size = strlen(cmd_temma);
204 if (cmd_size - 2 >= TEMMA_BUFFER)
205 {
206 LOG_ERROR("Command is too long!");
207 return false;
208 }
209
210 cmd_temma[cmd_size] = 0xD;
211 cmd_temma[cmd_size + 1] = 0xA;
212
213 // Special case for M since it has slewbits
214 if (cmd[0] == 'M')
215 {
216 std::string binary = std::bitset<8>(cmd[1]).to_string();
217 LOGF_DEBUG("CMD <M %s>", binary.c_str());
218 }
219 else
220 LOGF_DEBUG("CMD <%s>", cmd);
221
222 if (isSimulation())
223 {
224 if (response == nullptr)
225 return true;
226
227 switch (cmd[0])
228 {
229 // Version
230 case 'v':
231 strncpy(response, "vSimulation v1.0", TEMMA_BUFFER);
232 break;
233
234 // Get LST
235 case 'g':
236 if (TemmaInitialized == false || std::isnan(Longitude))
237 return false;
238 else
239 {
240 double lst = get_local_sidereal_time(Longitude);
241 snprintf(response, TEMMA_BUFFER, "%02d%02d%02d", (int)lst, ((int)(lst * 60)) % 60, ((int)(lst * 3600)) % 60);
242 }
243 break;
244
245 // Get coords
246 case 'E':
247 {
248 char sign;
249 double dec = currentDEC;
250 if (dec < 0)
251 sign = '-';
252 else
253 sign = '+';
254
255 dec = fabs(dec);
256
257 // Computing meridian side is quite involved.. so for simulation now just set it to east always if slewing or parking
258 char state = 'E';
260 state = 'F';
261 snprintf(response, TEMMA_BUFFER, "E%.2d%.2d%.2d%c%.2d%.2d%.1d%c", (int)currentRA, (int)(currentRA * (double)60) % 60,
262 ((int)(currentRA * (double)6000)) % 100, sign, (int)dec, (int)(dec * (double)60) % 60,
263 ((int)(dec * (double)600)) % 10, state);
264 }
265 break;
266
267 // Sync
268 case 'D':
269 currentRA = targetRA;
270 currentDEC = targetDEC;
271 strncpy(response, "R0", TEMMA_BUFFER);
272 break;
273
274 // Goto
275 case 'P':
277 strncpy(response, "R0", TEMMA_BUFFER);
278 break;
279
280 default:
281 LOGF_ERROR("Command %s is unhandled in Simulation.", cmd);
282 return false;
283 }
284
285 return true;
286 }
287
288 tcflush(PortFD, TCIOFLUSH);
289
290 // Ask mount for current position
291 if ( (rc = tty_write(PortFD, cmd_temma, strlen(cmd_temma), &bytesWritten)) != TTY_OK)
292 {
293 tty_error_msg(rc, errmsg, MAXRBUF);
294 LOGF_ERROR("%s: %s", __FUNCTION__, errmsg);
295 return false;
296 }
297
298 // If no response is required, let's return.
299 if (response == nullptr)
300 return true;
301
302 memset(response, 0, TEMMA_BUFFER);
303
304 if ( (rc = tty_nread_section(PortFD, response, TEMMA_BUFFER, 0xA, TEMMA_TIMEOUT, &bytesRead)) != TTY_OK)
305 {
306 tty_error_msg(rc, errmsg, MAXRBUF);
307 LOGF_ERROR("%s: %s", __FUNCTION__, errmsg);
308 return false;
309 }
310
311 tcflush(PortFD, TCIOFLUSH);
312
313 response[bytesRead - 2] = 0;
314
315 LOGF_DEBUG("RES <%s>", response);
316
317 return true;
318}
319
320bool TemmaMount::getCoords()
321{
322 char response[TEMMA_BUFFER];
323
324 bool rc = sendCommand("E", response);
325 if (rc == false)
326 return false;
327
328 if (response[0] != 'E')
329 return false;
330
331 int d, m, s;
332 sscanf(response + 1, "%02d%02d%02d", &d, &m, &s);
333 //LOG_DEBUG"%d %d %d\n",d,m,s);
334 currentRA = d * 3600 + m * 60 + s * .6;
335 currentRA /= 3600;
336
337 sscanf(response + 8, "%02d%02d%01d", &d, &m, &s);
338 //LOG_DEBUG"%d %d %d\n",d,m,s);
339 currentDEC = d * 3600 + m * 60 + s * 6;
340 currentDEC /= 3600;
341 if(response[7] == '-')
342 currentDEC *= -1;
343
344 char side = response[13];
345 switch(side)
346 {
347 case 'E':
349 break;
350
351 case 'W':
353 break;
354
355 case 'F':
356 // lets see if our goto has finished
357 if (strstr(response, "F") != nullptr)
358 {
361 else if (TrackState == SCOPE_PARKING)
362 {
363 SetParked(true);
364 // turn off the motor
365 setMotorsEnabled(false);
366 }
367 }
368 else
369 {
370 LOG_DEBUG("Goto in Progress...");
371 }
372 break;
373
374 }
375
376 return true;
377}
378
380{
381 // JM: Do not read mount until it is initilaized.
382 if (TemmaInitialized == false)
383 return false;
384
385 if (isSimulation())
386 {
387 mountSim();
388 return true;
389 }
390
391 bool rc = getCoords();
392
393 if (rc == false)
394 return false;
395
396 alignedRA = currentRA;
397 alignedDEC = currentDEC;
398
399 // TODO enable alignment system later
400#if 0
401 if (IsAlignmentSubsystemActive())
402 {
403 const char *maligns[3] = {"ZENITH", "NORTH", "SOUTH"};
404 double juliandate, lst;
405 //double alignedRA, alignedDEC;
406 struct INDI::IEquatorialCoordinates RaDec;
407 bool aligned;
408 juliandate = ln_get_julian_from_sys();
409 lst = ln_get_apparent_sidereal_time(juliandate) + (LocationN[1].value * 24.0 / 360.0);
410 // Use HA/Dec as telescope coordinate system
411 RaDec.ra = ((lst - currentRA) * 360.0) / 24.0;
412 //RaDec.ra = (ra * 360.0) / 24.0;
413 RaDec.dec = currentDEC;
414 INDI::AlignmentSubsystem::TelescopeDirectionVector TDV = TelescopeDirectionVectorFromLocalHourAngleDeclination(RaDec);
415 //AlignmentSubsystem::TelescopeDirectionVector TDV = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec);
417 "Status: Mnt. Algnt. %s LST %lf RA %lf DEC %lf HA %lf TDV(x %lf y %lf z %lf)",
418 maligns[GetApproximateMountAlignment()], lst, currentRA, currentDEC, RaDec.ra, TDV.x, TDV.y, TDV.z);
419
420 aligned = true;
421
422 if (!TransformTelescopeToCelestialJD(TDV, alignedRA, alignedDEC, ln_get_julian_from_sys()))
423 {
424 aligned = false;
426 "Failed TransformTelescopeToCelestial: Scope RA=%g Scope DE=%f, Aligned RA=%f DE=%f", currentRA, currentDEC, alignedRA,
427 alignedDEC);
428 }
429 else
430 {
432 "TransformTelescopeToCelestial: Scope RA=%f Scope DE=%f, Aligned RA=%f DE=%f", currentRA, currentDEC, alignedRA,
433 alignedDEC);
434 }
435 }
436
437 if (!aligned && (syncdata.lst != 0.0))
438 {
439 DEBUGF(DBG_SCOPE_STATUS, "Aligning with last sync delta RA %g DE %g", syncdata.deltaRA, syncdata.deltaDEC);
440 // should check values are in range!
441 alignedRA += syncdata.deltaRA;
442 alignedDEC += syncdata.deltaDEC;
443 if (alignedDEC > 90.0 || alignedDEC < -90.0)
444 {
445 alignedRA += 12.00;
446 if (alignedDEC > 0.0)
447 alignedDEC = 180.0 - alignedDEC;
448 else
449 alignedDEC = -180.0 - alignedDEC;
450 }
451 alignedRA = range24(alignedRA);
452 }
453#endif
454
456
457 // If NSWE directional slew is ongoing, continue to command the mount.
458 if (SlewActive)
459 {
460 char cmd[3] = {0};
461 cmd[0] = 'M';
462 cmd[1] = Slewbits;
463
464 sendCommand(cmd, nullptr);
465 }
466
467 return true;
468}
469
470bool TemmaMount::Sync(double ra, double dec)
471{
472 char cmd[TEMMA_BUFFER] = {0}, res[TEMMA_BUFFER] = {0};
473 char sign;
474
475 targetRA = ra;
476 targetDEC = dec;
477
478 /* sync involves jumping thru considerable hoops
479 first we have to set local sidereal time
480 then we have to send a Z
481 then we set local sidereal time again
482 and finally we send the co-ordinates we are syncing on
483 */
484 LOG_DEBUG("Sending LST --> Z --> LST before Sync.");
485 setLST();
486 sendCommand("Z");
487 setLST();
488
489 // now lets format up our sync command
490 if (dec < 0)
491 sign = '-';
492 else
493 sign = '+';
494
495 dec = fabs(dec);
496
497 snprintf(cmd, TEMMA_BUFFER, "D%.2d%.2d%.2d%c%.2d%.2d%.1d", (int)ra, (int)(ra * (double)60) % 60,
498 ((int)(ra * (double)6000)) % 100, sign, (int)dec, (int)(dec * (double)60) % 60,
499 ((int)(dec * (double)600)) % 10);
500
501 if (sendCommand(cmd, res) == false)
502 return false;
503
504 // if the first character is an R, it's a correct response
505 if (res[0] != 'R')
506 return false;
507
508 if (res[1] != '0')
509 {
510 // 0 = success
511 // 1 = RA error
512 // 2 = Dec error
513 // 3 = To many digits
514 return false;
515 }
516
517 return true;
518}
519
520bool TemmaMount::Goto(double ra, double dec)
521{
522 char cmd[TEMMA_BUFFER] = {0}, res[TEMMA_BUFFER] = {0};
523 char sign;
524
525 targetRA = ra;
526 targetDEC = dec;
527
528 /* goto involves hoops, but, not as many as a sync
529 first set sidereal time
530 then issue the goto command
531 */
532 if (MotorStatus == false)
533 {
534 LOG_DEBUG("Goto turns on motors");
535 setMotorsEnabled(true);
536 }
537
538 setLST();
539
540 // now lets format up our goto command
541 if (dec < 0)
542 sign = '-';
543 else
544 sign = '+';
545
546 dec = fabs(dec);
547
548 snprintf(cmd, TEMMA_BUFFER, "P%.2d%.2d%.2d%c%.2d%.2d%.1d", (int)ra, (int)(ra * (double)60) % 60,
549 ((int)(ra * (double)6000)) % 100, sign, (int)dec, (int)(dec * (double)60) % 60,
550 ((int)(dec * (double)600)) % 10);
551
552 if (sendCommand(cmd, res) == false)
553 return false;
554
555 // if the first character is an R, it's a correct response
556 if (res[0] != 'R')
557 return false;
558 if (res[1] != '0')
559 {
560 // 0 = success
561 // 1 = RA error
562 // 2 = Dec error
563 // 3 = To many digits
564 return false;
565 }
566
568 return true;
569}
570
572{
573 double lst = get_local_sidereal_time(Longitude);
574
575 // Set Axis 1 parking to LST + 3 minutes as a safe offset
576 // so that a GOTO to it would work
577 // this would end up with mount looking at pole with counter-weight down
578 SetAxis1Park(range24(lst + 3 / 60.0));
579 LOGF_DEBUG("heading to Park position %4.2f %4.2f", GetAxis1Park(), GetAxis2Park());
580
582
584 LOG_INFO("Parking is in progress...");
585
586 return true;
587}
588
590{
591 // Get LST and set it as Axis1 Park position
592 double lst = get_local_sidereal_time(Longitude);
593 SetAxis1Park(lst);
594
595 LOGF_INFO("Syncing to Park position %4.2f %4.2f", GetAxis1Park(), GetAxis2Park());
597
598 SetParked(false);
599
600 setMotorsEnabled(true);
601 if (MotorStatus)
603 else
605
606 return true;
607}
608
610{
613 return true;
614}
615
617{
618 double lst = get_local_sidereal_time(Longitude);
619 SetAxis1Park(range24(lst + 3 / 60.0));
620 SetAxis2Park(Latitude >= 0 ? 90 : -90);
621
622 return true;
623}
624
626{
627 char res[TEMMA_BUFFER] = {0};
628
629 if (sendCommand("PS") == false)
630 return false;
631
632 if (sendCommand("s", res) == false)
633 return false;
634
635 return true;
636}
637
639{
640 char cmd[TEMMA_BUFFER] = {0};
641
642 LOGF_DEBUG("Temma::MoveNS %d dir %d", command, dir);
643 if (!MotorStatus)
644 setMotorsEnabled(true);
645 if (!MotorStatus)
646 return false;
647
648 Slewbits = 0;
649 Slewbits |= 64; // dec says always on
650
651 LOGF_DEBUG("Temma::MoveNS %d dir %d", command, dir);
652 if (command == MOTION_START)
653 {
654 if (SlewRate != 0)
655 Slewbits |= 1;
656 if (dir != DIRECTION_NORTH)
657 {
658 LOG_DEBUG("Start slew Dec Up");
659 Slewbits |= 16;
660 }
661 else
662 {
663 LOG_DEBUG("Start Slew Dec down");
664 Slewbits |= 8;
665 }
666 //sprintf(buf,"M \r\n");
667 //buf[1]=Slewbits;
668 //tty_write(PortFD,buf,4,&bytesWritten);
669 SlewActive = true;
670 }
671 else
672 {
673 // No direction bytes to turn it off
674 LOG_DEBUG("Abort slew e/w");
675 //Abort();
676 SlewActive = false;
677 }
678 cmd[0] = 'M';
679 cmd[1] = Slewbits;
680
681 return sendCommand(cmd);
682}
683
685{
686 char cmd[TEMMA_BUFFER] = {0};
687
688 if (!MotorStatus)
689 setMotorsEnabled(true);
690 if (!MotorStatus)
691 return false;
692
693 Slewbits = 0;
694 Slewbits |= 64; // documentation says always on
695
696 LOGF_DEBUG("Temma::MoveWE %d dir %d", command, dir);
697 if (command == MOTION_START)
698 {
699 if (SlewRate != 0)
700 Slewbits |= 1;
701 if (dir != DIRECTION_WEST)
702 {
703 LOG_DEBUG("Start slew East");
704 Slewbits |= 4;
705 }
706 else
707 {
708 LOG_DEBUG("Start Slew West");
709 Slewbits |= 2;
710 }
711 SlewActive = true;
712 }
713 else
714 {
715 // No direction bytes to turn it off
716 LOG_DEBUG("Abort slew e/w");
717 SlewActive = false;
718 }
719
720 cmd[0] = 'M';
721 cmd[1] = Slewbits;
722
723 return sendCommand(cmd);
724}
725
726#if 0
727bool TemmaMount::SetSlewRate(int index)
728{
729 // Is this possible for Temma?
730 //LOGF_DEBUG("Temma::Slew rate %d", index);
731 SlewRate = index;
732 return true;
733}
734#endif
735
736// TODO For large ms > 1000ms this function should be asynchronous
738{
739 char cmd[TEMMA_BUFFER] = {0};
740 char bits;
741
742 LOGF_DEBUG("Guide North %4.0f", ms);
743 if (!MotorStatus)
744 return IPS_ALERT;
745 if (SlewActive)
746 return IPS_ALERT;
747
748 bits = 0;
749 bits |= 64; // documentation says always on
750 bits |= 8; // going north
751 cmd[0] = 'M';
752 cmd[1] = bits;
753 sendCommand(cmd);
754 usleep(ms * 1000);
755 bits = 64;
756 cmd[1] = bits;
757 sendCommand(cmd);
758
759 return IPS_OK;
760}
761
763{
764 char cmd[TEMMA_BUFFER] = {0};
765 char bits;
766
767 LOGF_DEBUG("Guide South %4.0f", ms);
768
769 if (!MotorStatus)
770 return IPS_ALERT;
771 if (SlewActive)
772 return IPS_ALERT;
773
774 bits = 0;
775 bits |= 64; // documentation says always on
776 bits |= 16; // going south
777 cmd[0] = 'M';
778 cmd[1] = bits;
779 sendCommand(cmd);
780 usleep(ms * 1000);
781 bits = 64;
782 cmd[1] = bits;
783 sendCommand(cmd);
784
785 return IPS_OK;
786}
787
789{
790 char cmd[TEMMA_BUFFER] = {0};
791 char bits;
792 LOGF_DEBUG("Guide East %4.0f", ms);
793 if (!MotorStatus)
794 return IPS_ALERT;
795 if (SlewActive)
796 return IPS_ALERT;
797
798 bits = 0;
799 bits |= 64; // doc says always on
800 bits |= 2; // going east
801 cmd[0] = 'M';
802 cmd[1] = bits;
803 sendCommand(cmd);
804 usleep(ms * 1000);
805 bits = 64;
806 cmd[1] = bits;
807 sendCommand(cmd);
808 return IPS_OK;
809}
810
812{
813 char cmd[TEMMA_BUFFER] = {0};
814 char bits;
815 LOGF_DEBUG("Guide West %4.0f", ms);
816 if (!MotorStatus)
817 return IPS_ALERT;
818 if (SlewActive)
819 return IPS_ALERT;
820
821 bits = 0;
822 bits |= 64; // documentation says always on
823 bits |= 4; // going west
824 cmd[0] = 'M';
825 cmd[1] = bits;
826 sendCommand(cmd);
827 usleep(ms * 1000);
828 bits = 64;
829 cmd[1] = bits;
830 sendCommand(cmd);
831
832 return IPS_OK;
833}
834
835#if 0
836bool TemmaMount::updateTime(ln_date *utc, double utc_offset)
837{
838 INDI_UNUSED(utc);
839 INDI_UNUSED(utc_offset);
840 LOG_DEBUG("Temma::UpdateTime()");
841 return true;
842}
843#endif
844
845bool TemmaMount::updateLocation(double latitude, double longitude, double elevation)
846{
847 INDI_UNUSED(elevation);
848
849 Longitude = longitude;
850 Latitude = latitude;
851
852 double lst = get_local_sidereal_time(Longitude);
853
854 // A temma mount must have the LST and Latitude set
855 // Prior to these being set, reads will return garbage
856 if (TemmaInitialized == false)
857 {
858 setLatitude(latitude);
859 setLST();
860
861 TemmaInitialized = true;
862
863 // We were NOT initialized, so, in case there is not park position set
864 // Sync to the position of bar vertical, telescope pointed at pole
865 LOGF_DEBUG("Temma is initialized. Latitude: %.2f LST: %.2f", latitude, lst);
866 SetAxis1Park(lst);
867
868 LOGF_INFO("Syncing to default home position %4.2f %4.2f", GetAxis1Park(), GetAxis2Park());
870 }
871
872#if 0
873 // if the mount is parked, then we should sync it on our park position
874 else if (isParked())
875 {
876 // // Here we have to sync on our park position
877 // double RightAscension;
878 // // Get the park position
879 // RightAscension = lst - (rangeHA(GetAxis1Park()));
880 // RightAscension = range24(RightAscension);
881
882 double lst = get_local_sidereal_time(Longitude);
883 SetAxis1Park(lst);
884
885 LOGF_INFO("Syncing to Park position %4.2f %4.2f", GetAxis1Park(), GetAxis2Park());
887 LOG_DEBUG("Turning motors off.");
888 setMotorsEnabled(false);
889 }
890 else
891 {
892 sleep(1);
893 LOG_DEBUG("Mount is not parked");
894 //SetTemmaMotorStatus(true);
895 }
896#endif
897
898 return true;
899}
900
901#if 0
902INDI::IEquatorialCoordinates TemmaMount::TelescopeToSky(double ra, double dec)
903{
904 double RightAscension, Declination;
906
907 if (GetAlignmentDatabase().size() > 1)
908 {
910
911 /* Use this if we ar converting eq co-ords
912 // but it's broken for now
913 eq.ra=ra*360/24;
914 eq.dec=dec;
915 TDV=TelescopeDirectionVectorFromEquatorialCoordinates(eq);
916 */
917
918 /* This code does a conversion from ra/dec to alt/az
919 // before calling the alignment stuff
920 IGeographicCoordinates here;
921 INDI::IHorizontalCoordinates altaz;
922
923 here.lat=LocationN[LOCATION_LATITUDE].value;
924 here.lng=LocationN[LOCATION_LONGITUDE].value;
925 eq.ra=ra*360.0/24.0; // this is wanted in degrees, not hours
926 eq.dec=dec;
927 ln_get_hrz_from_equ(&eq,&here,ln_get_julian_from_sys(),&altaz);
928 TDV=TelescopeDirectionVectorFromAltitudeAzimuth(altaz);
929 */
930
931 /* and here we convert from ra/dec to hour angle / dec before calling alignment stuff */
932 double lha, lst;
933 lst = get_local_sidereal_time(LocationN[LOCATION_LONGITUDE].value);
934 lha = get_local_hour_angle(lst, ra);
935 // convert lha to degrees
936 lha = lha * 360 / 24;
937 eq.ra = lha;
938 eq.dec = dec;
939 TDV = TelescopeDirectionVectorFromLocalHourAngleDeclination(eq);
940
941 if (TransformTelescopeToCelestialJD(TDV, RightAscension, Declination, ln_get_julian_from_sys()))
942 {
943 // if we get here, the conversion was successful
944 //LOG_DEBUG"new values %6.4f %6.4f %6.4f %6.4f Deltas %3.0lf %3.0lf\n",ra,dec,RightAscension,Declination,(ra-RightAscension)*60,(dec-Declination)*60);
945 }
946 else
947 {
948 //if the conversion failed, return raw data
949 RightAscension = ra;
950 Declination = dec;
951 }
952 }
953 else
954 {
955 // With less than 2 align points
956 // Just return raw data
957 RightAscension = ra;
958 Declination = dec;
959 }
960
961 eq.ra = RightAscension;
962 eq.dec = Declination;
963 return eq;
964}
965
966INDI::IEquatorialCoordinates TemmaMount::SkyToTelescope(double ra, double dec)
967{
970 double RightAscension, Declination;
971
972 /*
973 */
974
975 if (GetAlignmentDatabase().size() > 1)
976 {
977 // if the alignment system has been turned off
978 // this transformation will fail, and we fall thru
979 // to using raw co-ordinates from the mount
980 if (TransformCelestialToTelescopeJD(ra, dec, ln_get_julian_from_sys(), TDV))
981 {
982 /* Initial attempt, using RA/DEC co-ordinates talking to alignment system
983 EquatorialCoordinatesFromTelescopeDirectionVector(TDV,eq);
984 RightAscension=eq.ra*24.0/360;
985 Declination=eq.dec;
986 if(RightAscension < 0) RightAscension+=24.0;
987 DEBUGF(INDI::Logger::DBG_SESSION,"Transformed Co-ordinates %g %g\n",RightAscension,Declination);
988 */
989
990 // nasty altaz kludge, use al/az co-ordinates to talk to alignment system
991 /*
992 eq.ra=ra*360/24;
993 eq.dec=dec;
994 here.lat=LocationN[LOCATION_LATITUDE].value;
995 here.lng=LocationN[LOCATION_LONGITUDE].value;
996 ln_get_hrz_from_equ(&eq,&here,ln_get_julian_from_sys(),&altaz);
997 AltitudeAzimuthFromTelescopeDirectionVector(TDV,altaz);
998 // now convert the resulting altaz into radec
999 ln_get_equ_from_hrz(&altaz,&here,ln_get_julian_from_sys(),&eq);
1000 RightAscension=eq.ra*24.0/360.0;
1001 Declination=eq.dec;
1002 */
1003
1004 /* now lets convert from telescope to lha/dec */
1005 double lst;
1006 LocalHourAngleDeclinationFromTelescopeDirectionVector(TDV, eq);
1007 // and now we have to convert from lha back to RA
1008 lst = get_local_sidereal_time(LocationN[LOCATION_LONGITUDE].value);
1009 eq.ra = eq.ra * 24 / 360;
1010 RightAscension = lst - eq.ra;
1011 RightAscension = range24(RightAscension);
1012 Declination = eq.dec;
1013 }
1014 else
1015 {
1016 LOGF_INFO("Transform failed, using raw co-ordinates %g %g", ra, dec);
1017 RightAscension = ra;
1018 Declination = dec;
1019 }
1020 }
1021 else
1022 {
1023 RightAscension = ra;
1024 Declination = dec;
1025 }
1026
1027 eq.ra = RightAscension;
1028 eq.dec = Declination;
1029 //eq.ra=ra;
1030 //eq.dec=dec;
1031 return eq;
1032}
1033#endif
1034
1035bool TemmaMount::getVersion()
1036{
1037 char res[TEMMA_BUFFER] = {0};
1038 for (int i = 0; i < 3; i++)
1039 {
1040 if (sendCommand("v", res) && res[0] == 'v')
1041 break;
1042 usleep(100000);
1043 }
1044
1045 if (res[0] != 'v')
1046 {
1047 LOG_ERROR("Read version failed.");
1048 return false;
1049 }
1050
1051
1052 LOGF_INFO("Detected version: %s", res + 1);
1053
1054 return true;
1055}
1056
1057bool TemmaMount::motorsEnabled()
1058{
1059 char res[TEMMA_BUFFER] = {0};
1060
1061 if (sendCommand("STN-COD", res) == false)
1062 return false;
1063
1064 if (strstr(res, "off") != nullptr)
1065 MotorStatus = true;
1066 else
1067 MotorStatus = false;
1068
1069 LOGF_DEBUG("Motor is %s", res);
1070
1071 return MotorStatus;
1072}
1073
1074bool TemmaMount::setMotorsEnabled(bool enable)
1075{
1076 char res[TEMMA_BUFFER] = {0};
1077 bool rc = false;
1078
1079 // STN-ON --> Standby mode ON --> Motor OFF
1080 // STN-OFF --> Standby mode OFF --> Motor ON
1081 if (enable)
1082 rc = sendCommand("STN-OFF", res);
1083 else
1084 rc = sendCommand("STN-ON", res);
1085
1086 if (rc == false)
1087 return false;
1088
1089 motorsEnabled();
1090
1091 return true;
1092}
1093
1094/* bit of a hack, returns true if lst is a sane value, false if it is not sane */
1095bool TemmaMount::getLST(double &lst)
1096{
1097 char res[TEMMA_BUFFER] = {0};
1098
1099 if (sendCommand("g", res) == false)
1100 return false;
1101
1102 int hh = 0, mm = 0, ss = 0;
1103 if (sscanf(res + 1, "%02d%02d%02d", &hh, &mm, &ss) == 3)
1104 {
1105 lst = hh + mm / 60.0 + ss / 3600.0;
1106 return true;
1107 }
1108
1109 return false;
1110}
1111
1112bool TemmaMount::setLST()
1113{
1114 char cmd[TEMMA_BUFFER] = {0};
1115 double lst = get_local_sidereal_time(Longitude);
1116 snprintf(cmd, TEMMA_BUFFER, "T%.2d%.2d%.2d", (int)lst, ((int)(lst * 60)) % 60, ((int)(lst * 3600)) % 60);
1117
1118 return sendCommand(cmd);
1119}
1120
1121bool TemmaMount::getLatitude(double &lat)
1122{
1123 char res[TEMMA_BUFFER] = {0};
1124
1125 if (sendCommand("i", res) == false)
1126 return false;
1127
1128 int dd = 0, mm = 0, parial_m = 0;
1129 if (sscanf(res + 1, "%02d%02d%01d", &dd, &mm, &parial_m) == 3)
1130 {
1131 lat = dd + mm / 60.0 + parial_m / 600.0;
1132 return true;
1133 }
1134
1135 return false;
1136}
1137
1138bool TemmaMount::setLatitude(double lat)
1139{
1140 char cmd[TEMMA_BUFFER];
1141 char sign;
1142 double l;
1143 int d, m, s;
1144
1145 if (lat > 0)
1146 {
1147 sign = '+';
1148 }
1149 else
1150 {
1151 sign = '-';
1152 }
1153 l = fabs(lat);
1154 d = (int)l;
1155 l = (l - d) * 60;
1156 m = (int)l;
1157 l = (l - m) * 6;
1158 s = (int)l;
1159
1160 snprintf(cmd, TEMMA_BUFFER, "I%c%.2d%.2d%.1d", sign, d, m, s);
1161
1162 return sendCommand(cmd);
1163}
1164
1166{
1167 //LOG_DEBUG("Calling get version");
1168 /*
1169 This is an ugly hack, but, it works
1170 On first open we often dont get an immediate read from the temma
1171 but it seems to read much more reliably if we enforce a short wait
1172 between opening the port and doing the first query for version
1173
1174 */
1175 usleep(100);
1176 if (getVersion() == false)
1177 return false;
1178
1179 double lst = 0;
1180 if (getLST(lst))
1181 {
1182 LOG_DEBUG("Temma is initialized.");
1183 TemmaInitialized = true;
1184 }
1185 else
1186 {
1187 LOG_DEBUG("Temma is not initialized.");
1188 TemmaInitialized = false;
1189 }
1190
1191 motorsEnabled();
1192
1193 return true;
1194}
1195
1196#if 0
1197int TemmaMount::TemmaRead(char *buf, int size)
1198{
1199 int bytesRead = 0;
1200 int count = 0;
1201 int ptr = 0;
1202 int rc = 0;
1203
1204 while (ptr < size)
1205 {
1206 rc = tty_read(PortFD, &buf[ptr], 1, 2,
1207 &bytesRead); // Read 1 byte of response into the buffer with 5 second timeout
1208 if (bytesRead == 1)
1209 {
1210 // we got a byte
1211 count++;
1212 if (count > 1)
1213 {
1214 //LOG_DEBUG"%d ",buf[ptr]);
1215 if (buf[ptr] == 10)
1216 {
1217 if (buf[ptr - 1] == 13)
1218 {
1219 // we have the cr/lf from the response
1220 // Send it back to the caller
1221 return count;
1222 }
1223 }
1224 }
1225 ptr++;
1226 }
1227 else
1228 {
1229 fprintf(stderr, "timed out reading %d\n", count);
1230 LOGF_DEBUG("We timed out reading bytes %d", count);
1231 return 0;
1232 }
1233 }
1234 // if we get here, we got more than size bytes, and still dont have a cr/lf
1235 fprintf(stderr, "Read error after %d bytes\n", bytesRead);
1236 LOGF_DEBUG("Read return error after %d bytes", bytesRead);
1237 return -1;
1238}
1239#endif
1240
1242{
1243 if (mode == TRACK_SIDEREAL)
1244 return sendCommand("LL");
1245 else if (mode == TRACK_SOLAR)
1246 return sendCommand("LK");
1247
1248 return false;
1249}
1250
1252{
1253 if (enabled)
1254 {
1255 setMotorsEnabled(true);
1257 }
1258 else
1259 {
1260 setMotorsEnabled(false);
1261 return sendCommand("PS");
1262 }
1263}
1264
1265void TemmaMount::mountSim()
1266{
1267 static struct timeval ltv;
1268 struct timeval tv;
1269 double dt = 0, da = 0, dx = 0;
1270 int nlocked = 0;
1271
1272 /* update elapsed time since last poll, don't presume exactly POLLMS */
1273 gettimeofday(&tv, nullptr);
1274
1275 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1276 ltv = tv;
1277
1278 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1279 ltv = tv;
1280 da = TEMMA_SLEWRATE * dt;
1281
1282 /* Process per current state. We check the state of EQUATORIAL_COORDS and act accordingly */
1283 switch (TrackState)
1284 {
1285
1286 case SCOPE_IDLE:
1287 currentRA += (TRACKRATE_SIDEREAL / 3600.0 * dt / 15.);
1288 break;
1289
1290 case SCOPE_TRACKING:
1292 {
1293 case TRACK_SIDEREAL:
1294 da = 0;
1295 dx = 0;
1296 break;
1297
1298 case TRACK_LUNAR:
1299 da = ((TRACKRATE_LUNAR - TRACKRATE_SIDEREAL) / 3600.0 * dt / 15.);
1300 dx = 0;
1301 break;
1302
1303 case TRACK_SOLAR:
1304 da = ((TRACKRATE_SOLAR - TRACKRATE_SIDEREAL) / 3600.0 * dt / 15.);
1305 dx = 0;
1306 break;
1307
1308 case TRACK_CUSTOM:
1309 da = ((TrackRateNP[AXIS_RA].getValue() - TRACKRATE_SIDEREAL) / 3600.0 * dt / 15.);
1310 dx = (TrackRateNP[AXIS_DE].getValue() / 3600.0 * dt);
1311 break;
1312
1313 }
1314
1315 currentRA += da;
1316 currentDEC += dx;
1317 break;
1318
1319 case SCOPE_SLEWING:
1320 case SCOPE_PARKING:
1321 /* slewing - nail it when both within one pulse @ LX200_GENERIC_SLEWRATE */
1322 nlocked = 0;
1323
1324 dx = targetRA - currentRA;
1325
1326 if (fabs(dx) <= da)
1327 {
1328 currentRA = targetRA;
1329 nlocked++;
1330 }
1331 else if (dx > 0)
1332 currentRA += da / 15.;
1333 else
1334 currentRA -= da / 15.;
1335
1336 dx = targetDEC - currentDEC;
1337 if (fabs(dx) <= da)
1338 {
1339 currentDEC = targetDEC;
1340 nlocked++;
1341 }
1342 else if (dx > 0)
1343 currentDEC += da;
1344 else
1345 currentDEC -= da;
1346
1347 if (nlocked == 2)
1348 {
1351 else
1352 SetParked(true);
1353 }
1354
1355 break;
1356
1357 default:
1358 break;
1359 }
1360
1362}
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
void setParity(const uint8_t &value)
setParity Set parity to be used in the serial connection. Default 0 (NONE)
bool isConnected() const
INDI::PropertySwitch getSwitch(const char *name) const
virtual void ISGetProperties(const char *dev)
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
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 loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
uint32_t getDriverInterface() const
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Process the client newBLOB command.
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void initProperties(const char *groupName)
Initialize guider properties. It is recommended to call this function within initProperties() of your...
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Call this function whenever client updates GuideNSNP or GuideWSP properties in the primary device....
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis1ParkDefault(double steps)
SetRAPark Set default RA/AZ parking position.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
double GetAxis1Park() const
virtual bool SetSlewRate(int index)
SetSlewRate Set desired slew rate index.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
double GetAxis2Park() const
bool isParked()
isParked is mount currently parked?
virtual int AddTrackMode(const char *name, const char *label, bool isDefault=false)
AddTrackMode.
Connection::Serial * serialConnection
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
virtual bool updateTime(ln_date *utc, double utc_offset)
Update telescope time, date, and UTC offset.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
void SetAxis2ParkDefault(double steps)
SetDEParkDefault Set default DEC/ALT parking position.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) override
Process the client newBLOB command.
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual const char * getDefaultName() override
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual bool Park() override
Park the telescope to its home position.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
INDI::PropertyNumber TrackRateNP
INDI::PropertySwitch TrackModeSP
#define currentDEC
Definition ieq45.cpp:48
#define currentRA
Definition ieq45.cpp:47
double ra
double dec
ISState
Switch state.
Definition indiapi.h:148
@ ISS_ON
Definition indiapi.h:150
IPState
Property state.
Definition indiapi.h:158
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_OK
Definition indiapi.h:160
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_NORTH
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
double range24(double r)
range24 Limits a number to be between 0-24 range.
Definition indicom.c:1311
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:759
double get_local_hour_angle(double sideral_time, double ra)
get_local_hour_angle Returns local hour angle of an object
Definition indicom.c:1379
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sidereal time given longitude and system clock.
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define MAXRBUF
Definition indidriver.c:52
#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 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 DEBUGF(priority, msg,...)
Definition indilogger.h:57
#define TRACKRATE_SOLAR
Definition indimacros.h:45
#define TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define TRACKRATE_LUNAR
Definition indimacros.h:48
#define getDeviceName
INumber eq[]
Namespace to encapsulate the INDI Alignment Subsystem classes. For more information see "INDI Alignme...
@ binary
binary array (ordered collection of bytes)
uint8_t cmd[4]
Definition pwc-ioctl.h:2
Holds a normalised direction vector (direction cosines)
Definition Common.h:69
#define TEMMA_BUFFER
#define TEMMA_SLEW_RATES
#define TEMMA_TIMEOUT
#define TEMMA_SLEWRATE