Instrument Neutral Distributed Interface INDI  2.0.2
esatto.cpp
Go to the documentation of this file.
1 /*
2  Esatto Focuser
3  Copyright (C) 2022 Jasem Mutlaq
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 */
20 
21 #include "esatto.h"
22 
23 #include <cmath>
24 #include <cstring>
25 #include <memory>
26 #include <algorithm>
27 
29 
30 static std::unique_ptr<Esatto> sesto(new Esatto());
31 
32 static const char *ENVIRONMENT_TAB = "Environment";
33 
35 {
36  setVersion(1, 0);
37 
38  // Can move in Absolute & Relative motions, can AbortFocuser motion.
40 
41 }
42 
47 {
48 
50 
51  FocusBacklashN[0].min = 0;
52  FocusBacklashN[0].max = 10000;
53  FocusBacklashN[0].step = 1;
54  FocusBacklashN[0].value = 0;
55 
56  setConnectionParams();
57 
58  // Firmware information
59  FirmwareTP[FIRMWARE_SN].fill("SERIALNUMBER", "Serial Number", "");
60  FirmwareTP[FIRMWARE_VERSION].fill("VERSION", "Version", "");
61  FirmwareTP.fill(getDeviceName(), "FOCUS_FIRMWARE", "Firmware", CONNECTION_TAB, IP_RO, 0, IPS_IDLE);
62 
63  // Voltage Information
64  VoltageNP[VOLTAGE_12V].fill("VOLTAGE_12V", "12v", "%.2f", 0, 100, 0., 0.);
65  VoltageNP[VOLTAGE_USB].fill("VOLTAGE_USB", "USB", "%.2f", 0, 100, 0., 0.);
66  VoltageNP.fill(getDeviceName(), "VOLTAGE_IN", "Voltage in", ENVIRONMENT_TAB, IP_RO, 0, IPS_IDLE);
67 
68  // Focuser temperature
69  TemperatureNP[TEMPERATURE_MOTOR].fill("TEMPERATURE", "Motor (c)", "%.2f", -50, 70., 0., 0.);
70  TemperatureNP[TEMPERATURE_EXTERNAL].fill("TEMPERATURE_ETX", "External (c)", "%.2f", -50, 70., 0., 0.);
71  TemperatureNP.fill(getDeviceName(), "FOCUS_TEMPERATURE", "Temperature", ENVIRONMENT_TAB, IP_RO, 0, IPS_IDLE);
72 
73  // Speed Moves
74  FastMoveSP[FASTMOVE_IN].fill("FASTMOVE_IN", "Move In", ISS_OFF);
75  FastMoveSP[FASTMOVE_OUT].fill("FASTMOVE_OUT", "Move out", ISS_OFF);
76  FastMoveSP[FASTMOVE_STOP].fill("FASTMOVE_STOP", "Stop", ISS_OFF);
77  FastMoveSP.fill(getDeviceName(), "FAST_MOVE", "Calibration Move", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
78 
79  // Override the default Max. Position to make it Read-Only
80  IUFillNumberVector(&FocusMaxPosNP, FocusMaxPosN, 1, getDeviceName(), "FOCUS_MAX", "Max. Position", MAIN_CONTROL_TAB, IP_RO,
81  0, IPS_IDLE);
82 
83  // Relative and absolute movement
84  FocusRelPosN[0].min = 0.;
85  FocusRelPosN[0].max = 50000.;
86  FocusRelPosN[0].value = 0;
87  FocusRelPosN[0].step = 1000;
88 
89  FocusAbsPosN[0].min = 0.;
90  FocusAbsPosN[0].max = 200000.;
91  FocusAbsPosN[0].value = 0;
92  FocusAbsPosN[0].step = 10000;
93 
94  FocusMaxPosN[0].value = 2097152;
95  PresetN[0].max = FocusMaxPosN[0].value;
96  PresetN[1].max = FocusMaxPosN[0].value;
97  PresetN[2].max = FocusMaxPosN[0].value;
98 
100 
102 
103  return true;
104 }
105 
110 {
111  if (isConnected())
112  getStartupValues();
113 
115 
116  if (isConnected())
117  {
118  defineProperty(FirmwareTP);
119 
120  if (updateVoltageIn())
121  defineProperty(VoltageNP);
122 
123  if (updateTemperature())
124  defineProperty(TemperatureNP);
125  }
126  else
127  {
128  if (TemperatureNP.getState() == IPS_OK)
129  deleteProperty(TemperatureNP);
130  deleteProperty(FirmwareTP);
131  deleteProperty(VoltageNP.getName());
132  }
133 
134  return true;
135 }
136 
141 {
142  if (Ack())
143  {
144  LOGF_INFO("%s is online. Getting focus parameters...", getDeviceName());
145 
146  return true;
147  }
148 
149  LOG_INFO("Error retrieving data from device, please ensure focuser is powered and the port is correct.");
150  return false;
151 }
152 
157 {
158  return "Esatto";
159 }
160 
164 bool Esatto::updateTemperature()
165 {
166  double temperature = 0;
167 
168  if (isSimulation())
169  temperature = 23.5;
170  else if ( m_Esatto->getMotorTemp(temperature) == false)
171  return false;
172 
173  if (temperature > 90)
174  return false;
175 
176  TemperatureNP[TEMPERATURE_MOTOR].setValue(temperature);
177  TemperatureNP.setState(IPS_OK);
178 
179  // External temperature - Optional
180  if (m_Esatto->getExternalTemp(temperature))
181  {
182  if (temperature < 90)
183  TemperatureNP[TEMPERATURE_EXTERNAL].setValue(temperature);
184  else
185  TemperatureNP[TEMPERATURE_EXTERNAL].setValue(-273.15);
186  }
187 
188  return true;
189 }
190 
194 bool Esatto::updatePosition()
195 {
196  uint32_t steps = 0;
197  if (isSimulation())
198  steps = static_cast<uint32_t>(FocusAbsPosN[0].value);
199  else if (m_Esatto->getAbsolutePosition(steps) == false)
200  return false;
201 
202  FocusAbsPosN[0].value = steps;
203  return true;
204 }
205 
209 bool Esatto::updateVoltageIn()
210 {
211  double voltage;
212  if (m_Esatto->getVoltage12v(voltage))
213  VoltageNP[VOLTAGE_12V].setValue(voltage);
214 
215  VoltageNP.setState((voltage >= 11.0) ? IPS_OK : IPS_ALERT);
216  if (m_Esatto->getVoltageUSB(voltage))
217  VoltageNP[VOLTAGE_USB].setValue(voltage);
218  return true;
219 }
220 
224 bool Esatto::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
225 {
226  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
227  {
228  // Fast motion
229  if (FastMoveSP.isNameMatch(name))
230  {
231  FastMoveSP.update(states, names, n);
232  auto current_switch = FastMoveSP.findOnSwitchIndex();
233 
234  switch (current_switch)
235  {
236  case FASTMOVE_IN:
237  m_Esatto->fastMoveIn();
238  break;
239  case FASTMOVE_OUT:
240  m_Esatto->fastMoveOut();
241  break;
242  case FASTMOVE_STOP:
243  m_Esatto->stop();
244  break;
245  default:
246  break;
247  }
248 
249  FastMoveSP.setState(IPS_BUSY);
250  FastMoveSP.apply();
251  return true;
252  }
253  }
254  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
255 }
256 
260 IPState Esatto::MoveAbsFocuser(uint32_t targetTicks)
261 {
262  if (m_Esatto->goAbsolutePosition(targetTicks) == false)
263  return IPS_ALERT;
264 
265  return IPS_BUSY;
266 }
267 
272 {
273  int reversed = (IUFindOnSwitchIndex(&FocusReverseSP) == INDI_ENABLED) ? -1 : 1;
274  int relativeTicks = ((dir == FOCUS_INWARD) ? -ticks : ticks) * reversed;
275  double newPosition = FocusAbsPosN[0].value + relativeTicks;
276 
277  bool rc = MoveAbsFocuser(newPosition);
278 
279  return (rc ? IPS_BUSY : IPS_ALERT);
280 }
281 
286 {
287  if (isSimulation())
288  return true;
289 
290  return m_Esatto->stop();
291 }
292 
297 {
298  //if (!isConnected() || FocusAbsPosNP.s == IPS_BUSY || FocusRelPosNP.s == IPS_BUSY)
299  if (!isConnected())
300  {
302  return;
303  }
304 
305  auto lastPos = FocusAbsPosN[0].value;
306  bool rc = updatePosition();
307  if (rc && (std::abs(lastPos - FocusAbsPosN[0].value) > 0)) {
308  if (FocusAbsPosNP.s == IPS_BUSY && m_Esatto->isBusy() == false) {
309  // To prevent reporting a bit too old position as the final one
310  updatePosition();
311 
314  IDSetNumber(&FocusRelPosNP, nullptr);
315  }
316 
317  IDSetNumber(&FocusAbsPosNP, nullptr);
318  }
319 
320  if (m_TemperatureCounter++ == TEMPERATURE_FREQUENCY)
321  {
322  auto lastValue = TemperatureNP[0].value;
323  rc = updateTemperature();
324  if (rc && std::abs(lastValue - TemperatureNP[0].value) >= 0.1)
325  TemperatureNP.apply();
326 
327  auto current12V = VoltageNP[VOLTAGE_12V].getValue();
328  auto currentUSB = VoltageNP[VOLTAGE_USB].getValue();
329  if (updateVoltageIn())
330  {
331  if (std::abs(current12V - VoltageNP[VOLTAGE_12V].getValue()) >= 0.1 ||
332  std::abs(currentUSB - VoltageNP[VOLTAGE_USB].getValue()) >= 0.1)
333  {
334  VoltageNP.apply();
335  if (VoltageNP[VOLTAGE_12V].getValue() < 11.0)
336  LOG_WARN("Please check 12v DC power supply is connected.");
337  }
338  }
339  m_TemperatureCounter = 0; // Reset the counter
340  }
341 
343 }
344 
348 bool Esatto::getStartupValues()
349 {
350  auto rc1 = updatePosition();
351  uint32_t steps {0};
352  auto rc2 = m_Esatto->getBacklash(steps);
353  if (rc2)
354  FocusBacklashN[0].value = steps;
355 
356  auto rc3 = updateMaxLimit();
357 
358  return rc1 && rc2 && rc3;
359 }
360 
364 bool Esatto::ReverseFocuser(bool enable)
365 {
366  INDI_UNUSED(enable);
367  return false;
368 }
369 
373 bool Esatto::Ack()
374 {
375  std::string response;
376  m_Esatto.reset(new PrimalucaLabs::Esatto(getDeviceName(), PortFD));
377 
378  if(m_Esatto->getSerialNumber(response))
379  LOGF_INFO("Serial number: %s", response.c_str());
380  else
381  return false;
382 
383  FirmwareTP[FIRMWARE_SN].setText(response.c_str());
384 
385  if (m_Esatto->getFirmwareVersion(response))
386  {
387  LOGF_INFO("Firmware version: %s", response.c_str());
388  IUSaveText(&FirmwareTP[FIRMWARE_VERSION], response.c_str());
389  }
390 
391  return true;
392 }
393 
397 void Esatto::setConnectionParams()
398 {
401 }
402 
406 bool Esatto::SetFocuserBacklash(int32_t steps)
407 {
408  return m_Esatto->setBacklash(steps);
409 }
410 
414 bool Esatto::updateMaxLimit()
415 {
416  uint32_t maxLimit = 0;
417 
418  if (m_Esatto->getMaxPosition(maxLimit) == false)
419  return false;
420 
421  FocusMaxPosN[0].max = maxLimit;
422  if (FocusMaxPosN[0].value > maxLimit)
423  FocusMaxPosN[0].value = maxLimit;
424 
425  FocusAbsPosN[0].min = 0;
426  FocusAbsPosN[0].max = maxLimit;
427  FocusAbsPosN[0].value = 0;
428  FocusAbsPosN[0].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
429 
430  FocusRelPosN[0].min = 0.;
431  FocusRelPosN[0].max = FocusAbsPosN[0].step * 10;
432  FocusRelPosN[0].value = 0;
433  FocusRelPosN[0].step = FocusAbsPosN[0].step;
434 
435  PresetN[0].max = maxLimit;
436  PresetN[0].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
437  PresetN[1].max = maxLimit;
438  PresetN[1].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
439  PresetN[2].max = maxLimit;
440  PresetN[2].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
441 
442 
444  return true;
445 }
void setWordSize(const uint8_t &value)
setWordSize Set word size to be used in the serial connection. Default 8
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
Definition: esatto.h:28
const char * getDefaultName() override
Definition: esatto.cpp:156
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: esatto.cpp:224
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: esatto.cpp:285
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: esatto.cpp:271
Esatto()
Definition: esatto.cpp:34
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition: esatto.cpp:260
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
Definition: esatto.cpp:406
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: esatto.cpp:46
virtual bool Handshake() override
perform handshake with device to check communication
Definition: esatto.cpp:140
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: esatto.cpp:296
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
Definition: esatto.cpp:364
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: esatto.cpp:109
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
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.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
ISwitchVectorProperty FocusReverseSP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INumberVectorProperty FocusMaxPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
INumber PresetN[3]
Definition: indifocuser.h:107
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:42
Connection::Serial * serialConnection
Definition: indifocuser.h:116
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
IPState getState() const
const char * getName() const
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ 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_ATMOST1
Definition: indiapi.h:174
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 IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_WARN(txt)
Definition: indilogger.h:73
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define ENVIRONMENT_TAB
const char * CONNECTION_TAB