Instrument Neutral Distributed Interface INDI 2.2.5
indisensorinterface.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2010, 2017 Ilia Platone, Jasem Mutlaq. All rights reserved.
3
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*******************************************************************************/
19
20#include "defaultdevice.h"
21#include "indisensorinterface.h"
24
25#include "indicom.h"
26#include "libastro.h"
28#include "locale_compat.h"
29#include "indiutility.h"
30
31#include <fitsio.h>
32
33#include <libnova/julian_day.h>
34#include <libnova/ln_types.h>
35#include <libnova/precession.h>
36
37#include <regex>
38
39#include <dirent.h>
40#include <cerrno>
41#include <locale.h>
42#include <cstdlib>
43#include <zlib.h>
44#include <sys/stat.h>
45
46namespace INDI
47{
48
50{
51 //ctor
52 capability = 0;
53
54 InIntegration = false;
55
56 AutoLoop = false;
57 SendIntegration = false;
58 ShowMarker = false;
59
60 IntegrationTime = 0.0;
61
62 RA = -1000;
63 Dec = -1000;
64 MPSAS = -1000;
65 Latitude = -1000;
66 Longitude = -1000;
67 Elevation = -1000;
69
70 Continuum = static_cast<uint8_t *>(malloc(sizeof(uint8_t))); // Seed for realloc
71 Real = static_cast<uint8_t *>(malloc(sizeof(uint8_t)));
72 Imaginary = static_cast<uint8_t *>(malloc(sizeof(uint8_t)));
73 Magnitude = static_cast<uint8_t *>(malloc(sizeof(uint8_t)));
74 Phase = static_cast<uint8_t *>(malloc(sizeof(uint8_t)));
75 BufferSize = 0;
76 NAxis = 2;
77
78 BPS = 8;
79
80 strncpy(integrationExtention, "raw", MAXINDIBLOBFMT);
81}
82
84{
85 free(Continuum);
86 free(Real);
87 free(Imaginary);
88 free(Magnitude);
89 free(Phase);
90 BufferSize = 0;
91 Continuum = nullptr;
92 Real = nullptr;
93 Imaginary = nullptr;
94 Magnitude = nullptr;
95 Phase = nullptr;
96}
97
98
100{
101 //IDLog("Sensor UpdateProperties isConnected returns %d %d\n",isConnected(),Connected);
102 if (isConnected())
103 {
105
106 if (CanAbort())
108
110
111 if (HasCooler())
113
115
117
119
120 if (UploadSettingsT[UPLOAD_DIR].text == nullptr)
121 IUSaveText(&UploadSettingsT[UPLOAD_DIR], getenv("HOME"));
123 }
124 else
125 {
127 if (CanAbort())
130
132
133 if (HasCooler())
135
137
140 }
141
142 if (HasStreaming())
143 Streamer->updateProperties();
144
145 if (HasDSP())
146 DSP->updateProperties();
147 return true;
148}
149
151{
153
155 loadConfig(true, "ACTIVE_DEVICES");
156
157 if (HasStreaming())
158 Streamer->ISGetProperties(dev);
159
160 if (HasDSP())
161 DSP->ISGetProperties(dev);
162}
163
165{
166 if (!IUSnoopNumber(root, &EqNP))
167 {
168 RA = EqNP.np[0].value;
169 Dec = EqNP.np[1].value;
170 //IDLog("Snooped RA %4.2f Dec %4.2f\n", RA, Dec);
171 }
172 if (!IUSnoopNumber(root, &LocationNP))
173 {
177 //IDLog("Snooped Latitude %4.2f Longitude %4.2f Elevation %4.2f\n", RA, Dec, Elevation);
178 }
179 if (!IUSnoopNumber(root, &ScopeParametersNP))
180 {
183 //IDLog("Snooped primaryAperture %4.2f primaryFocalLength %4.2f\n", primaryAperture, primaryFocalLength);
184 }
185
187}
188
189bool SensorInterface::processText(const char *dev, const char *name, char *texts[], char *names[], int n)
190{
191 // first check if it's for our device
192 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
193 {
194 // This is for our device
195 // Now lets see if it's something we process here
196 if (!strcmp(name, ActiveDeviceTP.name))
197 {
199 IUUpdateText(&ActiveDeviceTP, texts, names, n);
200 IDSetText(&ActiveDeviceTP, nullptr);
201
202 // Update the property name!
203 snprintf(EqNP.device, MAXINDIDEVICE, "%s", ActiveDeviceT[0].text);
204 snprintf(LocationNP.device, MAXINDIDEVICE, "%s", ActiveDeviceT[0].text);
205 snprintf(ScopeParametersNP.device, MAXINDIDEVICE, "%s", ActiveDeviceT[0].text);
206
207 IDSnoopDevice(ActiveDeviceT[0].text, "EQUATORIAL_EOD_COORD");
208 IDSnoopDevice(ActiveDeviceT[0].text, "GEOGRAPHIC_COORD");
209 IDSnoopDevice(ActiveDeviceT[0].text, "TELESCOPE_INFO");
210 IDSnoopDevice(ActiveDeviceT[1].text, "GEOGRAPHIC_COORD");
211
212 // Tell children active devices was updated.
214
215 // We processed this one, so, tell the world we did it
216 return true;
217 }
218
219 if (!strcmp(name, FITSHeaderTP.name))
220 {
221 IUUpdateText(&FITSHeaderTP, texts, names, n);
223 IDSetText(&FITSHeaderTP, nullptr);
224 return true;
225 }
226
227 if (!strcmp(name, UploadSettingsTP.name))
228 {
229 IUUpdateText(&UploadSettingsTP, texts, names, n);
231 IDSetText(&UploadSettingsTP, nullptr);
232 return true;
233 }
234 }
235
236 if (HasStreaming())
237 Streamer->ISNewText(dev, name, texts, names, n);
238
239 if (HasDSP())
240 DSP->ISNewText(dev, name, texts, names, n);
241
242 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
243}
244
245bool SensorInterface::processNumber(const char *dev, const char *name, double values[], char *names[], int n)
246{
247 // first check if it's for our device
248 //IDLog("SensorInterface::processNumber %s\n",name);
249 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
250 {
251 if (!strcmp(name, "SENSOR_INTEGRATION"))
252 {
253 if ((values[0] < FramedIntegrationN[0].min || values[0] > FramedIntegrationN[0].max))
254 {
255 DEBUGF(Logger::DBG_ERROR, "Requested integration value (%g) seconds out of bounds [%g,%g].",
256 values[0], FramedIntegrationN[0].min, FramedIntegrationN[0].max);
259 return false;
260 }
261
262 FramedIntegrationN[0].value = IntegrationTime = values[0];
263
265 {
266 if (CanAbort() && AbortIntegration() == false)
267 DEBUG(Logger::DBG_WARNING, "Warning: Aborting integration failed.");
268 }
269
272 else
275 return true;
276 }
277
278 // Sensor TEMPERATURE:
279 if (!strcmp(name, TemperatureNP.name))
280 {
281 if (values[0] < TemperatureN[0].min || values[0] > TemperatureN[0].max)
282 {
284 DEBUGF(Logger::DBG_ERROR, "Error: Bad temperature value! Range is [%.1f, %.1f] [C].",
286 IDSetNumber(&TemperatureNP, nullptr);
287 return false;
288 }
289
290 int rc = SetTemperature(values[0]);
291
292 if (rc == 0)
294 else if (rc == 1)
296 else
298
299 IDSetNumber(&TemperatureNP, nullptr);
300 return true;
301 }
302 }
303
304 if (HasStreaming())
305 Streamer->ISNewNumber(dev, name, values, names, n);
306
307 if (HasDSP())
308 DSP->ISNewNumber(dev, name, values, names, n);
309
310 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
311}
312
313bool SensorInterface::processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
314{
315 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
316 {
317 if (!strcmp(name, UploadSP.name))
318 {
319 int prevMode = IUFindOnSwitchIndex(&UploadSP);
320 IUUpdateSwitch(&UploadSP, states, names, n);
321 UploadSP.s = IPS_OK;
322 IDSetSwitch(&UploadSP, nullptr);
323
324 if (UploadS[0].s == ISS_ON)
325 {
326 DEBUG(Logger::DBG_SESSION, "Upload settings set to client only.");
327 if (prevMode != 0)
329 }
330 else if (UploadS[1].s == ISS_ON)
331 {
332 DEBUG(Logger::DBG_SESSION, "Upload settings set to local only.");
334 }
335 else
336 {
337 DEBUG(Logger::DBG_SESSION, "Upload settings set to client and local.");
339 }
340 return true;
341 }
342
343 if (!strcmp(name, TelescopeTypeSP.name))
344 {
345 IUUpdateSwitch(&TelescopeTypeSP, states, names, n);
347 IDSetSwitch(&TelescopeTypeSP, nullptr);
348 return true;
349 }
350
351 // Primary Device Abort Expsoure
352 if (strcmp(name, AbortIntegrationSP.name) == 0)
353 {
355
356 if (AbortIntegration())
357 {
360 FramedIntegrationN[0].value = 0;
361 }
362 else
363 {
366 }
367
370
371 return true;
372 }
373 }
374
375 if (HasStreaming())
376 Streamer->ISNewSwitch(dev, name, states, names, n);
377
378 if (HasDSP())
379 DSP->ISNewSwitch(dev, name, states, names, n);
380
381 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
382}
383
384bool SensorInterface::processBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
385 char *formats[], char *names[], int n)
386{
387 if (HasDSP())
388 DSP->ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
389
390 return INDI::DefaultDevice::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
391}
392
394{
395 INDI::DefaultDevice::initProperties(); // let the base class flesh in what it wants
396
397 // Sensor Temperature
398 IUFillNumber(&TemperatureN[0], "SENSOR_TEMPERATURE_VALUE", "Temperature (C)", "%5.2f", -50.0, 50.0, 0., 0.);
399 IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "SENSOR_TEMPERATURE", "Temperature",
401
402 /**********************************************/
403 /**************** Primary Device ****************/
404 /**********************************************/
405
406 // Sensor Integration
407 IUFillNumber(&FramedIntegrationN[0], "SENSOR_INTEGRATION_VALUE", "Time (s)", "%5.2f", 0.01, 3600, 1.0, 1.0);
409 "Integration", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE);
410
411 // Sensor Abort
412 if(CanAbort())
413 {
414 IUFillSwitch(&AbortIntegrationS[0], "ABORT", "Abort", ISS_OFF);
415 IUFillSwitchVector(&AbortIntegrationSP, AbortIntegrationS, 1, getDeviceName(), "SENSOR_ABORT_INTEGRATION",
416 "Integration Abort", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
417 }
418
419
420 /**********************************************/
421 /************** Upload Settings ***************/
422 /**********************************************/
423 // Upload Data
424 IUFillBLOB(&FitsB[0], "Continuum", "Sensor Continuum Blob", "");
425 IUFillBLOB(&FitsB[1], "Real", "Sensor Data Real", "");
426 IUFillBLOB(&FitsB[2], "Imaginary", "Sensor Imaginary Blob", "");
427 IUFillBLOB(&FitsB[3], "Magnitude", "Sensor Magnitude Blob", "");
428 IUFillBLOB(&FitsB[4], "Phase", "Sensor Phase Blob", "");
429
430 IUFillBLOBVector(&FitsBP, FitsB, 5, getDeviceName(), "SENSOR", "Integration Data", MAIN_CONTROL_TAB,
431 IP_RO, 60, IPS_IDLE);
432
433 // Upload Mode
434 IUFillSwitch(&UploadS[0], "UPLOAD_CLIENT", "Client", ISS_ON);
435 IUFillSwitch(&UploadS[1], "UPLOAD_LOCAL", "Local", ISS_OFF);
436 IUFillSwitch(&UploadS[2], "UPLOAD_BOTH", "Both", ISS_OFF);
438 0, IPS_IDLE);
439
440 // Upload Settings
441 IUFillText(&UploadSettingsT[UPLOAD_DIR], "UPLOAD_DIR", "Dir", "");
442 IUFillText(&UploadSettingsT[UPLOAD_PREFIX], "UPLOAD_PREFIX", "Prefix", "INTEGRATION_XXX");
443 IUFillTextVector(&UploadSettingsTP, UploadSettingsT, 2, getDeviceName(), "UPLOAD_SETTINGS", "Upload Settings",
445
446 // Upload File Path
447 IUFillText(&FileNameT[0], "FILE_PATH", "Path", "");
448 IUFillTextVector(&FileNameTP, FileNameT, 1, getDeviceName(), "SENSOR_FILE_PATH", "Filename", OPTIONS_TAB, IP_RO, 60,
449 IPS_IDLE);
450
451 /**********************************************/
452 /****************** FITS Header****************/
453 /**********************************************/
454
455 IUFillText(&FITSHeaderT[FITS_OBSERVER], "FITS_OBSERVER", "Observer", "Unknown");
456 IUFillText(&FITSHeaderT[FITS_OBJECT], "FITS_OBJECT", "Object", "Unknown");
457 IUFillTextVector(&FITSHeaderTP, FITSHeaderT, 2, getDeviceName(), "FITS_HEADER", "FITS Header", INFO_TAB, IP_RW, 60,
458 IPS_IDLE);
459
460 /**********************************************/
461 /**************** Snooping ********************/
462 /**********************************************/
463
464 // Snooped Devices
465 IUFillText(&ActiveDeviceT[0], "ACTIVE_TELESCOPE", "Telescope", "Telescope Simulator");
466 IUFillText(&ActiveDeviceT[1], "ACTIVE_GPS", "GPS", "GPS Simulator");
467 IUFillTextVector(&ActiveDeviceTP, ActiveDeviceT, 2, getDeviceName(), "ACTIVE_DEVICES", "Snoop devices", OPTIONS_TAB,
468 IP_RW, 60, IPS_IDLE);
469
470 // Snoop properties of interest
471 IUFillNumber(&EqN[0], "RA", "RA (hh:mm:ss)", "%010.6m", 0, 24, 0, 0);
472 IUFillNumber(&EqN[1], "DEC", "DEC (dd:mm:ss)", "%010.6m", -90, 90, 0, 0);
473 IUFillNumberVector(&EqNP, EqN, 2, getDeviceName(), "EQUATORIAL_EOD_COORD", "Eq. Coordinates", MAIN_CONTROL_TAB,
474 IP_RW, 60, IPS_IDLE);
475
476 IUFillNumber(&LocationN[0], "LAT", "Latitude (dd:mm:ss)", "%010.6m", -90, 90, 0, 0.0);
477 IUFillNumber(&LocationN[1], "LONG", "Longitude (dd:mm:ss)", "%010.6m", 0, 360, 0, 0.0);
478 IUFillNumber(&LocationN[2], "ELEV", "Elevation (m)", "%g", -200, 10000, 0, 0);
479 IUFillNumberVector(&LocationNP, LocationN, 3, getDeviceName(), "GEOGRAPHIC_COORD", "Location", MAIN_CONTROL_TAB,
480 IP_RO, 60, IPS_IDLE);
481
482 IUFillNumber(&ScopeParametersN[0], "TELESCOPE_APERTURE", "Aperture (mm)", "%g", 10, 5000, 0, 0.0);
483 IUFillNumber(&ScopeParametersN[1], "TELESCOPE_FOCAL_LENGTH", "Focal Length (mm)", "%g", 10, 10000, 0, 0.0);
484 IUFillNumber(&ScopeParametersN[2], "GUIDER_APERTURE", "Guider Aperture (mm)", "%g", 10, 5000, 0, 0.0);
485 IUFillNumber(&ScopeParametersN[3], "GUIDER_FOCAL_LENGTH", "Guider Focal Length (mm)", "%g", 10, 10000, 0, 0.0);
486 IUFillNumberVector(&ScopeParametersNP, ScopeParametersN, 4, getDeviceName(), "TELESCOPE_INFO", "Scope Properties",
487 OPTIONS_TAB, IP_RW, 60, IPS_OK);
488
489 IDSnoopDevice(ActiveDeviceT[0].text, "EQUATORIAL_EOD_COORD");
490 IDSnoopDevice(ActiveDeviceT[0].text, "GEOGRAPHIC_COORD");
491 IDSnoopDevice(ActiveDeviceT[0].text, "TELESCOPE_INFO");
492 IDSnoopDevice(ActiveDeviceT[1].text, "GEOGRAPHIC_COORD");
493
494 if (sensorConnection & CONNECTION_SERIAL)
495 {
498 {
499 return callHandshake();
500 });
502 }
503
504 if (sensorConnection & CONNECTION_TCP)
505 {
506 tcpConnection = new Connection::TCP(this);
508 {
509 return callHandshake();
510 });
511
513 }
514
515 return true;
516}
517
519{
520 capability = cap;
521
523
524 HasStreaming();
525 HasDSP();
526}
527
528void SensorInterface::setMinMaxStep(const char *property, const char *element, double min, double max, double step,
529 bool sendToClient)
530{
531 INumberVectorProperty *nvp = nullptr;
532
533 if (!strcmp(property, FramedIntegrationNP.name))
534 nvp = &FramedIntegrationNP;
535 else
536 return;
537
538 INumber *np = IUFindNumber(nvp, element);
539 if (np)
540 {
541 np->min = min;
542 np->max = max;
543 np->step = step;
544
545 if (sendToClient)
546 IUUpdateMinMax(nvp);
547 }
548}
549
550void SensorInterface::setBufferSize(int nbuf, bool allocMem)
551{
552 if (nbuf == BufferSize)
553 return;
554
555 BufferSize = nbuf;
556
557 // Reset size
558 if(HasStreaming())
559 {
560 Streamer->setPixelFormat(INDI_MONO, getBPS());
561 Streamer->setSize(getBufferSize() * 8 / abs(getBPS()), 1);
562 }
563
564 // DSP
565 if (HasDSP())
566 DSP->setSizes(1, new int[1] { BufferSize * 8 / getBPS() });
567
568 if (allocMem == false)
569 return;
570
571 Continuum = static_cast<uint8_t *>(realloc(Continuum, nbuf * sizeof(uint8_t)));
572 Real = static_cast<uint8_t *>(realloc(Real, nbuf * sizeof(uint8_t)));
573 Imaginary = static_cast<uint8_t *>(realloc(Imaginary, nbuf * sizeof(uint8_t)));
574 Magnitude = static_cast<uint8_t *>(realloc(Magnitude, nbuf * sizeof(uint8_t)));
575 Phase = static_cast<uint8_t *>(realloc(Phase, nbuf * sizeof(uint8_t)));
576}
577
579{
580 INDI_UNUSED(duration);
581 DEBUGF(Logger::DBG_WARNING, "SensorInterface::StartIntegration %4.2f - Should never get here", duration);
582 return false;
583}
584
586{
587 FramedIntegrationN[0].value = duration;
588
590}
591
593{
594 integrationTime = duration;
595 // JM 2020-04-28: FIXME
596 // This does not compile on MacOS, so commenting now.
597 // IP 2020-04-29: trying with some adaptation
598 startIntegrationTime = time_ns();
599}
600
602{
603 static char iso8601[32];
604 struct tm *tp;
605 time_t t = (time_t)startIntegrationTime;
606
607 tp = gmtime(&t);
608 strftime(iso8601, sizeof(iso8601), "%Y-%m-%dT%H:%M:%S", tp);
609 return iso8601;
610}
611
617
619{
620 return NAxis;
621}
622
624{
625 NAxis = value;
626}
627
629{
630 snprintf(integrationExtention, MAXINDIBLOBFMT, "%s", ext);
631 if(HasDSP())
632 DSP->setCaptureFileExtension(ext);
633}
634
635
637{
638 DEBUG(Logger::DBG_WARNING, "SensorInterface::AbortIntegration - Should never get here");
639 return false;
640}
641
642void SensorInterface::addFITSKeywords(fitsfile *fptr, uint8_t* buf, int len)
643{
644#ifndef WITH_MINMAX
645 INDI_UNUSED(buf);
646 INDI_UNUSED(len);
647#endif
648 int status = 0;
649 char dev_name[32];
650 char exp_start[32];
651 char timestamp[32];
652 double integrationTime;
653
654 char *orig = setlocale(LC_NUMERIC, "C");
655
656 char fitsString[MAXINDIDEVICE];
657
658 // SENSOR
659 snprintf(fitsString, MAXINDIDEVICE, "%s", getDeviceName());
660 fits_update_key_s(fptr, TSTRING, "INSTRUME", fitsString, "Sensor Name", &status);
661
662 // Telescope
663 snprintf(fitsString, MAXINDIDEVICE, "%s", ActiveDeviceT[0].text);
664 fits_update_key_s(fptr, TSTRING, "TELESCOP", fitsString, "Telescope name", &status);
665
666 // Observer
667 snprintf(fitsString, MAXINDIDEVICE, "%s", FITSHeaderT[FITS_OBSERVER].text);
668 fits_update_key_s(fptr, TSTRING, "OBSERVER", fitsString, "Observer name", &status);
669
670 // Object
671 snprintf(fitsString, MAXINDIDEVICE, "%s", FITSHeaderT[FITS_OBJECT].text);
672 fits_update_key_s(fptr, TSTRING, "OBJECT", fitsString, "Object name", &status);
673
674 integrationTime = getIntegrationTime();
675
676 snprintf(dev_name, 32, "%s", getDeviceName());
677 snprintf(exp_start, 32, "%s", getIntegrationStartTime());
678 snprintf(timestamp, 32, "%lf", startIntegrationTime);
679
680 fits_update_key_s(fptr, TDOUBLE, "EXPTIME", &(integrationTime), "Total Integration Time (s)", &status);
681
682 if (HasCooler())
683 fits_update_key_s(fptr, TDOUBLE, "SENSOR-TEMP", &(TemperatureN[0].value), "PrimarySensorInterface Temperature (Celsius)",
684 &status);
685
686#ifdef WITH_MINMAX
687 if (getNAxis() == 2)
688 {
689 double min_val, max_val;
690 getMinMax(&min_val, &max_val, buf, len, getBPS());
691
692 fits_update_key_s(fptr, TDOUBLE, "DATAMIN", &min_val, "Minimum value", &status);
693 fits_update_key_s(fptr, TDOUBLE, "DATAMAX", &max_val, "Maximum value", &status);
694 }
695#endif
696
697 if (primaryAperture != -1)
698 fits_update_key_s(fptr, TDOUBLE, "APTDIA", &primaryAperture, "Diameter (mm)", &status);
699
700 if (primaryFocalLength != -1)
701 fits_update_key_s(fptr, TDOUBLE, "FOCALLEN", &primaryFocalLength, "Focal Length (mm)", &status);
702
703 if (MPSAS != -1000)
704 {
705 fits_update_key_s(fptr, TDOUBLE, "MPSAS", &MPSAS, "Sky Quality (mag per arcsec^2)", &status);
706 }
707
708 if (Latitude != -1000 && Longitude != -1000 && Elevation != -1000)
709 {
710 char lat_str[MAXINDIFORMAT];
711 char lon_str[MAXINDIFORMAT];
712 char el_str[MAXINDIFORMAT];
713 fs_sexa(lat_str, Latitude, 2, 360000);
714 fs_sexa(lon_str, Longitude, 2, 360000);
715 snprintf(el_str, MAXINDIFORMAT, "%lf", Elevation);
716 fits_update_key_s(fptr, TSTRING, "SITELAT", lat_str, "Location Latitude", &status);
717 fits_update_key_s(fptr, TSTRING, "SITELONG", lon_str, "Location Longitude", &status);
718 fits_update_key_s(fptr, TSTRING, "SITEELEV", el_str, "Location Elevation", &status);
719 }
720 if (RA != -1000 && Dec != -1000)
721 {
722 INDI::IEquatorialCoordinates epochPos { 0, 0 }, J2000Pos { 0, 0 };
723 epochPos.rightascension = RA;
724 epochPos.declination = Dec;
725
726 // Convert from JNow to J2000
727 //TODO use exp_start instead of julian from system
728 INDI::ObservedToJ2000(&epochPos, ln_get_julian_from_sys(), &J2000Pos);
729
730 double raJ2000 = J2000Pos.rightascension;
731 double decJ2000 = J2000Pos.declination;
732 char ra_str[32], de_str[32];
733
734 fs_sexa(ra_str, raJ2000, 2, 360000);
735 fs_sexa(de_str, decJ2000, 2, 360000);
736
737 char *raPtr = ra_str, *dePtr = de_str;
738 while (*raPtr != '\0')
739 {
740 if (*raPtr == ':')
741 *raPtr = ' ';
742 raPtr++;
743 }
744 while (*dePtr != '\0')
745 {
746 if (*dePtr == ':')
747 *dePtr = ' ';
748 dePtr++;
749 }
750
751 fits_update_key_s(fptr, TSTRING, "OBJCTRA", ra_str, "Object RA", &status);
752 fits_update_key_s(fptr, TSTRING, "OBJCTDEC", de_str, "Object DEC", &status);
753
754 int epoch = 2000;
755
756 //fits_update_key_s(fptr, TINT, "EPOCH", &epoch, "Epoch", &status);
757 fits_update_key_s(fptr, TINT, "EQUINOX", &epoch, "Equinox", &status);
758 }
759
760 fits_update_key_s(fptr, TSTRING, "EPOCH", timestamp, "Unix Epoch of start of integration", &status);
761
762 fits_update_key_s(fptr, TSTRING, "DATE-OBS", exp_start, "UTC start date of observation", &status);
763
764 fits_write_comment(fptr, "Generated by INDI", &status);
765
766 setlocale(LC_NUMERIC, orig);
767}
768
769void SensorInterface::fits_update_key_s(fitsfile *fptr, int type, std::string name, void *p, std::string explanation,
770 int *status)
771{
772 // this function is for removing warnings about deprecated string conversion to char* (from arg 5)
773 fits_update_key(fptr, type, name.c_str(), p, const_cast<char *>(explanation.c_str()), status);
774}
775
776void* SensorInterface::sendFITS(uint8_t *buf, int len, IBLOB blob)
777{
778 bool sendIntegration = (UploadS[0].s == ISS_ON || UploadS[2].s == ISS_ON);
779 bool saveIntegration = (UploadS[1].s == ISS_ON || UploadS[2].s == ISS_ON);
780 fitsfile *fptr = nullptr;
781 void *memptr;
782 size_t memsize;
783 int img_type = 0;
784 int byte_type = 0;
785 int status = 0;
786 long naxis = 2;
787 long naxes[2] = {0};
788 int nelements = 0;
789 std::string bit_depth;
790 char error_status[MAXRBUF];
791 switch (getBPS())
792 {
793 case 8:
794 byte_type = TBYTE;
795 img_type = BYTE_IMG;
796 bit_depth = "8 bits per sample";
797 break;
798
799 case 16:
800 byte_type = TUSHORT;
801 img_type = USHORT_IMG;
802 bit_depth = "16 bits per sample";
803 break;
804
805 case 32:
806 byte_type = TLONG;
807 img_type = LONG_IMG;
808 bit_depth = "32 bits per sample";
809 break;
810
811 case 64:
812 byte_type = TLONGLONG;
813 img_type = LONGLONG_IMG;
814 bit_depth = "64 bits double per sample";
815 break;
816
817 case -32:
818 byte_type = TFLOAT;
819 img_type = FLOAT_IMG;
820 bit_depth = "32 bits double per sample";
821 break;
822
823 case -64:
824 byte_type = TDOUBLE;
825 img_type = DOUBLE_IMG;
826 bit_depth = "64 bits double per sample";
827 break;
828
829 default:
830 DEBUGF(Logger::DBG_ERROR, "Unsupported bits per sample value %d", getBPS());
831 return nullptr;
832 }
833 naxes[0] = len;
834 naxes[0] = naxes[0] < 1 ? 1 : naxes[0];
835 naxes[1] = 1;
836 nelements = naxes[0];
837
838 /*DEBUGF(Logger::DBG_DEBUG, "Exposure complete. Image Depth: %s. Width: %d Height: %d nelements: %d", bit_depth.c_str(), naxes[0],
839 naxes[1], nelements);*/
840
841 // Now we have to send fits format data to the client
842 memsize = 5760;
843 memptr = malloc(memsize);
844 if (!memptr)
845 {
846 DEBUGF(Logger::DBG_ERROR, "Error: failed to allocate memory: %lu", static_cast<unsigned long>(memsize));
847 }
848
849 fits_create_memfile(&fptr, &memptr, &memsize, 2880, realloc, &status);
850
851 if (status)
852 {
853 fits_report_error(stderr, status); /* print out any error messages */
854 fits_get_errstatus(status, error_status);
855 DEBUGF(Logger::DBG_ERROR, "FITS Error: %s", error_status);
856 if(memptr != nullptr)
857 free(memptr);
858 return nullptr;
859 }
860
861 fits_create_img(fptr, img_type, naxis, naxes, &status);
862
863 if (status)
864 {
865 fits_report_error(stderr, status); /* print out any error messages */
866 fits_get_errstatus(status, error_status);
867 DEBUGF(Logger::DBG_ERROR, "FITS Error: %s", error_status);
868 if(memptr != nullptr)
869 free(memptr);
870 return nullptr;
871 }
872
873 addFITSKeywords(fptr, buf, len);
874
875 fits_write_img(fptr, byte_type, 1, nelements, buf, &status);
876
877 if (status)
878 {
879 fits_report_error(stderr, status); /* print out any error messages */
880 fits_get_errstatus(status, error_status);
881 DEBUGF(Logger::DBG_ERROR, "FITS Error: %s", error_status);
882 if(memptr != nullptr)
883 free(memptr);
884 return nullptr;
885 }
886
887 fits_close_file(fptr, &status);
888
889 uploadFile(memptr, memsize, blob, sendIntegration, saveIntegration);
890
891 return memptr;
892}
893
895{
896 // Reset POLLMS to default value
898
899 if(HasDSP())
900 {
901 uint8_t* buf = (uint8_t*)malloc(getBufferSize());
902 memcpy(buf, getBuffer(), getBufferSize());
903 DSP->processBLOB(buf, 1, new int[1] { getBufferSize() * 8 / getBPS() }, getBPS());
904 free(buf);
905 }
906 // Run async
907 std::thread(&SensorInterface::IntegrationCompletePrivate, this).detach();
908
909 return true;
910}
911
912bool SensorInterface::IntegrationCompletePrivate()
913{
914 bool sendIntegration = (UploadS[0].s == ISS_ON || UploadS[2].s == ISS_ON);
915 bool saveIntegration = (UploadS[1].s == ISS_ON || UploadS[2].s == ISS_ON);
916 bool autoLoop = false;
917
918 if (sendIntegration || saveIntegration)
919 {
920 void* blob = nullptr;
921 void* magnitude = nullptr;
922 void* phase = nullptr;
923 void* real = nullptr;
924 void* imaginary = nullptr;
925 if (!strcmp(getIntegrationFileExtension(), "fits"))
926 {
927 blob = sendFITS(getBuffer(), getBufferSize() * 8 / abs(getBPS()), FitsB[0]);
928 magnitude = sendFITS(getMagnitude(), getBufferSize() * 8 / abs(getBPS()), FitsB[1]);
929 phase = sendFITS(getPhase(), getBufferSize() * 8 / abs(getBPS()), FitsB[2]);
930 real = sendFITS(getReal(), getBufferSize() * 8 / abs(getBPS()), FitsB[3]);
931 imaginary = sendFITS(getImaginary(), getBufferSize() * 8 / abs(getBPS()), FitsB[4]);
932 FitsBP.s = IPS_OK;
933 }
934 else
935 {
936 uploadFile(getBuffer(), getBufferSize(), FitsB[0], sendIntegration,
937 saveIntegration);
938 uploadFile(getBuffer(), getBufferSize(), FitsB[1], sendIntegration,
939 saveIntegration);
940 uploadFile(getBuffer(), getBufferSize(), FitsB[2], sendIntegration,
941 saveIntegration);
942 uploadFile(getBuffer(), getBufferSize(), FitsB[3], sendIntegration,
943 saveIntegration);
944 uploadFile(getBuffer(), getBufferSize(), FitsB[4], sendIntegration,
945 saveIntegration);
946 }
947
948 if (sendIntegration)
949 IDSetBLOB(&FitsBP, nullptr);
950 if(blob != nullptr)
951 free(blob);
952 if(blob != nullptr)
953 free(magnitude);
954 if(blob != nullptr)
955 free(phase);
956 if(blob != nullptr)
957 free(real);
958 if(blob != nullptr)
959 free(imaginary);
960
961 DEBUG(Logger::DBG_DEBUG, "Upload complete");
962 }
963
966
967 if (autoLoop)
968 {
971
974 else
975 {
976 DEBUG(Logger::DBG_DEBUG, "Autoloop: Sensor Integration Error!");
978 }
979
981 }
982
983 return true;
984}
985
986bool SensorInterface::uploadFile(const void *fitsData, size_t totalBytes, IBLOB blob, bool sendIntegration,
987 bool saveIntegration)
988{
989
990 DEBUGF(Logger::DBG_DEBUG, "Uploading file. Ext: %s, Size: %d, sendIntegration? %s, saveIntegration? %s",
991 getIntegrationFileExtension(), totalBytes, sendIntegration ? "Yes" : "No", saveIntegration ? "Yes" : "No");
992
993 blob.blob = const_cast<void *>(fitsData);
994 blob.bloblen = totalBytes;
995 snprintf(blob.format, MAXINDIBLOBFMT, ".%s", getIntegrationFileExtension());
996 if (saveIntegration)
997 {
998
999 FILE *fp = nullptr;
1000 char integrationFileName[MAXRBUF];
1001
1002 std::string prefix = UploadSettingsT[UPLOAD_PREFIX].text;
1003 int maxIndex = getFileIndex(UploadSettingsT[UPLOAD_DIR].text, UploadSettingsT[UPLOAD_PREFIX].text,
1004 blob.format);
1005
1006 if (maxIndex < 0)
1007 {
1008 DEBUGF(Logger::DBG_ERROR, "Error iterating directory %s. %s", UploadSettingsT[0].text,
1009 strerror(errno));
1010 return false;
1011 }
1012
1013 if (maxIndex > 0)
1014 {
1015 char ts[32];
1016 struct tm *tp;
1017 time_t t;
1018 time(&t);
1019 tp = localtime(&t);
1020 strftime(ts, sizeof(ts), "%Y-%m-%dT%H-%M-%S", tp);
1021 std::string filets(ts);
1022 prefix = std::regex_replace(prefix, std::regex("ISO8601"), filets);
1023
1024 char indexString[8];
1025 snprintf(indexString, 8, "%03d", maxIndex);
1026 std::string prefixIndex = indexString;
1027 //prefix.replace(prefix.find("XXX"), std::string::npos, prefixIndex);
1028 prefix = std::regex_replace(prefix, std::regex("XXX"), prefixIndex);
1029 }
1030
1031 snprintf(integrationFileName, MAXRBUF, "%s/%s%s", UploadSettingsT[0].text, prefix.c_str(), blob.format);
1032
1033 fp = fopen(integrationFileName, "w");
1034 if (fp == nullptr)
1035 {
1036 DEBUGF(Logger::DBG_ERROR, "Unable to save image file (%s). %s", integrationFileName, strerror(errno));
1037 return false;
1038 }
1039
1040 int n = 0;
1041 for (int nr = 0; nr < static_cast<int>(blob.bloblen); nr += n)
1042 n = fwrite((static_cast<char *>(blob.blob) + nr), 1, blob.bloblen - nr, fp);
1043
1044 fclose(fp);
1045
1046 // Save image file path
1047 IUSaveText(&FileNameT[0], integrationFileName);
1048
1049 DEBUGF(Logger::DBG_SESSION, "Image saved to %s", integrationFileName);
1051 IDSetText(&FileNameTP, nullptr);
1052 }
1053
1054 blob.size = totalBytes;
1055
1056 DEBUG(Logger::DBG_DEBUG, "Upload complete");
1057
1058 return true;
1059}
1060
1062{
1063 LOG_ERROR("Streaming is not supported.");
1064 return false;
1065}
1066
1068{
1069 LOG_ERROR("Streaming is not supported.");
1070 return false;
1071}
1072
1074{
1076 DEBUGF(Logger::DBG_WARNING, "SensorInterface::SetTemperature %4.2f - Should never get here", temperature);
1077 return -1;
1078}
1079
1081{
1083
1088
1089 if (HasStreaming())
1090 Streamer->saveConfigItems(fp);
1091
1092 if (HasDSP())
1093 DSP->saveConfigItems(fp);
1094
1095 return true;
1096}
1097
1098void SensorInterface::getMinMax(double *min, double *max, uint8_t *buf, int len, int bpp)
1099{
1100 int ind = 0, i, j;
1101 int integrationHeight = 1;
1102 int integrationWidth = len;
1103 double lmin = 0, lmax = 0;
1104
1105 switch (bpp)
1106 {
1107 case 8:
1108 {
1109 uint8_t *integrationBuffer = static_cast<uint8_t *>(buf);
1110 lmin = lmax = integrationBuffer[0];
1111
1112 for (i = 0; i < integrationHeight; i++)
1113 for (j = 0; j < integrationWidth; j++)
1114 {
1115 ind = (i * integrationWidth) + j;
1116 if (integrationBuffer[ind] < lmin)
1117 lmin = integrationBuffer[ind];
1118 else if (integrationBuffer[ind] > lmax)
1119 lmax = integrationBuffer[ind];
1120 }
1121 }
1122 break;
1123
1124 case 16:
1125 {
1126 uint16_t *integrationBuffer = reinterpret_cast<uint16_t *>(buf);
1127 lmin = lmax = integrationBuffer[0];
1128
1129 for (i = 0; i < integrationHeight; i++)
1130 for (j = 0; j < integrationWidth; j++)
1131 {
1132 ind = (i * integrationWidth) + j;
1133 if (integrationBuffer[ind] < lmin)
1134 lmin = integrationBuffer[ind];
1135 else if (integrationBuffer[ind] > lmax)
1136 lmax = integrationBuffer[ind];
1137 }
1138 }
1139 break;
1140
1141 case 32:
1142 {
1143 uint32_t *integrationBuffer = reinterpret_cast<uint32_t *>(buf);
1144 lmin = lmax = integrationBuffer[0];
1145
1146 for (i = 0; i < integrationHeight; i++)
1147 for (j = 0; j < integrationWidth; j++)
1148 {
1149 ind = (i * integrationWidth) + j;
1150 if (integrationBuffer[ind] < lmin)
1151 lmin = integrationBuffer[ind];
1152 else if (integrationBuffer[ind] > lmax)
1153 lmax = integrationBuffer[ind];
1154 }
1155 }
1156 break;
1157
1158 case 64:
1159 {
1160 unsigned long *integrationBuffer = reinterpret_cast<unsigned long *>(buf);
1161 lmin = lmax = integrationBuffer[0];
1162
1163 for (i = 0; i < integrationHeight; i++)
1164 for (j = 0; j < integrationWidth; j++)
1165 {
1166 ind = (i * integrationWidth) + j;
1167 if (integrationBuffer[ind] < lmin)
1168 lmin = integrationBuffer[ind];
1169 else if (integrationBuffer[ind] > lmax)
1170 lmax = integrationBuffer[ind];
1171 }
1172 }
1173 break;
1174
1175 case -32:
1176 {
1177 double *integrationBuffer = reinterpret_cast<double *>(buf);
1178 lmin = lmax = integrationBuffer[0];
1179
1180 for (i = 0; i < integrationHeight; i++)
1181 for (j = 0; j < integrationWidth; j++)
1182 {
1183 ind = (i * integrationWidth) + j;
1184 if (integrationBuffer[ind] < lmin)
1185 lmin = integrationBuffer[ind];
1186 else if (integrationBuffer[ind] > lmax)
1187 lmax = integrationBuffer[ind];
1188 }
1189 }
1190 break;
1191
1192 case -64:
1193 {
1194 double *integrationBuffer = reinterpret_cast<double *>(buf);
1195 lmin = lmax = integrationBuffer[0];
1196
1197 for (i = 0; i < integrationHeight; i++)
1198 for (j = 0; j < integrationWidth; j++)
1199 {
1200 ind = (i * integrationWidth) + j;
1201 if (integrationBuffer[ind] < lmin)
1202 lmin = integrationBuffer[ind];
1203 else if (integrationBuffer[ind] > lmax)
1204 lmax = integrationBuffer[ind];
1205 }
1206 }
1207 break;
1208 }
1209 *min = lmin;
1210 *max = lmax;
1211}
1212
1213std::string regex_replace_compat2(const std::string &input, const std::string &pattern, const std::string &replace)
1214{
1215 std::stringstream s;
1216 std::regex_replace(std::ostreambuf_iterator<char>(s), input.begin(), input.end(), std::regex(pattern), replace);
1217 return s.str();
1218}
1219
1220int SensorInterface::getFileIndex(const char *dir, const char *prefix, const char *ext)
1221{
1222 INDI_UNUSED(ext);
1223
1224 DIR *dpdf = nullptr;
1225 struct dirent *epdf = nullptr;
1226 std::vector<std::string> files = std::vector<std::string>();
1227
1228 std::string prefixIndex = prefix;
1229 prefixIndex = regex_replace_compat2(prefixIndex, "_ISO8601", "");
1230 prefixIndex = regex_replace_compat2(prefixIndex, "_XXX", "");
1231
1232 // Create directory if does not exist
1233 struct stat st;
1234
1235 if (stat(dir, &st) == -1)
1236 {
1237 LOGF_DEBUG("Creating directory %s...", dir);
1238 if (INDI::mkpath(dir, 0755) == -1)
1239 LOGF_ERROR("Error creating directory %s (%s)", dir, strerror(errno));
1240 }
1241
1242 dpdf = opendir(dir);
1243 if (dpdf != nullptr)
1244 {
1245 while ((epdf = readdir(dpdf)))
1246 {
1247 if (strstr(epdf->d_name, prefixIndex.c_str()))
1248 files.push_back(epdf->d_name);
1249 }
1250 closedir(dpdf);
1251 }
1252 else
1253 return -1;
1254
1255 int maxIndex = 0;
1256
1257 for (int i = 0; i < static_cast<int>(files.size()); i++)
1258 {
1259 int index = -1;
1260
1261 std::string file = files.at(i);
1262 std::size_t start = file.find_last_of("_");
1263 std::size_t end = file.find_last_of(".");
1264 if (start != std::string::npos)
1265 {
1266 index = atoi(file.substr(start + 1, end).c_str());
1267 if (index > maxIndex)
1268 maxIndex = index;
1269 }
1270 }
1271
1272 return (maxIndex + 1);
1273}
1274
1276{
1277 BPS = bps;
1278
1279 // Reset size
1280 if (HasStreaming())
1281 Streamer->setSize(getBufferSize() * 8 / BPS);
1282
1283 // DSP
1284 if (HasDSP())
1285 DSP->setSizes(1, new int[1] { getBufferSize() * 8 / BPS });
1286
1287}
1288
1290{
1291 return false;
1292}
1293
1294
1295bool SensorInterface::callHandshake()
1296{
1297 if (sensorConnection > 0)
1298 {
1301 else if (getActiveConnection() == tcpConnection)
1303 }
1304
1305 return Handshake();
1306}
1307
1309{
1311
1312 if (value == 0 || (mask & value) == 0)
1313 {
1314 DEBUGF(Logger::DBG_ERROR, "Invalid connection mode %d", value);
1315 return;
1316 }
1317
1318 sensorConnection = value;
1319}
1320}
double temperature
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 ...
The TCP class manages connection with devices over the network via TCP/IP. Upon successful connection...
int getPortFD() const
bool isConnected() const
const char * getDeviceName() const
void setCurrentPollingPeriod(uint32_t msec)
setCurrentPollingPeriod Change the current polling period to call TimerHit() function in the driver.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
virtual void ISGetProperties(const char *dev)
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
uint32_t getPollingPeriod() const
getPollingPeriod Return the polling period.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
uint32_t getDriverInterface() const
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Process the client newBLOB command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
Connection::Interface * getActiveConnection()
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
void setIntegrationFailed()
setIntegrationFailed Alert the client that the Integration failed.
ITextVectorProperty FileNameTP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
void SetCapability(uint32_t cap)
SetCapability Set the Sensor capabilities. Al fields must be initialized.
ITextVectorProperty FITSHeaderTP
virtual bool StartStreaming()
StartStreaming Start live video streaming.
virtual bool StopStreaming()
StopStreaming Stop live video streaming.
ITextVectorProperty UploadSettingsTP
INumberVectorProperty LocationNP
bool processSwitch(const char *dev, const char *name, ISState states[], char *names[], int n)
uint8_t * getReal()
getBuffer Get real buffer of the stream of the Sensor device.
void setBPS(int bps)
setBPP Set depth of Sensor device.
INumberVectorProperty EqNP
void setBufferSize(int nbuf, bool allocMem=true)
setBufferSize Set desired buffer size. The function will allocate memory accordingly....
void setNAxis(int value)
setNAxis Set FITS number of axis
void setSensorConnection(const uint8_t &value)
setSensorConnection Set Sensor connection mode. Child class should call this in the constructor befor...
void setIntegrationLeft(double duration)
setIntegrationLeft Update Integration time left. Inform the client of the new Integration time left v...
ITextVectorProperty ActiveDeviceTP
IBLOBVectorProperty FitsBP
virtual bool AbortIntegration()
Abort ongoing Integration.
uint8_t * getImaginary()
getBuffer Get imaginary buffer of the stream of the Sensor device.
std::unique_ptr< StreamManager > Streamer
void setIntegrationTime(double duration)
setIntegrationTime Set desired Sensor frame Integration duration for next Integration....
int PortFD
For Serial & TCP connections.
bool processText(const char *dev, const char *name, char *texts[], char *names[], int n)
virtual void setMinMaxStep(const char *property, const char *element, double min, double max, double step, bool sendToClient=true)
setMinMaxStep for a number property element
virtual void addFITSKeywords(fitsfile *fptr, uint8_t *buf, int len)
Add FITS keywords to a fits file.
int getBufferSize() const
getContinuumBufferSize Get allocated continuum buffer size to hold the Sensor integrationd stream.
bool processBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
INumberVectorProperty TemperatureNP
void fits_update_key_s(fitsfile *fptr, int type, std::string name, void *p, std::string explanation, int *status)
const char * getIntegrationStartTime()
getIntegrationStartTime
INumberVectorProperty FramedIntegrationNP
ISwitchVectorProperty AbortIntegrationSP
uint8_t * getMagnitude()
getBuffer Get magnitude buffer of the stream of the Sensor device.
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save configuration items in XML file.
virtual bool IntegrationComplete()
Uploads target Device exposed buffer as FITS to the client. Derived classes should class this functio...
ISwitchVectorProperty TelescopeTypeSP
void processProperties(const char *dev)
uint8_t * getBuffer()
getBuffer Get continuum buffer of the stream of the Sensor device.
bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
ISwitchVectorProperty UploadSP
virtual bool Handshake()
perform handshake with device to check communication
Connection::TCP * tcpConnection
bool processSnoopDevice(XMLEle *root)
int getBPS() const
getBPS Get Sensor depth (bits per sample).
void setIntegrationFileExtension(const char *ext)
setIntegrationExtension Set integration extension
Connection::Serial * serialConnection
virtual void activeDevicesUpdated()
activeDevicesUpdated Inform children that ActiveDevices property was updated so they can snoop on the...
double getIntegrationTime() const
getIntegrationTime Get requested Integration duration for the Sensor device in seconds.
INumberVectorProperty ScopeParametersNP
uint8_t * getPhase()
getBuffer Get phase buffer of the stream of the Sensor device.
virtual int SetTemperature(double temperature)
Set Sensor temperature.
virtual bool StartIntegration(double duration)
Start integration from the Sensor device.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * INFO_TAB
INFO_TAB Where all the properties for general information are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
double max(void)
int errno
#define min(a, b)
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
#define MAXINDIBLOBFMT
Definition indiapi.h:194
#define MAXINDIDEVICE
Definition indiapi.h:191
#define MAXINDIFORMAT
Definition indiapi.h:193
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_ATMOST1
Definition indiapi.h:172
@ INDI_MONO
double time_ns()
Get a unix timestamp with nanosecond precision.
Definition indicom.c:315
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition indicom.c:130
Implementations for common driver routines.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
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
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
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
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition indidevapi.c:275
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
int IUSnoopNumber(XMLEle *root, INumberVectorProperty *nvp)
Update a snooped number vector property from the given XML root element.
Definition indidevapi.c:555
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 IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition indidevapi.c:28
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
void IUFillBLOBVector(IBLOBVectorProperty *bvp, IBLOB *bp, int nbp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a BLOB vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:528
void IUFillBLOB(IBLOB *bp, const char *name, const char *label, const char *format)
Assign attributes for a BLOB property. The BLOB's data and auxiliary elements will be set to NULL.
Definition indidevapi.c:434
#define INDI_UNUSED(x)
Definition indidevapi.h:132
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.
#define MAXRBUF
Definition indidriver.c:52
void IDSnoopDevice(const char *snooped_device, const char *snooped_property)
Function a Driver calls to snoop on another Device. Snooped messages will then arrive via ISSnoopDevi...
Definition indidriver.c:143
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
void IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt,...)
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
Definition indilogger.h:56
#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 DEBUGF(priority, msg,...)
Definition indilogger.h:57
The DSP Namespace adds signal processing to INDI drivers. Primarily written for sensors and detectors...
Namespace to encapsulate INDI client, drivers, and mediator classes.
std::string regex_replace_compat2(const std::string &input, const std::string &pattern, const std::string &replace)
void ObservedToJ2000(IEquatorialCoordinates *observed, double jd, IEquatorialCoordinates *J2000pos)
ObservedToJ2000 converts an observed position to a J2000 catalogue position removes aberration,...
Definition libastro.cpp:51
int mkpath(std::string s, mode_t mode)
uint16_t type
Definition pwc-ioctl.h:0
One Blob (Binary Large Object) descriptor.
One number descriptor.
char name[MAXINDINAME]
Definition indiapi.h:473
Number vector property descriptor.
Definition indiapi.h:317
char device[MAXINDIDEVICE]
Definition indiapi.h:319
char name[MAXINDINAME]
Definition indiapi.h:321
double value
Definition indiapi.h:288
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248
void uploadFile(const char *filename)
Definition stv.c:1710