Instrument Neutral Distributed Interface INDI  2.0.2
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"
27 #include "stream/streammanager.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 
46 namespace 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  Buffer = static_cast<uint8_t *>(malloc(sizeof(uint8_t))); // Seed for realloc
71  BufferSize = 0;
72  NAxis = 2;
73 
74  BPS = 8;
75 
76  strncpy(integrationExtention, "raw", MAXINDIBLOBFMT);
77 }
78 
80 {
81  free(Buffer);
82  BufferSize = 0;
83  Buffer = nullptr;
84 }
85 
86 
88 {
89  //IDLog("Sensor UpdateProperties isConnected returns %d %d\n",isConnected(),Connected);
90  if (isConnected())
91  {
93 
94  if (CanAbort())
96 
98 
99  if (HasCooler())
101 
103 
105 
107 
108  if (UploadSettingsT[UPLOAD_DIR].text == nullptr)
109  IUSaveText(&UploadSettingsT[UPLOAD_DIR], getenv("HOME"));
111  }
112  else
113  {
115  if (CanAbort())
118 
120 
121  if (HasCooler())
123 
125 
128  }
129 
130  if (HasStreaming())
131  Streamer->updateProperties();
132 
133  if (HasDSP())
134  DSP->updateProperties();
135  return true;
136 }
137 
139 {
141 
143  loadConfig(true, "ACTIVE_DEVICES");
144 
145  if (HasStreaming())
146  Streamer->ISGetProperties(dev);
147 
148  if (HasDSP())
149  DSP->ISGetProperties(dev);
150 }
151 
153 {
154  if (!IUSnoopNumber(root, &EqNP))
155  {
156  RA = EqNP.np[0].value;
157  Dec = EqNP.np[1].value;
158  //IDLog("Snooped RA %4.2f Dec %4.2f\n", RA, Dec);
159  }
160  if (!IUSnoopNumber(root, &LocationNP))
161  {
162  Latitude = LocationNP.np[0].value;
163  Longitude = LocationNP.np[1].value;
164  Elevation = LocationNP.np[2].value;
165  //IDLog("Snooped Latitude %4.2f Longitude %4.2f Elevation %4.2f\n", RA, Dec, Elevation);
166  }
167  if (!IUSnoopNumber(root, &ScopeParametersNP))
168  {
171  //IDLog("Snooped primaryAperture %4.2f primaryFocalLength %4.2f\n", primaryAperture, primaryFocalLength);
172  }
173 
175 }
176 
177 bool SensorInterface::processText(const char *dev, const char *name, char *texts[], char *names[], int n)
178 {
179  // first check if it's for our device
180  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
181  {
182  // This is for our device
183  // Now lets see if it's something we process here
184  if (!strcmp(name, ActiveDeviceTP.name))
185  {
187  IUUpdateText(&ActiveDeviceTP, texts, names, n);
188  IDSetText(&ActiveDeviceTP, nullptr);
189 
190  // Update the property name!
191  strncpy(EqNP.device, ActiveDeviceT[0].text, MAXINDIDEVICE);
192  strncpy(LocationNP.device, ActiveDeviceT[0].text, MAXINDIDEVICE);
194 
195  IDSnoopDevice(ActiveDeviceT[0].text, "EQUATORIAL_EOD_COORD");
196  IDSnoopDevice(ActiveDeviceT[0].text, "GEOGRAPHIC_COORD");
197  IDSnoopDevice(ActiveDeviceT[0].text, "TELESCOPE_INFO");
198  IDSnoopDevice(ActiveDeviceT[1].text, "GEOGRAPHIC_COORD");
199 
200  // Tell children active devices was updated.
202 
203  // We processed this one, so, tell the world we did it
204  return true;
205  }
206 
207  if (!strcmp(name, FITSHeaderTP.name))
208  {
209  IUUpdateText(&FITSHeaderTP, texts, names, n);
211  IDSetText(&FITSHeaderTP, nullptr);
212  return true;
213  }
214 
215  if (!strcmp(name, UploadSettingsTP.name))
216  {
217  IUUpdateText(&UploadSettingsTP, texts, names, n);
219  IDSetText(&UploadSettingsTP, nullptr);
220  return true;
221  }
222  }
223 
224  if (HasStreaming())
225  Streamer->ISNewText(dev, name, texts, names, n);
226 
227  if (HasDSP())
228  DSP->ISNewText(dev, name, texts, names, n);
229 
230  return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
231 }
232 
233 bool SensorInterface::processNumber(const char *dev, const char *name, double values[], char *names[], int n)
234 {
235  // first check if it's for our device
236  //IDLog("SensorInterface::processNumber %s\n",name);
237  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
238  {
239  if (!strcmp(name, "SENSOR_INTEGRATION"))
240  {
241  if ((values[0] < FramedIntegrationN[0].min || values[0] > FramedIntegrationN[0].max))
242  {
243  DEBUGF(Logger::DBG_ERROR, "Requested integration value (%g) seconds out of bounds [%g,%g].",
244  values[0], FramedIntegrationN[0].min, FramedIntegrationN[0].max);
246  IDSetNumber(&FramedIntegrationNP, nullptr);
247  return false;
248  }
249 
250  FramedIntegrationN[0].value = IntegrationTime = values[0];
251 
253  {
254  if (CanAbort() && AbortIntegration() == false)
255  DEBUG(Logger::DBG_WARNING, "Warning: Aborting integration failed.");
256  }
257 
260  else
262  IDSetNumber(&FramedIntegrationNP, nullptr);
263  return true;
264  }
265 
266  // Sensor TEMPERATURE:
267  if (!strcmp(name, TemperatureNP.name))
268  {
269  if (values[0] < TemperatureN[0].min || values[0] > TemperatureN[0].max)
270  {
272  DEBUGF(Logger::DBG_ERROR, "Error: Bad temperature value! Range is [%.1f, %.1f] [C].",
274  IDSetNumber(&TemperatureNP, nullptr);
275  return false;
276  }
277 
278  int rc = SetTemperature(values[0]);
279 
280  if (rc == 0)
282  else if (rc == 1)
284  else
286 
287  IDSetNumber(&TemperatureNP, nullptr);
288  return true;
289  }
290  }
291 
292  if (HasStreaming())
293  Streamer->ISNewNumber(dev, name, values, names, n);
294 
295  if (HasDSP())
296  DSP->ISNewNumber(dev, name, values, names, n);
297 
298  return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
299 }
300 
301 bool SensorInterface::processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
302 {
303  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
304  {
305  if (!strcmp(name, UploadSP.name))
306  {
307  int prevMode = IUFindOnSwitchIndex(&UploadSP);
308  IUUpdateSwitch(&UploadSP, states, names, n);
309  UploadSP.s = IPS_OK;
310  IDSetSwitch(&UploadSP, nullptr);
311 
312  if (UploadS[0].s == ISS_ON)
313  {
314  DEBUG(Logger::DBG_SESSION, "Upload settings set to client only.");
315  if (prevMode != 0)
317  }
318  else if (UploadS[1].s == ISS_ON)
319  {
320  DEBUG(Logger::DBG_SESSION, "Upload settings set to local only.");
322  }
323  else
324  {
325  DEBUG(Logger::DBG_SESSION, "Upload settings set to client and local.");
327  }
328  return true;
329  }
330 
331  if (!strcmp(name, TelescopeTypeSP.name))
332  {
333  IUUpdateSwitch(&TelescopeTypeSP, states, names, n);
335  IDSetSwitch(&TelescopeTypeSP, nullptr);
336  return true;
337  }
338 
339  // Primary Device Abort Expsoure
340  if (strcmp(name, AbortIntegrationSP.name) == 0)
341  {
343 
344  if (AbortIntegration())
345  {
348  FramedIntegrationN[0].value = 0;
349  }
350  else
351  {
354  }
355 
356  IDSetSwitch(&AbortIntegrationSP, nullptr);
357  IDSetNumber(&FramedIntegrationNP, nullptr);
358 
359  return true;
360  }
361  }
362 
363  if (HasStreaming())
364  Streamer->ISNewSwitch(dev, name, states, names, n);
365 
366  if (HasDSP())
367  DSP->ISNewSwitch(dev, name, states, names, n);
368 
369  return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
370 }
371 
372 bool SensorInterface::processBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
373  char *formats[], char *names[], int n)
374 {
375  if (HasDSP())
376  DSP->ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
377 
378  return INDI::DefaultDevice::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
379 }
380 
382 {
383  INDI::DefaultDevice::initProperties(); // let the base class flesh in what it wants
384 
385  // Sensor Temperature
386  IUFillNumber(&TemperatureN[0], "SENSOR_TEMPERATURE_VALUE", "Temperature (C)", "%5.2f", -50.0, 50.0, 0., 0.);
387  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "SENSOR_TEMPERATURE", "Temperature",
389 
390  /**********************************************/
391  /**************** Primary Device ****************/
392  /**********************************************/
393 
394  // Sensor Integration
395  IUFillNumber(&FramedIntegrationN[0], "SENSOR_INTEGRATION_VALUE", "Time (s)", "%5.2f", 0.01, 3600, 1.0, 1.0);
397  "Integration", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE);
398 
399  // Sensor Abort
400  if(CanAbort())
401  {
402  IUFillSwitch(&AbortIntegrationS[0], "ABORT", "Abort", ISS_OFF);
403  IUFillSwitchVector(&AbortIntegrationSP, AbortIntegrationS, 1, getDeviceName(), "SENSOR_ABORT_INTEGRATION",
404  "Integration Abort", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
405  }
406 
407 
408  /**********************************************/
409  /************** Upload Settings ***************/
410  /**********************************************/
411  // Upload Data
412  IUFillBLOB(&FitsB, "DATA", "Sensor Data Blob", "");
413 
414  IUFillBLOBVector(&FitsBP, &FitsB, 1, getDeviceName(), "SENSOR", "Integration Data", MAIN_CONTROL_TAB,
415  IP_RO, 60, IPS_IDLE);
416 
417  // Upload Mode
418  IUFillSwitch(&UploadS[0], "UPLOAD_CLIENT", "Client", ISS_ON);
419  IUFillSwitch(&UploadS[1], "UPLOAD_LOCAL", "Local", ISS_OFF);
420  IUFillSwitch(&UploadS[2], "UPLOAD_BOTH", "Both", ISS_OFF);
421  IUFillSwitchVector(&UploadSP, UploadS, 3, getDeviceName(), "UPLOAD_MODE", "Upload", OPTIONS_TAB, IP_RW, ISR_1OFMANY,
422  0, IPS_IDLE);
423 
424  // Upload Settings
425  IUFillText(&UploadSettingsT[UPLOAD_DIR], "UPLOAD_DIR", "Dir", "");
426  IUFillText(&UploadSettingsT[UPLOAD_PREFIX], "UPLOAD_PREFIX", "Prefix", "INTEGRATION_XXX");
427  IUFillTextVector(&UploadSettingsTP, UploadSettingsT, 2, getDeviceName(), "UPLOAD_SETTINGS", "Upload Settings",
428  OPTIONS_TAB, IP_RW, 60, IPS_IDLE);
429 
430  // Upload File Path
431  IUFillText(&FileNameT[0], "FILE_PATH", "Path", "");
432  IUFillTextVector(&FileNameTP, FileNameT, 1, getDeviceName(), "SENSOR_FILE_PATH", "Filename", OPTIONS_TAB, IP_RO, 60,
433  IPS_IDLE);
434 
435  /**********************************************/
436  /****************** FITS Header****************/
437  /**********************************************/
438 
439  IUFillText(&FITSHeaderT[FITS_OBSERVER], "FITS_OBSERVER", "Observer", "Unknown");
440  IUFillText(&FITSHeaderT[FITS_OBJECT], "FITS_OBJECT", "Object", "Unknown");
441  IUFillTextVector(&FITSHeaderTP, FITSHeaderT, 2, getDeviceName(), "FITS_HEADER", "FITS Header", INFO_TAB, IP_RW, 60,
442  IPS_IDLE);
443 
444  /**********************************************/
445  /**************** Snooping ********************/
446  /**********************************************/
447 
448  // Snooped Devices
449  IUFillText(&ActiveDeviceT[0], "ACTIVE_TELESCOPE", "Telescope", "Telescope Simulator");
450  IUFillText(&ActiveDeviceT[1], "ACTIVE_GPS", "GPS", "GPS Simulator");
451  IUFillTextVector(&ActiveDeviceTP, ActiveDeviceT, 2, getDeviceName(), "ACTIVE_DEVICES", "Snoop devices", OPTIONS_TAB,
452  IP_RW, 60, IPS_IDLE);
453 
454  // Snoop properties of interest
455  IUFillNumber(&EqN[0], "RA", "RA (hh:mm:ss)", "%010.6m", 0, 24, 0, 0);
456  IUFillNumber(&EqN[1], "DEC", "DEC (dd:mm:ss)", "%010.6m", -90, 90, 0, 0);
457  IUFillNumberVector(&EqNP, EqN, 2, getDeviceName(), "EQUATORIAL_EOD_COORD", "Eq. Coordinates", MAIN_CONTROL_TAB,
458  IP_RW, 60, IPS_IDLE);
459 
460  IUFillNumber(&LocationN[0], "LAT", "Latitude (dd:mm:ss)", "%010.6m", -90, 90, 0, 0.0);
461  IUFillNumber(&LocationN[1], "LONG", "Longitude (dd:mm:ss)", "%010.6m", 0, 360, 0, 0.0);
462  IUFillNumber(&LocationN[2], "ELEV", "Elevation (m)", "%g", -200, 10000, 0, 0);
463  IUFillNumberVector(&LocationNP, LocationN, 3, getDeviceName(), "GEOGRAPHIC_COORD", "Location", MAIN_CONTROL_TAB,
464  IP_RO, 60, IPS_IDLE);
465 
466  IUFillNumber(&ScopeParametersN[0], "TELESCOPE_APERTURE", "Aperture (mm)", "%g", 10, 5000, 0, 0.0);
467  IUFillNumber(&ScopeParametersN[1], "TELESCOPE_FOCAL_LENGTH", "Focal Length (mm)", "%g", 10, 10000, 0, 0.0);
468  IUFillNumber(&ScopeParametersN[2], "GUIDER_APERTURE", "Guider Aperture (mm)", "%g", 10, 5000, 0, 0.0);
469  IUFillNumber(&ScopeParametersN[3], "GUIDER_FOCAL_LENGTH", "Guider Focal Length (mm)", "%g", 10, 10000, 0, 0.0);
470  IUFillNumberVector(&ScopeParametersNP, ScopeParametersN, 4, getDeviceName(), "TELESCOPE_INFO", "Scope Properties",
471  OPTIONS_TAB, IP_RW, 60, IPS_OK);
472 
473  IDSnoopDevice(ActiveDeviceT[0].text, "EQUATORIAL_EOD_COORD");
474  IDSnoopDevice(ActiveDeviceT[0].text, "GEOGRAPHIC_COORD");
475  IDSnoopDevice(ActiveDeviceT[0].text, "TELESCOPE_INFO");
476  IDSnoopDevice(ActiveDeviceT[1].text, "GEOGRAPHIC_COORD");
477 
478  if (sensorConnection & CONNECTION_SERIAL)
479  {
482  {
483  return callHandshake();
484  });
486  }
487 
488  if (sensorConnection & CONNECTION_TCP)
489  {
490  tcpConnection = new Connection::TCP(this);
492  {
493  return callHandshake();
494  });
495 
497  }
498 
499  return true;
500 }
501 
503 {
504  capability = cap;
505 
507 
508  HasStreaming();
509  HasDSP();
510 }
511 
512 void SensorInterface::setMinMaxStep(const char *property, const char *element, double min, double max, double step,
513  bool sendToClient)
514 {
515  INumberVectorProperty *nvp = nullptr;
516 
517  if (!strcmp(property, FramedIntegrationNP.name))
518  nvp = &FramedIntegrationNP;
519  else
520  return;
521 
522  INumber *np = IUFindNumber(nvp, element);
523  if (np)
524  {
525  np->min = min;
526  np->max = max;
527  np->step = step;
528 
529  if (sendToClient)
530  IUUpdateMinMax(nvp);
531  }
532 }
533 
534 void SensorInterface::setBufferSize(int nbuf, bool allocMem)
535 {
536  if (nbuf == BufferSize)
537  return;
538 
539  BufferSize = nbuf;
540 
541  // Reset size
542  if(HasStreaming())
543  {
544  Streamer->setPixelFormat(INDI_MONO, getBPS());
545  Streamer->setSize(getBufferSize() * 8 / abs(getBPS()), 1);
546  }
547 
548  // DSP
549  if (HasDSP())
550  DSP->setSizes(1, new int[1] { BufferSize * 8 / getBPS() });
551 
552  if (allocMem == false)
553  return;
554 
555  Buffer = static_cast<uint8_t *>(realloc(Buffer, nbuf * sizeof(uint8_t)));
556 }
557 
559 {
560  INDI_UNUSED(duration);
561  DEBUGF(Logger::DBG_WARNING, "SensorInterface::StartIntegration %4.2f - Should never get here", duration);
562  return false;
563 }
564 
566 {
567  FramedIntegrationN[0].value = duration;
568 
569  IDSetNumber(&FramedIntegrationNP, nullptr);
570 }
571 
573 {
574  integrationTime = duration;
575  // JM 2020-04-28: FIXME
576  // This does not compile on MacOS, so commenting now.
577  // IP 2020-04-29: trying with some adaptation
578  startIntegrationTime = time_ns();
579 }
580 
582 {
583  static char iso8601[32];
584  struct tm *tp;
585  time_t t = (time_t)startIntegrationTime;
586 
587  tp = gmtime(&t);
588  strftime(iso8601, sizeof(iso8601), "%Y-%m-%dT%H:%M:%S", tp);
589  return iso8601;
590 }
591 
593 {
595  IDSetNumber(&FramedIntegrationNP, nullptr);
596 }
597 
599 {
600  return NAxis;
601 }
602 
604 {
605  NAxis = value;
606 }
607 
609 {
610  strncpy(integrationExtention, ext, MAXINDIBLOBFMT);
611  if(HasDSP())
612  DSP->setCaptureFileExtension(ext);
613 }
614 
615 
617 {
618  DEBUG(Logger::DBG_WARNING, "SensorInterface::AbortIntegration - Should never get here");
619  return false;
620 }
621 
622 void SensorInterface::addFITSKeywords(fitsfile *fptr, uint8_t* buf, int len)
623 {
624 #ifndef WITH_MINMAX
625  INDI_UNUSED(buf);
626  INDI_UNUSED(len);
627 #endif
628  int status = 0;
629  char dev_name[32];
630  char exp_start[32];
631  char timestamp[32];
632  double integrationTime;
633 
634  char *orig = setlocale(LC_NUMERIC, "C");
635 
636  char fitsString[MAXINDIDEVICE];
637 
638  // SENSOR
639  strncpy(fitsString, getDeviceName(), MAXINDIDEVICE);
640  fits_update_key_s(fptr, TSTRING, "INSTRUME", fitsString, "Sensor Name", &status);
641 
642  // Telescope
643  strncpy(fitsString, ActiveDeviceT[0].text, MAXINDIDEVICE);
644  fits_update_key_s(fptr, TSTRING, "TELESCOP", fitsString, "Telescope name", &status);
645 
646  // Observer
647  strncpy(fitsString, FITSHeaderT[FITS_OBSERVER].text, MAXINDIDEVICE);
648  fits_update_key_s(fptr, TSTRING, "OBSERVER", fitsString, "Observer name", &status);
649 
650  // Object
651  strncpy(fitsString, FITSHeaderT[FITS_OBJECT].text, MAXINDIDEVICE);
652  fits_update_key_s(fptr, TSTRING, "OBJECT", fitsString, "Object name", &status);
653 
654  integrationTime = getIntegrationTime();
655 
656  strncpy(dev_name, getDeviceName(), 32);
657  strncpy(exp_start, getIntegrationStartTime(), 32);
658  snprintf(timestamp, 32, "%lf", startIntegrationTime);
659 
660  fits_update_key_s(fptr, TDOUBLE, "EXPTIME", &(integrationTime), "Total Integration Time (s)", &status);
661 
662  if (HasCooler())
663  fits_update_key_s(fptr, TDOUBLE, "SENSOR-TEMP", &(TemperatureN[0].value), "PrimarySensorInterface Temperature (Celsius)",
664  &status);
665 
666 #ifdef WITH_MINMAX
667  if (getNAxis() == 2)
668  {
669  double min_val, max_val;
670  getMinMax(&min_val, &max_val, buf, len, getBPS());
671 
672  fits_update_key_s(fptr, TDOUBLE, "DATAMIN", &min_val, "Minimum value", &status);
673  fits_update_key_s(fptr, TDOUBLE, "DATAMAX", &max_val, "Maximum value", &status);
674  }
675 #endif
676 
677  if (primaryAperture != -1)
678  fits_update_key_s(fptr, TDOUBLE, "APTDIA", &primaryAperture, "Diameter (mm)", &status);
679 
680  if (primaryFocalLength != -1)
681  fits_update_key_s(fptr, TDOUBLE, "FOCALLEN", &primaryFocalLength, "Focal Length (mm)", &status);
682 
683  if (MPSAS != -1000)
684  {
685  fits_update_key_s(fptr, TDOUBLE, "MPSAS", &MPSAS, "Sky Quality (mag per arcsec^2)", &status);
686  }
687 
688  if (Latitude != -1000 && Longitude != -1000 && Elevation != -1000)
689  {
690  char lat_str[MAXINDIFORMAT];
691  char lon_str[MAXINDIFORMAT];
692  char el_str[MAXINDIFORMAT];
693  fs_sexa(lat_str, Latitude, 2, 360000);
694  fs_sexa(lon_str, Longitude, 2, 360000);
695  snprintf(el_str, MAXINDIFORMAT, "%lf", Elevation);
696  fits_update_key_s(fptr, TSTRING, "SITELAT", lat_str, "Location Latitude", &status);
697  fits_update_key_s(fptr, TSTRING, "SITELONG", lon_str, "Location Longitude", &status);
698  fits_update_key_s(fptr, TSTRING, "SITEELEV", el_str, "Location Elevation", &status);
699  }
700  if (RA != -1000 && Dec != -1000)
701  {
702  INDI::IEquatorialCoordinates epochPos { 0, 0 }, J2000Pos { 0, 0 };
703  epochPos.rightascension = RA;
704  epochPos.declination = Dec;
705 
706  // Convert from JNow to J2000
707  //TODO use exp_start instead of julian from system
708  INDI::ObservedToJ2000(&epochPos, ln_get_julian_from_sys(), &J2000Pos);
709 
710  double raJ2000 = J2000Pos.rightascension;
711  double decJ2000 = J2000Pos.declination;
712  char ra_str[32], de_str[32];
713 
714  fs_sexa(ra_str, raJ2000, 2, 360000);
715  fs_sexa(de_str, decJ2000, 2, 360000);
716 
717  char *raPtr = ra_str, *dePtr = de_str;
718  while (*raPtr != '\0')
719  {
720  if (*raPtr == ':')
721  *raPtr = ' ';
722  raPtr++;
723  }
724  while (*dePtr != '\0')
725  {
726  if (*dePtr == ':')
727  *dePtr = ' ';
728  dePtr++;
729  }
730 
731  fits_update_key_s(fptr, TSTRING, "OBJCTRA", ra_str, "Object RA", &status);
732  fits_update_key_s(fptr, TSTRING, "OBJCTDEC", de_str, "Object DEC", &status);
733 
734  int epoch = 2000;
735 
736  //fits_update_key_s(fptr, TINT, "EPOCH", &epoch, "Epoch", &status);
737  fits_update_key_s(fptr, TINT, "EQUINOX", &epoch, "Equinox", &status);
738  }
739 
740  fits_update_key_s(fptr, TSTRING, "EPOCH", timestamp, "Unix Epoch of start of integration", &status);
741 
742  fits_update_key_s(fptr, TSTRING, "DATE-OBS", exp_start, "UTC start date of observation", &status);
743 
744  fits_write_comment(fptr, "Generated by INDI", &status);
745 
746  setlocale(LC_NUMERIC, orig);
747 }
748 
749 void SensorInterface::fits_update_key_s(fitsfile *fptr, int type, std::string name, void *p, std::string explanation,
750  int *status)
751 {
752  // this function is for removing warnings about deprecated string conversion to char* (from arg 5)
753  fits_update_key(fptr, type, name.c_str(), p, const_cast<char *>(explanation.c_str()), status);
754 }
755 
756 void* SensorInterface::sendFITS(uint8_t *buf, int len)
757 {
758  bool sendIntegration = (UploadS[0].s == ISS_ON || UploadS[2].s == ISS_ON);
759  bool saveIntegration = (UploadS[1].s == ISS_ON || UploadS[2].s == ISS_ON);
760  fitsfile *fptr = nullptr;
761  void *memptr;
762  size_t memsize;
763  int img_type = 0;
764  int byte_type = 0;
765  int status = 0;
766  long naxis = 2;
767  long naxes[2] = {0};
768  int nelements = 0;
769  std::string bit_depth;
770  char error_status[MAXRBUF];
771  switch (getBPS())
772  {
773  case 8:
774  byte_type = TBYTE;
775  img_type = BYTE_IMG;
776  bit_depth = "8 bits per sample";
777  break;
778 
779  case 16:
780  byte_type = TUSHORT;
781  img_type = USHORT_IMG;
782  bit_depth = "16 bits per sample";
783  break;
784 
785  case 32:
786  byte_type = TLONG;
787  img_type = LONG_IMG;
788  bit_depth = "32 bits per sample";
789  break;
790 
791  case 64:
792  byte_type = TLONGLONG;
793  img_type = LONGLONG_IMG;
794  bit_depth = "64 bits double per sample";
795  break;
796 
797  case -32:
798  byte_type = TFLOAT;
799  img_type = FLOAT_IMG;
800  bit_depth = "32 bits double per sample";
801  break;
802 
803  case -64:
804  byte_type = TDOUBLE;
805  img_type = DOUBLE_IMG;
806  bit_depth = "64 bits double per sample";
807  break;
808 
809  default:
810  DEBUGF(Logger::DBG_ERROR, "Unsupported bits per sample value %d", getBPS());
811  return nullptr;
812  }
813  naxes[0] = len;
814  naxes[0] = naxes[0] < 1 ? 1 : naxes[0];
815  naxes[1] = 1;
816  nelements = naxes[0];
817 
818  /*DEBUGF(Logger::DBG_DEBUG, "Exposure complete. Image Depth: %s. Width: %d Height: %d nelements: %d", bit_depth.c_str(), naxes[0],
819  naxes[1], nelements);*/
820 
821  // Now we have to send fits format data to the client
822  memsize = 5760;
823  memptr = malloc(memsize);
824  if (!memptr)
825  {
826  DEBUGF(Logger::DBG_ERROR, "Error: failed to allocate memory: %lu", static_cast<unsigned long>(memsize));
827  }
828 
829  fits_create_memfile(&fptr, &memptr, &memsize, 2880, realloc, &status);
830 
831  if (status)
832  {
833  fits_report_error(stderr, status); /* print out any error messages */
834  fits_get_errstatus(status, error_status);
835  DEBUGF(Logger::DBG_ERROR, "FITS Error: %s", error_status);
836  if(memptr != nullptr)
837  free(memptr);
838  return nullptr;
839  }
840 
841  fits_create_img(fptr, img_type, naxis, naxes, &status);
842 
843  if (status)
844  {
845  fits_report_error(stderr, status); /* print out any error messages */
846  fits_get_errstatus(status, error_status);
847  DEBUGF(Logger::DBG_ERROR, "FITS Error: %s", error_status);
848  if(memptr != nullptr)
849  free(memptr);
850  return nullptr;
851  }
852 
853  addFITSKeywords(fptr, buf, len);
854 
855  fits_write_img(fptr, byte_type, 1, nelements, buf, &status);
856 
857  if (status)
858  {
859  fits_report_error(stderr, status); /* print out any error messages */
860  fits_get_errstatus(status, error_status);
861  DEBUGF(Logger::DBG_ERROR, "FITS Error: %s", error_status);
862  if(memptr != nullptr)
863  free(memptr);
864  return nullptr;
865  }
866 
867  fits_close_file(fptr, &status);
868 
869  uploadFile(memptr, memsize, sendIntegration, saveIntegration);
870 
871  return memptr;
872 }
873 
875 {
876  // Reset POLLMS to default value
878 
879  if(HasDSP())
880  {
881  uint8_t* buf = (uint8_t*)malloc(getBufferSize());
882  memcpy(buf, getBuffer(), getBufferSize());
883  DSP->processBLOB(buf, 1, new int[1] { getBufferSize() * 8 / getBPS() }, getBPS());
884  free(buf);
885  }
886  // Run async
887  std::thread(&SensorInterface::IntegrationCompletePrivate, this).detach();
888 
889  return true;
890 }
891 
892 bool SensorInterface::IntegrationCompletePrivate()
893 {
894  bool sendIntegration = (UploadS[0].s == ISS_ON || UploadS[2].s == ISS_ON);
895  bool saveIntegration = (UploadS[1].s == ISS_ON || UploadS[2].s == ISS_ON);
896  bool autoLoop = false;
897 
898  if (sendIntegration || saveIntegration)
899  {
900  void* blob = nullptr;
901  if (!strcmp(getIntegrationFileExtension(), "fits"))
902  {
903  blob = sendFITS(getBuffer(), getBufferSize() * 8 / abs(getBPS()));
904  }
905  else
906  {
907  uploadFile(getBuffer(), getBufferSize(), sendIntegration,
908  saveIntegration);
909  }
910 
911  if (sendIntegration)
912  IDSetBLOB(&FitsBP, nullptr);
913  if(blob != nullptr)
914  free(blob);
915 
916  DEBUG(Logger::DBG_DEBUG, "Upload complete");
917  }
918 
920  IDSetNumber(&FramedIntegrationNP, nullptr);
921 
922  if (autoLoop)
923  {
926 
929  else
930  {
931  DEBUG(Logger::DBG_DEBUG, "Autoloop: Sensor Integration Error!");
933  }
934 
935  IDSetNumber(&FramedIntegrationNP, nullptr);
936  }
937 
938  return true;
939 }
940 
941 bool SensorInterface::uploadFile(const void *fitsData, size_t totalBytes, bool sendIntegration,
942  bool saveIntegration)
943 {
944 
945  DEBUGF(Logger::DBG_DEBUG, "Uploading file. Ext: %s, Size: %d, sendIntegration? %s, saveIntegration? %s",
946  getIntegrationFileExtension(), totalBytes, sendIntegration ? "Yes" : "No", saveIntegration ? "Yes" : "No");
947 
948  FitsB.blob = const_cast<void *>(fitsData);
949  FitsB.bloblen = totalBytes;
950  snprintf(FitsB.format, MAXINDIBLOBFMT, ".%s", getIntegrationFileExtension());
951  if (saveIntegration)
952  {
953 
954  FILE *fp = nullptr;
955  char integrationFileName[MAXRBUF];
956 
957  std::string prefix = UploadSettingsT[UPLOAD_PREFIX].text;
958  int maxIndex = getFileIndex(UploadSettingsT[UPLOAD_DIR].text, UploadSettingsT[UPLOAD_PREFIX].text,
959  FitsB.format);
960 
961  if (maxIndex < 0)
962  {
963  DEBUGF(Logger::DBG_ERROR, "Error iterating directory %s. %s", UploadSettingsT[0].text,
964  strerror(errno));
965  return false;
966  }
967 
968  if (maxIndex > 0)
969  {
970  char ts[32];
971  struct tm *tp;
972  time_t t;
973  time(&t);
974  tp = localtime(&t);
975  strftime(ts, sizeof(ts), "%Y-%m-%dT%H-%M-%S", tp);
976  std::string filets(ts);
977  prefix = std::regex_replace(prefix, std::regex("ISO8601"), filets);
978 
979  char indexString[8];
980  snprintf(indexString, 8, "%03d", maxIndex);
981  std::string prefixIndex = indexString;
982  //prefix.replace(prefix.find("XXX"), std::string::npos, prefixIndex);
983  prefix = std::regex_replace(prefix, std::regex("XXX"), prefixIndex);
984  }
985 
986  snprintf(integrationFileName, MAXRBUF, "%s/%s%s", UploadSettingsT[0].text, prefix.c_str(), FitsB.format);
987 
988  fp = fopen(integrationFileName, "w");
989  if (fp == nullptr)
990  {
991  DEBUGF(Logger::DBG_ERROR, "Unable to save image file (%s). %s", integrationFileName, strerror(errno));
992  return false;
993  }
994 
995  int n = 0;
996  for (int nr = 0; nr < static_cast<int>(FitsB.bloblen); nr += n)
997  n = fwrite((static_cast<char *>(FitsB.blob) + nr), 1, FitsB.bloblen - nr, fp);
998 
999  fclose(fp);
1000 
1001  // Save image file path
1002  IUSaveText(&FileNameT[0], integrationFileName);
1003 
1004  DEBUGF(Logger::DBG_SESSION, "Image saved to %s", integrationFileName);
1005  FileNameTP.s = IPS_OK;
1006  IDSetText(&FileNameTP, nullptr);
1007  }
1008 
1009  FitsB.size = totalBytes;
1010  FitsBP.s = IPS_OK;
1011 
1012  DEBUG(Logger::DBG_DEBUG, "Upload complete");
1013 
1014  return true;
1015 }
1016 
1018 {
1019  LOG_ERROR("Streaming is not supported.");
1020  return false;
1021 }
1022 
1024 {
1025  LOG_ERROR("Streaming is not supported.");
1026  return false;
1027 }
1028 
1029 int SensorInterface::SetTemperature(double temperature)
1030 {
1031  INDI_UNUSED(temperature);
1032  DEBUGF(Logger::DBG_WARNING, "SensorInterface::SetTemperature %4.2f - Should never get here", temperature);
1033  return -1;
1034 }
1035 
1037 {
1039 
1044 
1045  if (HasStreaming())
1046  Streamer->saveConfigItems(fp);
1047 
1048  if (HasDSP())
1049  DSP->saveConfigItems(fp);
1050 
1051  return true;
1052 }
1053 
1054 void SensorInterface::getMinMax(double *min, double *max, uint8_t *buf, int len, int bpp)
1055 {
1056  int ind = 0, i, j;
1057  int integrationHeight = 1;
1058  int integrationWidth = len;
1059  double lmin = 0, lmax = 0;
1060 
1061  switch (bpp)
1062  {
1063  case 8:
1064  {
1065  uint8_t *integrationBuffer = static_cast<uint8_t *>(buf);
1066  lmin = lmax = integrationBuffer[0];
1067 
1068  for (i = 0; i < integrationHeight; i++)
1069  for (j = 0; j < integrationWidth; j++)
1070  {
1071  ind = (i * integrationWidth) + j;
1072  if (integrationBuffer[ind] < lmin)
1073  lmin = integrationBuffer[ind];
1074  else if (integrationBuffer[ind] > lmax)
1075  lmax = integrationBuffer[ind];
1076  }
1077  }
1078  break;
1079 
1080  case 16:
1081  {
1082  uint16_t *integrationBuffer = reinterpret_cast<uint16_t *>(buf);
1083  lmin = lmax = integrationBuffer[0];
1084 
1085  for (i = 0; i < integrationHeight; i++)
1086  for (j = 0; j < integrationWidth; j++)
1087  {
1088  ind = (i * integrationWidth) + j;
1089  if (integrationBuffer[ind] < lmin)
1090  lmin = integrationBuffer[ind];
1091  else if (integrationBuffer[ind] > lmax)
1092  lmax = integrationBuffer[ind];
1093  }
1094  }
1095  break;
1096 
1097  case 32:
1098  {
1099  uint32_t *integrationBuffer = reinterpret_cast<uint32_t *>(buf);
1100  lmin = lmax = integrationBuffer[0];
1101 
1102  for (i = 0; i < integrationHeight; i++)
1103  for (j = 0; j < integrationWidth; j++)
1104  {
1105  ind = (i * integrationWidth) + j;
1106  if (integrationBuffer[ind] < lmin)
1107  lmin = integrationBuffer[ind];
1108  else if (integrationBuffer[ind] > lmax)
1109  lmax = integrationBuffer[ind];
1110  }
1111  }
1112  break;
1113 
1114  case 64:
1115  {
1116  unsigned long *integrationBuffer = reinterpret_cast<unsigned long *>(buf);
1117  lmin = lmax = integrationBuffer[0];
1118 
1119  for (i = 0; i < integrationHeight; i++)
1120  for (j = 0; j < integrationWidth; j++)
1121  {
1122  ind = (i * integrationWidth) + j;
1123  if (integrationBuffer[ind] < lmin)
1124  lmin = integrationBuffer[ind];
1125  else if (integrationBuffer[ind] > lmax)
1126  lmax = integrationBuffer[ind];
1127  }
1128  }
1129  break;
1130 
1131  case -32:
1132  {
1133  double *integrationBuffer = reinterpret_cast<double *>(buf);
1134  lmin = lmax = integrationBuffer[0];
1135 
1136  for (i = 0; i < integrationHeight; i++)
1137  for (j = 0; j < integrationWidth; j++)
1138  {
1139  ind = (i * integrationWidth) + j;
1140  if (integrationBuffer[ind] < lmin)
1141  lmin = integrationBuffer[ind];
1142  else if (integrationBuffer[ind] > lmax)
1143  lmax = integrationBuffer[ind];
1144  }
1145  }
1146  break;
1147 
1148  case -64:
1149  {
1150  double *integrationBuffer = reinterpret_cast<double *>(buf);
1151  lmin = lmax = integrationBuffer[0];
1152 
1153  for (i = 0; i < integrationHeight; i++)
1154  for (j = 0; j < integrationWidth; j++)
1155  {
1156  ind = (i * integrationWidth) + j;
1157  if (integrationBuffer[ind] < lmin)
1158  lmin = integrationBuffer[ind];
1159  else if (integrationBuffer[ind] > lmax)
1160  lmax = integrationBuffer[ind];
1161  }
1162  }
1163  break;
1164  }
1165  *min = lmin;
1166  *max = lmax;
1167 }
1168 
1169 std::string regex_replace_compat2(const std::string &input, const std::string &pattern, const std::string &replace)
1170 {
1171  std::stringstream s;
1172  std::regex_replace(std::ostreambuf_iterator<char>(s), input.begin(), input.end(), std::regex(pattern), replace);
1173  return s.str();
1174 }
1175 
1176 int SensorInterface::getFileIndex(const char *dir, const char *prefix, const char *ext)
1177 {
1178  INDI_UNUSED(ext);
1179 
1180  DIR *dpdf = nullptr;
1181  struct dirent *epdf = nullptr;
1182  std::vector<std::string> files = std::vector<std::string>();
1183 
1184  std::string prefixIndex = prefix;
1185  prefixIndex = regex_replace_compat2(prefixIndex, "_ISO8601", "");
1186  prefixIndex = regex_replace_compat2(prefixIndex, "_XXX", "");
1187 
1188  // Create directory if does not exist
1189  struct stat st;
1190 
1191  if (stat(dir, &st) == -1)
1192  {
1193  LOGF_DEBUG("Creating directory %s...", dir);
1194  if (INDI::mkpath(dir, 0755) == -1)
1195  LOGF_ERROR("Error creating directory %s (%s)", dir, strerror(errno));
1196  }
1197 
1198  dpdf = opendir(dir);
1199  if (dpdf != nullptr)
1200  {
1201  while ((epdf = readdir(dpdf)))
1202  {
1203  if (strstr(epdf->d_name, prefixIndex.c_str()))
1204  files.push_back(epdf->d_name);
1205  }
1206  closedir(dpdf);
1207  }
1208  else
1209  return -1;
1210 
1211  int maxIndex = 0;
1212 
1213  for (int i = 0; i < static_cast<int>(files.size()); i++)
1214  {
1215  int index = -1;
1216 
1217  std::string file = files.at(i);
1218  std::size_t start = file.find_last_of("_");
1219  std::size_t end = file.find_last_of(".");
1220  if (start != std::string::npos)
1221  {
1222  index = atoi(file.substr(start + 1, end).c_str());
1223  if (index > maxIndex)
1224  maxIndex = index;
1225  }
1226  }
1227 
1228  return (maxIndex + 1);
1229 }
1230 
1232 {
1233  BPS = bps;
1234 
1235  // Reset size
1236  if (HasStreaming())
1237  Streamer->setSize(getBufferSize() * 8 / BPS);
1238 
1239  // DSP
1240  if (HasDSP())
1241  DSP->setSizes(1, new int[1] { getBufferSize() * 8 / BPS });
1242 }
1243 
1244 
1246 {
1247  return false;
1248 }
1249 
1250 bool SensorInterface::callHandshake()
1251 {
1252  if (sensorConnection > 0)
1253  {
1256  else if (getActiveConnection() == tcpConnection)
1258  }
1259 
1260  return Handshake();
1261 }
1262 
1263 void SensorInterface::setSensorConnection(const uint8_t &value)
1264 {
1266 
1267  if (value == 0 || (mask & value) == 0)
1268  {
1269  DEBUGF(Logger::DBG_ERROR, "Invalid connection mode %d", value);
1270  return;
1271  }
1272 
1273  sensorConnection = value;
1274 }
1275 }
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 ...
The TCP class manages connection with devices over the network via TCP/IP. Upon successfull connectio...
Definition: connectiontcp.h:38
int getPortFD() const
Definition: connectiontcp.h:84
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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...
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()
Initilize properties initial state and value. The child class must implement this function.
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.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
Connection::Interface * getActiveConnection()
uint16_t getDriverInterface() const
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)
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 * getBuffer()
getBuffer Get raw 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
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. Dervied classes should class this functio...
ISwitchVectorProperty TelescopeTypeSP
void processProperties(const char *dev)
bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
bool initProperties()
Initilize 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 exntension
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
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
double min(void)
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
#define MAXINDIBLOBFMT
Definition: indiapi.h:196
#define MAXINDIDEVICE
Definition: indiapi.h:193
#define MAXINDIFORMAT
Definition: indiapi.h:195
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_ATMOST1
Definition: indiapi.h:174
@ INDI_MONO
Definition: indibasetypes.h:71
double time_ns()
Get a unix timestamp with nanosecond precision.
Definition: indicom.c:326
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:141
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:25
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
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indidevapi.c:66
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
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 IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
int IUSnoopNumber(XMLEle *root, INumberVectorProperty *nvp)
Update a snooped number vector property from the given XML root element.
Definition: indidevapi.c:337
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 IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indidevapi.c:20
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
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:310
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:216
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
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 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.
Definition: indidriver.c:1296
void IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt,...)
Definition: indidriver.c:1287
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#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
#define MAXRBUF
Definition: indiserver.cpp:102
The DSP Namespace adds signal processing to INDI drivers. Primarily written for sensors and detectors...
Definition: convolution.cpp:40
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:50
int mkpath(std::string s, mode_t mode)
Definition: indiutility.cpp:51
__le16 type
Definition: pwc-ioctl.h:0
One number descriptor.
char name[MAXINDINAME]
Definition: indiapi.h:475
Number vector property descriptor.
Definition: indiapi.h:319
char device[MAXINDIDEVICE]
Definition: indiapi.h:321
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250
void uploadFile(const char *filename)
Definition: stv.c:1710