Instrument Neutral Distributed Interface INDI 2.2.5
uranusmeteo.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2022 Jasem Mutlaq. All rights reserved.
3
4 Pegasus Uranus Meteo Sensor Driver.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 The full GNU General Public License is included in this distribution in the
22 file called LICENSE.
23*******************************************************************************/
24
25#include "uranusmeteo.h"
26#include "indicom.h"
28
29#include <regex>
30#include <termios.h>
31#include <chrono>
32#include <iomanip>
33
34// We declare an auto pointer to UranusMeteo.
35static std::unique_ptr<UranusMeteo> ppba(new UranusMeteo());
36
41{
42 setVersion(1, 0);
43 m_SkyQualityUpdateTimer.setInterval(60000);
44 m_SkyQualityUpdateTimer.callOnTimeout(std::bind(&UranusMeteo::measureSkyQuality, this));
45}
46
51{
53
55
56 WI::initProperties(MAIN_CONTROL_TAB, ENVIRONMENT_TAB);
57
58 // To distinguish them from GPS properties.
59 WI::UpdatePeriodNP.setLabel("Weather Update");
60 WI::RefreshSP.setLabel("Weather Refresh");
61
63
67 SensorNP[AmbientTemperature].fill("AmbientTemperature", "AmbientTemperature", "%.2f", -100, 100, 10, 0);
68 SensorNP[RelativeHumidity].fill("RelativeHumidity", "RelativeHumidity", "%.2f%", 0, 100, 10, 0);
69 SensorNP[DewPoint].fill("DewPoint", "DewPoint (C)", "%.2f", 0, 100, 10, 0);
70 SensorNP[AbsolutePressure].fill("AbsolutePressure", "AbsolutePressure (hPA)", "%.2f", 0, 100, 10, 0);
71 SensorNP[RelativePressure].fill("RelativePressure", "RelativePressure (hPA)", "%.2f", 0, 100, 10, 0);
72 SensorNP[BarometricAltitude].fill("BarometricAltitude", "BarometricAltitude (m)", "%.2f", 0, 100, 10, 0);
73 SensorNP[SkyTemperature].fill("SkyTemperature", "SkyTemperature (C)", "%.2f", 0, 100, 10, 0);
74 SensorNP[InfraredTemperature].fill("InfraredTemperature", "InfraredTemperature (C)", "%.2f", 0, 100, 10, 0);
75 SensorNP[BatteryUsage].fill("BatteryUsage", "BatteryUsage", "%.2f%", 0, 100, 10, 0);
76 SensorNP[BatteryVoltage].fill("BatteryVoltage", "BatteryVoltage", "%.2f", 0, 100, 10, 0);
77 SensorNP.fill(getDeviceName(), "SENSORS", "Sensors", SENSORS_TAB, IP_RO, 60, IPS_IDLE);
78
82 CloudsNP[TemperatureDifference].fill("TemperatureDifference", "Temperature Difference (C)", "%.2f", -1000, 1000, 10, 0);
83 CloudsNP[CloudIndex].fill("CloudIndex", "Cloud Coverage (%)", "%.2f", 0, 100, 10, 0);
84 CloudsNP[CloudSkyTemperature].fill("CloudSkyTemperature", "Sky Temperature (C)", "%.2f", -1000, 1000, 10, 0);
85 CloudsNP[CloudAmbientTemperature].fill("CloudAmbientTemperature", "Ambient Temperature (C)", "%.2f", -1000, 1000, 10, 0);
86 CloudsNP[InfraredEmissivity].fill("InfraredEmissivity", "Infrared Emissivity", "%.2f", 0, 1, 0.1, 0);
87 CloudsNP.fill(getDeviceName(), "CLOUDS", "Clouds", CLOUDS_TAB, IP_RO, 60, IPS_IDLE);
88
92 SkyQualityNP[MPAS].fill("MPAS", "MPAS (mag/arcsec^2)", "%.2f", 0, 30, 10, 0);
93 SkyQualityNP[NELM].fill("NELM", "Naked Eye Llimit (mag)", "%.2f", 0, 100, 10, 0);
94 SkyQualityNP[FullSpectrum].fill("FullSpectrum", "Full Spectrum", "%.2f", -1000, 1000, 10, 0);
95 SkyQualityNP[VisualSpectrum].fill("VisualSpectrum", "Visual Spectrum", "%.2f", -1000, 1000, 10, 0);
96 SkyQualityNP[InfraredSpectrum].fill("InfraredSpectrum", "Infrared Spectrum", "%.2f", 0, 1, 0.1, 0);
97 SkyQualityNP[Lux].fill("Lux", "Lux", "%.2f", 0, 1e6, 10, 0);
98 SkyQualityNP.fill(getDeviceName(), "SKYQUALITY", "Sky Quality", SKYQUALITY_TAB, IP_RO, 60, IPS_IDLE);
99
100 SkyQualityUpdateNP[0].fill("VALUE", "Period (s)", "%.f", 0, 3600, 60, 60);
101 SkyQualityUpdateNP.fill(getDeviceName(), "SKYQUALITY_TIMER", "Update", SKYQUALITY_TAB, IP_RW, 60, IPS_IDLE);
102
106 GPSNP[GPSFix].fill("GPSFix", "GPS Fix", "%.f", 0, 3, 1, 0);
107 GPSNP[GPSTime].fill("GPSTime", "Unix Time", "%.f", 0, 1e9, 10, 0);
108 GPSNP[UTCOffset].fill("UTCOffset", "UTC Offset", "%.2f", -12, 12, 1, 0);
109 GPSNP[Latitude].fill("Latitude", "Latitude", "%.2f", -90, 90, 10, 0);
110 GPSNP[Longitude].fill("Longitude", "Longitude", "%.2f", -180, 180, 10, 0);
111 GPSNP[SatelliteNumber].fill("SatelliteNumber", "Sat. #", "%.f", 0, 30, 10, 0);
112 GPSNP[Altitude].fill("Altitude", "Altitude (m)", "%.2f", -1000, 10000, 10, 0);
113 GPSNP[GPSSpeed].fill("GPSSpeed", "Speed (kph)", "%.2f", 0, 30, 10, 0);
114 GPSNP[GPSBearing].fill("GPSBearing", "Bearing (deg)", "%.2f", 0, 360, 10, 0);
115 GPSNP.fill(getDeviceName(), "GPS", "GPS", GPS_TAB, IP_RO, 60, IPS_IDLE);
116
120 addParameter("WEATHER_CLOUD", "Cloud (%)", 0, 85, 15);
121 addParameter("WEATHER_MPAS", "MPAS (mag/arcsec^2)", 1, 30, 15);
122 addParameter("WEATHER_TEMPERATURE", "Temperature (C)", -20, 50, 15);
123 addParameter("WEATHER_HUMIDITY", "Humidity (%)", 0, 75, 15);
124
125 setCriticalParameter("WEATHER_CLOUD");
126 setCriticalParameter("WEATHER_TEMPERATURE");
127 setCriticalParameter("WEATHER_HUMIDITY");
128
133 serialConnection = new Connection::Serial(this);
135 serialConnection->registerHandshake([&]()
136 {
137 return Handshake();
138 });
139 registerConnection(serialConnection);
140
141 return true;
142}
143
148{
150
151 if (isConnected())
152 {
153 defineProperty(SensorNP);
154 defineProperty(CloudsNP);
155
156 defineProperty(SkyQualityNP);
157 defineProperty(SkyQualityUpdateNP);
158
159 defineProperty(GPSNP);
160
162 m_SetupComplete = true;
163
164 readSensors();
165 readClouds();
166
167 measureSkyQuality();
168 }
169 else
170 {
171
172 deleteProperty(SensorNP);
173 deleteProperty(CloudsNP);
174
175 deleteProperty(SkyQualityNP);
176 deleteProperty(SkyQualityUpdateNP);
177
178 deleteProperty(GPSNP);
179
181 m_SetupComplete = false;
182 }
183
184 return true;
185}
186
191{
192 return "Uranus Meteo Sensor";
193}
194
198bool UranusMeteo::Handshake()
199{
200 PortFD = serialConnection->getPortFD();
201 m_SetupComplete = false;
202 char response[PEGASUS_LEN] = {0};
203
204 if (sendCommand("M#", response))
205 return (!strcmp(response, "MS_OK"));
206
207 return false;
208}
209
214{
215 char response[PEGASUS_LEN] = {0};
216
217 if (sendCommand("GP", response))
218 {
219 std::vector<std::string> result = split(response + 3, ":");
220
221 if (result == m_GPS)
222 return IPS_OK;
223
224 m_Sensors = result;
225
226 try
227 {
228 GPSNP[GPSFix].setValue(std::stod(result[GPSFix]));
229 GPSNP[GPSTime].setValue(std::stod(result[GPSTime]));
230 GPSNP[UTCOffset].setValue(std::stod(result[UTCOffset]));
231 GPSNP[Latitude].setValue(std::stod(result[Latitude]));
232 GPSNP[Longitude].setValue(std::stod(result[Longitude]));
233 GPSNP[SatelliteNumber].setValue(std::stod(result[SatelliteNumber]));
234 GPSNP[Altitude].setValue(std::stod(result[Altitude]));
235 GPSNP[GPSSpeed].setValue(std::stod(result[GPSSpeed]));
236 GPSNP[GPSBearing].setValue(std::stod(result[GPSBearing]));
237
238 GPSNP.setState(IPS_OK);
239 GPSNP.apply();
240
241 if (GPSNP[GPSFix].getValue() < 3)
242 return IPS_BUSY;
243
244 LocationNP[LOCATION_LATITUDE].value = GPSNP[Latitude].getValue();
245 LocationNP[LOCATION_LONGITUDE].value = GPSNP[Longitude].getValue();
246 // 2017-11-15 Jasem: INDI Longitude is 0 to 360 East+
247 if (LocationNP[LOCATION_LONGITUDE].value < 0)
248 LocationNP[LOCATION_LONGITUDE].value += 360;
249
250 // GPS altitude is a real position fix and, unlike barometric altitude,
251 // does not drift with weather (pressure changes of a few tens of hPa
252 // shift the barometric estimate by tens of meters for a stationary site).
253 LocationNP[LOCATION_ELEVATION].value = GPSNP[Altitude].getValue();
254
255 // Get GPS Time
256 char ts[32] = {0};
257 time_t raw_time = static_cast<time_t>(GPSNP[GPSTime].getValue());
258
259 // GP already reports Unix UTC time directly (verified against wall-clock
260 // UTC on real hardware: the raw field matched `date -u` exactly). The
261 // previous code assumed it was local time and subtracted the host's UTC
262 // offset, which shifted TIME_UTC by exactly that offset -- e.g. 2 hours
263 // off under CEST. No conversion is needed; use the offset the device
264 // itself reports instead of deriving one from the host's timezone.
265 m_GPSTime = raw_time;
266 struct tm *utc = gmtime(&raw_time);
267 // Format it
268 strftime(ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", utc);
269 IUSaveText(&TimeTP[0], ts);
270
271 auto utcOffset = GPSNP[UTCOffset].getValue();
272 snprintf(ts, sizeof(ts), "%.2f", utcOffset);
273 IUSaveText(&TimeTP[1], ts);
274
275 // Set UTC offset in device
276 char command[PEGASUS_LEN] = {0};
277 snprintf(command, PEGASUS_LEN, "C3:%d", static_cast<int>(utcOffset));
278 sendCommand(command, response);
279
280 return IPS_OK;
281 }
282 catch(...)
283 {
284 LOGF_WARN("Failed to process sensor response: %s (%d bytes)", response, strlen(response));
285 return IPS_ALERT;
286 }
287 }
288
289 return IPS_ALERT;
290}
291
296{
297 setParameterValue("WEATHER_TEMPERATURE", SensorNP[AmbientTemperature].getValue());
298 setParameterValue("WEATHER_HUMIDITY", SensorNP[RelativeHumidity].getValue());
299 setParameterValue("WEATHER_CLOUD", CloudsNP[CloudIndex].getValue());
300 setParameterValue("WEATHER_MPAS", SkyQualityNP[MPAS].getValue());
301 return IPS_OK;
302}
303
307bool UranusMeteo::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
308{
309 if (dev && !strcmp(dev, getDeviceName()))
310 {
311 if (WI::processSwitch(dev, name, states, names, n))
312 return true;
313 }
314
315 return INDI::GPS::ISNewSwitch(dev, name, states, names, n);
316}
317
321bool UranusMeteo::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
322{
323 if (dev && !strcmp(dev, getDeviceName()))
324 {
325 // Sky Quality Update
326 if (SkyQualityUpdateNP.isNameMatch(name))
327 {
328 SkyQualityUpdateNP.update(values, names, n);
329 auto value = SkyQualityUpdateNP[0].getValue();
330 if (value > 0)
331 {
332 m_SkyQualityUpdateTimer.start(value * 1000);
333 SkyQualityUpdateNP.setState(IPS_OK);
334 }
335 else
336 {
337 LOG_INFO("Sky Quality Update is disabled.");
338 SkyQualityUpdateNP.setState(IPS_IDLE);
339 }
340 SkyQualityUpdateNP.apply();
341 return true;
342 }
343
344 if (WI::processNumber(dev, name, values, names, n))
345 return true;
346 }
347
348 return INDI::GPS::ISNewNumber(dev, name, values, names, n);
349}
350
355{
356 if (!isConnected() || m_SetupComplete == false)
357 {
359 return;
360 }
361
362 readSensors();
363 readClouds();
364
366}
367
372{
375 SkyQualityUpdateNP.save(fp);
376 return true;
377}
378
382bool UranusMeteo::readSensors()
383{
384 char response[PEGASUS_LEN] = {0};
385
386 if (sendCommand("MA", response))
387 {
388 std::vector<std::string> result = split(response + 6, ":");
389
390 if (result == m_Sensors)
391 return true;
392
393 m_Sensors = result;
394
395 try
396 {
397 SensorNP[AmbientTemperature].setValue(std::stod(result[AmbientTemperature]));
398 SensorNP[RelativeHumidity].setValue(std::stod(result[RelativeHumidity]));
399 SensorNP[DewPoint].setValue(std::stod(result[DewPoint]));
400 SensorNP[AbsolutePressure].setValue(std::stod(result[AbsolutePressure]));
401 SensorNP[BarometricAltitude].setValue(std::stod(result[BarometricAltitude]));
402 SensorNP[SkyTemperature].setValue(std::stod(result[SkyTemperature]));
403 SensorNP[InfraredTemperature].setValue(std::stod(result[InfraredTemperature]));
404 SensorNP[BatteryUsage].setValue(std::stod(result[BatteryUsage]));
405 SensorNP[BatteryVoltage].setValue(std::stod(result[BatteryVoltage]));
406
407 SensorNP.setState(IPS_OK);
408 SensorNP.apply();
409 return true;
410 }
411 catch(...)
412 {
413 LOGF_WARN("Failed to process sensor response: %s (%d bytes)", response, strlen(response));
414 return false;
415 }
416 }
417
418 return false;
419}
420
424bool UranusMeteo::readSkyQuality()
425{
426 char response[PEGASUS_LEN] = {0};
427
428 if (sendCommand("SQ", response))
429 {
430 std::vector<std::string> result = split(response + 3, ":");
431
432 if (result == m_SkyQuality)
433 return true;
434
435 m_SkyQuality = result;
436
437 try
438 {
439 SkyQualityNP[MPAS].setValue(std::stod(result[MPAS]));
440 SkyQualityNP[NELM].setValue(std::stod(result[NELM]));
441 SkyQualityNP[FullSpectrum].setValue(std::stod(result[FullSpectrum]));
442 SkyQualityNP[VisualSpectrum].setValue(std::stod(result[VisualSpectrum]));
443 SkyQualityNP[InfraredSpectrum].setValue(std::stod(result[InfraredSpectrum]));
444 SkyQualityNP[Lux].setValue(std::stod(result[Lux]));
445
446 SkyQualityNP.setState(IPS_OK);
447 SkyQualityNP.apply();
448 return true;
449 }
450 catch(...)
451 {
452 LOGF_WARN("Failed to process sky quality response: %s (%d bytes)", response, strlen(response));
453 return false;
454 }
455 }
456
457 return false;
458}
459
463bool UranusMeteo::readClouds()
464{
465 char response[PEGASUS_LEN] = {0};
466
467 if (sendCommand("CI", response))
468 {
469 std::vector<std::string> result = split(response + 3, ":");
470
471 if (result == m_Clouds)
472 return true;
473
474 m_Clouds = result;
475
476 try
477 {
478 CloudsNP[TemperatureDifference].setValue(std::stod(result[TemperatureDifference]));
479 CloudsNP[CloudIndex].setValue(std::stod(result[CloudIndex]));
480 CloudsNP[CloudSkyTemperature].setValue(std::stod(result[CloudSkyTemperature]));
481 CloudsNP[CloudAmbientTemperature].setValue(std::stod(result[CloudAmbientTemperature]));
482 CloudsNP[InfraredEmissivity].setValue(std::stod(result[InfraredEmissivity]));
483
484 CloudsNP.setState(IPS_OK);
485 CloudsNP.apply();
486 return true;
487 }
488 catch(...)
489 {
490 LOGF_WARN("Failed to process cloud response: %s (%d bytes)", response, strlen(response));
491 return false;
492 }
493 }
494
495 return false;
496}
497
501void UranusMeteo::measureSkyQuality()
502{
503 char response[PEGASUS_LEN] = {0};
504 LOG_DEBUG("Measuring sky quality...");
505 if (sendCommand("SQ:1", response))
506 {
507 readSkyQuality();
508 if (SkyQualityUpdateNP[0].getValue() > 0)
509 m_SkyQualityUpdateTimer.start(SkyQualityUpdateNP[0].getValue() * 1000);
510 }
511}
512
516bool UranusMeteo::readMoon()
517{
518 return false;
519}
520
524bool UranusMeteo::readTwilight()
525{
526 return false;
527}
528
532bool UranusMeteo::readConfig()
533{
534 return false;
535}
536
540bool UranusMeteo::sendCommand(const char * cmd, char * res)
541{
542 int nbytes_read = 0, nbytes_written = 0, tty_rc = 0;
543 char command[PEGASUS_LEN] = {0};
544 LOGF_DEBUG("CMD <%s>", cmd);
545
546 for (int i = 0; i < 2; i++)
547 {
548 tcflush(PortFD, TCIOFLUSH);
549 snprintf(command, PEGASUS_LEN, "%s\r\n", cmd);
550 if ( (tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
551 continue;
552
553 if (!res)
554 {
555 tcflush(PortFD, TCIOFLUSH);
556 return true;
557 }
558
559 if ( (tty_rc = tty_nread_section(PortFD, res, PEGASUS_LEN, PEGASUS_STOP_CHAR, PEGASUS_TIMEOUT, &nbytes_read)) != TTY_OK
560 || nbytes_read == 1)
561 continue;
562
563 tcflush(PortFD, TCIOFLUSH);
564 res[nbytes_read - 2] = '\0';
565 LOGF_DEBUG("RES <%s>", res);
566 return true;
567 }
568
569 if (tty_rc != TTY_OK)
570 {
571 char errorMessage[MAXRBUF];
572 tty_error_msg(tty_rc, errorMessage, MAXRBUF);
573 LOGF_ERROR("Serial error: %s", errorMessage);
574 }
575
576 return false;
577}
578
579
583std::vector<std::string> UranusMeteo::split(const std::string &input, const std::string &regex)
584{
585 // passing -1 as the submatch index parameter performs splitting
586 std::regex re(regex);
587 std::sregex_token_iterator
588 first{input.begin(), input.end(), re, -1},
589 last;
590 return {first, last};
591}
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
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
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.
INDI::PropertyNumber LocationNP
INDI::PropertyText TimeTP
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition indigps.cpp:60
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition indigps.cpp:88
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition indigps.cpp:70
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
Definition indigps.cpp:43
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save refresh period
Definition indigps.cpp:102
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
save Save property to configuration file.
void setLabel(const char *label)
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
void callOnTimeout(const std::function< void()> &callback)
Definition inditimer.cpp:76
void start()
Starts or restarts the timer with the timeout specified in interval.
Definition inditimer.cpp:82
void setInterval(int msec)
Set the timeout interval in milliseconds.
Provides interface to implement weather reporting and safety monitoring functionality.
void addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning, bool flipWarning=false)
Add a physical weather measurable parameter to the weather driver.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process weather switch properties.
bool setCriticalParameter(std::string name)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process weather number properties.
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save parameters ranges in the config file.
void initProperties(const char *statusGroup, const char *paramsGroup)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
INDI::PropertySwitch RefreshSP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
INDI::PropertyNumber UpdatePeriodNP
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save refresh period
virtual IPState updateWeather() override
updateWeather Update weather conditions from device or service. The function should not change the st...
const char * getDefaultName() override
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual IPState updateGPS() override
updateGPS Retrieve Location & Time from GPS. Update LocationNP & TimeTP properties (value and 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:148
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
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:759
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
#define MAXRBUF
Definition indidriver.c:52
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
uint8_t cmd[4]
Definition pwc-ioctl.h:2