Instrument Neutral Distributed Interface INDI  2.0.2
astrometrydriver.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2017 Jasem Mutlaq. All rights reserved.
3 
4  INDI Astrometry.net Driver
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the Free
8  Software Foundation; either version 2 of the License, or (at your option)
9  any later version.
10 
11  This program is distributed in the hope that it will be useful, but WITHOUT
12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 
21  The full GNU General Public License is included in this distribution in the
22  file called LICENSE.
23 *******************************************************************************/
24 
25 #include "astrometrydriver.h"
26 
27 #include <memory>
28 
29 #include <cerrno>
30 #include <cstring>
31 
32 #include <zlib.h>
33 
34 // We declare an auto pointer to AstrometryDriver.
35 std::unique_ptr<AstrometryDriver> astrometry(new AstrometryDriver());
36 
38 {
39  setVersion(1, 0);
40 }
41 
43 {
45 
46  /**********************************************/
47  /**************** Astrometry ******************/
48  /**********************************************/
49 
50  // Solver Enable/Disable
51  IUFillSwitch(&SolverS[SOLVER_ENABLE], "ASTROMETRY_SOLVER_ENABLE", "Enable", ISS_OFF);
52  IUFillSwitch(&SolverS[SOLVER_DISABLE], "ASTROMETRY_SOLVER_DISABLE", "Disable", ISS_ON);
53  IUFillSwitchVector(&SolverSP, SolverS, 2, getDeviceName(), "ASTROMETRY_SOLVER", "Solver", MAIN_CONTROL_TAB, IP_RW,
54  ISR_1OFMANY, 0, IPS_IDLE);
55 
56  // Solver Settings
57  IUFillText(&SolverSettingsT[ASTROMETRY_SETTINGS_BINARY], "ASTROMETRY_SETTINGS_BINARY", "Solver",
58  "/usr/bin/solve-field");
59  IUFillText(&SolverSettingsT[ASTROMETRY_SETTINGS_OPTIONS], "ASTROMETRY_SETTINGS_OPTIONS", "Options",
60  "--no-verify --no-plots --resort --downsample 2 -O");
61  IUFillTextVector(&SolverSettingsTP, SolverSettingsT, 2, getDeviceName(), "ASTROMETRY_SETTINGS", "Settings",
63 
64  // Solver Results
65  IUFillNumber(&SolverResultN[ASTROMETRY_RESULTS_PIXSCALE], "ASTROMETRY_RESULTS_PIXSCALE", "Pixscale (arcsec/pixel)",
66  "%g", 0, 10000, 1, 0);
67  IUFillNumber(&SolverResultN[ASTROMETRY_RESULTS_ORIENTATION], "ASTROMETRY_RESULTS_ORIENTATION",
68  "Orientation (E of N) °", "%g", -360, 360, 1, 0);
69  IUFillNumber(&SolverResultN[ASTROMETRY_RESULTS_RA], "ASTROMETRY_RESULTS_RA", "RA (J2000)", "%g", 0, 24, 1, 0);
70  IUFillNumber(&SolverResultN[ASTROMETRY_RESULTS_DE], "ASTROMETRY_RESULTS_DE", "DE (J2000)", "%g", -90, 90, 1, 0);
71  IUFillNumber(&SolverResultN[ASTROMETRY_RESULTS_PARITY], "ASTROMETRY_RESULTS_PARITY", "Parity", "%g", -1, 1, 1, 0);
72  IUFillNumberVector(&SolverResultNP, SolverResultN, 5, getDeviceName(), "ASTROMETRY_RESULTS", "Results",
74 
75  // Solver Data Blob
76  IUFillBLOB(&SolverDataB[0], "ASTROMETRY_DATA_BLOB", "Image", "");
77  IUFillBLOBVector(&SolverDataBP, SolverDataB, 1, getDeviceName(), "ASTROMETRY_DATA", "Upload", MAIN_CONTROL_TAB,
78  IP_WO, 60, IPS_IDLE);
79 
80  /**********************************************/
81  /**************** Snooping ********************/
82  /**********************************************/
83 
84  // Snooped Devices
85  IUFillText(&ActiveDeviceT[0], "ACTIVE_CCD", "CCD", "CCD Simulator");
86  IUFillTextVector(&ActiveDeviceTP, ActiveDeviceT, 1, getDeviceName(), "ACTIVE_DEVICES", "Snoop devices", OPTIONS_TAB,
87  IP_RW, 60, IPS_IDLE);
88 
89  // Primary CCD Chip Data Blob
90  IUFillBLOB(&CCDDataB[0], "CCD1", "Image", "");
91  IUFillBLOBVector(&CCDDataBP, CCDDataB, 1, ActiveDeviceT[0].text, "CCD1", "Image Data", "Image Info", IP_RO, 60,
92  IPS_IDLE);
93 
94  IDSnoopDevice(ActiveDeviceT[0].text, "CCD1");
95  IDSnoopBLOBs(ActiveDeviceT[0].text, "CCD1", B_ONLY);
96 
98 
100 
101  return true;
102 }
103 
105 {
107 
109  loadConfig(true, "ACTIVE_DEVICES");
110 }
111 
113 {
115 
116  if (isConnected())
117  {
121  }
122  else
123  {
124  if (SolverS[0].s == ISS_ON)
125  {
127  }
131  }
132 
133  return true;
134 }
135 
137 {
138  return "Astrometry";
139 }
140 
142 {
143  return true;
144 }
145 
147 {
148  return true;
149 }
150 
151 bool AstrometryDriver::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
152 {
153  return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
154 }
155 
156 bool AstrometryDriver::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
157  char *formats[], char *names[], int n)
158 {
159  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
160  {
161  if (strcmp(name, SolverDataBP.name) == 0)
162  {
164  IDSetBLOB(&SolverDataBP, nullptr);
165 
166  // If the client explicitly uploaded the data then we solve it.
167  if (SolverS[SOLVER_ENABLE].s == ISS_OFF)
168  {
171  SolverSP.s = IPS_BUSY;
172  LOG_INFO("Astrometry solver is enabled.");
174  }
175 
176  processBLOB(reinterpret_cast<uint8_t *>(blobs[0]), static_cast<uint32_t>(sizes[0]),
177  static_cast<uint32_t>(blobsizes[0]));
178 
179  return true;
180  }
181  }
182 
183  return INDI::DefaultDevice::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
184 }
185 
186 bool AstrometryDriver::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
187 {
188  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
189  {
190  // This is for our device
191  // Now lets see if it's something we process here
192  if (strcmp(name, ActiveDeviceTP.name) == 0)
193  {
195  IUUpdateText(&ActiveDeviceTP, texts, names, n);
196  IDSetText(&ActiveDeviceTP, nullptr);
197 
198  // Update the property name!
199  strncpy(CCDDataBP.device, ActiveDeviceT[0].text, MAXINDIDEVICE);
200  IDSnoopDevice(ActiveDeviceT[0].text, "CCD1");
201  IDSnoopBLOBs(ActiveDeviceT[0].text, "CCD1", B_ONLY);
202 
203  // We processed this one, so, tell the world we did it
204  return true;
205  }
206 
207  if (strcmp(name, SolverSettingsTP.name) == 0)
208  {
209  IUUpdateText(&SolverSettingsTP, texts, names, n);
211  IDSetText(&SolverSettingsTP, nullptr);
212  return true;
213  }
214  }
215 
216  return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
217 }
218 
219 bool AstrometryDriver::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
220 {
221  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
222  {
223  // Astrometry Enable/Disable
224  if (strcmp(name, SolverSP.name) == 0)
225  {
226  pthread_mutex_lock(&lock);
227 
228  IUUpdateSwitch(&SolverSP, states, names, n);
229  SolverSP.s = IPS_OK;
230 
231  if (SolverS[SOLVER_ENABLE].s == ISS_ON)
232  {
233  LOG_INFO("Astrometry solver is enabled.");
235  }
236  else
237  {
238  LOG_INFO("Astrometry solver is disabled.");
240  }
241 
242  IDSetSwitch(&SolverSP, nullptr);
243 
244  pthread_mutex_unlock(&lock);
245  return true;
246  }
247  }
248 
249  return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
250 }
251 
253 {
254  if (SolverS[SOLVER_ENABLE].s == ISS_ON && IUSnoopBLOB(root, &CCDDataBP) == 0)
255  {
256  processBLOB(reinterpret_cast<uint8_t *>(CCDDataB[0].blob), static_cast<uint32_t>(CCDDataB[0].size),
257  static_cast<uint32_t>(CCDDataB[0].bloblen));
258  return true;
259  }
260 
262 }
263 
265 {
268  return true;
269 }
270 
271 bool AstrometryDriver::processBLOB(uint8_t *data, uint32_t size, uint32_t len)
272 {
273  FILE *fp = nullptr;
274  char imageFileName[MAXRBUF];
275 
276  uint8_t *processedData = data;
277 
278  // If size != len then we have compressed buffer
279  if (size != len)
280  {
281  uint8_t *dataBuffer = new uint8_t[size];
282  uLongf destLen = size;
283 
284  if (dataBuffer == nullptr)
285  {
286  LOG_DEBUG("Unable to allocate memory for data buffer");
287  return false;
288  }
289 
290  int r = uncompress(dataBuffer, &destLen, data, len);
291  if (r != Z_OK)
292  {
293  LOGF_ERROR("Astrometry compression error: %d", r);
294  delete[] dataBuffer;
295  return false;
296  }
297 
298  if (destLen != size)
299  {
300  LOGF_WARN("Discrepency between uncompressed data size %ld and expected size %ld",
301  size, destLen);
302  }
303 
304  processedData = dataBuffer;
305  }
306 
307  strncpy(imageFileName, "/tmp/ccdsolver.fits", MAXRBUF);
308 
309  fp = fopen(imageFileName, "w");
310  if (fp == nullptr)
311  {
312  LOGF_ERROR("Unable to save image file (%s). %s", imageFileName, strerror(errno));
313  if (size != len)
314  delete[] processedData;
315 
316  return false;
317  }
318 
319  int n = 0;
320  for (uint32_t nr = 0; nr < size; nr += n)
321  n = fwrite(processedData + nr, 1, size - nr, fp);
322 
323  fclose(fp);
324 
325  // Do not forget to release uncompressed buffer
326  if (size != len)
327  delete[] processedData;
328 
329  pthread_mutex_lock(&lock);
330  SolverSP.s = IPS_BUSY;
331  LOG_INFO("Solving image...");
332  IDSetSwitch(&SolverSP, nullptr);
333  pthread_mutex_unlock(&lock);
334 
335  int result = pthread_create(&solverThread, nullptr, &AstrometryDriver::runSolverHelper, this);
336 
337  if (result != 0)
338  {
339  SolverSP.s = IPS_ALERT;
340  LOGF_INFO("Failed to create solver thread: %s", strerror(errno));
341  IDSetSwitch(&SolverSP, nullptr);
342  }
343 
344  return true;
345 }
346 
348 {
349  (static_cast<AstrometryDriver *>(context))->runSolver();
350  return nullptr;
351 }
352 
353 void AstrometryDriver::runSolver()
354 {
355  char cmd[MAXRBUF] = {0}, line[256] = {0}, parity_str[8] = {0};
356  float ra = -1000, dec = -1000, angle = -1000, pixscale = -1000, parity = 0;
357  snprintf(cmd, MAXRBUF, "%s %s -W /tmp/solution.wcs /tmp/ccdsolver.fits",
359 
360  LOGF_DEBUG("%s", cmd);
361  FILE *handle = popen(cmd, "r");
362  if (handle == nullptr)
363  {
364  LOGF_DEBUG("Failed to run solver: %s", strerror(errno));
365  pthread_mutex_lock(&lock);
366  SolverSP.s = IPS_ALERT;
367  IDSetSwitch(&SolverSP, nullptr);
368  pthread_mutex_unlock(&lock);
369  return;
370  }
371 
372  while (fgets(line, sizeof(line), handle) != nullptr)
373  {
374  LOGF_DEBUG("%s", line);
375 
376  sscanf(line, "Field rotation angle: up is %f", &angle);
377  sscanf(line, "Field center: (RA,Dec) = (%f,%f)", &ra, &dec);
378  sscanf(line, "Field parity: %s", parity_str);
379  sscanf(line, "%*[^p]pixel scale %f", &pixscale);
380 
381  if (strcmp(parity_str, "pos") == 0)
382  parity = 1;
383  else if (strcmp(parity_str, "neg") == 0)
384  parity = -1;
385 
386  if (ra != -1000 && dec != -1000 && angle != -1000 && pixscale != -1000)
387  {
388  // Pixscale is arcsec/pixel. Astrometry result is in arcmin
389  SolverResultN[ASTROMETRY_RESULTS_PIXSCALE].value = pixscale;
390  // Astrometry.net angle, E of N
392  // Astrometry.net J2000 RA in degrees
394  // Astrometry.net J2000 DEC in degrees
396  // Astrometry.net parity
398 
400  IDSetNumber(&SolverResultNP, nullptr);
401 
402  pthread_mutex_lock(&lock);
403  SolverSP.s = IPS_OK;
404  IDSetSwitch(&SolverSP, nullptr);
405  pthread_mutex_unlock(&lock);
406 
407  pclose(handle);
408  LOG_INFO("Solver complete.");
409  return;
410  }
411 
412  pthread_mutex_lock(&lock);
413  if (SolverS[SOLVER_DISABLE].s == ISS_ON)
414  {
415  SolverSP.s = IPS_IDLE;
416  IDSetSwitch(&SolverSP, nullptr);
417  pthread_mutex_unlock(&lock);
418  pclose(handle);
419  LOG_INFO("Solver canceled.");
420  return;
421  }
422  pthread_mutex_unlock(&lock);
423  }
424 
425  pclose(handle);
426 
427  pthread_mutex_lock(&lock);
428  SolverSP.s = IPS_ALERT;
429  IDSetSwitch(&SolverSP, nullptr);
430  LOG_INFO("Solver failed.");
431  pthread_mutex_unlock(&lock);
432 
433  pthread_exit(nullptr);
434 }
std::unique_ptr< AstrometryDriver > astrometry(new AstrometryDriver())
The AstrometryDriver class is an INDI driver frontend for astrometry.net.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
static void * runSolverHelper(void *context)
INumber SolverResultN[5]
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) override
Process the client newBLOB command.
bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
IBLOBVectorProperty SolverDataBP
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
ITextVectorProperty ActiveDeviceTP
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
ISwitchVectorProperty SolverSP
INumberVectorProperty SolverResultNP
ITextVectorProperty SolverSettingsTP
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
bool Disconnect() override
Disconnect from device.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
IBLOBVectorProperty CCDDataBP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
const char * getDefaultName() override
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
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.
void defineProperty(INumberVectorProperty *property)
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....
void addDebugControl()
Add Debug control to the driver.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
void ISGetProperties(const char *dev)
Get Device Properties.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
int errno
double ra
double dec
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
#define MAXINDIDEVICE
Definition: indiapi.h:193
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
@ IP_WO
Definition: indiapi.h:185
@ 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
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
int IUSnoopBLOB(XMLEle *root, IBLOBVectorProperty *bvp)
Update a snooped BLOB vector property from the given XML root element.
Definition: indidevapi.c:502
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
@ B_ONLY
Definition: indidevapi.h:269
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSnoopBLOBs(const char *snooped_device, const char *snooped_property, BLOBHandling bh)
Function a Driver calls to control whether they will receive BLOBs from snooped devices.
Definition: indidriver.c:161
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 IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt,...)
Definition: indidriver.c:1287
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define MAXRBUF
Definition: indiserver.cpp:102
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:475
char device[MAXINDIDEVICE]
Definition: indiapi.h:473
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250