Instrument Neutral Distributed Interface INDI 2.2.5
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.
38static 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", "",
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
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
133bool 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
156bool 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
215void 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
222bool 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
279{
280 return "Astromechanics LPM";
281}
282
283bool 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 log(const std::string &log)
Definition Utils.cpp:161
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
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
const char * getDeviceName() const
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()
Initialize 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
@ CONNECTION_SERIAL
Definition lpm.h:47
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
@ DISCARD_READINGS
Definition lpm.h:53
@ SAVE_READINGS
Definition lpm.h:52
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
virtual bool initProperties() override
Initialize 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.
int errno
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
#define NARRAY(a)
Handy macro to find the number of elements in array a[]. Must be used with actual array,...
Definition indiapi.h:498
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_ATMOST1
Definition indiapi.h:172
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:653
const char * indi_timestamp()
Create an ISO 8601 formatted time stamp. The format is YYYY-MM-DDTHH:MM:SS.
Definition indicom.c:337
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
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:490
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:509
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:376
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:416
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:398
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:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#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
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248