1 #include "rbfocus.h"
2 
3 #include "indicom.h"
4 
5 #include <cmath>
6 #include <cstring>
7 #include <memory>
8 
9 #include <termios.h>
10 #include <unistd.h>
11 
12 static std::unique_ptr<RBFOCUS> rbfocus(new RBFOCUS());
13 
RBFOCUS()14 RBFOCUS::RBFOCUS()
15 {
16     // Absolute, Abort, and Sync
17     FI::SetCapability(FOCUSER_CAN_ABS_MOVE | FOCUSER_CAN_ABORT | FOCUSER_CAN_SYNC);
18     setVersion(1, 0);
19 }
20 
initProperties()21 bool RBFOCUS::initProperties()
22 {
23     INDI::Focuser::initProperties();
24 
25     // Focuser temperature
26     IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
27     IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
28                        MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
29 
30     IUFillSwitch(&focuserHoldS[HOLD_ON], "HOLD_ON", "Hold Enabled", ISS_OFF);
31     IUFillSwitch(&focuserHoldS[HOLD_OFF], "HOLD_OFF", "Hold Disabled", ISS_OFF);
32     IUFillSwitchVector(&focuserHoldSP, focuserHoldS, 2, getDeviceName(), "Focuser Hold", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0,
33                        IPS_IDLE);
34 
35     IUFillSwitch(&dirS[NORMAL], "NORMAL", "Normal", ISS_OFF);
36     IUFillSwitch(&dirS[REVERSED], "REVERSED", "Reverse", ISS_OFF);
37     IUFillSwitchVector(&dirSP, dirS, 2, getDeviceName(), "Direction", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0,
38                        IPS_IDLE);
39 
40 
41     // Relative and absolute movement
42     FocusRelPosN[0].min   = 0.;
43     FocusRelPosN[0].max   = 50000.;
44     FocusRelPosN[0].value = 0.;
45     FocusRelPosN[0].step  = 1000;
46 
47     FocusAbsPosN[0].min   = 0.;
48     FocusAbsPosN[0].max   = 100000.;
49     FocusAbsPosN[0].value = 0;
50     FocusAbsPosN[0].step  = 1000;
51 
52 
53     return true;
54 }
55 
updateProperties()56 bool RBFOCUS::updateProperties()
57 {
58     INDI::Focuser::updateProperties();
59 
60     if (isConnected())
61     {
62         defineProperty(&TemperatureNP);
63         defineProperty(&focuserHoldSP);
64         defineProperty(&dirSP);
65         LOG_INFO("Focuser ready.");
66     }
67     else
68     {
69         deleteProperty(TemperatureNP.name);
70         deleteProperty(focuserHoldSP.name);
71         deleteProperty(dirSP.name);
72     }
73 
74     return true;
75 }
76 
Handshake()77 bool RBFOCUS::Handshake()
78 {
79     if (Ack())
80     {
81 
82         LOG_INFO("RBF is online.");
83         readVersion();
84         MaxPos();
85         readHold();
86         readDir();
87         return true;
88     }
89 
90     LOG_INFO("Error retrieving data from RBFocuser, please ensure RBFocus controller is powered and the port is correct.");
91     return false;
92 }
93 
getDefaultName()94 const char * RBFOCUS::getDefaultName()
95 {
96     return "RB Focuser";
97 }
98 
Ack()99 bool RBFOCUS::Ack()
100 {
101     int nbytes_written = 0, nbytes_read = 0, rc = -1;
102     char errstr[MAXRBUF];
103     char resp[5] = {0};
104 
105     tcflush(PortFD, TCIOFLUSH);
106 
107     int numChecks = 0;
108     bool success = false;
109     while (numChecks < 3 && !success)
110     {
111         numChecks++;
112         //wait 1 second between each test.
113         sleep(1);
114 
115         bool transmissionSuccess = (rc = tty_write(PortFD, "#", 1, &nbytes_written)) == TTY_OK;
116         if(!transmissionSuccess)
117         {
118             tty_error_msg(rc, errstr, MAXRBUF);
119             LOGF_ERROR("Handshake Attempt %i, tty transmission error: %s.", numChecks, errstr);
120         }
121 
122         bool responseSuccess = (rc = tty_read(PortFD, resp, 4, DRIVER_TIMEOUT, &nbytes_read)) == TTY_OK;
123         if(!responseSuccess)
124         {
125             tty_error_msg(rc, errstr, MAXRBUF);
126             LOGF_ERROR("Handshake Attempt %i, updatePosition response error: %s.", numChecks, errstr);
127         }
128 
129         success = transmissionSuccess && responseSuccess;
130     }
131 
132     if(!success)
133     {
134         LOG_INFO("Handshake failed after 3 attempts");
135         return false;
136     }
137 
138     tcflush(PortFD, TCIOFLUSH);
139 
140     return !strcmp(resp, "OK!#");
141 }
142 
143 
readTemperature()144 bool RBFOCUS::readTemperature()
145 {
146     char res[DRIVER_RES] = {0};
147 
148        if (sendCommand("Q#", res) == false)
149            return false;
150 
151        int32_t temp = 0;
152        int rc = sscanf(res, "C%d#", &temp);
153        if (rc > 0)
154            // Hundredth of a degree
155            TemperatureN[0].value = temp / 100.0;
156        else
157        {
158            LOGF_ERROR("Unknown error: focuser temperature value (%s)", res);
159            return false;
160        }
161 
162        return true;
163 }
164 
readVersion()165 bool RBFOCUS::readVersion()
166 {
167    return true;
168 }
readHold()169 bool RBFOCUS::readHold()
170 {
171     char res[DRIVER_RES] = {0};
172 
173     if (sendCommand("V#", res) == false){
174         return false;
175 }
176 
177         if(strcmp(res, "Enable")==0)
178         {
179             focuserHoldS[HOLD_ON].s = ISS_ON;
180 
181         }
182         else if (strcmp(res, "Disable")==0)
183         {
184             focuserHoldS[HOLD_OFF].s = ISS_ON;
185 
186 
187         }
188 
189 
190 
191     return true;
192 }
readDir()193 bool RBFOCUS::readDir()
194 {
195     char res[DRIVER_RES] = {0};
196 
197     if (sendCommand("B#", res) == false){
198         return false;
199 }
200 
201         if(strcmp(res, "Reversed")==0)
202         {
203             dirS[REVERSED].s = ISS_ON;
204 
205         }
206         else if (strcmp(res, "Normal")==0)
207         {
208             dirS[NORMAL].s = ISS_ON;
209 
210 
211         }
212 
213 
214 
215     return true;
216 }
readPosition()217 bool RBFOCUS::readPosition()
218 {
219     char res[DRIVER_RES] = {0};
220 
221     if (sendCommand("P#", res) == false)
222         return false;
223 
224     int32_t pos;
225     int rc = sscanf(res, "%d#", &pos);
226 
227     if (rc > 0)
228         FocusAbsPosN[0].value = pos;
229     else
230     {
231         return false;
232     }
233 
234     return true;
235 }
236 
isMoving()237 bool RBFOCUS::isMoving()
238 {
239     char res[DRIVER_RES] = {0};
240 
241     if (sendCommand("J#", res) == false)
242         return false;
243 
244     if (strcmp(res, "M1:OK") == 0)
245         return true;
246     else if (strcmp(res, "M0:OK") == 0)
247         return false;
248 
249     LOGF_ERROR("Unknown error: isMoving value (%s)", res);
250     return false;
251 
252 }
253 
MaxPos()254 bool RBFOCUS::MaxPos(){
255     char res[DRIVER_RES] = {0};
256 
257     if (sendCommand("X#", res) == false)
258         return false;
259 
260     uint32_t mPos = 0;
261     int rc = sscanf(res, "%u#", &mPos);
262      if (rc >0){
263 
264         FocusMaxPosN[0].value = mPos;
265         RBFOCUS::SyncPresets(mPos);
266        }else
267      {
268          LOGF_ERROR("Invalid Response: focuser hold value (%s)", res);
269          return false;
270      }
271 
272     return true;
273 
274 
275 }
276 
SetFocuserMaxPosition(uint32_t mPos)277 bool RBFOCUS::SetFocuserMaxPosition(uint32_t mPos)
278 {
279     char cmd[DRIVER_RES] = {0};
280 
281     snprintf(cmd, DRIVER_RES, "H%d#", mPos);
282 
283     if(sendCommand(cmd))
284     {
285         Focuser::SyncPresets(mPos);
286 
287         return true;
288     }
289     return false;
290 }
SyncFocuser(uint32_t ticks)291 bool RBFOCUS::SyncFocuser(uint32_t ticks)
292 {
293     char cmd[DRIVER_RES] = {0};
294     snprintf(cmd, DRIVER_RES, "I%d#", ticks);
295     return sendCommand(cmd);
296 }
297 
MoveAbsFocuser(uint32_t targetTicks)298 IPState RBFOCUS::MoveAbsFocuser(uint32_t targetTicks)
299 {
300     char cmd[DRIVER_RES] = {0};
301     snprintf(cmd, DRIVER_RES, "T%d#", targetTicks);
302 
303     if (sendCommand(cmd) == false)
304         return IPS_BUSY;
305 
306     targetPos = targetTicks;
307         return IPS_BUSY;
308 }
309 
310 
311 
TimerHit()312 void RBFOCUS::TimerHit()
313 {
314     if (!isConnected())
315     {
316         SetTimer(getCurrentPollingPeriod());
317         return;
318     }
319 
320     bool rc = readPosition();
321     if (rc)
322     {
323         if (fabs(lastPos - FocusAbsPosN[0].value) > 5)
324         {
325             IDSetNumber(&FocusAbsPosNP, nullptr);
326             lastPos = FocusAbsPosN[0].value;
327         }
328     }
329 
330     rc = readTemperature();
331     if (rc)
332     {
333         if (fabs(lastTemperature - TemperatureN[0].value) >= 0.5)
334         {
335             IDSetNumber(&TemperatureNP, nullptr);
336             lastTemperature = TemperatureN[0].value;
337         }
338     }
339 
340     if (FocusAbsPosNP.s == IPS_BUSY)
341     {
342         if (!isMoving())
343         {
344             FocusAbsPosNP.s = IPS_OK;
345             IDSetNumber(&FocusAbsPosNP, nullptr);
346             lastPos = FocusAbsPosN[0].value;
347             LOG_INFO("Focuser reached requested position.");
348         }
349     }
350 
351     SetTimer(getCurrentPollingPeriod());
352 }
353 
AbortFocuser()354 bool RBFOCUS::AbortFocuser()
355 {
356     return sendCommand("L#");
357 }
setHold()358 bool RBFOCUS::setHold()
359 {
360     return sendCommand("C#");
361 }
setDir()362 bool RBFOCUS::setDir()
363 {
364     return sendCommand("D#");
365 }
ISNewSwitch(const char * dev,const char * name,ISState * states,char * names[],int n)366 bool RBFOCUS::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n){
367     if (strcmp(focuserHoldSP.name, name) == 0)
368     {
369         int current_mode = IUFindOnSwitchIndex(&focuserHoldSP);
370 
371         IUUpdateSwitch(&focuserHoldSP, states, names, n);
372 
373         int target_mode = IUFindOnSwitchIndex(&focuserHoldSP);
374 
375         if (current_mode == target_mode)
376         {
377             focuserHoldSP.s = IPS_OK;
378             IDSetSwitch(&focuserHoldSP, nullptr);
379         }
380 
381         bool rc = setHold();
382         if (!rc)
383         {
384             IUResetSwitch(&focuserHoldSP);
385             focuserHoldS[current_mode].s = ISS_ON;
386             focuserHoldSP.s              = IPS_ALERT;
387             IDSetSwitch(&focuserHoldSP, nullptr);
388             return false;
389         }
390 
391         focuserHoldSP.s = IPS_OK;
392         IDSetSwitch(&focuserHoldSP, nullptr);
393         return true;
394     }
395 
396     if (strcmp(dirSP.name, name) == 0)
397     {
398         int current_mode = IUFindOnSwitchIndex(&dirSP);
399 
400         IUUpdateSwitch(&dirSP, states, names, n);
401 
402         int target_mode = IUFindOnSwitchIndex(&dirSP);
403 
404         if (current_mode == target_mode)
405         {
406             dirSP.s = IPS_OK;
407             IDSetSwitch(&dirSP, nullptr);
408         }
409 
410         bool rc = setDir();
411         if (!rc)
412         {
413             IUResetSwitch(&dirSP);
414             dirS[current_mode].s = ISS_ON;
415             dirSP.s              = IPS_ALERT;
416             IDSetSwitch(&dirSP, nullptr);
417             return false;
418         }
419 
420         dirSP.s = IPS_OK;
421         IDSetSwitch(&dirSP, nullptr);
422         return true;
423     }
424 
425 
426     return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
427 }
428 
429 
sendCommand(const char * cmd,char * res)430 bool RBFOCUS::sendCommand(const char * cmd, char * res)
431 {
432     int nbytes_written = 0, nbytes_read = 0, rc = -1;
433 
434     tcflush(PortFD, TCIOFLUSH);
435 
436     LOGF_DEBUG("CMD <%s>", cmd);
437 
438     if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
439     {
440         char errstr[MAXRBUF] = {0};
441         tty_error_msg(rc, errstr, MAXRBUF);
442         LOGF_ERROR("Serial write error: %s.", errstr);
443         return false;
444     }
445 
446     if (res == nullptr)
447         return true;
448 
449     if ((rc = tty_nread_section(PortFD, res, DRIVER_RES, DRIVER_DEL, DRIVER_TIMEOUT, &nbytes_read)) != TTY_OK)
450     {
451         char errstr[MAXRBUF] = {0};
452         tty_error_msg(rc, errstr, MAXRBUF);
453         LOGF_ERROR("Serial read error: %s.", errstr);
454         return false;
455     }
456 
457     // Remove the #
458     res[nbytes_read - 1] = 0;
459 
460     LOGF_DEBUG("RES <%s>", res);
461 
462     tcflush(PortFD, TCIOFLUSH);
463 
464     return true;
465 }
466