Instrument Neutral Distributed Interface INDI 2.2.5
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.
35std::unique_ptr<AstrometryDriver> astrometry(new AstrometryDriver());
36
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,
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{
106 DefaultDevice::ISGetProperties(dev);
107
109}
110
134
136{
137 return "Astrometry";
138}
139
141{
142 return true;
143}
144
146{
147 return true;
148}
149
150bool AstrometryDriver::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
151{
152 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
153}
154
155bool AstrometryDriver::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
156 char *formats[], char *names[], int n)
157{
158 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
159 {
160 if (strcmp(name, SolverDataBP.name) == 0)
161 {
163 IDSetBLOB(&SolverDataBP, nullptr);
164
165 // If the client explicitly uploaded the data then we solve it.
166 if (SolverS[SOLVER_ENABLE].s == ISS_OFF)
167 {
171 LOG_INFO("Astrometry solver is enabled.");
173 }
174
175 processBLOB(reinterpret_cast<uint8_t *>(blobs[0]), static_cast<uint32_t>(sizes[0]),
176 static_cast<uint32_t>(blobsizes[0]));
177
178 return true;
179 }
180 }
181
182 return INDI::DefaultDevice::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
183}
184
185bool AstrometryDriver::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
186{
187 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
188 {
189 // This is for our device
190 // Now lets see if it's something we process here
191 if (strcmp(name, ActiveDeviceTP.name) == 0)
192 {
194 IUUpdateText(&ActiveDeviceTP, texts, names, n);
195 IDSetText(&ActiveDeviceTP, nullptr);
196
197 // Update the property name!
198 snprintf(CCDDataBP.device, MAXINDIDEVICE, "%s", ActiveDeviceT[0].text);
199 IDSnoopDevice(ActiveDeviceT[0].text, "CCD1");
200 IDSnoopBLOBs(ActiveDeviceT[0].text, "CCD1", B_ONLY);
201
202 // We processed this one, so, tell the world we did it
203 return true;
204 }
205
206 if (strcmp(name, SolverSettingsTP.name) == 0)
207 {
208 IUUpdateText(&SolverSettingsTP, texts, names, n);
210 IDSetText(&SolverSettingsTP, nullptr);
211 return true;
212 }
213 }
214
215 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
216}
217
218bool AstrometryDriver::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
219{
220 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
221 {
222 // Astrometry Enable/Disable
223 if (strcmp(name, SolverSP.name) == 0)
224 {
225 pthread_mutex_lock(&lock);
226
227 IUUpdateSwitch(&SolverSP, states, names, n);
228 SolverSP.s = IPS_OK;
229
230 if (SolverS[SOLVER_ENABLE].s == ISS_ON)
231 {
232 LOG_INFO("Astrometry solver is enabled.");
234 }
235 else
236 {
237 LOG_INFO("Astrometry solver is disabled.");
239 }
240
241 IDSetSwitch(&SolverSP, nullptr);
242
243 pthread_mutex_unlock(&lock);
244 return true;
245 }
246 }
247
248 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
249}
250
252{
253 if (SolverS[SOLVER_ENABLE].s == ISS_ON && IUSnoopBLOB(root, &CCDDataBP) == 0)
254 {
255 processBLOB(reinterpret_cast<uint8_t *>(CCDDataB[0].blob), static_cast<uint32_t>(CCDDataB[0].size),
256 static_cast<uint32_t>(CCDDataB[0].bloblen));
257 return true;
258 }
259
261}
262
264{
267 return true;
268}
269
270bool AstrometryDriver::processBLOB(uint8_t *data, uint32_t size, uint32_t len)
271{
272 FILE *fp = nullptr;
273 char imageFileName[MAXRBUF];
274
275 uint8_t *processedData = data;
276
277 // If size != len then we have compressed buffer
278 if (size != len)
279 {
280 uint8_t *dataBuffer = new uint8_t[size];
281 uLongf destLen = size;
282
283 if (dataBuffer == nullptr)
284 {
285 LOG_DEBUG("Unable to allocate memory for data buffer");
286 return false;
287 }
288
289 int r = uncompress(dataBuffer, &destLen, data, len);
290 if (r != Z_OK)
291 {
292 LOGF_ERROR("Astrometry compression error: %d", r);
293 delete[] dataBuffer;
294 return false;
295 }
296
297 if (destLen != size)
298 {
299 LOGF_WARN("Discrepancy between uncompressed data size %ld and expected size %ld",
300 size, destLen);
301 }
302
303 processedData = dataBuffer;
304 }
305
306 strncpy(imageFileName, "/tmp/ccdsolver.fits", MAXRBUF);
307
308 fp = fopen(imageFileName, "w");
309 if (fp == nullptr)
310 {
311 LOGF_ERROR("Unable to save image file (%s). %s", imageFileName, strerror(errno));
312 if (size != len)
313 delete[] processedData;
314
315 return false;
316 }
317
318 int n = 0;
319 for (uint32_t nr = 0; nr < size; nr += n)
320 n = fwrite(processedData + nr, 1, size - nr, fp);
321
322 fclose(fp);
323
324 // Do not forget to release uncompressed buffer
325 if (size != len)
326 delete[] processedData;
327
328 pthread_mutex_lock(&lock);
330 LOG_INFO("Solving image...");
331 IDSetSwitch(&SolverSP, nullptr);
332 pthread_mutex_unlock(&lock);
333
334 int result = pthread_create(&solverThread, nullptr, &AstrometryDriver::runSolverHelper, this);
335
336 if (result != 0)
337 {
339 LOGF_INFO("Failed to create solver thread: %s", strerror(errno));
340 IDSetSwitch(&SolverSP, nullptr);
341 }
342
343 return true;
344}
345
347{
348 (static_cast<AstrometryDriver *>(context))->runSolver();
349 return nullptr;
350}
351
352void AstrometryDriver::runSolver()
353{
354 char cmd[MAXRBUF] = {0}, line[256] = {0}, parity_str[8] = {0};
355 float ra = -1000, dec = -1000, angle = -1000, pixscale = -1000, parity = 0;
356 snprintf(cmd, MAXRBUF, "%s %s -W /tmp/solution.wcs /tmp/ccdsolver.fits",
358
359 LOGF_DEBUG("%s", cmd);
360 FILE *handle = popen(cmd, "r");
361 if (handle == nullptr)
362 {
363 LOGF_DEBUG("Failed to run solver: %s", strerror(errno));
364 pthread_mutex_lock(&lock);
366 IDSetSwitch(&SolverSP, nullptr);
367 pthread_mutex_unlock(&lock);
368 return;
369 }
370
371 while (fgets(line, sizeof(line), handle) != nullptr)
372 {
373 LOGF_DEBUG("%s", line);
374
375 sscanf(line, "Field rotation angle: up is %f", &angle);
376 sscanf(line, "Field center: (RA,Dec) = (%f,%f)", &ra, &dec);
377 sscanf(line, "Field parity: %s", parity_str);
378 sscanf(line, "%*[^p]pixel scale %f", &pixscale);
379
380 if (strcmp(parity_str, "pos") == 0)
381 parity = 1;
382 else if (strcmp(parity_str, "neg") == 0)
383 parity = -1;
384
385 if (ra != -1000 && dec != -1000 && angle != -1000 && pixscale != -1000)
386 {
387 // Pixscale is arcsec/pixel. Astrometry result is in arcmin
389 // Astrometry.net angle, E of N
391 // Astrometry.net J2000 RA in degrees
393 // Astrometry.net J2000 DEC in degrees
395 // Astrometry.net parity
397
399 IDSetNumber(&SolverResultNP, nullptr);
400
401 pthread_mutex_lock(&lock);
402 SolverSP.s = IPS_OK;
403 IDSetSwitch(&SolverSP, nullptr);
404 pthread_mutex_unlock(&lock);
405
406 pclose(handle);
407 LOG_INFO("Solver complete.");
408 return;
409 }
410
411 pthread_mutex_lock(&lock);
412 if (SolverS[SOLVER_DISABLE].s == ISS_ON)
413 {
415 IDSetSwitch(&SolverSP, nullptr);
416 pthread_mutex_unlock(&lock);
417 pclose(handle);
418 LOG_INFO("Solver canceled.");
419 return;
420 }
421 pthread_mutex_unlock(&lock);
422 }
423
424 pclose(handle);
425
426 pthread_mutex_lock(&lock);
428 IDSetSwitch(&SolverSP, nullptr);
429 LOG_INFO("Solver failed.");
430 pthread_mutex_unlock(&lock);
431
432 pthread_exit(nullptr);
433}
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)
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
Initialize 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
const char * getDeviceName() const
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 setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
virtual bool initProperties()
Initialize 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 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.
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:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
#define MAXINDIDEVICE
Definition indiapi.h:191
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
@ IP_WO
Definition indiapi.h:183
@ 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
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:490
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:509
int IUSnoopBLOB(XMLEle *root, IBLOBVectorProperty *bvp)
Update a snooped BLOB vector property from the given XML root element.
Definition indidevapi.c:720
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
@ B_ONLY
Definition indidevapi.h:270
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
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,...)
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 IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt,...)
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define 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
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:473
char device[MAXINDIDEVICE]
Definition indiapi.h:471
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248