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