Instrument Neutral Distributed Interface INDI  2.0.2
lpm.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2019 Christian Liska. All rights reserved.
3 
4  INDI Astromechanic Light Pollution Meter Driver
5  https://www.astromechanics.org/lpm.html
6 
7  This program is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the Free
9  Software Foundation; either version 2 of the License, or (at your option)
10  any later version.
11 
12  This program is distributed in the hope that it will be useful, but WITHOUT
13  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15  more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 
22  The full GNU General Public License is included in this distribution in the
23  file called LICENSE.
24 *******************************************************************************/
25 #include "lpm.h"
26 
27 #include "indicom.h"
29 
30 #include <cerrno>
31 #include <memory>
32 #include <cstring>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <math.h>
36 
37 // We declare an auto pointer to LPM.
38 static std::unique_ptr<LPM> lpm(new LPM());
39 
40 #define UNIT_TAB "Unit"
41 
43 {
44  setVersion(0, 1);
45 }
46 
48 {
49  if (fp)
50  {
51  fclose(fp);
52  }
53 }
54 
56 {
58 
59  // Readings from device
60  IUFillNumber(&AverageReadingN[0], "SKY_BRIGHTNESS", "Quality (mag/arcsec^2)", "%6.2f", -20, 30, 0, 0);
61  IUFillNumber(&AverageReadingN[1], "AVG_SKY_BRIGHTNESS", "Avg. Quality (mag/argsec^2)", "%6.2f", -20, 30, 0, 0);
62  IUFillNumber(&AverageReadingN[2], "MIN_SKY_BRIGHTNESS", "Min. Quality (mag/argsec^2)", "%6.2f", -20, 30, 0, 0);
63  IUFillNumber(&AverageReadingN[3], "MAX_SKY_BRIGHTNESS", "Max. Quality (mag/argsec^2)", "%6.2f", -20, 30, 0, 0);
64  IUFillNumber(&AverageReadingN[4], "NAKED_EYES_LIMIT", "NELM (V mags)", "%6.2f", -20, 30, 0, 0);
65 
66  IUFillNumberVector(&AverageReadingNP, AverageReadingN, 5, getDeviceName(), "SKY_QUALITY", "Readings",
68 
69  // add reset button for SQ-Measurements
70  IUFillSwitch(&ResetB[0], "RESET_BUTTON", "Reset", ISS_OFF);
71  IUFillSwitchVector(&ResetBP, ResetB, 1, getDeviceName(), "RESET_READINGS", "",
73 
74  IUFillSwitch(&SaveB[SAVE_READINGS], "SAVE_BUTTON", "Save Readings", ISS_OFF);
75  IUFillSwitch(&SaveB[DISCARD_READINGS], "DISCARD_BUTTON", "Discard Readings", ISS_OFF);
76  IUFillSwitchVector(&SaveBP, SaveB, 2, getDeviceName(), "SAVE_READINGS", "",
78 
79  // LPM-Readings-Log
80  std::string defaultDirectory = std::string(getenv("HOME")) + std::string("/lpm");
81  IUFillText(&RecordFileT[0], "RECORD_FILE_DIR", "Dir.", defaultDirectory.data());
82  IUFillText(&RecordFileT[1], "RECORD_FILE_NAME", "Name", "lpmlog.txt");
83  IUFillTextVector(&RecordFileTP, RecordFileT, NARRAY(RecordFileT), getDeviceName(), "RECORD_FILE", "Record File",
85  snprintf(filename, 2048, "%s/%s", RecordFileT[0].text, RecordFileT[1].text);
86 
87  // Unit Info
88  IUFillNumber(&UnitInfoN[0], "Calibdata", "", "%6.2f", -20, 30, 0, 0);
89  IUFillNumberVector(&UnitInfoNP, UnitInfoN, 1, getDeviceName(), "Unit Info", "",
90  UNIT_TAB, IP_RO, 0, IPS_IDLE);
91 
92  if (lpmConnection & CONNECTION_SERIAL)
93  {
94  serialConnection = new Connection::Serial(this);
95  serialConnection->registerHandshake([&]()
96  {
97  return getDeviceInfo();
98  });
100  registerConnection(serialConnection);
101  }
102 
103  addDebugControl();
105 
106  return true;
107 }
108 
110 {
112 
113  if (isConnected())
114  {
115  defineProperty(&AverageReadingNP);
116  defineProperty(&UnitInfoNP);
117  defineProperty(&ResetBP);
118  defineProperty(&RecordFileTP);
119  defineProperty(&SaveBP);
120  }
121  else
122  {
123  deleteProperty(AverageReadingNP.name);
124  deleteProperty(UnitInfoNP.name);
125  deleteProperty(RecordFileTP.name);
126  deleteProperty(ResetBP.name);
127  deleteProperty(SaveBP.name);
128  }
129 
130  return true;
131 }
132 
133 bool LPM::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
134 {
135  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
136  {
137  if (strcmp(name, RecordFileTP.name) == 0)
138  {
139  IUUpdateText(&RecordFileTP, texts, names, n);
140  RecordFileTP.s = IPS_OK;
141  IDSetText(&RecordFileTP, nullptr);
142  snprintf(filename, 2048, "%s/%s", RecordFileT[0].text, RecordFileT[1].text);
143  LOGF_INFO("filename changed to %s", filename);
144  if (fp != nullptr)
145  {
146  fclose(fp);
147  openFilePtr();
148  }
149  return true;
150  }
151  }
152 
153  return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
154 }
155 
156 bool LPM::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
157 {
158  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
159  {
160  // Reset
161  if (!strcmp(name, ResetBP.name))
162  {
163  IUUpdateSwitch(&ResetBP, states, names, n);
164  ResetB[0].s = ISS_OFF;
165  ResetBP.s = IPS_OK;
166  IDSetSwitch(&ResetBP, nullptr);
167 
168  AverageReadingN[0].value = 0;
169  AverageReadingN[1].value = 0;
170  AverageReadingN[2].value = 0;
171  AverageReadingN[3].value = 0;
172 
173  sumSQ = 0;
174  count = 0;
175  return true;
176  }
177  else if (!strcmp(name, SaveBP.name))
178  {
179  IUUpdateSwitch(&SaveBP, states, names, n);
180 
181  if (SaveB[SAVE_READINGS].s == ISS_ON)
182  {
183  LOGF_INFO("Save readings to %s", filename);
184  if (fp == nullptr)
185  {
186  openFilePtr();
187  }
188  SaveBP.s = IPS_OK;
189  }
190  else
191  {
192  LOG_INFO("Discard readings");
193  if (fp != nullptr)
194  {
195  LOG_DEBUG("close file pointer");
196  fclose(fp);
197  fp = nullptr;
198  }
199  else
200  {
201  LOG_WARN("no file open!");
202  }
203  SaveBP.s = IPS_IDLE;
204  }
205 
206  IDSetSwitch(&SaveBP, nullptr);
207  return true;
208  }
209 
210  }
211 
212  return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
213 }
214 
215 void LPM::openFilePtr()
216 {
217  LOG_DEBUG("open file pointer");
218  mkdir(RecordFileT[0].text, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
219  fp = fopen(filename, "a");
220 }
221 
222 bool LPM::getReadings()
223 {
224  const char *cmd = "V#";
225  char res[32] = {0};
226  int nbytes_written = 0;
227  int nbytes_read = 0;
228 
229  tty_write_string(PortFD, cmd, &nbytes_written);
230  if (tty_read_section(PortFD, res, '#', 60000, &nbytes_read) == TTY_OK)
231  {
232  LOGF_DEBUG("RES (%s)", res);
233  float mpsas;
234  int rc =
235  sscanf(res, "%f#", &mpsas);
236  if (rc < 1)
237  {
238  LOGF_ERROR("Failed to parse input %s", res);
239  return false;
240  }
241  else
242  {
243  if (fp != nullptr)
244  {
245  LOG_DEBUG("save reading...");
246  fprintf(fp, "%f\t%s\n", mpsas, indi_timestamp());
247  fflush(fp);
248  }
249  count++;
250  }
251 
252  AverageReadingN[0].value = mpsas;
253  sumSQ += mpsas;
254 
255  if (count > 1)
256  {
257  AverageReadingN[1].value = sumSQ / count;
258  if (mpsas < AverageReadingN[2].value)
259  {
260  AverageReadingN[2].value = mpsas;
261  }
262  if (mpsas > AverageReadingN[3].value)
263  {
264  AverageReadingN[3].value = mpsas;
265  }
266  }
267  else
268  {
269  AverageReadingN[2].value = mpsas;
270  AverageReadingN[3].value = mpsas;
271  }
272  //NELM (see http://unihedron.com/projects/darksky/NELM2BCalc.html)
273  AverageReadingN[4].value = 7.93 - 5 * log(pow(10, (4.316 - (mpsas / 5))) + 1) / log(10);
274  }
275  return true;
276 }
277 
278 const char *LPM::getDefaultName()
279 {
280  return "Astromechanics LPM";
281 }
282 
283 bool LPM::getDeviceInfo()
284 {
285  const char *cmd = "C#";
286  char buffer[5] = {0};
287 
288  if (getActiveConnection() == serialConnection)
289  {
290  PortFD = serialConnection->getPortFD();
291  }
292 
293  LOGF_DEBUG("CMD: %s", cmd);
294 
295  ssize_t written = write(PortFD, cmd, 2);
296 
297  if (written < 2)
298  {
299  LOGF_ERROR("Error getting device info while writing to device: %s", strerror(errno));
300  return false;
301  }
302 
303  ssize_t received = 0;
304 
305  while (received < 5)
306  {
307  ssize_t response = read(PortFD, buffer + received, 5 - received);
308  if (response < 0)
309  {
310  LOGF_ERROR("Error getting device info while reading response: %s", strerror(errno));
311  return false;
312  }
313 
314  received += response;
315  }
316 
317  if (received < 5)
318  {
319  LOG_ERROR("Error getting device info");
320  return false;
321  }
322 
323  LOGF_DEBUG("RES: %s", buffer);
324 
325  float calib;
326  int rc = sscanf(buffer, "%f#", &calib);
327 
328  if (rc < 1)
329  {
330  LOGF_ERROR("Failed to parse input %s", buffer);
331  return false;
332  }
333 
334  UnitInfoN[0].value = calib;
335 
336  return true;
337 }
338 
340 {
341  if (!isConnected())
342  return;
343 
344  bool rc = getReadings();
345 
346  AverageReadingNP.s = rc ? IPS_OK : IPS_ALERT;
347  IDSetNumber(&AverageReadingNP, nullptr);
348 
350 }
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the intial connection to the device...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void addPollPeriodControl()
Add Polling period control to the driver.
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
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.
virtual bool initProperties()
Initilize properties initial state and value. The child class must implement this function.
Connection::Interface * getActiveConnection()
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
Definition: lpm.h:30
const char * getDefaultName() override
Definition: lpm.cpp:278
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: lpm.cpp:109
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: lpm.cpp:339
LPM()
Definition: lpm.cpp:42
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition: lpm.cpp:133
@ DISCARD_READINGS
Definition: lpm.h:53
@ SAVE_READINGS
Definition: lpm.h:52
@ CONNECTION_SERIAL
Definition: lpm.h:47
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: lpm.cpp:55
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: lpm.cpp:156
virtual ~LPM()
Definition: lpm.cpp:47
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
int errno
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
#define NARRAY(a)
Handy macro to find the number of elements in array a[]. Must be used with actual array,...
Definition: indiapi.h:500
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_ATMOST1
Definition: indiapi.h:174
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:566
const char * indi_timestamp()
Create an ISO 8601 formatted time stamp. The format is YYYY-MM-DDTHH:MM:SS.
Definition: indicom.c:348
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
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
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
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 IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidevapi.c:198
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 IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:1396
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOG_WARN(txt)
Definition: indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define UNIT_TAB
Definition: lpm.cpp:40
std::vector< uint8_t > buffer
int mkdir(const char *path, mode_t mode)
Definition: indiutility.cpp:41
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250