28 #define _FILE_OFFSET_BITS 64
43 #include <libnova/julian_day.h>
44 #include <libnova/precession.h>
45 #include <libnova/airmass.h>
46 #include <libnova/transform.h>
47 #include <libnova/ln_types.h>
67 uint16_t INDIWSServer::m_global_port = 11623;
70 std::string
join(std::vector<std::string>
const &strings, std::string delim)
73 std::copy(strings.begin(), strings.end(),
74 std::ostream_iterator<std::string>(ss, delim.c_str()));
90 m_ValidCCDRotation =
false;
103 RA = std::numeric_limits<double>::quiet_NaN();
104 Dec = std::numeric_limits<double>::quiet_NaN();
106 J2000RA = std::numeric_limits<double>::quiet_NaN();
107 J2000DE = std::numeric_limits<double>::quiet_NaN();
109 MPSAS = std::numeric_limits<double>::quiet_NaN();
110 RotatorAngle = std::numeric_limits<double>::quiet_NaN();
113 FocuserTemp = std::numeric_limits<double>::quiet_NaN();
115 Airmass = std::numeric_limits<double>::quiet_NaN();
116 Latitude = std::numeric_limits<double>::quiet_NaN();
117 Longitude = std::numeric_limits<double>::quiet_NaN();
118 Azimuth = std::numeric_limits<double>::quiet_NaN();
119 Altitude = std::numeric_limits<double>::quiet_NaN();
187 IUFillNumber(&
PrimaryCCD.ImageExposureN[0],
"CCD_EXPOSURE_VALUE",
"Duration (s)",
"%5.2f", 0.01, 3600, 1.0, 1.0);
255 IUFillNumber(&
PrimaryCCD.RapidGuideDataN[0],
"GUIDESTAR_X",
"Guide star position X",
"%5.2f", 0, 1024, 0, 0);
256 IUFillNumber(&
PrimaryCCD.RapidGuideDataN[1],
"GUIDESTAR_Y",
"Guide star position Y",
"%5.2f", 0, 1024, 0, 0);
259 "CCD_RAPID_GUIDE_DATA",
"Rapid Guide Data", RAPIDGUIDE_TAB,
IP_RO, 60,
IPS_IDLE);
298 IUFillNumber(&
GuideCCD.ImageExposureN[0],
"GUIDER_EXPOSURE_VALUE",
"Duration (s)",
"%5.2f", 0.01, 3600, 1.0, 1.0);
330 "GUIDER_RAPID_GUIDE_SETUP",
"Rapid Guide Setup", RAPIDGUIDE_TAB,
IP_RW,
ISR_NOFMANY, 0,
333 IUFillNumber(&
GuideCCD.RapidGuideDataN[0],
"GUIDESTAR_X",
"Guide star position X",
"%5.2f", 0, 1024, 0, 0);
334 IUFillNumber(&
GuideCCD.RapidGuideDataN[1],
"GUIDESTAR_Y",
"Guide star position Y",
"%5.2f", 0, 1024, 0, 0);
335 IUFillNumber(&
GuideCCD.RapidGuideDataN[2],
"GUIDESTAR_FIT",
"Guide star fit",
"%5.2f", 0, 1024, 0, 0);
337 "GUIDER_RAPID_GUIDE_DATA",
"Rapid Guide Data", RAPIDGUIDE_TAB,
IP_RO, 60,
IPS_IDLE);
362 char configName[64] = {0};
364 m_ConfigCaptureFormatName = configName;
469 IUFillNumber(&
EqN[1],
"DEC",
"Dec (dd:mm:ss)",
"%010.6m", -90, 90, 0, 0);
478 "Main Control",
IP_RW,
525 DSP->ISGetProperties(dev);
601 if (RapidGuideEnabled)
606 if (GuiderRapidGuideEnabled)
621 #ifdef HAVE_WEBSOCKET
651 if (RapidGuideEnabled)
676 if (GuiderRapidGuideEnabled)
706 #ifdef HAVE_WEBSOCKET
723 DSP->updateProperties();
735 double newra, newdec;
736 newra =
EqN[0].value;
737 newdec =
EqN[1].value;
738 if ((newra !=
RA) || (newdec !=
Dec))
758 else if (!strcmp(
"TELESCOPE_PIER_SIDE", propName))
767 if (!strcmp(elemName,
"PIER_EAST") && !strcmp(
pcdataXMLEle(ep),
"On"))
769 else if (!strcmp(elemName,
"PIER_WEST") && !strcmp(
pcdataXMLEle(ep),
"On"))
773 else if (!strcmp(propName,
"TELESCOPE_INFO"))
779 if (!strcmp(name,
"TELESCOPE_APERTURE"))
783 else if (!strcmp(name,
"TELESCOPE_FOCAL_LENGTH"))
789 else if (!strcmp(propName,
"FILTER_NAME"))
791 LOG_DEBUG(
"SNOOP: FILTER_NAME update...");
798 else if (!strcmp(propName,
"FILTER_SLOT"))
800 LOG_DEBUG(
"SNOOP: FILTER_SLOT update...");
806 else if (!strcmp(propName,
"SKY_QUALITY"))
812 if (!strcmp(name,
"SKY_BRIGHTNESS"))
819 else if (!strcmp(propName,
"ABS_ROTATOR_ANGLE"))
825 if (!strcmp(name,
"ANGLE"))
834 else if (!strcmp(propName,
"ABS_FOCUS_POSITION"))
840 if (!strcmp(name,
"FOCUS_ABSOLUTE_POSITION"))
847 else if (!strcmp(propName,
"FOCUS_TEMPERATURE"))
853 if (!strcmp(name,
"TEMPERATURE"))
862 else if (!strcmp(propName,
"GEOGRAPHIC_COORD"))
868 if (!strcmp(name,
"LONG"))
874 else if (!strcmp(name,
"LAT"))
884 bool CCD::ISNewText(
const char * dev,
const char * name,
char * texts[],
char * names[],
int n)
910 LOG_DEBUG(
"No mount is set. Clearing all mount watchers.");
911 RA = std::numeric_limits<double>::quiet_NaN();
912 Dec = std::numeric_limits<double>::quiet_NaN();
913 J2000RA = std::numeric_limits<double>::quiet_NaN();
914 J2000DE = std::numeric_limits<double>::quiet_NaN();
915 Latitude = std::numeric_limits<double>::quiet_NaN();
916 Longitude = std::numeric_limits<double>::quiet_NaN();
917 Airmass = std::numeric_limits<double>::quiet_NaN();
918 Azimuth = std::numeric_limits<double>::quiet_NaN();
919 Altitude = std::numeric_limits<double>::quiet_NaN();
929 LOG_DEBUG(
"No rotator is set. Clearing all rotator watchers.");
930 MPSAS = std::numeric_limits<double>::quiet_NaN();
942 LOG_DEBUG(
"No focuser is set. Clearing all focuser watchers.");
944 FocuserTemp = std::numeric_limits<double>::quiet_NaN();
956 LOG_DEBUG(
"No filter wheel is set. Clearing All filter wheel watchers.");
986 if (name.empty() && value.empty() && comment.empty())
988 LOG_ERROR(
"Cannot add an empty FITS record.");
995 if (name ==
"INDI_CLEAR")
997 m_CustomFITSKeywords.clear();
998 LOG_INFO(
"Custom FITS headers cleared.");
1000 else if (name.empty() ==
false && value.empty() ==
false)
1003 std::regex checkDouble(
"^[-+]?([0-9]*?[.,][0-9]+|[0-9]+)$");
1005 std::regex checkInteger(
"^[-+]?([0-9]*)$");
1010 if (std::regex_match(value, checkInteger))
1012 auto lValue = std::stol(value);
1013 FITSRecord record(name.c_str(), lValue, comment.c_str());
1014 m_CustomFITSKeywords[name.c_str()] = record;
1017 else if (std::regex_match(value, checkDouble))
1019 auto dValue = std::stod(value);
1020 FITSRecord record(name.c_str(), dValue, 6, comment.c_str());
1021 m_CustomFITSKeywords[name.c_str()] = record;
1027 FITSRecord record(name.c_str(), value.c_str(), comment.c_str());
1028 m_CustomFITSKeywords[name.c_str()] = record;
1032 catch (std::exception &e)
1035 FITSRecord record(name.c_str(), value.c_str(), comment.c_str());
1036 m_CustomFITSKeywords[name.c_str()] = record;
1039 else if (comment.empty() ==
false)
1042 m_CustomFITSKeywords[comment.c_str()] = record;
1061 Streamer->ISNewText(dev, name, texts, names, n);
1065 DSP->ISNewText(dev, name, texts, names, n);
1070 bool CCD::ISNewNumber(
const char * dev,
const char * name,
double values[],
char * names[],
int n)
1076 if (!strcmp(name,
"CCD_EXPOSURE"))
1079 (values[0] <
PrimaryCCD.ImageExposureN[0].min || values[0] >
PrimaryCCD.ImageExposureN[0].max))
1081 LOGF_ERROR(
"Requested exposure value (%g) seconds out of bounds [%g,%g].",
1113 if (!strcmp(name,
"GUIDER_EXPOSURE"))
1116 (values[0] <
GuideCCD.ImageExposureN[0].min || values[0] >
GuideCCD.ImageExposureN[0].max))
1118 LOGF_ERROR(
"Requested guide exposure value (%g) seconds out of bounds [%g,%g].",
1119 values[0],
GuideCCD.ImageExposureN[0].min,
GuideCCD.ImageExposureN[0].max);
1139 if (!strcmp(name,
"CCD_BINNING"))
1149 else if (values[0] == 0 || values[1] == 0)
1153 LOGF_ERROR(
"%.fx%.f binning is invalid.", values[0], values[1]);
1158 if (!strcmp(np->name,
"HOR_BIN"))
1182 if (!strcmp(name,
"GUIDER_BINNING"))
1192 else if (values[0] == 0 || values[1] == 0)
1196 LOGF_ERROR(
"%.fx%.f binning is invalid.", values[0], values[1]);
1201 if (!strcmp(np->name,
"HOR_BIN"))
1234 if (!strcmp(name,
"CCD_FRAME"))
1236 int x = -1, y = -1, w = -1, h = -1;
1237 for (
int i = 0; i < n; i++)
1239 if (!strcmp(names[i],
"X"))
1241 else if (!strcmp(names[i],
"Y"))
1243 else if (!strcmp(names[i],
"WIDTH"))
1245 else if (!strcmp(names[i],
"HEIGHT"))
1251 if (x < 0 || y < 0 || w <= 0 || h <= 0)
1253 LOGF_ERROR(
"Invalid frame requested (%d,%d) (%d x %d)", x, y, w, h);
1271 if (!strcmp(name,
"GUIDER_FRAME"))
1280 values[2], values[4]);
1292 if (!strcmp(name,
"CCD_GUIDESTAR"))
1300 if (!strcmp(name,
"GUIDER_GUIDESTAR"))
1330 LOGF_ERROR(
"Error: Bad temperature value! Range is [%.1f, %.1f] [C].",
1336 double nextTemperature = values[0];
1380 LOG_INFO(
"Temperature ramp is disabled.");
1382 LOGF_INFO(
"Temperature ramp is enabled. Gradual cooling and warming is regulated at %.f Celsius per minute.",
1432 m_ValidCCDRotation =
true;
1442 Streamer->ISNewNumber(dev, name, values, names, n);
1446 DSP->ISNewNumber(dev, name, values, names, n);
1501 LOG_WARN(
"Experimental Feature: After a frame is downloaded, the next frame capture immediately starts to avoid any delays.");
1518 #ifdef HAVE_WEBSOCKET
1532 else if (wsServer.is_running())
1552 LOG_INFO(
"World Coordinate System is enabled.");
1557 LOG_INFO(
"World Coordinate System is disabled.");
1561 m_ValidCCDRotation =
false;
1622 GuideCCD.ImageExposureN[0].value = 0;
1668 "The CCD does not have a shutter. Cover the camera in order to take a bias frame.");
1675 "The CCD does not have a shutter. Cover the camera in order to take a dark frame.");
1701 "The CCD does not have a shutter. Cover the camera in order to take a bias frame.");
1708 "The CCD does not have a shutter. Cover the camera in order to take a dark frame.");
1731 if (previousIndex >= 0)
1767 if (strcmp(name,
PrimaryCCD.RapidGuideSP.name) == 0)
1773 if (RapidGuideEnabled)
1789 if (strcmp(name,
GuideCCD.RapidGuideSP.name) == 0)
1795 if (GuiderRapidGuideEnabled)
1811 if (strcmp(name,
PrimaryCCD.RapidGuideSetupSP.name) == 0)
1825 if (strcmp(name,
GuideCCD.RapidGuideSetupSP.name) == 0)
1841 Streamer->ISNewSwitch(dev, name, states, names, n);
1845 DSP->ISNewSwitch(dev, name, states, names, n);
1850 bool CCD::ISNewBLOB(
const char *dev,
const char *name,
int sizes[],
int blobsizes[],
char *blobs[],
1851 char *formats[],
char *names[],
int n)
1855 DSP->ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
1943 double effectiveFocalLength = std::numeric_limits<double>::quiet_NaN();
1944 double effectiveAperture = std::numeric_limits<double>::quiet_NaN();
1947 AutoCNumeric locale;
1948 fitsKeywords.push_back({
"ROWORDER",
"TOP-DOWN",
"Row Order"});
1949 fitsKeywords.push_back({
"INSTRUME",
getDeviceName(),
"CCD Name"});
1954 fitsKeywords.push_back({
"TELESCOP",
ActiveDeviceT[0].text,
"Telescope name"});
1962 if (std::isnan(effectiveFocalLength))
1963 LOG_WARN(
"Telescope focal length is missing.");
1964 if (std::isnan(effectiveAperture))
1965 LOG_WARN(
"Telescope aperture is missing.");
1967 double subPixSize1 =
static_cast<double>(targetChip->
getPixelSizeX());
1968 double subPixSize2 =
static_cast<double>(targetChip->
getPixelSizeY());
1969 uint32_t subW = targetChip->
getSubW();
1970 uint32_t subH = targetChip->
getSubH();
1971 uint32_t subBinX = targetChip->
getBinX();
1972 uint32_t subBinY = targetChip->
getBinY();
1976 fitsKeywords.push_back({
"EXPTIME",
exposureDuration, 6,
"Total Exposure Time (s)"});
1979 fitsKeywords.push_back({
"DARKTIME",
exposureDuration, 6,
"Total Dark Exposure Time (s)"});
1983 fitsKeywords.push_back({
"CCD-TEMP",
TemperatureN[0].value, 3,
"CCD Temperature (Celsius)"});
1985 fitsKeywords.push_back({
"PIXSIZE1", subPixSize1, 6,
"Pixel Size 1 (microns)"});
1986 fitsKeywords.push_back({
"PIXSIZE2", subPixSize2, 6,
"Pixel Size 2 (microns)"});
1987 fitsKeywords.push_back({
"XBINNING", targetChip->
getBinX(),
"Binning factor in width"});
1988 fitsKeywords.push_back({
"YBINNING", targetChip->
getBinY(),
"Binning factor in height"});
1990 double xpixsz = subPixSize1 * subBinX;
1991 double ypixsz = subPixSize2 * subBinY;
1992 fitsKeywords.push_back({
"XPIXSZ", xpixsz, 6,
"X binned pixel size in microns"});
1993 fitsKeywords.push_back({
"YPIXSZ", ypixsz, 6,
"Y binned pixel size in microns"});
1998 fitsKeywords.push_back({
"FRAME",
"Light",
"Frame Type"});
1999 fitsKeywords.push_back({
"IMAGETYP",
"Light Frame",
"Frame Type"});
2002 fitsKeywords.push_back({
"FRAME",
"Bias",
"Frame Type"});
2003 fitsKeywords.push_back({
"IMAGETYP",
"Bias Frame",
"Frame Type"});
2006 fitsKeywords.push_back({
"FRAME",
"Flat",
"Frame Type"});
2007 fitsKeywords.push_back({
"IMAGETYP",
"Flat Frame",
"Frame Type"});
2010 fitsKeywords.push_back({
"FRAME",
"Dark",
"Frame Type"});
2011 fitsKeywords.push_back({
"IMAGETYP",
"Dark Frame",
"Frame Type"});
2023 double min_val, max_val;
2024 getMinMax(&min_val, &max_val, targetChip);
2026 fitsKeywords.push_back({
"DATAMIN", min_val, 6,
"Minimum value"});
2027 fitsKeywords.push_back({
"DATAMAX", max_val, 6,
"Maximum value"});
2033 fitsKeywords.push_back({
"XBAYROFF", atoi(
BayerT[0].text),
"X offset of Bayer array"});
2034 fitsKeywords.push_back({
"YBAYROFF", atoi(
BayerT[1].text),
"Y offset of Bayer array"});
2035 fitsKeywords.push_back({
"BAYERPAT",
BayerT[2].text,
"Bayer color pattern"});
2038 if (!std::isnan(effectiveFocalLength))
2039 fitsKeywords.push_back({
"FOCALLEN", effectiveFocalLength, 3,
"Focal Length (mm)"});
2041 if (!std::isnan(effectiveAperture))
2042 fitsKeywords.push_back({
"APTDIA", effectiveAperture, 3,
"Telescope diameter (mm)"});
2044 if (!std::isnan(MPSAS))
2046 fitsKeywords.push_back({
"MPSAS", MPSAS, 6,
"Sky Quality (mag per arcsec^2)"});
2049 if (!std::isnan(RotatorAngle))
2051 fitsKeywords.push_back({
"ROTATANG", RotatorAngle, 3,
"Rotator angle in degrees"});
2056 if (FocuserPos != -1)
2058 fitsKeywords.push_back({
"FOCUSPOS", FocuserPos,
"Focus position in steps"});
2060 if (!std::isnan(FocuserTemp))
2062 fitsKeywords.push_back({
"FOCUSTEM", FocuserTemp, 3,
"Focuser temperature in degrees C"});
2066 if (!std::isnan(effectiveFocalLength))
2068 double pixScale = subPixSize1 / effectiveFocalLength * 206.3 * subBinX;
2069 fitsKeywords.push_back({
"SCALE", pixScale, 6,
"arcsecs per pixel"});
2073 if ( targetChip->getFrameType() ==
CCDChip::LIGHT_FRAME && !std::isnan(RA) && !std::isnan(Dec) && (std::isnan(J2000RA)
2074 || std::isnan(J2000DE) || !J2000Valid) )
2078 epochPos.declination = Dec;
2083 J2000RA = J2000Pos.rightascension;
2084 J2000DE = J2000Pos.declination;
2088 if ( targetChip->getFrameType() ==
CCDChip::LIGHT_FRAME && !std::isnan(J2000RA) && !std::isnan(J2000DE) )
2090 if (!std::isnan(Latitude) && !std::isnan(Longitude))
2095 J2000Pos.declination = J2000DE;
2102 IGeographicCoordinates observer;
2103 observer.latitude = Latitude;
2104 observer.longitude = Longitude;
2107 Azimuth = horizontalPos.
azimuth;
2109 Airmass = ln_get_airmass(Altitude, 750);
2112 char ra_str[32] = {0}, de_str[32] = {0};
2114 fs_sexa(ra_str, J2000RA, 2, 360000);
2115 fs_sexa(de_str, J2000DE, 2, 360000);
2117 char * raPtr = ra_str, *dePtr = de_str;
2118 while (*raPtr !=
'\0')
2124 while (*dePtr !=
'\0')
2131 if (!std::isnan(Latitude) && !std::isnan(Longitude))
2133 fitsKeywords.push_back({
"SITELAT", Latitude, 6,
"Latitude of the imaging site in degrees"});
2134 fitsKeywords.push_back({
"SITELONG", Longitude, 6,
"Longitude of the imaging site in degrees"});
2136 if (!std::isnan(Airmass))
2139 fitsKeywords.push_back({
"AIRMASS", Airmass, 6,
"Airmass"});
2140 fitsKeywords.push_back({
"OBJCTAZ", Azimuth, 6,
"Azimuth of center of image in Degrees"});
2141 fitsKeywords.push_back({
"OBJCTALT", Altitude, 6,
"Altitude of center of image in Degrees"});
2143 fitsKeywords.push_back({
"OBJCTRA", ra_str,
"Object J2000 RA in Hours"});
2144 fitsKeywords.push_back({
"OBJCTDEC", de_str,
"Object J2000 DEC in Degrees"});
2146 fitsKeywords.push_back({
"RA", J2000RA * 15, 6,
"Object J2000 RA in Degrees"});
2147 fitsKeywords.push_back({
"DEC", J2000DE, 6,
"Object J2000 DEC in Degrees"});
2153 fitsKeywords.push_back({
"PIERSIDE",
"WEST",
"West, looking East"});
2156 fitsKeywords.push_back({
"PIERSIDE",
"EAST",
"East, looking West"});
2161 fitsKeywords.push_back({
"EQUINOX", 2000,
"Equinox"});
2164 if (WorldCoordS[0].s ==
ISS_ON && m_ValidCCDRotation && !std::isnan(effectiveFocalLength))
2166 double J2000RAHours = J2000RA * 15;
2167 fitsKeywords.push_back({
"CRVAL1", J2000RAHours, 10,
"CRVAL1"});
2168 fitsKeywords.push_back({
"CRVAL2", J2000DE, 10,
"CRVAL1"});
2170 char radecsys[8] =
"FK5";
2171 char ctype1[16] =
"RA---TAN";
2172 char ctype2[16] =
"DEC--TAN";
2174 fitsKeywords.push_back({
"RADECSYS", radecsys,
"RADECSYS"});
2175 fitsKeywords.push_back({
"CTYPE1", ctype1,
"CTYPE1"});
2176 fitsKeywords.push_back({
"CTYPE2", ctype2,
"CTYPE2"});
2178 double crpix1 = subW / subBinX / 2.0;
2179 double crpix2 = subH / subBinY / 2.0;
2181 fitsKeywords.push_back({
"CRPIX1", crpix1, 10,
"CRPIX1"});
2182 fitsKeywords.push_back({
"CRPIX2", crpix2, 10,
"CRPIX2"});
2184 double secpix1 = subPixSize1 / effectiveFocalLength * 206.3 * subBinX;
2185 double secpix2 = subPixSize2 / effectiveFocalLength * 206.3 * subBinY;
2187 fitsKeywords.push_back({
"SECPIX1", secpix1, 10,
"SECPIX1"});
2188 fitsKeywords.push_back({
"SECPIX2", secpix2, 10,
"SECPIX2"});
2190 double degpix1 = secpix1 / 3600.0;
2191 double degpix2 = secpix2 / 3600.0;
2193 fitsKeywords.push_back({
"CDELT1", degpix1, 10,
"CDELT1"});
2194 fitsKeywords.push_back({
"CDELT2", degpix2, 10,
"CDELT2"});
2197 double rotation = 360 - CCDRotationN[0].value;
2201 fitsKeywords.push_back({
"CROTA1", rotation, 10,
"CROTA1"});
2202 fitsKeywords.push_back({
"CROTA2", rotation, 10,
"CROTA2"});
2217 fitsKeywords.push_back({
"DATE-OBS", exposureStartTime,
"UTC start date of observation"});
2218 fitsKeywords.push_back(FITSRecord(
"Generated by INDI"));
2221 void CCD::fits_update_key_s(fitsfile * fptr,
int type, std::string name,
void * p, std::string explanation,
2225 fits_update_key(fptr,
type, name.c_str(), p,
const_cast<char *
>(explanation.c_str()), status);
2231 setCurrentPollingPeriod(getPollingPeriod());
2234 std::thread(&CCD::ExposureCompletePrivate,
this, targetChip).detach();
2239 bool CCD::ExposureCompletePrivate(
CCDChip * targetChip)
2256 if (processFastExposure(targetChip) ==
false)
2259 bool sendImage = (UploadS[UPLOAD_CLIENT].s ==
ISS_ON || UploadS[UPLOAD_BOTH].s ==
ISS_ON);
2260 bool saveImage = (UploadS[UPLOAD_LOCAL].s ==
ISS_ON || UploadS[UPLOAD_BOTH].s ==
ISS_ON);
2264 sendImage = saveImage =
false;
2266 if (sendImage || saveImage)
2268 if (EncodeFormatSP[FORMAT_FITS].getState() ==
ISS_ON)
2275 long naxis = targetChip->
getNAxis();
2283 switch (targetChip->
getBPP())
2287 img_type = BYTE_IMG;
2291 byte_type = TUSHORT;
2292 img_type = USHORT_IMG;
2297 img_type = ULONG_IMG;
2305 nelements = naxes[0] * naxes[1];
2315 std::unique_lock<std::mutex> guard(ccdBufferLock);
2318 uint32_t size = 8640 + nelements * (targetChip->
getBPP() / 8);
2322 fits_report_error(stderr, status);
2323 fits_get_errstatus(status, error_status);
2330 fits_create_img(fptr, img_type, naxis, naxes, &status);
2334 fits_report_error(stderr, status);
2335 fits_get_errstatus(status, error_status);
2341 std::vector<FITSRecord> fitsKeywords;
2346 for (
auto &record : m_CustomFITSKeywords)
2347 fitsKeywords.push_back(record.second);
2349 for (
auto &keyword : fitsKeywords)
2352 switch(keyword.type())
2357 fits_write_comment(fptr, keyword.comment().c_str(), &key_status);
2360 fits_update_key_str(fptr, keyword.key().c_str(), keyword.valueString().c_str(), keyword.comment().c_str(), &key_status);
2363 fits_update_key_lng(fptr, keyword.key().c_str(), keyword.valueInt(), keyword.comment().c_str(), &key_status);
2366 fits_update_key_dbl(fptr, keyword.key().c_str(), keyword.valueDouble(), keyword.decimal(), keyword.comment().c_str(),
2372 fits_get_errstatus(key_status, error_status);
2373 LOGF_ERROR(
"FITS key %s Error: %s", keyword.key().c_str(), error_status);
2377 fits_write_img(fptr, byte_type, 1, nelements, targetChip->
getFrameBuffer(), &status);
2381 fits_report_error(stderr, status);
2382 fits_get_errstatus(status, error_status);
2403 else if (EncodeFormatSP[FORMAT_XISF].getState() ==
ISS_ON)
2405 std::vector<FITSRecord> fitsKeywords;
2411 AutoCNumeric locale;
2412 LibXISF::Image image;
2413 LibXISF::XISFWriter xisfWriter;
2415 for (
auto &keyword : fitsKeywords)
2417 image.addFITSKeyword({keyword.key().c_str(), keyword.valueString().c_str(), keyword.comment().c_str()});
2418 image.addFITSKeywordAsProperty(keyword.key().c_str(), keyword.valueString());
2423 targetChip->
getNAxis() == 2 ? 1 : 3);
2424 switch(targetChip->
getBPP())
2427 image.setSampleFormat(LibXISF::Image::UInt8);
2430 image.setSampleFormat(LibXISF::Image::UInt16);
2433 image.setSampleFormat(LibXISF::Image::UInt32);
2443 image.setImageType(LibXISF::Image::Light);
2446 image.setImageType(LibXISF::Image::Bias);
2449 image.setImageType(LibXISF::Image::Dark);
2452 image.setImageType(LibXISF::Image::Flat);
2456 if (targetChip->SendCompressed)
2458 image.setCompression(LibXISF::DataBlock::LZ4);
2459 image.setByteshuffling(targetChip->
getBPP() / 8);
2463 image.setColorFilterArray({2, 2, BayerT[2].text});
2467 image.setColorSpace(LibXISF::Image::RGB);
2470 std::unique_lock<std::mutex> guard(ccdBufferLock);
2471 std::memcpy(image.imageData(), targetChip->
getFrameBuffer(), image.imageDataSize());
2472 xisfWriter.writeImage(image);
2474 LibXISF::ByteArray xisfFile;
2475 xisfWriter.save(xisfFile);
2476 bool rc =
uploadFile(targetChip, xisfFile.data(), xisfFile.size(), sendImage, saveImage);
2483 catch (LibXISF::Error &error)
2495 std::unique_lock<std::mutex> guard(ccdBufferLock);
2508 if (FastExposureToggleS[INDI_ENABLED].s !=
ISS_ON)
2511 UploadComplete(targetChip);
2515 bool CCD::uploadFile(CCDChip * targetChip,
const void * fitsData,
size_t totalBytes,
bool sendImage,
2518 uint8_t * compressedData =
nullptr;
2520 DEBUGF(Logger::DBG_DEBUG,
"Uploading file. Ext: %s, Size: %d, sendImage? %s, saveImage? %s",
2521 targetChip->getImageExtension(), totalBytes, sendImage ?
"Yes" :
"No", saveImage ?
"Yes" :
"No");
2525 targetChip->FitsB.blob =
const_cast<void *
>(fitsData);
2526 targetChip->FitsB.bloblen = totalBytes;
2527 snprintf(targetChip->FitsB.format,
MAXINDIBLOBFMT,
".%s", targetChip->getImageExtension());
2529 FILE * fp =
nullptr;
2532 std::string prefix = UploadSettingsT[UPLOAD_PREFIX].text;
2533 int maxIndex = getFileIndex(UploadSettingsT[UPLOAD_DIR].text, UploadSettingsT[UPLOAD_PREFIX].text,
2534 targetChip->FitsB.format);
2538 LOGF_ERROR(
"Error iterating directory %s. %s", UploadSettingsT[0].text,
2550 strftime(ts,
sizeof(ts),
"%Y-%m-%dT%H-%M-%S", tp);
2551 std::string filets(ts);
2552 prefix = std::regex_replace(prefix, std::regex(
"ISO8601"), filets);
2554 char indexString[8];
2555 snprintf(indexString, 8,
"%03d", maxIndex);
2556 std::string prefixIndex = indexString;
2558 prefix = std::regex_replace(prefix, std::regex(
"XXX"), prefixIndex);
2561 snprintf(imageFileName,
MAXRBUF,
"%s/%s%s", UploadSettingsT[0].text, prefix.c_str(), targetChip->FitsB.format);
2563 fp = fopen(imageFileName,
"w");
2566 LOGF_ERROR(
"Unable to save image file (%s). %s", imageFileName, strerror(
errno));
2571 for (
int nr = 0; nr < targetChip->FitsB.bloblen; nr += n)
2572 n = fwrite((
static_cast<char *
>(targetChip->FitsB.blob) + nr), 1, targetChip->FitsB.bloblen - nr, fp);
2579 DEBUGF(Logger::DBG_SESSION,
"Image saved to %s", imageFileName);
2584 if (targetChip->SendCompressed && EncodeFormatSP[FORMAT_XISF].getState() !=
ISS_ON)
2586 if (EncodeFormatSP[FORMAT_FITS].getState() ==
ISS_ON && !strcmp(targetChip->getImageExtension(),
"fits"))
2590 size_t compressedBytes = 0;
2592 if (
fp_pack_data_to_data(
reinterpret_cast<const char *
>(fitsData), totalBytes, &compressedData, &compressedBytes, fpvar,
2595 free(compressedData);
2596 LOG_ERROR(
"Error: Ran out of memory compressing image");
2600 targetChip->FitsB.blob = compressedData;
2601 targetChip->FitsB.bloblen = compressedBytes;
2602 snprintf(targetChip->FitsB.format,
MAXINDIBLOBFMT,
".%s.fz", targetChip->getImageExtension());
2606 uLong compressedBytes =
sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
2607 compressedData =
new uint8_t[compressedBytes];
2609 if (fitsData ==
nullptr || compressedData ==
nullptr)
2612 delete [] compressedData;
2613 LOG_ERROR(
"Error: Ran out of memory compressing image");
2617 int r = compress2(compressedData, &compressedBytes, (
const Bytef *)fitsData, totalBytes, 9);
2621 LOG_ERROR(
"Error: Failed to compress image");
2622 delete [] compressedData;
2626 targetChip->FitsB.blob = compressedData;
2627 targetChip->FitsB.bloblen = compressedBytes;
2628 snprintf(targetChip->FitsB.format,
MAXINDIBLOBFMT,
".%s.z", targetChip->getImageExtension());
2633 targetChip->FitsB.blob =
const_cast<void *
>(fitsData);
2634 targetChip->FitsB.bloblen = totalBytes;
2635 snprintf(targetChip->FitsB.format,
MAXINDIBLOBFMT,
".%s", targetChip->getImageExtension());
2638 targetChip->FitsB.size = totalBytes;
2639 targetChip->FitsBP.s =
IPS_OK;
2643 #ifdef HAVE_WEBSOCKET
2644 if (HasWebSocket() && WebSocketS[WEBSOCKET_ENABLED].s ==
ISS_ON)
2646 auto start = std::chrono::high_resolution_clock::now();
2649 wsServer.send_text(std::string(targetChip->FitsB.format));
2650 wsServer.send_binary(targetChip->FitsB.blob, targetChip->FitsB.bloblen);
2652 auto end = std::chrono::high_resolution_clock::now();
2653 std::chrono::duration<double> diff = end - start;
2654 LOGF_DEBUG(
"Websocket transfer took %g seconds", diff.count());
2659 auto start = std::chrono::high_resolution_clock::now();
2660 IDSetBLOB(&targetChip->FitsBP,
nullptr);
2661 auto end = std::chrono::high_resolution_clock::now();
2662 std::chrono::duration<double> diff = end - start;
2663 LOGF_DEBUG(
"BLOB transfer took %g seconds", diff.count());
2668 delete [] compressedData;
2670 DEBUG(Logger::DBG_DEBUG,
"Upload complete");
2675 bool CCD::processFastExposure(
CCDChip * targetChip)
2678 if (FastExposureToggleS[INDI_ENABLED].s ==
ISS_ON)
2684 if (FastExposureCountN[0].value > 1)
2686 if (UploadS[UPLOAD_LOCAL].s !=
ISS_ON)
2688 if (FastExposureCountNP.s !=
IPS_BUSY)
2690 FastExposureToggleStartup = std::chrono::system_clock::now();
2694 auto end = std::chrono::system_clock::now();
2696 m_UploadTime = (std::chrono::duration_cast<std::chrono::milliseconds>(end - FastExposureToggleStartup)).count() / 1000.0 -
2698 LOGF_DEBUG(
"Image download and upload/save took %.3f seconds.", m_UploadTime);
2700 FastExposureToggleStartup = end;
2705 FastExposureCountN[0].value--;
2708 if (UploadS[UPLOAD_LOCAL].s ==
ISS_ON || m_UploadTime < duration)
2710 if (StartExposure(duration))
2711 PrimaryCCD.ImageExposureNP.s =
IPS_BUSY;
2713 PrimaryCCD.ImageExposureNP.s =
IPS_ALERT;
2714 if (duration * 1000 < getCurrentPollingPeriod())
2715 setCurrentPollingPeriod(duration * 950);
2719 LOGF_ERROR(
"Rapid exposure not possible since upload time is %.2f seconds while exposure time is %.2f seconds.",
2722 PrimaryCCD.ImageExposureNP.s =
IPS_ALERT;
2723 IDSetNumber(&PrimaryCCD.ImageExposureNP,
nullptr);
2724 FastExposureCountN[0].value = 1;
2742 void CCD::SetCCDParams(
int x,
int y,
int bpp,
float xf,
float yf)
2744 PrimaryCCD.setResolution(x, y);
2745 PrimaryCCD.setFrame(0, 0, x, y);
2747 PrimaryCCD.setBin(1, 1);
2748 PrimaryCCD.setPixelSize(xf, yf);
2749 PrimaryCCD.setBPP(bpp);
2752 void CCD::SetGuiderParams(
int x,
int y,
int bpp,
float xf,
float yf)
2754 capability |= CCD_HAS_GUIDE_HEAD;
2756 GuideCCD.setResolution(x, y);
2757 GuideCCD.setFrame(0, 0, x, y);
2758 GuideCCD.setPixelSize(xf, yf);
2759 GuideCCD.setBPP(bpp);
2762 bool CCD::saveConfigItems(FILE * fp)
2764 DefaultDevice::saveConfigItems(fp);
2773 if (PrimaryCCD.getCCDInfo()->p !=
IP_RO)
2776 CaptureFormatSP.save(fp);
2777 EncodeFormatSP.save(fp);
2780 TemperatureRampNP.save(fp);
2788 if (CanSubFrame() && PrimaryCCD.ImageFrameN[2].value > 0)
2798 Streamer->saveConfigItems(fp);
2801 DSP->saveConfigItems(fp);
2809 LOG_ERROR(
"The CCD does not support guiding.");
2816 LOG_ERROR(
"The CCD does not support guiding.");
2823 LOG_ERROR(
"The CCD does not support guiding.");
2830 LOG_ERROR(
"The CCD does not support guiding.");
2834 void CCD::getMinMax(
double *
min,
double *
max,
CCDChip * targetChip)
2839 double lmin = 0, lmax = 0;
2841 switch (targetChip->
getBPP())
2846 lmin = lmax = imageBuffer[0];
2848 for (i = 0; i < imageHeight; i++)
2849 for (j = 0; j < imageWidth; j++)
2851 ind = (i * imageWidth) + j;
2852 if (imageBuffer[ind] < lmin)
2853 lmin = imageBuffer[ind];
2854 else if (imageBuffer[ind] > lmax)
2855 lmax = imageBuffer[ind];
2862 uint16_t * imageBuffer =
reinterpret_cast<uint16_t*
>(targetChip->
getFrameBuffer());
2863 lmin = lmax = imageBuffer[0];
2865 for (i = 0; i < imageHeight; i++)
2866 for (j = 0; j < imageWidth; j++)
2868 ind = (i * imageWidth) + j;
2869 if (imageBuffer[ind] < lmin)
2870 lmin = imageBuffer[ind];
2871 else if (imageBuffer[ind] > lmax)
2872 lmax = imageBuffer[ind];
2879 uint32_t * imageBuffer =
reinterpret_cast<uint32_t*
>(targetChip->
getFrameBuffer());
2880 lmin = lmax = imageBuffer[0];
2882 for (i = 0; i < imageHeight; i++)
2883 for (j = 0; j < imageWidth; j++)
2885 ind = (i * imageWidth) + j;
2886 if (imageBuffer[ind] < lmin)
2887 lmin = imageBuffer[ind];
2888 else if (imageBuffer[ind] > lmax)
2889 lmax = imageBuffer[ind];
2900 std::stringstream s;
2901 std::regex_replace(std::ostreambuf_iterator<char>(s), input.begin(), input.end(), std::regex(pattern), replace);
2905 int CCD::getFileIndex(
const char * dir,
const char * prefix,
const char * ext)
2909 DIR * dpdf =
nullptr;
2910 struct dirent * epdf =
nullptr;
2911 std::vector<std::string> files = std::vector<std::string>();
2913 std::string prefixIndex = prefix;
2920 if (stat(dir, &st) == -1)
2922 if (
errno == ENOENT)
2924 DEBUGF(Logger::DBG_DEBUG,
"Creating directory %s...", dir);
2935 dpdf = opendir(dir);
2936 if (dpdf !=
nullptr)
2938 while ((epdf = readdir(dpdf)))
2940 if (strstr(epdf->d_name, prefixIndex.c_str()))
2941 files.push_back(epdf->d_name);
2951 for (uint32_t i = 0; i < files.size(); i++)
2955 std::string file = files.at(i);
2956 std::size_t start = file.find_last_of(
"_");
2957 std::size_t end = file.find_last_of(
".");
2958 if (start != std::string::npos)
2960 index = atoi(file.substr(start + 1, end).c_str());
2961 if (index > maxIndex)
2967 return (maxIndex + 1);
2972 GuiderInterface::GuideComplete(axis);
2975 bool CCD::StartStreaming()
2977 LOG_ERROR(
"Streaming is not supported.");
2981 bool CCD::StopStreaming()
2983 LOG_ERROR(
"Streaming is not supported.");
2987 #ifdef HAVE_WEBSOCKET
2988 void CCD::wsThreadHelper(
void * context)
2990 static_cast<CCD *
>(context)->wsThreadEntry();
2993 void CCD::wsThreadEntry()
3002 void CCD::checkTemperatureTarget()
3006 if (std::abs(m_TargetTemperature - TemperatureN[0].value) <= TemperatureRampNP[RAMP_THRESHOLD].getValue())
3008 TemperatureNP.s =
IPS_OK;
3009 m_TemperatureCheckTimer.stop();
3013 else if (TemperatureRampNP[RAMP_SLOPE].getValue() > 0 && m_TemperatureElapsedTimer.elapsed() >= 60000)
3015 double nextTemperature = 0;
3017 if (m_TargetTemperature < TemperatureN[0].value)
3019 nextTemperature =
std::max(m_TargetTemperature, TemperatureN[0].value - TemperatureRampNP[RAMP_SLOPE].getValue());
3024 nextTemperature =
std::min(m_TargetTemperature, TemperatureN[0].value + TemperatureRampNP[RAMP_SLOPE].getValue());
3027 m_TemperatureElapsedTimer.restart();
3028 SetTemperature(nextTemperature);
3036 auto pos = std::find_if(m_CaptureFormats.begin(), m_CaptureFormats.end(), [format](
auto & oneFormat)
3038 return format.name == oneFormat.name;
3040 if (pos != m_CaptureFormats.end())
3044 auto count = CaptureFormatSP.size();
3045 CaptureFormatSP.resize(count + 1);
3047 const bool isOn = (format.
name == m_ConfigCaptureFormatName) || (m_ConfigCaptureFormatName.empty() && format.
isDefault);
3049 m_CaptureFormats.push_back(format);
3052 bool CCD::SetCaptureFormat(uint8_t index)
const char * getDeviceName() const
The CCDChip class provides functionality of a CCD Chip within a CCD.
void setExposureComplete()
setExposureComplete Mark exposure as complete by setting ImageExposure property to IPS_OK
uint8_t * getFrameBuffer()
getFrameBuffer Get raw frame buffer of the CCD chip.
bool finishFITSFile(int &status)
Finish any pending write to fits file.
float getPixelSizeY() const
getPixelSizeY Get vertical pixel size in microns.
size_t * fitsMemorySizePointer()
int getSubH() const
getSubH Get the height of the frame
double getExposureDuration() const
getExposureDuration Get requested exposure duration for the CCD chip in seconds.
int getBPP() const
getBPP Get CCD Chip depth (bits per pixel).
const char * getExposureStartTime()
getExposureStartTime
char * getImageExtension()
CCD_FRAME getFrameType() const
isInterlaced
int getBinY() const
getBinY Get vertical binning of the CCD chip.
void closeFITSFile()
closeFITSFile Close the in-memory FITS File.
float getPixelSizeX() const
getPixelSizeX Get horizontal pixel size in microns.
void setFrame(uint32_t subx, uint32_t suby, uint32_t subw, uint32_t subh)
setFrame Set desired frame resolutoin for an exposure.
int getXRes() const
getXRes Get the horizontal resolution in pixels of the CCD Chip.
int getBinX() const
getBinX Get horizontal binning of the CCD chip.
void setFrameType(CCD_FRAME type)
setFrameType Set desired frame type for next exposure.
void setImageExtension(const char *ext)
setImageExtension Set image exntension
void ** fitsMemoryBlockPointer()
void setExposureFailed()
setExposureFailed Alert the client that the exposure failed.
int getSubW() const
getSubW Get the width of the frame
fitsfile ** fitsFilePointer()
int getFrameBufferSize() const
getFrameBufferSize Get allocated frame buffer size to hold the CCD image frame.
void setBin(uint8_t hor, uint8_t ver)
setBin Set CCD Chip binnig
int getYRes() const
Get the vertical resolution in pixels of the CCD Chip.
bool openFITSFile(uint32_t size, int &status)
openFITSFile Allocate memory buffer for internal FITS file structure and open
Class to provide general functionality of CCD cameras with a single CCD sensor, or a primary CCD sens...
INumberVectorProperty EqNP
Properties.
ISwitchVectorProperty UploadSP
static constexpr const char * WCS_TAB
virtual bool StartExposure(float duration)
Start exposing primary CCD chip.
ITextVectorProperty ActiveDeviceTP
ActiveDeviceTP defines 4 devices the camera driver can listen to (snoop) for properties of interest s...
INumberVectorProperty CCDRotationNP
std::unique_ptr< StreamManager > Streamer
double GuiderExposureTime
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.
ISwitchVectorProperty WebSocketSP
INDI::Timer m_TemperatureCheckTimer
static constexpr const char * GUIDE_CONTROL_TAB
Group Names.
ITextVectorProperty FileNameTP
FileNameTP File name of locally-saved images. By default, images are uploaded to the client but when ...
virtual void activeDevicesUpdated()
activeDevicesUpdated Inform children that ActiveDevices property was updated so they can snoop on the...
ISwitchVectorProperty FastExposureToggleSP
virtual bool UpdateCCDFrame(int x, int y, int w, int h)
CCD calls this function when CCD Frame dimension needs to be updated in the hardware....
virtual void addFITSKeywords(CCDChip *targetChip, std::vector< FITSRecord > &fitsKeywords)
Generate FITS keywords that will be added to FIST/XISF file.
ITextVectorProperty BayerTP
BayerTP Bayer pattern offset and type.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
INDI::PropertyNumber TemperatureRampNP
Temperature Ramp in C/Min with configurable threshold.
ITextVectorProperty UploadSettingsTP
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...
virtual int SetTemperature(double temperature)
Set CCD temperature.
INDI::PropertyNumber ScopeInfoNP
INumber WebSocketSettingsN[1]
INDI::PropertyText FITSHeaderTP
virtual bool UpdateGuiderFrame(int x, int y, int w, int h)
CCD calls this function when Guide head frame dimension is updated by the client. Derived classes sho...
virtual bool UpdateCCDBin(int hor, int ver)
CCD calls this function when CCD Binning needs to be updated in the hardware. Derived classes should ...
INDI::PropertySwitch EncodeFormatSP
Specifies Driver image encoding format (FITS, Native, JPG, ..etc)
virtual bool SetCaptureFormat(uint8_t index)
SetCaptureFormat Set Active Capture format.
INumberVectorProperty J2000EqNP
J200EqNP Snoop property to read the equatorial J2000 coordinates of the mount. ActiveDeviceTP defines...
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool StartGuideExposure(float duration)
Start exposing guide CCD chip.
virtual bool AbortExposure()
Abort ongoing exposure.
char exposureStartTime[MAXINDINAME]
virtual bool UpdateGuiderFrameType(CCDChip::CCD_FRAME fType)
CCD calls this function when Guide frame type is updated by the client.
INumberVectorProperty FastExposureCountNP
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
ISwitch FastExposureToggleS[2]
INumberVectorProperty WebSocketSettingsNP
virtual void checkTemperatureTarget()
checkTemperatureTarget Checks the current temperature against target temperature and calculates the n...
std::vector< std::string > FilterNames
virtual void SetGuiderParams(int x, int y, int bpp, float xf, float yf)
Setup CCD paramters for guide head CCD. Child classes call this function to update CCD parameters.
ISwitchVectorProperty WorldCoordSP
INDI::ElapsedTimer m_TemperatureElapsedTimer
virtual bool UpdateGuiderBin(int hor, int ver)
CCD calls this function when Guide head binning is updated by the client. Derived classes should impl...
INumber FastExposureCountN[1]
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
INDI::PropertySwitch CaptureFormatSP
Specifies Camera NATIVE capture format (e.g. Mono, RGB, RAW8..etc).
void SetCCDCapability(uint32_t cap)
SetCCDCapability Set the CCD capabilities. Al fields must be initialized.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
INumberVectorProperty TemperatureNP
TemperatureNP Camera Temperature in Celcius.
static void wsThreadHelper(void *context)
virtual bool UpdateCCDFrameType(CCDChip::CCD_FRAME fType)
CCD calls this function when CCD frame type needs to be updated in the hardware.
virtual void SetCCDParams(int x, int y, int bpp, float xf, float yf)
Setup CCD paramters for primary CCD. Child classes call this function to update CCD parameters.
virtual bool UpdateCCDUploadMode(CCD_UPLOAD_MODE mode)
CCD calls this function when client upload mode switch is updated.
double snoopedFocalLength
double m_TargetTemperature
virtual bool AbortGuideExposure()
Abort ongoing exposure.
void addPollPeriodControl()
Add Polling period control to the driver.
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
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.
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 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)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool initProperties()
Initilize properties initial state and value. The child class must implement this function.
void syncDriverInfo()
syncDriverInfo sends the current driver information to the client.
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....
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 start()
Starts this timer. Once started, a timer value can be checked with elapsed().
INumberVectorProperty GuideNSNP
void initGuiderProperties(const char *deviceName, const char *groupName)
Initilize guider properties. It is recommended to call this function within initProperties() of your ...
INumberVectorProperty GuideWENP
void processGuiderProperties(const char *name, double values[], char *names[], int n)
Call this function whenever client updates GuideNSNP or GuideWSP properties in the primary device....
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
const char * getName() const
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
INDI::WidgetViewSwitch * findOnSwitch() const
int findOnSwitchIndex() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
bool update(const char *const texts[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
INDI::PropertyViewText * getText() const
void callOnTimeout(const std::function< void()> &callback)
void start()
Starts or restarts the timer with the timeout specified in interval.
void setInterval(int msec)
Set the timeout interval in milliseconds.
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,...
int fp_pack_data_to_data(const char *inputBuffer, size_t inputBufferSize, unsigned char **outputBuffer, size_t *outputBufferSize, fpstate fpvar, int *islossless)
int fp_init(fpstate *fpptr)
const char * IMAGE_SETTINGS_TAB
std::string join(std::vector< std::string > const &strings, std::string delim)
const char * IMAGE_INFO_TAB
const char * GUIDE_HEAD_TAB
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[].
Implementations for common driver routines.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
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.
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
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.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
int IUSnoopNumber(XMLEle *root, INumberVectorProperty *nvp)
Update a snooped number vector property from the given XML root element.
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.
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.
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.
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
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.
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.
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.
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.
int IUGetConfigOnSwitchName(const char *dev, const char *property, char *name, size_t size)
IUGetConfigOnSwitchLabel Opens configuration file and returns the name of the ON switch property,...
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
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...
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
int IUGetConfigOnSwitchIndex(const char *dev, const char *property, int *index)
IUGetConfigOnSwitchIndex Opens configuration file and reads single switch property to find ON switch ...
void IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt,...)
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
#define DEBUGF(priority, msg,...)
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
The DSP Namespace adds signal processing to INDI drivers. Primarily written for sensors and detectors...
Namespace to encapsulate INDI client, drivers, and mediator classes.
void J2000toObserved(IEquatorialCoordinates *J2000pos, double jd, IEquatorialCoordinates *observed)
*J2000toObserved converts catalogue to observed
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
void ObservedToJ2000(IEquatorialCoordinates *observed, double jd, IEquatorialCoordinates *J2000pos)
ObservedToJ2000 converts an observed position to a J2000 catalogue position removes aberration,...
std::string regex_replace_compat(const std::string &input, const std::string &pattern, const std::string &replace)
int mkpath(std::string s, mode_t mode)
@ error
throw a parse_error exception in case of a tag
char device[MAXINDIDEVICE]
void uploadFile(const char *filename)
void addFITSKeywords(fitsfile *fptr, IMAGE_INFO *image_info)