Instrument Neutral Distributed Interface INDI 2.2.5
synscandriverlegacy.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2010 Gerry Rozema. All rights reserved.
3 Copyright(c) 2018 Jasem Mutlaq. All rights reserved.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*******************************************************************************/
19
20#include "synscandriverlegacy.h"
23#include "indicom.h"
24#include "libastro.h"
25
26#include <libnova/transform.h>
27#include <libnova/precession.h>
28// libnova specifies round() on old systems and it collides with the new gcc 5.x/6.x headers
29#define HAVE_ROUND
30#include <libnova/utility.h>
31
32#include <cmath>
33#include <memory>
34#include <cstring>
35
36#define SYNSCAN_SLEW_RATES 9
37
38constexpr uint16_t SynscanLegacyDriver::SLEW_RATE[];
39
47
49{
50 if (isConnected())
51 return true;
52
53 bool rc = INDI::Telescope::Connect();
54
55 if (rc)
56 return AnalyzeMount();
57
58 return rc;
59}
60
62{
63 return "SynScan Legacy";
64}
65
67{
69
74
75 // Slew Rates
76 SlewRateSP[0].setLabel("1x");
77 SlewRateSP[1].setLabel("8x");
78 SlewRateSP[2].setLabel("16x");
79 SlewRateSP[3].setLabel("32x");
80 SlewRateSP[4].setLabel("64x");
81 SlewRateSP[5].setLabel("128x");
82 SlewRateSP[6].setLabel("400x");
83 SlewRateSP[7].setLabel("600x");
84 SlewRateSP[8].setLabel("MAX");
86 // Max is the default
88
92 IUFillText(&BasicMountInfoT[MI_FW_VERSION], "FW_VERSION", "Firmware version", "-");
93 IUFillText(&BasicMountInfoT[MI_MOUNT_CODE], "MOUNT_CODE", "Mount code", "-");
94 IUFillText(&BasicMountInfoT[MI_ALIGN_STATUS], "ALIGNMENT_STATUS", "Alignment status", "-");
95 IUFillText(&BasicMountInfoT[MI_GOTO_STATUS], "GOTO_STATUS", "Goto status", "-");
96 IUFillText(&BasicMountInfoT[MI_POINT_STATUS], "MOUNT_POINTING_STATUS",
97 "Mount pointing status", "-");
98 IUFillText(&BasicMountInfoT[MI_TRACK_MODE], "TRACKING_MODE", "Tracking mode", "-");
99 IUFillTextVector(&BasicMountInfoTP, BasicMountInfoT, 6, getDeviceName(), "BASIC_MOUNT_INFO",
100 "Mount information", MountInfoPage, IP_RO, 60, IPS_IDLE);
101
105 // IUFillSwitch(&UseWiFiS[WIFI_ENABLED], "Enabled", "Enabled", ISS_OFF);
106 // IUFillSwitch(&UseWiFiS[WIFI_DISABLED], "Disabled", "Disabled", ISS_ON);
107 // IUFillSwitchVector(&UseWiFiSP, UseWiFiS, 2, getDeviceName(), "WIFI_SELECT", "Use WiFi?", CONNECTION_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
108
111
113
114 return true;
115}
116
117bool SynscanLegacyDriver::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
118{
119 return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
120}
121
122bool SynscanLegacyDriver::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
123{
124#if 0
125 if (!strcmp(dev, getDeviceName()))
126 {
127 if (!strcmp(name, UseWiFiSP.name))
128 {
129 if (isConnected())
130 {
131 UseWiFiSP.s = IPS_ALERT;
132 IDSetSwitch(&UseWiFiSP, nullptr);
133 LOG_ERROR("Cannot select WiFi mode while already connected. It must be selected when when disconnected.");
134 return true;
135 }
136
137 IUUpdateSwitch(&UseWiFiSP, states, names, n);
138
139 if (UseWiFiS[WIFI_ENABLED].s == ISS_ON)
140 {
142 tcpConnection->setDefaultHost("192.168.4.2");
144
145 LOG_INFO("Driver is configured for WiFi connection to 192.168.4.2 at TCP port 11882");
146
147 saveConfig(true, "WIFI_SELECT");
148 UseWiFiSP.s = IPS_OK;
149 IDSetSwitch(&UseWiFiSP, nullptr);
150
151 ISState newStates[] = { ISS_OFF, ISS_ON };
152 const char *newNames[] = { "CONNECTION_SERIAL", "CONNECTION_TCP" };
153 ISNewSwitch(getDeviceName(), "CONNECTION_MODE", newStates, const_cast<char **>(newNames), 2);
154 }
155 else
156 {
158 LOG_INFO("Driver is configured for serial connection to the hand controller.");
159
160 UseWiFiSP.s = IPS_OK;
161 IDSetSwitch(&UseWiFiSP, nullptr);
162
163 ISState newStates[] = { ISS_ON, ISS_OFF };
164 const char *newNames[] = { "CONNECTION_SERIAL", "CONNECTION_TCP" };
165 ISNewSwitch(getDeviceName(), "CONNECTION_MODE", newStates, const_cast<char **>(newNames), 2);
166 }
167
168 return true;
169 }
170 }
171#endif
172 return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
173}
174
175bool SynscanLegacyDriver::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
176 char *formats[], char *names[], int n)
177{
178 return INDI::Telescope::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
179}
180
181bool SynscanLegacyDriver::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
182{
183 return INDI::Telescope::ISNewText(dev, name, texts, names, n);
184}
185
187{
189
190 if (isConnected())
191 {
192 UpdateMountInformation(false);
193 defineProperty(&BasicMountInfoTP);
194 }
195 else
196 {
197 deleteProperty(BasicMountInfoTP.name);
198 }
199
200 return true;
201}
202
203int SynscanLegacyDriver::HexStrToInteger(const std::string &res)
204{
205 int result = 0;
206
207 try
208 {
209 result = std::stoi(res, nullptr, 16);
210 }
211 catch (std::invalid_argument &)
212 {
213 LOGF_ERROR("Failed to parse %s to integer.", res.c_str());
214 }
215
216 return result;
217}
218
220{
221 LOG_DEBUG("Analyzing Mount...");
222
223 bool rc = true;
224 int tmp = 0;
225 int bytesWritten = 0;
226 int bytesRead;
227 char res[MAX_SYN_BUF] = {0};
228
229 // JM 2018-08-15 Why are we reading caps here? Looks like it serves no purpose
230 //caps = GetTelescopeCapability();
231
232 rc = ReadLocation();
233 if (rc)
234 {
235 CanSetLocation = true;
236 ReadTime();
237 }
238
239 if (isSimulation() == false)
240 {
241 bytesRead = 0;
242 memset(res, 0, MAX_SYN_BUF);
243 LOG_DEBUG("CMD <J>");
244 tty_write(PortFD, "J", 1, &bytesWritten);
245 tty_read(PortFD, res, 2, 2, &bytesRead);
246 LOGF_DEBUG("RES <%s>", res);
247
248 if (res[0] == 0)
249 {
250 LOG_ERROR("Mount is not aligned. Please align the mount first and connection again.");
251 return false;
252 }
253
255 {
256 // Read the handset version
257 bytesRead = 0;
258 memset(res, 0, MAX_SYN_BUF);
259 LOG_DEBUG("Getting Firmware version...");
260 LOG_DEBUG("CMD <V>");
261 tty_write(PortFD, "V", 1, &bytesWritten);
262
263 tty_read(PortFD, res, 7, 2, &bytesRead);
264 LOGF_DEBUG("RES <%s>", res);
265
266 if (bytesRead == 3)
267 {
268 int tmp1 { 0 }, tmp2 { 0 };
269
270 tmp = res[0];
271 tmp1 = res[1];
272 tmp2 = res[2];
273 FirmwareVersion = tmp2;
274 FirmwareVersion /= 100;
275 FirmwareVersion += tmp1;
276 FirmwareVersion /= 100;
277 FirmwareVersion += tmp;
278 }
279 else
280 {
281 FirmwareVersion = (double)HexStrToInteger(std::string(&res[0], 2));
282 FirmwareVersion += (double)HexStrToInteger(std::string(&res[2], 2)) / 100;
283 FirmwareVersion += (double)HexStrToInteger(std::string(&res[4], 2)) / 10000;
284 }
285
286 LOGF_INFO("Firmware version: %lf", FirmwareVersion);
287
288 if (FirmwareVersion < 3.38 || (FirmwareVersion >= 4.0 && FirmwareVersion < 4.38))
289 {
290 LOG_WARN("Firmware version is too old. Update Synscan firmware to v4.38+");
291 }
292 else
293 {
294 NewFirmware = true;
295 }
296
297 HandsetFwVersion = std::to_string(FirmwareVersion);
298
299 // Mount Model
300 memset(res, 0, MAX_SYN_BUF);
301 LOG_DEBUG("CMD <m>");
302 tty_write(PortFD, "m", 1, &bytesWritten);
303 tty_read(PortFD, res, 2, 2, &bytesRead);
304 LOGF_DEBUG("RES <%s>", res);
305
306 if (bytesRead == 2)
307 {
308 // This workaround is needed because the firmware 3.39 sends these bytes swapped.
309 if (res[1] == '#')
310 MountCode = static_cast<int>(*reinterpret_cast<unsigned char * >(&res[0]));
311 else
312 MountCode = static_cast<int>(*reinterpret_cast<unsigned char*>(&res[1]));
313 }
314 }
315
316 // Check the tracking status
317 LOG_DEBUG("Getting Tracking status...");
318 memset(res, 0, MAX_SYN_BUF);
319 LOG_DEBUG("CMD <t>");
320 tty_write(PortFD, "t", 1, &bytesWritten);
321 tty_read(PortFD, res, 2, 2, &bytesRead);
322 LOGF_DEBUG("RES <%s>", res);
323
324 if (res[1] == '#' && static_cast<int>(res[0]) != 0)
325 {
327 }
328 }
329
330 initParking();
331
332 LOG_DEBUG("Analyzing mount complete.");
333
334 return true;
335}
336
338{
339 LOG_DEBUG("Initializing parking...");
340 if (InitPark())
341 {
344 }
345 else
346 {
347 SetAxis1Park(0);
348 SetAxis2Park(90);
351 }
352}
353
355{
356 if (isSimulation())
357 {
358 MountSim();
359 return true;
360 }
361
362 char res[MAX_SYN_BUF] = {0};
363 int bytesWritten, bytesRead;
364 double ra, dec;
365 long unsigned int n1, n2;
366
367 LOG_DEBUG("CMD <Ka>");
368 tty_write(PortFD, "Ka", 2, &bytesWritten); // test for an echo
369
370 tty_read(PortFD, res, 2, 2, &bytesRead); // Read 2 bytes of response
371 LOGF_DEBUG("RES <%s>", res);
372
373 if (res[1] != '#')
374 {
375 LOG_WARN("Synscan Mount not responding");
376 // Usually, Abort() recovers the communication
377 RecoverTrials++;
378 Abort();
379 // HasFailed = true;
380 return false;
381 }
382 RecoverTrials = 0;
383
384 /*
385 // With 3.37 firmware, on the older line of eq6 mounts
386 // The handset does not always initialize the communication with the motors correctly
387 // We can check for this condition by querying the motors for firmware version
388 // and if it returns zero, it means we need to power cycle the handset
389 // and try again after it restarts again
390
391 if(HasFailed) {
392 int v1,v2;
393 v1=PassthruCommand(0xfe,0x11,1,0,2);
394 v2=PassthruCommand(0xfe,0x10,1,0,2);
395 fprintf(stderr,"Motor firmware versions %d %d\n",v1,v2);
396 if((v1==0)||(v2==0)) {
397 IDMessage(getDeviceName(),"Cannot proceed");
398 IDMessage(getDeviceName(),"Handset is responding, but Motors are Not Responding");
399 return false;
400 }
401 // if we get here, both motors are responding again
402 // so the problem is solved
403 HasFailed=false;
404 }
405 */
406
407 // on subsequent passes, we just need to read the time
408 // if (HasTime())
409 // {
410 // ReadTime();
411 // }
412 if (HasLocation())
413 {
414 // this flag is set when we get a new lat/long from the host
415 // so we should go thru the read routine once now, so things update
416 // correctly in the client displays
417 if (ReadLatLong)
418 {
419 ReadLocation();
420 }
421 }
422
423 // Query mount information
424 memset(res, 0, MAX_SYN_BUF);
425 LOG_DEBUG("CMD <J>");
426 tty_write(PortFD, "J", 1, &bytesWritten);
427 tty_read(PortFD, res, 2, 2, &bytesRead);
428 LOGF_DEBUG("RES <%s>", res);
429 if (res[1] == '#')
430 {
431 AlignmentStatus = std::to_string((int)res[0]);
432 }
433 memset(res, 0, MAX_SYN_BUF);
434 LOG_DEBUG("CMD <L>");
435 tty_write(PortFD, "L", 1, &bytesWritten);
436 tty_read(PortFD, res, 2, 2, &bytesRead);
437 LOGF_DEBUG("RES <%s>", res);
438 if (res[1] == '#')
439 {
440 GotoStatus = res[0];
441 }
442 memset(res, 0, MAX_SYN_BUF);
443 LOG_DEBUG("CMD <p>");
444 tty_write(PortFD, "p", 1, &bytesWritten);
445 tty_read(PortFD, res, 2, 2, &bytesRead);
446 LOGF_DEBUG("RES <%s>", res);
447 if (res[1] == '#')
448 {
449 PointingStatus = res[0];
450
451 // INDI and mount pier sides are opposite to each other
452 setPierSide(res[0] == 'W' ? PIER_EAST : PIER_WEST);
453 }
454 memset(res, 0, MAX_SYN_BUF);
455 LOG_DEBUG("CMD <t>");
456 tty_write(PortFD, "t", 1, &bytesWritten);
457 tty_read(PortFD, res, 2, 2, &bytesRead);
458 LOGF_DEBUG("RES <%s>", res);
459 if (res[1] == '#')
460 {
461 TrackingStatus = res[0];
462 switch((int)res[0])
463 {
464 case 0:
465 TrackingMode = "Tracking off";
466 break;
467 case 1:
468 TrackingMode = "Alt/Az tracking";
469 break;
470 case 2:
471 TrackingMode = "EQ tracking";
472 break;
473 case 3:
474 TrackingMode = "PEC mode";
475 break;
476 }
477 }
478
479 UpdateMountInformation(true);
480
482 {
483 // We have a slew in progress
484 // lets see if it's complete
485 // This only works for ra/dec goto commands
486 // The goto complete flag doesn't trip for ALT/AZ commands
487 if (GotoStatus != "0")
488 {
489 // Nothing to do here
490 }
491 else if (MountCode < 128)
492 {
493 if (TrackingStatus[0] != 0)
495 else
497 }
498 }
500 {
501 if (FirmwareVersion == 4.103500)
502 {
503 // With this firmware the correct way
504 // is to check the slewing flat
505 memset(res, 0, 3);
506 LOG_DEBUG("CMD <L>");
507 tty_write(PortFD, "L", 1, &bytesWritten);
508 tty_read(PortFD, res, 2, 3, &bytesRead);
509 LOGF_DEBUG("RES <%s>", res);
510 if (res[0] != 48)
511 {
512 // Nothing to do here
513 }
514 else
515 {
516 if (NumPark++ < 2)
517 {
518 Park();
519 }
520 else
521 {
523 SetParked(true);
524 }
525 }
526 }
527 else
528 {
529 // ok, lets try read where we are
530 // and see if we have reached the park position
531 // newer firmware versions dont read it back the same way
532 // so we watch now to see if we get the same read twice in a row
533 // to confirm that it has stopped moving
534 memset(res, 0, MAX_SYN_BUF);
535 LOG_DEBUG("CMD <z>");
536 tty_write(PortFD, "z", 1, &bytesWritten);
537 tty_read(PortFD, res, 18, 2, &bytesRead);
538 LOGF_DEBUG("RES <%s>", res);
539
540 //IDMessage(getDeviceName(),"Park Read %s %d",res,StopCount);
541
542 if (strncmp((char *)res, LastParkRead, 18) == 0)
543 {
544 // We find that often after it stops from park
545 // it's off the park position by a small amount
546 // issuing another park command gets a small movement and then
547 if (++StopCount > 2)
548 {
549 if (NumPark++ < 2)
550 {
551 StopCount = 0;
552 //IDMessage(getDeviceName(),"Sending park again");
553 Park();
554 }
555 else
556 {
558 //ParkSP.s=IPS_OK;
559 //IDSetSwitch(&ParkSP,nullptr);
560 //IDMessage(getDeviceName(),"Telescope is Parked.");
561 SetParked(true);
562 }
563 }
564 else
565 {
566 //StopCount=0;
567 }
568 }
569 else
570 {
571 StopCount = 0;
572 }
573 snprintf(LastParkRead, 20, "%.20s", res);
574 }
575 }
576
577 memset(res, 0, MAX_SYN_BUF);
578 LOG_DEBUG("CMD <e>");
579 tty_write(PortFD, "e", 1, &bytesWritten);
580 tty_read(PortFD, res, 18, 1, &bytesRead);
581 LOGF_DEBUG("RES <%s>", res);
582 if (bytesRead != 18)
583 {
584 LOG_DEBUG("Read current position failed");
585 return false;
586 }
587
588 sscanf(res, "%lx,%lx#", &n1, &n2);
589 ra = static_cast<double>(n1) / 0x100000000 * 24.0;
590 dec = static_cast<double>(n2) / 0x100000000 * 360.0;
591
592 INDI::IEquatorialCoordinates epochPos { 0, 0 }, J2000Pos { 0, 0 };
593 J2000Pos.rightascension = range24(ra);
594 J2000Pos.declination = rangeDec(dec);
595
596 // Synscan reports J2000 coordinates so we need to convert from J2000 to JNow
597 INDI::J2000toObserved(&J2000Pos, ln_get_julian_from_sys(), &epochPos);
598
599 CurrentRA = epochPos.rightascension;
600 CurrentDEC = epochPos.declination;
601
602 // Now feed the rest of the system with corrected data
604
605 if (TrackState == SCOPE_SLEWING && MountCode >= 128 && (SlewTargetAz != -1 || SlewTargetAlt != -1))
606 {
607 INDI::IHorizontalCoordinates CurrentAltAz { 0, 0 };
608 double DiffAlt { 0 };
609 double DiffAz { 0 };
610
611 INDI::EquatorialToHorizontal(&epochPos, &m_Location, ln_get_julian_from_sys(), &CurrentAltAz);
612 DiffAlt = CurrentAltAz.altitude - SlewTargetAlt;
613 if (SlewTargetAlt != -1 && std::abs(DiffAlt) > 0.01)
614 {
615 int NewRate = 2;
616
617 if (std::abs(DiffAlt) > 4)
618 {
619 NewRate = 9;
620 }
621 else if (std::abs(DiffAlt) > 1.2)
622 {
623 NewRate = 7;
624 }
625 else if (std::abs(DiffAlt) > 0.5)
626 {
627 NewRate = 5;
628 }
629 else if (std::abs(DiffAlt) > 0.2)
630 {
631 NewRate = 4;
632 }
633 else if (std::abs(DiffAlt) > 0.025)
634 {
635 NewRate = 3;
636 }
637 LOGF_DEBUG("Slewing Alt axis: %1.3f-%1.3f -> %1.3f (speed: %d)",
638 CurrentAltAz.altitude, SlewTargetAlt, CurrentAltAz.altitude - SlewTargetAlt, CustomNSSlewRate);
639 if (NewRate != CustomNSSlewRate)
640 {
641 if (DiffAlt < 0)
642 {
643 CustomNSSlewRate = NewRate;
645 }
646 else
647 {
648 CustomNSSlewRate = NewRate;
650 }
651 }
652 }
653 else if (SlewTargetAlt != -1 && std::abs(DiffAlt) < 0.01)
654 {
656 SlewTargetAlt = -1;
657 LOG_DEBUG("Slewing on Alt axis finished");
658 }
659 DiffAz = CurrentAltAz.azimuth - SlewTargetAz;
660 if (DiffAz < -180)
661 DiffAz = (DiffAz + 360) * 2;
662 else if (DiffAz > 180)
663 DiffAz = (DiffAz - 360) * 2;
664 if (SlewTargetAz != -1 && std::abs(DiffAz) > 0.01)
665 {
666 int NewRate = 2;
667
668 if (std::abs(DiffAz) > 4)
669 {
670 NewRate = 9;
671 }
672 else if (std::abs(DiffAz) > 1.2)
673 {
674 NewRate = 7;
675 }
676 else if (std::abs(DiffAz) > 0.5)
677 {
678 NewRate = 5;
679 }
680 else if (std::abs(DiffAz) > 0.2)
681 {
682 NewRate = 4;
683 }
684 else if (std::abs(DiffAz) > 0.025)
685 {
686 NewRate = 3;
687 }
688 LOGF_DEBUG("Slewing Az axis: %1.3f-%1.3f -> %1.3f (speed: %d)",
689 CurrentAltAz.azimuth, SlewTargetAz, CurrentAltAz.azimuth - SlewTargetAz, CustomWESlewRate);
690 if (NewRate != CustomWESlewRate)
691 {
692 if (DiffAz > 0)
693 {
694 CustomWESlewRate = NewRate;
696 }
697 else
698 {
699 CustomWESlewRate = NewRate;
701 }
702 }
703 }
704 else if (SlewTargetAz != -1 && std::abs(DiffAz) < 0.01)
705 {
707 SlewTargetAz = -1;
708 LOG_DEBUG("Slewing on Az axis finished");
709 }
710 if (SlewTargetAz == -1 && SlewTargetAlt == -1)
711 {
713 }
714 }
715 return true;
716}
717
719{
720 char res[MAX_SYN_BUF] = {0};
721 int bytesWritten, bytesRead;
722
724 LOG_INFO("Tracking started.");
725
726 if (isSimulation())
727 return true;
728
729 // Start tracking
730 res[0] = 'T';
731 // Check the mount type to choose tracking mode
732 if (MountCode >= 128)
733 {
734 // Alt/Az tracking mode
735 res[1] = 1;
736 }
737 else
738 {
739 // EQ tracking mode
740 res[1] = 2;
741 }
742 tty_write(PortFD, res, 2, &bytesWritten);
743 tty_read(PortFD, res, 1, 2, &bytesRead);
744 if (bytesRead != 1 || res[0] != '#')
745 {
746 LOG_DEBUG("Timeout waiting for scope to start tracking.");
747 return false;
748 }
749 return true;
750}
751
752bool SynscanLegacyDriver::Goto(double ra, double dec)
753{
754 char res[MAX_SYN_BUF] = {0};
755 int bytesWritten, bytesRead;
756 INDI::IHorizontalCoordinates TargetAltAz { 0, 0 };
757
758 if (isSimulation() == false)
759 {
760 LOG_DEBUG("CMD <Ka>");
761 tty_write(PortFD, "Ka", 2, &bytesWritten); // test for an echo
762 tty_read(PortFD, res, 2, 2, &bytesRead); // Read 2 bytes of response
763 LOGF_DEBUG("RES <%s>", res);
764 if (res[1] != '#')
765 {
766 LOG_WARN("Wrong answer from the mount");
767 // this is not a correct echo
768 // so we are not talking to a mount properly
769 return false;
770 }
771 }
772
774 // EQ mount has a different Goto mode
775 if (MountCode < 128 && isSimulation() == false)
776 {
777 INDI::IEquatorialCoordinates epochPos { 0, 0 }, J2000Pos { 0, 0 };
778 epochPos.rightascension = ra;
779 epochPos.declination = dec;
780
781 // Synscan accepts J2000 coordinates so we need to convert from JNow to J2000
782 INDI::ObservedToJ2000(&epochPos, ln_get_julian_from_sys(), &J2000Pos);
783
784 // Mount deals in J2000 coords.
785 int n1 = J2000Pos.rightascension * 0x1000000 / 24;
786 int n2 = J2000Pos.declination * 0x1000000 / 360;
787
788 LOGF_DEBUG("Goto - JNow RA: %g JNow DE: %g J2000 RA: %g J2000 DE: %g", ra, dec, J2000Pos.rightascension,
789 J2000Pos.declination);
790
791 n1 = n1 << 8;
792 n2 = n2 << 8;
793 LOGF_DEBUG("CMD <%s>", res);
794 snprintf(res, MAX_SYN_BUF, "r%08X,%08X", n1, n2);
795 tty_write(PortFD, res, 18, &bytesWritten);
796 memset(&res[18], 0, 1);
797
798 tty_read(PortFD, res, 1, 60, &bytesRead);
799 if (bytesRead != 1 || res[0] != '#')
800 {
801 LOG_DEBUG("Timeout waiting for scope to complete goto.");
802 return false;
803 }
804
805 return true;
806 }
807
808 INDI::IEquatorialCoordinates epochPos { ra, dec };
809 INDI::EquatorialToHorizontal(&epochPos, &m_Location, ln_get_julian_from_sys(), &TargetAltAz);
810 LOGF_DEBUG("Goto - JNow RA: %g JNow DE: %g (az: %g alt: %g)", ra, dec, TargetAltAz.azimuth, TargetAltAz.altitude);
811 char RAStr[MAX_SYN_BUF] = {0}, DEStr[MAX_SYN_BUF] = {0}, AZStr[MAX_SYN_BUF] = {0}, ATStr[MAX_SYN_BUF] = {0};
812 fs_sexa(RAStr, ra, 2, 3600);
813 fs_sexa(DEStr, dec, 2, 3600);
814 fs_sexa(AZStr, TargetAltAz.azimuth, 2, 3600);
815 fs_sexa(ATStr, TargetAltAz.altitude, 2, 3600);
816
817 LOGF_INFO("Goto RA: %s DE: %s AZ: %s ALT: %s", RAStr, DEStr, AZStr, ATStr);
818
819 SlewTargetAz = TargetAltAz.azimuth;
820 SlewTargetAlt = TargetAltAz.altitude;
821
822 TargetRA = ra;
823 TargetDEC = dec;
824
825 return true;
826}
827
829{
830 char res[MAX_SYN_BUF] = {0};
831 int bytesWritten, bytesRead;
832
833 if (isSimulation() == false)
834 {
835 strncpy(LastParkRead, "", 1);
836 memset(res, 0, 3);
837 tty_write(PortFD, "Ka", 2, &bytesWritten); // test for an echo
838 tty_read(PortFD, res, 2, 2, &bytesRead); // Read 2 bytes of response
839 if (res[1] != '#')
840 {
841 // this is not a correct echo
842 // so we are not talking to a mount properly
843 return false;
844 }
845 // Now we stop tracking
846 res[0] = 'T';
847 res[1] = 0;
848 tty_write(PortFD, res, 2, &bytesWritten);
849 tty_read(PortFD, res, 1, 60, &bytesRead);
850 if (bytesRead != 1 || res[0] != '#')
851 {
852 LOG_DEBUG("Timeout waiting for scope to stop tracking.");
853 return false;
854 }
855
856 //sprintf((char *)res,"b%08X,%08X",0x0,0x40000000);
857 tty_write(PortFD, "b00000000,40000000", 18, &bytesWritten);
858 tty_read(PortFD, res, 1, 60, &bytesRead);
859 if (bytesRead != 1 || res[0] != '#')
860 {
861 LOG_DEBUG("Timeout waiting for scope to respond to park.");
862 return false;
863 }
864 }
865
867 if (NumPark == 0)
868 {
869 LOG_INFO("Parking Mount...");
870 }
871 StopCount = 0;
872 return true;
873}
874
876{
877 SetParked(false);
878 NumPark = 0;
879 return true;
880}
881
883{
884 LOG_INFO("Setting arbitrary park positions is not supported yet.");
885 return false;
886}
887
889{
890 // By default az to north, and alt to pole
891 LOG_DEBUG("Setting Park Data to Default.");
892 SetAxis1Park(0);
893 SetAxis2Park(90);
894
895 return true;
896}
897
899{
900 if (TrackState == SCOPE_IDLE || RecoverTrials >= 3)
901 return true;
902
903 char res[MAX_SYN_BUF] = {0};
904 int bytesWritten, bytesRead;
905
906 LOG_DEBUG("Abort mount...");
908
909 if (isSimulation())
910 return true;
911
912 SlewTargetAlt = -1;
913 SlewTargetAz = -1;
914 CustomNSSlewRate = -1;
915 CustomWESlewRate = -1;
916 // Stop tracking
917 res[0] = 'T';
918 res[1] = 0;
919
920 LOGF_DEBUG("CMD <%s>", res);
921 tty_write(PortFD, res, 2, &bytesWritten);
922
923 tty_read(PortFD, res, 1, 2, &bytesRead);
924 LOGF_DEBUG("RES <%s>", res);
925
926 if (bytesRead != 1 || res[0] != '#')
927 {
928 LOG_DEBUG("Timeout waiting for scope to stop tracking.");
929 return false;
930 }
931
932 // Hmmm twice only stops it
933 LOG_DEBUG("CMD <M>");
934 tty_write(PortFD, "M", 1, &bytesWritten);
935 tty_read(PortFD, res, 1, 1, &bytesRead);
936 LOGF_DEBUG("RES <%c>", res[0]);
937
938 LOG_DEBUG("CMD <M>");
939 tty_write(PortFD, "M", 1, &bytesWritten);
940 tty_read(PortFD, res, 1, 1, &bytesRead);
941 LOGF_DEBUG("RES <%c>", res[0]);
942
943 return true;
944}
945
947{
948 if (isSimulation())
949 return true;
950
951 if (command != MOTION_START)
952 {
953 PassthruCommand(37, 17, 2, 0, 0);
954 }
955 else
956 {
957 int tt = (CustomNSSlewRate == -1 ? SlewRate : CustomNSSlewRate);
958
959 tt = tt << 16;
960 if (dir != DIRECTION_NORTH)
961 {
962 PassthruCommand(37, 17, 2, tt, 0);
963 }
964 else
965 {
966 PassthruCommand(36, 17, 2, tt, 0);
967 }
968 }
969
970 return true;
971}
972
974{
975 if (isSimulation())
976 return true;
977
978 if (command != MOTION_START)
979 {
980 PassthruCommand(37, 16, 2, 0, 0);
981 }
982 else
983 {
984 int tt = (CustomWESlewRate == -1 ? SlewRate : CustomWESlewRate);
985
986 tt = tt << 16;
987 if (dir != DIRECTION_WEST)
988 {
989 PassthruCommand(36, 16, 2, tt, 0);
990 }
991 else
992 {
993 PassthruCommand(37, 16, 2, tt, 0);
994 }
995 }
996
997 return true;
998}
999
1001{
1002 SlewRate = s + 1;
1003 return true;
1004}
1005
1006int SynscanLegacyDriver::PassthruCommand(int cmd, int target, int msgsize, int data, int numReturn)
1007{
1008 char test[20] = {0};
1009 int bytesRead, bytesWritten;
1010 char a, b, c;
1011 int tt = data;
1012
1013 a = tt % 256;
1014 tt = tt >> 8;
1015 b = tt % 256;
1016 tt = tt >> 8;
1017 c = tt % 256;
1018
1019 // format up a passthru command
1020 memset(test, 0, 20);
1021 test[0] = 80; // passhtru
1022 test[1] = msgsize; // set message size
1023 test[2] = target; // set the target
1024 test[3] = cmd; // set the command
1025 test[4] = c; // set data bytes
1026 test[5] = b;
1027 test[6] = a;
1028 test[7] = numReturn;
1029
1030 LOGF_DEBUG("CMD <%s>", test);
1031 tty_write(PortFD, test, 8, &bytesWritten);
1032 memset(test, 0, 20);
1033 tty_read(PortFD, test, numReturn + 1, 2, &bytesRead);
1034 LOGF_DEBUG("RES <%s>", test);
1035 if (numReturn > 0)
1036 {
1037 int retval = 0;
1038 retval = test[0];
1039 if (numReturn > 1)
1040 {
1041 retval = retval << 8;
1042 retval += test[1];
1043 }
1044 if (numReturn > 2)
1045 {
1046 retval = retval << 8;
1047 retval += test[2];
1048 }
1049 return retval;
1050 }
1051
1052 return 0;
1053}
1054
1056{
1057 LOG_DEBUG("Reading time...");
1058
1059 if (isSimulation())
1060 {
1061 char timeString[MAXINDINAME] = {0};
1062 time_t now = time (nullptr);
1063 strftime(timeString, MAXINDINAME, "%T", gmtime(&now));
1064 TimeTP[UTC].setText("3");
1065 TimeTP[OFFSET].setText(timeString);
1067 TimeTP.apply();
1068 return true;
1069 }
1070
1071 char res[MAX_SYN_BUF] = {0};
1072 int bytesWritten = 0, bytesRead = 0;
1073
1074 // lets see if this hand controller responds to a time request
1075 bytesRead = 0;
1076 LOG_DEBUG("CMD <h>");
1077 tty_write(PortFD, "h", 1, &bytesWritten);
1078
1079 tty_read(PortFD, res, 9, 2, &bytesRead);
1080 LOGF_DEBUG("RES <%s>", res);
1081
1082 if (res[8] == '#')
1083 {
1084 ln_zonedate localTime;
1085 ln_date utcTime;
1086 int offset, daylightflag;
1087
1088 localTime.hours = res[0];
1089 localTime.minutes = res[1];
1090 localTime.seconds = res[2];
1091 localTime.months = res[3];
1092 localTime.days = res[4];
1093 localTime.years = res[5];
1094 offset = (int)res[6];
1095 // Negative GMT offset is read. It needs special treatment
1096 if (offset > 200)
1097 offset -= 256;
1098 localTime.gmtoff = offset;
1099 // this is the daylight savings flag in the hand controller, needed if we did not set the time
1100 daylightflag = res[7];
1101 localTime.years += 2000;
1102 localTime.gmtoff *= 3600;
1103 // now convert to utc
1104 ln_zonedate_to_date(&localTime, &utcTime);
1105
1106 // now we have time from the hand controller, we need to set some variables
1107 int sec;
1108 char utc[100];
1109 char ofs[16] = {0};
1110 sec = (int)utcTime.seconds;
1111 sprintf(utc, "%04d-%02d-%dT%d:%02d:%02d", utcTime.years, utcTime.months, utcTime.days, utcTime.hours,
1112 utcTime.minutes, sec);
1113 if (daylightflag == 1)
1114 offset = offset + 1;
1115 snprintf(ofs, 16, "%d", offset);
1116
1117 TimeTP[UTC].setText(utc);
1118 TimeTP[OFFSET].setText(ofs);
1120 TimeTP.apply(nullptr);
1121
1122 LOGF_INFO("Mount UTC Time %s Offset %d", utc, offset);
1123
1124 return true;
1125 }
1126 return false;
1127}
1128
1130{
1131 LOG_DEBUG("Reading Location...");
1132
1133 if (isSimulation())
1134 {
1135 LocationNP[LOCATION_LATITUDE].setValue(29.5);
1136 LocationNP[LOCATION_LONGITUDE].setValue(48);
1137 LocationNP.apply();
1138 ReadLatLong = false;
1139 return true;
1140 }
1141
1142 char res[MAX_SYN_BUF] = {0};
1143 int bytesWritten = 0, bytesRead = 0;
1144
1145 LOG_DEBUG("CMD <Ka>");
1146 // test for an echo
1147 tty_write(PortFD, "Ka", 2, &bytesWritten);
1148 // Read 2 bytes of response
1149 tty_read(PortFD, res, 2, 2, &bytesRead);
1150 LOGF_DEBUG("RES <%s>", res);
1151
1152 if (res[1] != '#')
1153 {
1154 LOG_WARN("Bad echo in ReadLocation");
1155 }
1156 else
1157 {
1158 // lets see if this hand controller responds to a location request
1159 bytesRead = 0;
1160 LOG_DEBUG("CMD <w>");
1161 tty_write(PortFD, "w", 1, &bytesWritten);
1162
1163 tty_read(PortFD, res, 9, 2, &bytesRead);
1164 LOGF_DEBUG("RES <%s>", res);
1165
1166 if (res[8] == '#')
1167 {
1168 double lat, lon;
1169 // lets parse this data now
1170 int a, b, c, d, e, f, g, h;
1171 a = res[0];
1172 b = res[1];
1173 c = res[2];
1174 d = res[3];
1175 e = res[4];
1176 f = res[5];
1177 g = res[6];
1178 h = res[7];
1179
1180 LOGF_DEBUG("Pos %d:%d:%d %d:%d:%d", a, b, c, e, f, g);
1181
1182 double t1, t2, t3;
1183
1184 t1 = c;
1185 t2 = b;
1186 t3 = a;
1187 t1 = t1 / 3600.0;
1188 t2 = t2 / 60.0;
1189 lat = t1 + t2 + t3;
1190
1191 t1 = g;
1192 t2 = f;
1193 t3 = e;
1194 t1 = t1 / 3600.0;
1195 t2 = t2 / 60.0;
1196 lon = t1 + t2 + t3;
1197
1198 if (d == 1)
1199 lat = lat * -1;
1200 if (h == 1)
1201 lon = 360 - lon;
1202 LocationNP[LOCATION_LATITUDE].setValue(lat);
1203 LocationNP[LOCATION_LONGITUDE].setValue(lon);
1204 LocationNP.apply();
1205
1206 saveConfig(true, "GEOGRAPHIC_COORD");
1207
1208 char LongitudeStr[32] = {0}, LatitudeStr[32] = {0};
1209 fs_sexa(LongitudeStr, lon, 2, 3600);
1210 fs_sexa(LatitudeStr, lat, 2, 3600);
1211 LOGF_INFO("Mount Longitude %s Latitude %s", LongitudeStr, LatitudeStr);
1212
1213 // We dont need to keep reading this one on every cycle
1214 // only need to read it when it's been changed
1215 ReadLatLong = false;
1216 return true;
1217 }
1218 else
1219 {
1220 LOG_INFO("Mount does not support setting location.");
1221 }
1222 }
1223 return false;
1224}
1225
1226bool SynscanLegacyDriver::updateTime(ln_date *utc, double utc_offset)
1227{
1228 char res[MAX_SYN_BUF] = {0};
1229 int bytesWritten = 0, bytesRead = 0;
1230
1231 // start by formatting a time for the hand controller
1232 // we are going to set controller to local time
1233 struct ln_zonedate ltm;
1234
1235 ln_date_to_zonedate(utc, &ltm, (long)utc_offset * 3600.0);
1236
1237 int yr = ltm.years;
1238
1239 yr = yr % 100;
1240
1241 res[0] = 'H';
1242 res[1] = ltm.hours;
1243 res[2] = ltm.minutes;
1244 res[3] = (char)(int)ltm.seconds;
1245 res[4] = ltm.months;
1246 res[5] = ltm.days;
1247 res[6] = yr;
1248 // Strangely enough static_cast<int>(double) results 0 for negative values on arm
1249 // We need to use old C-like casts in this case.
1250 res[7] = (char)(int)utc_offset; // offset from utc so hand controller is running in local time
1251 res[8] = 0; // and no daylight savings adjustments, it's already included in the offset
1252 // lets write a time to the hand controller
1253 bytesRead = 0;
1254
1255 LOGF_INFO("Setting mount date/time to %04d-%02d-%02d %d:%02d:%02d UTC Offset: %d",
1256 ltm.years, ltm.months, ltm.days, ltm.hours, ltm.minutes, ltm.seconds, utc_offset);
1257
1258 if (isSimulation())
1259 return true;
1260
1261 LOGF_DEBUG("CMD <%s>", res);
1262 tty_write(PortFD, res, 9, &bytesWritten);
1263
1264 tty_read(PortFD, res, 1, 2, &bytesRead);
1265 LOGF_DEBUG("RES <%c>", res[0]);
1266
1267 if (res[0] != '#')
1268 {
1269 LOG_INFO("Invalid return from set time");
1270 }
1271 return true;
1272}
1273
1274bool SynscanLegacyDriver::updateLocation(double latitude, double longitude, double elevation)
1275{
1276 INDI_UNUSED(elevation);
1277
1278 char res[MAX_SYN_BUF] = {0};
1279 int bytesWritten = 0, bytesRead = 0;
1280 int s = 0;
1281 bool IsWest = false;
1282 double tmp = 0;
1283
1284 ln_lnlat_posn p1 { 0, 0 };
1285 lnh_lnlat_posn p2;
1286
1287 if (isSimulation())
1288 {
1289 if (CurrentDEC == 0)
1290 {
1291 CurrentDEC = latitude > 0 ? 90 : -90;
1293 }
1294 return true;
1295 }
1296
1297 if (!CanSetLocation)
1298 {
1299 return true;
1300 }
1301 else
1302 {
1303 if (longitude > 180)
1304 {
1305 p1.lng = 360.0 - longitude;
1306 IsWest = true;
1307 }
1308 else
1309 {
1310 p1.lng = longitude;
1311 }
1312 p1.lat = latitude;
1313 ln_lnlat_to_hlnlat(&p1, &p2);
1314 LOGF_INFO("Update location to latitude %d:%d:%1.2f longitude %d:%d:%1.2f",
1315 p2.lat.degrees, p2.lat.minutes, p2.lat.seconds, p2.lng.degrees, p2.lng.minutes, p2.lng.seconds);
1316
1317 res[0] = 'W';
1318 res[1] = p2.lat.degrees;
1319 res[2] = p2.lat.minutes;
1320 tmp = p2.lat.seconds + 0.5;
1321 s = (int)tmp; // put in an int that's rounded
1322 res[3] = s;
1323 if (p2.lat.neg == 0)
1324 {
1325 res[4] = 0;
1326 }
1327 else
1328 {
1329 res[4] = 1;
1330 }
1331
1332 res[5] = p2.lng.degrees;
1333 res[6] = p2.lng.minutes;
1334 s = (int)(p2.lng.seconds + 0.5); // make an int, that's rounded
1335 res[7] = s;
1336 if (IsWest)
1337 res[8] = 1;
1338 else
1339 res[8] = 0;
1340 // All formatted, now send to the hand controller;
1341 bytesRead = 0;
1342
1343 LOGF_DEBUG("CMD <%s>", res);
1344 tty_write(PortFD, res, 9, &bytesWritten);
1345
1346 tty_read(PortFD, res, 1, 2, &bytesRead);
1347 LOGF_DEBUG("RES <%c>", res[0]);
1348
1349 if (res[0] != '#')
1350 {
1351 LOG_INFO("Invalid response for location setting");
1352 }
1353 // want to read it on the next cycle, so we update the fields in the client
1354 ReadLatLong = true;
1355
1356 return true;
1357 }
1358}
1359
1361{
1362 /*
1363 * Frank Liu, R&D Engineer for Skywatcher, says to only issue a Sync
1364 * command, and not to use the Position Reset command, when syncing. I
1365 * removed the position reset code for EQ mounts, but left it in for
1366 * Alt/Az mounts, since it seems to be working, at least for the person
1367 * (@kecsap) who put it in there in the first place. :)
1368 *
1369 * The code prior to kecsap's recent fix would always send a position
1370 * reset command, but it would send Alt/Az coordinates, even to an EQ
1371 * mount. This would really screw up EQ mount alignment.
1372 *
1373 * The reason a lone Sync command appeared to not work before, is because
1374 * it will only accept a Sync command if the offset is relatively small,
1375 * within 6-7 degrees or so. So you must already have done an alignment
1376 * through the handset (a 1-star alignment would suffice), and only use
1377 * the Sync command to "touch-up" the alignment. You can't take a scope,
1378 * power it on, point it to a random place in the sky, do a plate-solve,
1379 * and sync. That won't work.
1380 */
1381
1382 bool IsTrackingBeforeSync = (TrackState == SCOPE_TRACKING);
1383
1384 // Abort any motion before syncing
1385 Abort();
1386
1387 LOGF_INFO("Sync JNow %g %g -> %g %g", CurrentRA, CurrentDEC, ra, dec);
1388 char res[MAX_SYN_BUF] = {0};
1389 int bytesWritten, bytesRead;
1390
1391 if (isSimulation())
1392 {
1393 CurrentRA = ra;
1394 CurrentDEC = dec;
1395 return true;
1396 }
1397
1398 // Alt/Az sync mode
1399 if (MountCode >= 128)
1400 {
1401 INDI::IHorizontalCoordinates TargetAltAz { 0, 0 };
1403 INDI::EquatorialToHorizontal(&epochPos, &m_Location, ln_get_julian_from_sys(), &TargetAltAz);
1404 LOGF_DEBUG("Sync - ra: %g de: %g to az: %g alt: %g", ra, dec, TargetAltAz.azimuth, TargetAltAz.altitude);
1405 // Assemble the Reset Position command for Az axis
1406 int Az = (int)(TargetAltAz.azimuth * 16777216 / 360);
1407
1408 res[0] = 'P';
1409 res[1] = 4;
1410 res[2] = 16;
1411 res[3] = 4;
1412 *reinterpret_cast<unsigned char*>(&res[4]) = (unsigned char)(Az / 65536);
1413 Az -= (Az / 65536) * 65536;
1414 *reinterpret_cast<unsigned char*>(&res[5]) = (unsigned char)(Az / 256);
1415 Az -= (Az / 256) * 256;
1416 *reinterpret_cast<unsigned char*>(&res[6]) = (unsigned char)Az;
1417 res[7] = 0;
1418 tty_write(PortFD, res, 8, &bytesWritten);
1419 tty_read(PortFD, res, 1, 3, &bytesRead);
1420 // Assemble the Reset Position command for Alt axis
1421 int Alt = (int)(TargetAltAz.altitude * 16777216 / 360);
1422
1423 res[0] = 'P';
1424 res[1] = 4;
1425 res[2] = 17;
1426 res[3] = 4;
1427 *reinterpret_cast<unsigned char*>(&res[4]) = (unsigned char)(Alt / 65536);
1428 Alt -= (Alt / 65536) * 65536;
1429 *reinterpret_cast<unsigned char*>(&res[5]) = (unsigned char)(Alt / 256);
1430 Alt -= (Alt / 256) * 256;
1431 *reinterpret_cast<unsigned char*>(&res[6]) = (unsigned char)Alt;
1432 res[7] = 0;
1433 LOGF_DEBUG("CMD <%s>", res);
1434 tty_write(PortFD, res, 8, &bytesWritten);
1435
1436 tty_read(PortFD, res, 1, 2, &bytesRead);
1437 LOGF_DEBUG("CMD <%c>", res[0]);
1438 }
1439
1440 INDI::IEquatorialCoordinates epochPos { 0, 0 }, J2000Pos { 0, 0 };
1441
1442 epochPos.rightascension = ra;
1443 epochPos.declination = dec;
1444
1445 // Synscan accepts J2000 coordinates so we need to convert from JNow to J2000
1446 //ln_get_equ_prec2(&epochPos, ln_get_julian_from_sys(), JD2000, &J2000Pos);
1447 INDI::ObservedToJ2000(&epochPos, ln_get_julian_from_sys(), &J2000Pos);
1448
1449 // Pass the sync command to the handset
1450 int n1 = J2000Pos.rightascension * 0x1000000 / 24;
1451 int n2 = J2000Pos.declination * 0x1000000 / 360;
1452
1453 n1 = n1 << 8;
1454 n2 = n2 << 8;
1455 snprintf(res, MAX_SYN_BUF, "s%08X,%08X", n1, n2);
1456 memset(&res[18], 0, 1);
1457
1458 LOGF_DEBUG("CMD <%s>", res);
1459 tty_write(PortFD, res, 18, &bytesWritten);
1460
1461 tty_read(PortFD, res, 1, 60, &bytesRead);
1462 LOGF_DEBUG("RES <%c>", res[0]);
1463
1464 if (bytesRead != 1 || res[0] != '#')
1465 {
1466 LOG_DEBUG("Timeout waiting for scope to complete syncing.");
1467 return false;
1468 }
1469
1470 // Start tracking again
1471 if (IsTrackingBeforeSync)
1473
1474 return true;
1475}
1476
1477void SynscanLegacyDriver::UpdateMountInformation(bool inform_client)
1478{
1479 bool BasicMountInfoHasChanged = false;
1480 std::string MountCodeStr = std::to_string(MountCode);
1481
1482 if (std::string(BasicMountInfoT[MI_FW_VERSION].text) != HandsetFwVersion)
1483 {
1484 IUSaveText(&BasicMountInfoT[MI_FW_VERSION], HandsetFwVersion.c_str());
1485 BasicMountInfoHasChanged = true;
1486 }
1487 if (std::string(BasicMountInfoT[MI_MOUNT_CODE].text) != MountCodeStr)
1488 {
1489 IUSaveText(&BasicMountInfoT[MI_MOUNT_CODE], MountCodeStr.c_str());
1490 BasicMountInfoHasChanged = true;
1491 }
1492
1493 if (std::string(BasicMountInfoT[MI_ALIGN_STATUS].text) != AlignmentStatus)
1494 {
1495 IUSaveText(&BasicMountInfoT[MI_ALIGN_STATUS], AlignmentStatus.c_str());
1496 BasicMountInfoHasChanged = true;
1497 }
1498 if (std::string(BasicMountInfoT[MI_GOTO_STATUS].text) != GotoStatus)
1499 {
1500 IUSaveText(&BasicMountInfoT[MI_GOTO_STATUS], GotoStatus.c_str());
1501 BasicMountInfoHasChanged = true;
1502 }
1503 if (std::string(BasicMountInfoT[MI_POINT_STATUS].text) != PointingStatus)
1504 {
1505 IUSaveText(&BasicMountInfoT[MI_POINT_STATUS], PointingStatus.c_str());
1506 BasicMountInfoHasChanged = true;
1507 }
1508 if (std::string(BasicMountInfoT[MI_TRACK_MODE].text) != TrackingMode)
1509 {
1510 IUSaveText(&BasicMountInfoT[MI_TRACK_MODE], TrackingMode.c_str());
1511 BasicMountInfoHasChanged = true;
1512 }
1513
1514 if (BasicMountInfoHasChanged && inform_client)
1515 IDSetText(&BasicMountInfoTP, nullptr);
1516}
1517
1518void SynscanLegacyDriver::MountSim()
1519{
1520 static struct timeval ltv;
1521 struct timeval tv;
1522 double dt, da, dx;
1523 int nlocked;
1524
1525 /* update elapsed time since last poll, don't presume exactly POLLMS */
1526 gettimeofday(&tv, nullptr);
1527
1528 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1529 ltv = tv;
1530
1531 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1532 ltv = tv;
1533 double currentSlewRate = SLEW_RATE[SlewRateSP.findOnSwitchIndex()] * TRACKRATE_SIDEREAL / 3600.0;
1534 da = currentSlewRate * dt;
1535
1536 /* Process per current state. We check the state of EQUATORIAL_COORDS and act accordingly */
1537 switch (TrackState)
1538 {
1539 case SCOPE_IDLE:
1540 CurrentRA += (TrackRateNP[AXIS_RA].getValue() / 3600.0 * dt) / 15.0;
1542 break;
1543
1544 case SCOPE_TRACKING:
1545 break;
1546
1547 case SCOPE_SLEWING:
1548 case SCOPE_PARKING:
1549 /* slewing - nail it when both within one pulse @ SLEWRATE */
1550 nlocked = 0;
1551
1552 dx = TargetRA - CurrentRA;
1553
1554 // Take shortest path
1555 if (fabs(dx) > 12)
1556 dx *= -1;
1557
1558 if (fabs(dx) <= da)
1559 {
1561 nlocked++;
1562 }
1563 else if (dx > 0)
1564 CurrentRA += da / 15.;
1565 else
1566 CurrentRA -= da / 15.;
1567
1568 if (CurrentRA < 0)
1569 CurrentRA += 24;
1570 else if (CurrentRA > 24)
1571 CurrentRA -= 24;
1572
1573 dx = TargetDEC - CurrentDEC;
1574 if (fabs(dx) <= da)
1575 {
1577 nlocked++;
1578 }
1579 else if (dx > 0)
1580 CurrentDEC += da;
1581 else
1582 CurrentDEC -= da;
1583
1584 if (nlocked == 2)
1585 {
1588 else
1590 }
1591
1592 break;
1593
1594 default:
1595 break;
1596 }
1597
1599}
#define SLEW_RATE
void setDefaultHost(const char *addressHost)
void setDefaultPort(uint32_t addressPort)
bool isConnected() const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
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.
Connection::Interface * getActiveConnection()
virtual bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void setLabel(const char *label)
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.
void setTelescopeConnection(const uint8_t &value)
setTelescopeConnection Set telescope connection mode. Child class should call this in the constructor...
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....
Connection::TCP * tcpConnection
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
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 ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
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 Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual const char * getDefaultName() override
int HexStrToInteger(const std::string &str)
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool Park() override
Park the telescope to its home position.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
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.
INDI::PropertyNumber TrackRateNP
INDI::PropertySwitch SlewRateSP
INDI::PropertyText TimeTP
INDI::PropertySwitch MountTypeSP
INDI::PropertyNumber LocationNP
double ra
double dec
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RO
Definition indiapi.h:182
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
#define MAXINDINAME
Definition indiapi.h:189
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_EAST
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_SOUTH
@ 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
double rangeDec(double decdegrees)
rangeDec Limits declination value to be in -90 to 90 range.
Definition indicom.c:1341
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition indicom.c:130
Implementations for common driver routines.
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sidereal time given longitude and system clock.
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition indidevapi.c:416
#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 IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define getDeviceName
void J2000toObserved(IEquatorialCoordinates *J2000pos, double jd, IEquatorialCoordinates *observed)
*J2000toObserved converts catalogue to observed
Definition libastro.cpp:81
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition libastro.cpp:141
void ObservedToJ2000(IEquatorialCoordinates *observed, double jd, IEquatorialCoordinates *J2000pos)
ObservedToJ2000 converts an observed position to a J2000 catalogue position removes aberration,...
Definition libastro.cpp:51
uint16_t type
Definition pwc-ioctl.h:0
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:248
#define SYNSCAN_SLEW_RATES