Instrument Neutral Distributed Interface INDI  2.0.2
nstep.cpp
Go to the documentation of this file.
1 /*
2  NStep Focuser
3 
4  Copyright(c) 2019 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 Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 #include "nstep.h"
22 
23 #include "indicom.h"
24 
25 #include <cstring>
26 #include <termios.h>
27 #include <memory>
28 #include <thread>
29 #include <chrono>
30 
31 static std::unique_ptr<NStep> nstep(new NStep());
32 
34 {
35  setVersion(1, 2);
41 }
42 
44 {
46 
47  // Focuser temperature
48  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -100, 100, 0, 0);
49  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
51 
52  // Compensation Modes
53  IUFillSwitch(&CompensationModeS[COMPENSATION_MODE_OFF], "COMPENSATION_MODE_OFF", "Off", ISS_ON);
54  IUFillSwitch(&CompensationModeS[COMPENSATION_MODE_ONE_SHOT], "COMPENSATION_MODE_ONE_SHOT", "One shot", ISS_OFF);
55  IUFillSwitch(&CompensationModeS[COMPENSATION_MODE_AUTO], "COMPENSATION_MODE_AUTO", "Auto", ISS_OFF);
56  IUFillSwitchVector(&CompensationModeSP, CompensationModeS, 3, getDeviceName(), "COMPENSATION_MODE", "Mode",
57  COMPENSATION_TAB, IP_RW, ISR_1OFMANY, 0, IPS_OK);
58 
59  // Prime for Manual
60  IUFillSwitch(&PrimeManualS[0], "MANUAL_MODE_PRIME", "Prime Manual Mode", ISS_OFF);
61  IUFillSwitchVector(&PrimeManualSP, PrimeManualS, 1, getDeviceName(), "COMPENSATION_PRIME", "Prime",
62  COMPENSATION_TAB, IP_RW, ISR_1OFMANY, 0, IPS_OK);
63 
64  // Compensation Settings
65  IUFillNumber(&CompensationSettingsN[COMPENSATION_SETTING_CHANGE], "COMPENSATION_SETTING_CHANGE", "Delta T. (C)", "%.1f",
66  -99, 99, 0.1, 0);
67  IUFillNumber(&CompensationSettingsN[COMPENSATION_SETTING_STEP], "COMPENSATION_SETTING_STEP", "Steps per Delta", "%.0f", 0,
68  999, 1, 0);
69  IUFillNumber(&CompensationSettingsN[COMPENSATION_SETTING_BACKLASH], "COMPENSATION_SETTING_BACKLASH", "Backlash steps",
70  "%.0f", 0, 999, 1, 0);
71  IUFillNumber(&CompensationSettingsN[COMPENSATION_SETTING_TIMER], "COMPENSATION_SETTING_TIMER", "Averaged Time (s)", "%.0f",
72  0, 75, 1, 0);
73  IUFillNumberVector(&CompensationSettingsNP, CompensationSettingsN, 4, getDeviceName(), "COMPENSATION_SETTING", "Settings",
74  COMPENSATION_TAB, IP_RW, 0, IPS_OK);
75 
76  // Stepping Modes
77  IUFillSwitch(&SteppingModeS[STEPPING_WAVE], "STEPPING_WAVE", "Wave", ISS_OFF);
78  IUFillSwitch(&SteppingModeS[STEPPING_HALF], "STEPPING_HALF", "Half", ISS_OFF);
79  IUFillSwitch(&SteppingModeS[STEPPING_FULL], "STEPPING_FULL", "Full", ISS_ON);
80  IUFillSwitchVector(&SteppingModeSP, SteppingModeS, 3, getDeviceName(), "STEPPING_MODE", "Mode",
81  STEPPING_TAB, IP_RW, ISR_1OFMANY, 0, IPS_OK);
82 
83  // Stepping Phase
84  IUFillNumber(&SteppingPhaseN[0], "PHASES", "Wiring", "%.f", 0, 2, 1, 0);
85  IUFillNumberVector(&SteppingPhaseNP, SteppingPhaseN, 1, getDeviceName(), "STEPPING_PHASE", "Phase",
86  STEPPING_TAB, IP_RW, 0, IPS_OK);
87 
88  // Max Speed
89  IUFillNumber(&MaxSpeedN[0], "RATE", "Rate", "%.f", 1, 254, 10, 0);
90  IUFillNumberVector(&MaxSpeedNP, MaxSpeedN, 1, getDeviceName(), "MAX_SPEED", "Max Speed",
92 
93  // Coil Energized Status
94  IUFillSwitch(&CoilStatusS[COIL_ENERGIZED_OFF], "COIL_ENERGIZED_OFF", "De-energized", ISS_OFF);
95  IUFillSwitch(&CoilStatusS[COIL_ENERGIZED_ON], "COIL_ENERGIZED_ON", "Energized", ISS_OFF);
96  IUFillSwitchVector(&CoilStatusSP, CoilStatusS, 2, getDeviceName(), "COIL_MODE", "Coil After Move",
98 
100 
101  // Set limits as per documentation
102  FocusAbsPosN[0].min = 0;
103  FocusAbsPosN[0].max = 999999;
104  FocusAbsPosN[0].step = 1000;
105 
106  FocusRelPosN[0].min = 0;
107  FocusRelPosN[0].max = 999;
108  FocusRelPosN[0].step = 100;
109 
110  FocusSpeedN[0].min = 1;
111  FocusSpeedN[0].max = 254;
112  FocusSpeedN[0].step = 10;
113 
114  return true;
115 }
116 
118 {
119  return "Rigel NStep";
120 }
121 
123 {
124  if (isConnected())
125  {
126  // Read these values before defining focuser interface properties
127  readPosition();
128  readSpeedInfo();
129  }
130 
132 
133  if (isConnected())
134  {
135  if (readTemperature())
136  defineProperty(&TemperatureNP);
137 
138  bool rc = getStartupValues();
139 
140  // Settings
141  defineProperty(&MaxSpeedNP);
142  defineProperty(&CompensationModeSP);
143  defineProperty(&PrimeManualSP);
144  defineProperty(&CompensationSettingsNP);
145  defineProperty(&SteppingModeSP);
146  defineProperty(&SteppingPhaseNP);
147  defineProperty(&CoilStatusSP);
148 
149  if (rc)
150  LOG_INFO("NStep is ready.");
151  else
152  LOG_WARN("Failed to query startup values.");
153  }
154  else
155  {
156  if (TemperatureNP.s == IPS_OK)
157  deleteProperty(TemperatureNP.name);
158 
159  deleteProperty(MaxSpeedNP.name);
160  deleteProperty(CompensationModeSP.name);
161  deleteProperty(PrimeManualSP.name);
162  deleteProperty(CompensationSettingsNP.name);
163  deleteProperty(SteppingModeSP.name);
164  deleteProperty(SteppingPhaseNP.name);
165  deleteProperty(CoilStatusSP.name);
166  }
167 
168  return true;
169 }
170 
172 {
173  char cmd[NSTEP_LEN] = {0}, res[NSTEP_LEN] = {0};
174 
175  // Ack
176  cmd[0] = 0x6;
177 
178  bool rc = sendCommand(cmd, res, 1, 1);
179  if (rc == false)
180  return false;
181 
182  return res[0] == 'S';
183 }
184 
185 bool NStep::sendCommand(const char * cmd, char * res, int cmd_len, int res_len)
186 {
187  int nbytes_written = 0, nbytes_read = 0, rc = -1;
188 
189  tcflush(PortFD, TCIOFLUSH);
190 
191  if (cmd_len > 0)
192  {
193  char hex_cmd[NSTEP_LEN * 3] = {0};
194  hexDump(hex_cmd, cmd, cmd_len);
195  LOGF_DEBUG("CMD <%s>", hex_cmd);
196  rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
197  }
198  else
199  {
200  LOGF_DEBUG("CMD <%s>", cmd);
201  rc = tty_write_string(PortFD, cmd, &nbytes_written);
202  }
203 
204  if (rc != TTY_OK)
205  {
206  char errstr[MAXRBUF] = {0};
207  tty_error_msg(rc, errstr, MAXRBUF);
208  LOGF_ERROR("Serial write error: %s.", errstr);
209  return false;
210  }
211 
212  if (res == nullptr)
213  return true;
214 
215  if (res_len > 0)
216  rc = tty_read(PortFD, res, res_len, NSTEP_TIMEOUT, &nbytes_read);
217  else
218  rc = tty_nread_section(PortFD, res, NSTEP_LEN, NSTEP_STOP_CHAR, NSTEP_TIMEOUT, &nbytes_read);
219 
220  if (rc != TTY_OK)
221  {
222  char errstr[MAXRBUF] = {0};
223  tty_error_msg(rc, errstr, MAXRBUF);
224  LOGF_ERROR("Serial read error: %s.", errstr);
225  return false;
226  }
227 
228  if (res_len > 0)
229  {
230  char hex_res[NSTEP_LEN * 3] = {0};
231  hexDump(hex_res, res, res_len);
232  LOGF_DEBUG("RES <%s>", hex_res);
233  }
234  else
235  {
236  LOGF_DEBUG("RES <%s>", res);
237  }
238 
239  tcflush(PortFD, TCIOFLUSH);
240 
241  return true;
242 }
243 
244 void NStep::hexDump(char * buf, const char * data, int size)
245 {
246  for (int i = 0; i < size; i++)
247  sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
248 
249  if (size > 0)
250  buf[3 * size - 1] = '\0';
251 }
252 
253 bool NStep::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
254 {
255  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
256  {
257  // Compensation Settings
258  if (!strcmp(name, CompensationSettingsNP.name))
259  {
260  // Extract values
261  int change = 0, step = 0, backlash = 0, timer = 0;
262  for (int i = 0; i < n; i++)
263  {
264  if (!strcmp(names[i], CompensationSettingsN[COMPENSATION_SETTING_CHANGE].name))
265  {
266  change = values[i];
267  }
268  else if (!strcmp(names[i], CompensationSettingsN[COMPENSATION_SETTING_STEP].name))
269  {
270  step = values[i];
271  }
272  else if (!strcmp(names[i], CompensationSettingsN[COMPENSATION_SETTING_BACKLASH].name))
273  {
274  backlash = values[i];
275  }
276  else if (!strcmp(names[i], CompensationSettingsN[COMPENSATION_SETTING_TIMER].name))
277  {
278  timer = values[i];
279  }
280  }
281 
282  // Try to update settings
283  if (setCompensationSettings(change, step, backlash, timer))
284  {
285  IUUpdateNumber(&CompensationSettingsNP, values, names, n);
286  CompensationSettingsNP.s = IPS_OK;
287  }
288  else
289  {
290  CompensationSettingsNP.s = IPS_ALERT;
291  }
292 
293  IDSetNumber(&CompensationSettingsNP, nullptr);
294  return true;
295  }
296 
297 
298  // Stepping Phase
299  if (!strcmp(name, SteppingPhaseNP.name))
300  {
301  if (setSteppingPhase(static_cast<uint8_t>(values[0])))
302  {
303  IUUpdateNumber(&SteppingPhaseNP, values, names, n);
304  SteppingPhaseNP.s = IPS_OK;
305  }
306  else
307  SteppingPhaseNP.s = IPS_ALERT;
308 
309  IDSetNumber(&SteppingPhaseNP, nullptr);
310  return true;
311  }
312 
313  // Max Speed
314  if (!strcmp(name, MaxSpeedNP.name))
315  {
316  if (setMaxSpeed(static_cast<uint8_t>(values[0])))
317  {
318  IUUpdateNumber(&MaxSpeedNP, values, names, n);
319  MaxSpeedNP.s = IPS_OK;
320 
321  // We must update the Min/Max of focus speed
322  FocusSpeedN[0].max = values[0];
324  }
325  else
326  {
327  MaxSpeedNP.s = IPS_ALERT;
328  }
329 
330  IDSetNumber(&MaxSpeedNP, nullptr);
331  return true;
332  }
333  }
334 
335  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
336 }
337 
338 bool NStep::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
339 {
340  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
341  {
342  // Temperature Compensation Mode
343  if (!strcmp(name, CompensationModeSP.name))
344  {
345  int prevIndex = IUFindOnSwitchIndex(&CompensationModeSP);
346  IUUpdateSwitch(&CompensationModeSP, states, names, n);
347  int mode = IUFindOnSwitchIndex(&CompensationModeSP);
348  if (setCompensationMode(mode))
349  {
350  CompensationModeSP.s = IPS_OK;
351  // If it was set to one shot, we put it back to off?
352  switch (mode)
353  {
354  case COMPENSATION_MODE_OFF:
355  LOG_INFO("Temperature compensation is disabled.");
356  break;
357 
358  case COMPENSATION_MODE_ONE_SHOT:
359  IUResetSwitch(&CompensationModeSP);
360  CompensationModeS[COMPENSATION_MODE_OFF].s = ISS_ON;
361  LOG_INFO("One shot compensation applied.");
362  break;
363 
364  case COMPENSATION_MODE_AUTO:
365  LOG_INFO("Automatic temperature compensation is enabled.");
366  break;
367  }
368  }
369  else
370  {
371  IUResetSwitch(&CompensationModeSP);
372  CompensationModeS[prevIndex].s = ISS_ON;
373  CompensationModeSP.s = IPS_ALERT;
374  LOG_ERROR("Failed to change temperature compensation mode.");
375  }
376 
377  IDSetSwitch(&CompensationModeSP, nullptr);
378  return true;
379  }
380 
381  // Manual Prime
382  if (!strcmp(name, PrimeManualSP.name))
383  {
384  sendCommand(":TI");
385  PrimeManualSP.s = IPS_OK;
386  IDSetSwitch(&PrimeManualSP, nullptr);
387  LOG_INFO("Prime for manual complete. Click One Shot to apply manual compensation once.");
388  return true;
389  }
390 
391  // Stepping Mode
392  if (!strcmp(name, SteppingModeSP.name))
393  {
394  IUUpdateSwitch(&SteppingModeSP, states, names, n);
395  SteppingModeSP.s = IPS_OK;
396  IDSetSwitch(&SteppingModeSP, nullptr);
397  return true;
398  }
399 
400  // Coil Status after Move is done
401  if (!strcmp(name, CoilStatusSP.name))
402  {
403  int prevIndex = IUFindOnSwitchIndex(&CoilStatusSP);
404  IUUpdateSwitch(&CoilStatusSP, states, names, n);
405  int state = IUFindOnSwitchIndex(&CoilStatusSP);
406  if (setCoilStatus(state))
407  {
408  CoilStatusSP.s = IPS_OK;
409  if (state == COIL_ENERGIZED_ON)
410  LOG_WARN("Coil shall be kept energized after motion is complete. Watch for motor heating!");
411  else
412  LOG_INFO("Coil shall be de-energized after motion is complete.");
413  }
414  else
415  {
416  IUResetSwitch(&CoilStatusSP);
417  CoilStatusS[prevIndex].s = ISS_ON;
418  CoilStatusSP.s = IPS_ALERT;
419  LOG_ERROR("Failed to update coil energization status.");
420  }
421 
422  IDSetSwitch(&CoilStatusSP, nullptr);
423  return true;
424  }
425  }
426 
427  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
428 }
429 
430 bool NStep::getStartupValues()
431 {
432  bool rc1 = readCoilStatus();
433  bool rc2 = readSteppingInfo();
434  bool rc3 = readCompensationInfo();
435 
436  return (rc1 && rc2 && rc3);
437 }
438 
439 IPState NStep::MoveAbsFocuser(uint32_t targetTicks)
440 {
441  m_TargetDiff = targetTicks - FocusAbsPosN[0].value;
442  return IPS_BUSY;
443 }
444 
446 {
447  m_TargetDiff = ticks * ((dir == FOCUS_INWARD) ? -1 : 1);
448  return MoveAbsFocuser(FocusAbsPosN[0].value + m_TargetDiff);
449 }
450 
452 {
453  return sendCommand("F00000#");
454 }
455 
457 {
458  if (isConnected() == false)
459  return;
460 
461  double currentPosition = FocusAbsPosN[0].value;
462 
463  readPosition();
464 
465  // Check if we have a pending motion
466  // and if we STOPPED, then let's take the next action
467  if ( (FocusAbsPosNP.s == IPS_BUSY || FocusRelPosNP.s == IPS_BUSY) && isMoving() == false)
468  {
469  // Are we done moving?
470  if (m_TargetDiff == 0)
471  {
474  IDSetNumber(&FocusAbsPosNP, nullptr);
475  IDSetNumber(&FocusRelPosNP, nullptr);
476  }
477  else
478  {
479  // 999 is the max we can go in one command
480  // so we need to go 999 or LESS
481  // therefore for larger movements, we break it down.
482  int nextMotion = (std::abs(m_TargetDiff) > 999) ? 999 : std::abs(m_TargetDiff);
483  int direction = m_TargetDiff > 0 ? FOCUS_OUTWARD : FOCUS_INWARD;
484  int mode = IUFindOnSwitchIndex(&SteppingModeSP);
485  char cmd[NSTEP_LEN] = {0};
486  snprintf(cmd, NSTEP_LEN, ":F%d%d%03d#", direction, mode, nextMotion);
487  if (sendCommand(cmd) == false)
488  {
489  LOG_ERROR("Failed to issue motion command.");
490  if (FocusRelPosNP.s == IPS_BUSY)
491  {
493  IDSetNumber(&FocusRelPosNP, nullptr);
494  }
495  if (FocusAbsPosNP.s == IPS_BUSY)
496  {
498  IDSetNumber(&FocusAbsPosNP, nullptr);
499  }
500  }
501  else
502  // Reduce target diff depending on the motion direction
503  // Negative targetDiff increases eventually to zero
504  // Positive targetDiff decreases eventually to zero
505  m_TargetDiff = m_TargetDiff + (nextMotion * ((direction == FOCUS_INWARD) ? 1 : -1));
506  }
507  // Check if can update the absolute position in case it changed.
508  }
509  else if (currentPosition != FocusAbsPosN[0].value)
510  {
511  IDSetNumber(&FocusAbsPosNP, nullptr);
512  }
513 
514  // Read temperature
515  if (TemperatureNP.s == IPS_OK && m_TemperatureCounter++ == NSTEP_TEMPERATURE_FREQ)
516  {
517  m_TemperatureCounter = 0;
518  if (readTemperature())
519  IDSetNumber(&TemperatureNP, nullptr);
520  }
521 
523 }
524 
525 bool NStep::isMoving()
526 {
527  char res[NSTEP_LEN] = {0};
528 
529  bool rc = sendCommand("S", res, 1, 1);
530 
531  if (rc && res[0] == '1')
532  return true;
533 
534  return false;
535 }
536 
537 bool NStep::readTemperature()
538 {
539  char res[NSTEP_LEN] = {0};
540 
541  if (sendCommand(":RT", res, 3, 4) == false)
542  return false;
543 
544  float temperature = -1000;
545  sscanf(res, "%f", &temperature);
546 
547  // Divide by 10 to get actual value
548  temperature /= 10.0;
549 
550  if (temperature < -80)
551  return false;
552 
553  TemperatureN[0].value = temperature;
554  TemperatureNP.s = IPS_OK;
555 
556  return true;
557 }
558 
559 bool NStep::readPosition()
560 {
561  char res[NSTEP_LEN] = {0};
562 
563  if (sendCommand(":RP", res, 3, 7) == false)
564  return false;
565 
566  int32_t pos = 1e6;
567  sscanf(res, "%d", &pos);
568 
569  if (pos == 1e6)
570  return false;
571 
572  FocusAbsPosN[0].value = pos;
573 
574  return true;
575 }
576 
577 bool NStep::readCompensationInfo()
578 {
579  char res[NSTEP_LEN] = {0};
580  int32_t change = 1e6, step = 1e6, state = 1e6, backlash = 1e6, timer = 1e6;
581 
582  // State (Off, One shot, or Auto)
583  if (sendCommand(":RG", res, 3, 1) == false)
584  {
585  std::this_thread::sleep_for(std::chrono::milliseconds(250));
586  if (sendCommand(":RG", res, 3, 1) == false)
587  return false;
588  }
589  sscanf(res, "%d", &state);
590  if (state == 1e6)
591  return false;
592  IUResetSwitch(&CompensationModeSP);
593  CompensationModeS[state].s = ISS_ON;
594  CompensationModeSP.s = IPS_OK;
595 
596  // Change
597  memset(res, 0, NSTEP_LEN);
598  if (sendCommand(":RA", res, 3, 4) == false)
599  {
600  std::this_thread::sleep_for(std::chrono::milliseconds(250));
601  if (sendCommand(":RA", res, 3, 4) == false)
602  return false;
603  }
604  sscanf(res, "%d", &change);
605  if (change == 1e6)
606  return false;
607  CompensationSettingsN[COMPENSATION_SETTING_CHANGE].value = change;
608 
609  // Step
610  memset(res, 0, NSTEP_LEN);
611  if (sendCommand(":RB", res, 3, 3) == false)
612  {
613  std::this_thread::sleep_for(std::chrono::milliseconds(250));
614  if (sendCommand(":RB", res, 3, 3) == false)
615  return false;
616  }
617  sscanf(res, "%d", &step);
618  if (step == 1e6)
619  return false;
620  CompensationSettingsN[COMPENSATION_SETTING_STEP].value = step;
621 
622  // Backlash
623  memset(res, 0, NSTEP_LEN);
624  if (sendCommand(":RE", res, 3, 3) == false)
625  {
626  std::this_thread::sleep_for(std::chrono::milliseconds(250));
627  if (sendCommand(":RE", res, 3, 3) == false)
628  return false;
629  }
630  sscanf(res, "%d", &backlash);
631  if (backlash == 1e6)
632  return false;
633  CompensationSettingsN[COMPENSATION_SETTING_BACKLASH].value = backlash;
634 
635  // Timer
636  memset(res, 0, NSTEP_LEN);
637  if (sendCommand(":RH", res, 3, 2) == false)
638  {
639  std::this_thread::sleep_for(std::chrono::milliseconds(250));
640  if (sendCommand(":RH", res, 3, 2) == false)
641  return false;
642  }
643  sscanf(res, "%d", &timer);
644  if (timer == 1e6)
645  return false;
646  CompensationSettingsN[COMPENSATION_SETTING_TIMER].value = timer;
647  CompensationSettingsNP.s = IPS_OK;
648 
649  return true;
650 
651 }
652 
653 bool NStep::readSpeedInfo()
654 {
655  char res[NSTEP_LEN] = {0};
656  int32_t max_step = 1e6, current_step = 1e6;
657 
658  // Max Step
659  if (sendCommand(":RS", res, 3, 3) == false)
660  return false;
661  sscanf(res, "%d", &max_step);
662  if (max_step == 1e6)
663  return false;
664 
665  // Current Step
666  if (sendCommand(":RO", res, 3, 3) == false)
667  return false;
668  sscanf(res, "%d", &current_step);
669  if (current_step == 1e6)
670  return false;
671 
672  MaxSpeedN[0].value = 254 - max_step + 1;
673  MaxSpeedNP.s = IPS_OK;
674 
675  // nStep defines speed step rates from 1 to 254
676  // when 1 being the fastest, so for speed we flip the values
677  FocusSpeedN[0].max = 254 - max_step + 1;
678  FocusSpeedN[0].value = 254 - current_step + 1;
680 
681  return true;
682 }
683 
684 bool NStep::readSteppingInfo()
685 {
686  char res[NSTEP_LEN] = {0};
687 
688  if (sendCommand(":RW", res, 3, 1) == false)
689  return false;
690 
691  int32_t phase = 1e6;
692  sscanf(res, "%d", &phase);
693 
694  if (phase == 1e6)
695  return false;
696 
697  SteppingPhaseN[0].value = phase;
698  SteppingPhaseNP.s = IPS_OK;
699 
700  return true;
701 }
702 
703 bool NStep::readCoilStatus()
704 {
705  char res[NSTEP_LEN] = {0};
706 
707  if (sendCommand(":RC", res, 3, 1) == false)
708  return false;
709 
710  IUResetSwitch(&CoilStatusSP);
711 
712  CoilStatusS[COIL_ENERGIZED_OFF].s = (res[0] == '0') ? ISS_ON : ISS_OFF;
713  CoilStatusS[COIL_ENERGIZED_ON].s = (res[0] == '0') ? ISS_OFF : ISS_ON;
714  CoilStatusSP.s = IPS_OK;
715 
716  return true;
717 }
718 
719 bool NStep::SyncFocuser(uint32_t ticks)
720 {
721  char cmd[NSTEP_LEN] = {0};
722  snprintf(cmd, NSTEP_LEN, "#:CP+%06d#", ticks);
723  return sendCommand(cmd);
724 }
725 
726 bool NStep::SetFocuserSpeed(int speed)
727 {
728  // Speed and Current NStep steps are opposite.
729  // Speed 1 is slowest, translated to 254 for nStep.
730  char cmd[NSTEP_LEN] = {0};
731  snprintf(cmd, NSTEP_LEN, "#:CO%03d#", 254 - speed + 1);
732  return sendCommand(cmd);
733 }
734 
735 bool NStep::setMaxSpeed(uint8_t maxSpeed)
736 {
737  // INDI Focus Speed and Current NStep steps are opposite.
738  // INDI Speed 1 is slowest, translated to 254 for nStep.
739  // and vice versa
740  char cmd[NSTEP_LEN] = {0};
741  snprintf(cmd, NSTEP_LEN, ":CS%03d#", 254 - maxSpeed + 1);
742  return sendCommand(cmd);
743 }
744 
745 bool NStep::setCompensationMode(uint8_t mode)
746 {
747  char cmd[NSTEP_LEN] = {0};
748  snprintf(cmd, NSTEP_LEN, "#:TA%01d#", mode);
749  return sendCommand(cmd);
750 }
751 
752 bool NStep::setCompensationSettings(double change, double move, double backlash, double timer)
753 {
754  int temperature_change = change * 10;
755  char cmd[NSTEP_LEN] = {0};
756  snprintf(cmd, NSTEP_LEN, ":TT%+03d#", temperature_change);
757  bool rc1 = sendCommand(cmd);
758 
759  int temperature_steps = static_cast<int>(move);
760  snprintf(cmd, NSTEP_LEN, ":TS%03d#", temperature_steps);
761  bool rc2 = sendCommand(cmd);
762 
763  int temperature_backlash = static_cast<int>(backlash);
764  snprintf(cmd, NSTEP_LEN, ":TB%03d#", temperature_backlash);
765  bool rc3 = sendCommand(cmd);
766 
767 
768  int temperature_timer = static_cast<int>(timer);
769  bool rc4 = true;
770  if (timer > 0)
771  {
772  snprintf(cmd, NSTEP_LEN, ":TC%02d#", temperature_timer);
773  rc4 = sendCommand(cmd);
774  }
775 
776  return (rc1 && rc2 && rc3 && rc4);
777 }
778 
779 bool NStep::setSteppingPhase(uint8_t phase)
780 {
781  char cmd[NSTEP_LEN] = {0};
782  snprintf(cmd, NSTEP_LEN, "#:CW%01d#", phase);
783  return sendCommand(cmd);
784 }
785 
786 bool NStep::setCoilStatus(uint8_t status)
787 {
788  char cmd[NSTEP_LEN] = {0};
789  snprintf(cmd, NSTEP_LEN, "#:CC%01d#", status == COIL_ENERGIZED_OFF ? 1 : 0);
790  return sendCommand(cmd);
791 }
792 
794 {
796 
797  IUSaveConfigNumber(fp, &CompensationSettingsNP);
798  IUSaveConfigSwitch(fp, &CompensationModeSP);
799  IUSaveConfigSwitch(fp, &SteppingModeSP);
800 
801  return true;
802 }
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INumberVectorProperty FocusSpeedNP
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:42
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: nstep.h:29
NStep()
Definition: nstep.cpp:33
bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: nstep.cpp:43
bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
Definition: nstep.cpp:726
IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition: nstep.cpp:439
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: nstep.cpp:456
bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: nstep.cpp:793
IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: nstep.cpp:445
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: nstep.cpp:122
bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: nstep.cpp:253
const char * getDefaultName() override
Definition: nstep.cpp:117
virtual bool Handshake() override
perform handshake with device to check communication
Definition: nstep.cpp:171
bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: nstep.cpp:451
bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: nstep.cpp:338
bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
Definition: nstep.cpp:719
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
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
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
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
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1296
#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 MAXRBUF
Definition: indiserver.cpp:102
__u8 cmd[4]
Definition: pwc-ioctl.h:2
#define currentPosition
Definition: robofocus.cpp:37
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371