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>
68std::string
join(std::vector<std::string>
const &strings, std::string delim)
71 std::copy(strings.begin(), strings.end(),
72 std::ostream_iterator<std::string>(ss, delim.c_str()));
88 m_ValidCCDRotation =
false;
101 RA = std::numeric_limits<double>::quiet_NaN();
102 Dec = std::numeric_limits<double>::quiet_NaN();
104 J2000RA = std::numeric_limits<double>::quiet_NaN();
105 J2000DE = std::numeric_limits<double>::quiet_NaN();
107 MPSAS = std::numeric_limits<double>::quiet_NaN();
108 RotatorAngle = std::numeric_limits<double>::quiet_NaN();
112 FocuserTemp = std::numeric_limits<double>::quiet_NaN();
114 Airmass = std::numeric_limits<double>::quiet_NaN();
115 Latitude = std::numeric_limits<double>::quiet_NaN();
116 Longitude = std::numeric_limits<double>::quiet_NaN();
117 Azimuth = std::numeric_limits<double>::quiet_NaN();
118 Altitude = std::numeric_limits<double>::quiet_NaN();
163 TemperatureNP[0].
fill(
"CCD_TEMPERATURE_VALUE",
"Temperature (C)",
"%5.2f", -50.0, 50.0, 0., 0.);
197 PrimaryCCD.ImageExposureNP[0].
fill(
"CCD_EXPOSURE_VALUE",
"Duration (s)",
"%5.2f", 0.01, 3600, 1.0, 1.0);
209 PrimaryCCD.ImageBinNP[CCDChip::HOR_BIN].
fill(
"HOR_BIN",
"X",
"%2.0f", 1, 4, 1, 1);
210 PrimaryCCD.ImageBinNP[CCDChip::VER_BIN].
fill(
"VER_BIN",
"Y",
"%2.0f", 1, 4, 1, 1);
272 IUFillNumber(&
PrimaryCCD.RapidGuideDataN[0],
"GUIDESTAR_X",
"Guide star position X",
"%5.2f", 0, 1024, 0, 0);
273 IUFillNumber(&
PrimaryCCD.RapidGuideDataN[1],
"GUIDESTAR_Y",
"Guide star position Y",
"%5.2f", 0, 1024, 0, 0);
276 "CCD_RAPID_GUIDE_DATA",
"Rapid Guide Data", RAPIDGUIDE_TAB,
IP_RO, 60,
IPS_IDLE);
290 GuideCCD.ImageBinNP[CCDChip::HOR_BIN].
fill(
"HOR_BIN",
"X",
"%2.0f", 1, 4, 1, 1);
291 GuideCCD.ImageBinNP[CCDChip::VER_BIN].
fill(
"VER_BIN",
"Y",
"%2.0f", 1, 4, 1, 1);
315 GuideCCD.ImageExposureNP[0].
fill(
"GUIDER_EXPOSURE_VALUE",
"Duration (s)",
"%5.2f", 0.01, 3600, 1.0, 1.0);
348 "GUIDER_RAPID_GUIDE_SETUP",
"Rapid Guide Setup", RAPIDGUIDE_TAB,
IP_RW,
ISR_NOFMANY, 0,
351 IUFillNumber(&
GuideCCD.RapidGuideDataN[0],
"GUIDESTAR_X",
"Guide star position X",
"%5.2f", 0, 1024, 0, 0);
352 IUFillNumber(&
GuideCCD.RapidGuideDataN[1],
"GUIDESTAR_Y",
"Guide star position Y",
"%5.2f", 0, 1024, 0, 0);
353 IUFillNumber(&
GuideCCD.RapidGuideDataN[2],
"GUIDESTAR_FIT",
"Guide star fit",
"%5.2f", 0, 1024, 0, 0);
355 "GUIDER_RAPID_GUIDE_DATA",
"Rapid Guide Data", RAPIDGUIDE_TAB,
IP_RO, 60,
IPS_IDLE);
386 char configName[64] = {0};
388 m_ConfigCaptureFormatName = configName;
475 EqNP[
Ra].
fill(
"RA",
"Ra (hh:mm:ss)",
"%010.6m", 0, 24, 0, 0);
476 EqNP[
DEC].
fill(
"DEC",
"Dec (dd:mm:ss)",
"%010.6m", -90, 90, 0, 0);
483 "Main Control",
IP_RW,
534 DSP->ISGetProperties(dev);
612 if (RapidGuideEnabled)
617 if (GuiderRapidGuideEnabled)
657 if (RapidGuideEnabled)
682 if (GuiderRapidGuideEnabled)
721 DSP->updateProperties();
737 double newra, newdec;
738 newra =
EqNP[
Ra].getValue();
740 if ((newra !=
RA) || (newdec !=
Dec))
769 if (!strcmp(elemName,
"PIER_EAST") && !strcmp(
pcdataXMLEle(ep),
"On"))
771 else if (!strcmp(elemName,
"PIER_WEST") && !strcmp(
pcdataXMLEle(ep),
"On"))
782 if (!strcmp(name,
"TELESCOPE_APERTURE"))
786 else if (!strcmp(name,
"TELESCOPE_FOCAL_LENGTH"))
794 auto newFilterNames = std::vector<std::string>();
806 auto newFilterSlot = -1;
821 if (!strcmp(name,
"SKY_BRIGHTNESS"))
834 if (!strcmp(name,
"ANGLE"))
849 if (!strcmp(name,
"ROTATOR_ABSOLUTE_POSITION"))
863 if (!strcmp(name,
"FOCUS_ABSOLUTE_POSITION"))
876 if (!strcmp(name,
"TEMPERATURE"))
891 if (!strcmp(name,
"LONG"))
897 else if (!strcmp(name,
"LAT"))
910bool CCD::ISNewText(
const char * dev,
const char * name,
char * texts[],
char * names[],
int n)
918 std::vector<std::string> prevValues;
919 prevValues.reserve(n);
922 for (
int i = 0; i < n; i++)
926 bool hasChanged =
false;
927 for (
int i = 0; i < n; i++)
929 if (prevValues[i] != texts[i])
954 if (strlen(newMount) > 0)
962 else if (!std::isnan(
RA))
964 LOG_DEBUG(
"No mount is set. Clearing all mount watchers.");
966 std::numeric_limits<double>::quiet_NaN();
974 if (strlen(newRotator) > 0)
976 LOGF_DEBUG(
"Snopping on Rotator %s", newRotator);
980 else if (!std::isnan(
MPSAS))
982 LOG_DEBUG(
"No rotator is set. Clearing all rotator watchers.");
983 MPSAS = std::numeric_limits<double>::quiet_NaN();
991 if (strlen(newFocuser) > 0)
993 LOGF_DEBUG(
"Snopping on Focuser %s", newFocuser);
999 LOG_DEBUG(
"No focuser is set. Clearing all focuser watchers.");
1001 FocuserTemp = std::numeric_limits<double>::quiet_NaN();
1009 if (strlen(newFilterWheel) > 0)
1011 LOGF_DEBUG(
"Snopping on Filter Wheel %s", newFilterWheel);
1017 LOG_DEBUG(
"No filter wheel is set. Clearing All filter wheel watchers.");
1024 if (newSkyQuality != prevValues[
ACTIVE_SKYQUALITY] && strlen(newSkyQuality) > 0)
1046 std::transform(name.begin(), name.end(), name.begin(), [](
unsigned char c)
1048 return std::toupper(c);
1053 if (name.empty() && value.empty() && comment.empty())
1055 LOG_ERROR(
"Cannot add an empty FITS record.");
1062 if (name ==
"INDI_CLEAR")
1064 m_CustomFITSKeywords.clear();
1065 LOG_INFO(
"Custom FITS headers cleared.");
1067 else if (name.empty() ==
false && value.empty() ==
false)
1070 std::regex checkDouble(
"^[-+]?([0-9]*?[.,][0-9]+|[0-9]+)$");
1072 std::regex checkInteger(
"^[-+]?([0-9]*)$");
1077 if (std::regex_match(value, checkInteger))
1079 auto lValue = std::stol(value);
1080 FITSRecord record(name.c_str(), lValue, comment.c_str());
1081 m_CustomFITSKeywords[name] = record;
1084 else if (std::regex_match(value, checkDouble))
1086 auto dValue = std::stod(value);
1087 FITSRecord record(name.c_str(), dValue, 6, comment.c_str());
1088 m_CustomFITSKeywords[name] = record;
1095 std::replace(value.begin(), value.end(),
'/',
'\\');
1096 FITSRecord record(name.c_str(), value.c_str(), comment.c_str());
1097 m_CustomFITSKeywords[name] = record;
1101 catch (std::exception &e)
1104 std::replace(value.begin(), value.end(),
'/',
'\\');
1105 FITSRecord record(name.c_str(), value.c_str(), comment.c_str());
1106 m_CustomFITSKeywords[name] = record;
1109 else if (comment.empty() ==
false)
1112 m_CustomFITSKeywords[comment] = record;
1131 Streamer->ISNewText(dev, name, texts, names, n);
1135 DSP->ISNewText(dev, name, texts, names, n);
1143bool CCD::ISNewNumber(
const char * dev,
const char * name,
double values[],
char * names[],
int n)
1156 (values[0] <
PrimaryCCD.ImageExposureNP[0].getMin() || values[0] >
PrimaryCCD.ImageExposureNP[0].getMax()))
1158 LOGF_ERROR(
"Requested exposure value (%g) seconds out of bounds [%g,%g].",
1173 PrimaryCCD.ImageExposureNP[0].setValue(values[0]);
1199 (values[0] <
GuideCCD.ImageExposureNP[0].getMin() || values[0] >
GuideCCD.ImageExposureNP[0].getMax()))
1201 LOGF_ERROR(
"Requested guide exposure value (%g) seconds out of bounds [%g,%g].",
1202 values[0],
GuideCCD.ImageExposureNP[0].getMin(),
GuideCCD.ImageExposureNP[0].getMax());
1211 GuideCCD.ImageExposureNP[0].setValue(
GuideCCD.ImageExposureNP[0].getMin());
1216 GuideCCD.ImageExposureNP[0].setValue(values[0]);
1239 else if (values[0] == 0 || values[1] == 0)
1243 LOGF_ERROR(
"%.fx%.f binning is invalid.", values[0], values[1]);
1248 if (!strcmp(np->name,
"HOR_BIN"))
1283 else if (values[0] == 0 || values[1] == 0)
1287 LOGF_ERROR(
"%.fx%.f binning is invalid.", values[0], values[1]);
1292 if (!strcmp(np->name,
"HOR_BIN"))
1336 int x = -1, y = -1, w = -1, h = -1;
1337 for (
int i = 0; i < n; i++)
1339 if (!strcmp(names[i],
"X"))
1341 else if (!strcmp(names[i],
"Y"))
1343 else if (!strcmp(names[i],
"WIDTH"))
1345 else if (!strcmp(names[i],
"HEIGHT"))
1351 if (x < 0 || y < 0 || w <= 0 || h <= 0)
1353 LOGF_ERROR(
"Invalid frame requested (%d,%d) (%d x %d)", x, y, w, h);
1380 values[2], values[4]);
1393 if (!strcmp(name,
"CCD_GUIDESTAR"))
1401 if (!strcmp(name,
"GUIDER_GUIDESTAR"))
1424 LOGF_ERROR(
"Error: Bad temperature value! Range is [%.1f, %.1f] [C].",
1430 double nextTemperature = values[0];
1480 LOG_INFO(
"Temperature ramp is disabled.");
1482 LOGF_INFO(
"Temperature ramp is enabled. Gradual cooling and warming is regulated at %.f Celsius per minute.",
1532 m_ValidCCDRotation =
true;
1542 Streamer->ISNewNumber(dev, name, values, names, n);
1546 DSP->ISNewNumber(dev, name, values, names, n);
1568 LOG_INFO(
"Upload settings set to client only.");
1574 LOG_INFO(
"Upload settings set to local only.");
1579 LOG_INFO(
"Upload settings set to client and local.");
1604 LOG_WARN(
"Experimental Feature: After a frame is downloaded, the next frame capture immediately starts to avoid any delays.");
1628 LOG_INFO(
"World Coordinate System is enabled.");
1633 LOG_INFO(
"World Coordinate System is disabled.");
1637 m_ValidCCDRotation =
false;
1698 GuideCCD.ImageExposureNP[0].setValue(0);
1744 "The CCD does not have a shutter. Cover the camera in order to take a bias frame.");
1751 "The CCD does not have a shutter. Cover the camera in order to take a dark frame.");
1777 "The CCD does not have a shutter. Cover the camera in order to take a bias frame.");
1784 "The CCD does not have a shutter. Cover the camera in order to take a dark frame.");
1807 if (previousIndex >= 0)
1843 if (strcmp(name,
PrimaryCCD.RapidGuideSP.name) == 0)
1849 if (RapidGuideEnabled)
1865 if (strcmp(name,
GuideCCD.RapidGuideSP.name) == 0)
1871 if (GuiderRapidGuideEnabled)
1887 if (strcmp(name,
PrimaryCCD.RapidGuideSetupSP.name) == 0)
1901 if (strcmp(name,
GuideCCD.RapidGuideSetupSP.name) == 0)
1917 Streamer->ISNewSwitch(dev, name, states, names, n);
1921 DSP->ISNewSwitch(dev, name, states, names, n);
1929bool CCD::ISNewBLOB(
const char *dev,
const char *name,
int sizes[],
int blobsizes[],
char *blobs[],
1930 char *formats[],
char *names[],
int n)
1934 DSP->ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
2058 double effectiveFocalLength = std::numeric_limits<double>::quiet_NaN();
2059 double effectiveAperture = std::numeric_limits<double>::quiet_NaN();
2061 AutoCNumeric locale;
2062 fitsKeywords.push_back({
"ROWORDER",
"TOP-DOWN",
"Row Order"});
2063 fitsKeywords.push_back({
"INSTRUME",
getDeviceName(),
"Camera Name"});
2079 if (std::isnan(effectiveFocalLength))
2080 LOG_WARN(
"Telescope focal length is missing.");
2081 if (std::isnan(effectiveAperture))
2082 LOG_WARN(
"Telescope aperture is missing.");
2084 double subPixSize1 =
static_cast<double>(targetChip->
getPixelSizeX());
2085 double subPixSize2 =
static_cast<double>(targetChip->
getPixelSizeY());
2086 uint32_t subW = targetChip->
getSubW();
2087 uint32_t subH = targetChip->
getSubH();
2088 uint32_t subBinX = targetChip->
getBinX();
2089 uint32_t subBinY = targetChip->
getBinY();
2091 snprintf(dev_name,
sizeof(dev_name),
"%s",
getDeviceName());
2093 fitsKeywords.push_back({
"EXPTIME",
exposureDuration, 6,
"Total Exposure Time (s)"});
2096 fitsKeywords.push_back({
"DARKTIME",
exposureDuration, 6,
"Total Dark Exposure Time (s)"});
2100 fitsKeywords.push_back({
"CCD-TEMP",
TemperatureNP[0].getValue(), 3,
"CCD Temperature (Celsius)"});
2102 fitsKeywords.push_back({
"PIXSIZE1", subPixSize1, 6,
"Pixel Size 1 (microns)"});
2103 fitsKeywords.push_back({
"PIXSIZE2", subPixSize2, 6,
"Pixel Size 2 (microns)"});
2104 fitsKeywords.push_back({
"XBINNING", targetChip->
getBinX(),
"Binning factor in width"});
2105 fitsKeywords.push_back({
"YBINNING", targetChip->
getBinY(),
"Binning factor in height"});
2107 double xpixsz = subPixSize1 * subBinX;
2108 double ypixsz = subPixSize2 * subBinY;
2109 fitsKeywords.push_back({
"XPIXSZ", xpixsz, 6,
"X binned pixel size in microns"});
2110 fitsKeywords.push_back({
"YPIXSZ", ypixsz, 6,
"Y binned pixel size in microns"});
2115 fitsKeywords.push_back({
"FRAME",
"Light",
"Frame Type"});
2116 fitsKeywords.push_back({
"IMAGETYP",
"Light Frame",
"Frame Type"});
2119 fitsKeywords.push_back({
"FRAME",
"Bias",
"Frame Type"});
2120 fitsKeywords.push_back({
"IMAGETYP",
"Bias Frame",
"Frame Type"});
2123 fitsKeywords.push_back({
"FRAME",
"Flat",
"Frame Type"});
2124 fitsKeywords.push_back({
"IMAGETYP",
"Flat Frame",
"Frame Type"});
2127 fitsKeywords.push_back({
"FRAME",
"Dark",
"Frame Type"});
2128 fitsKeywords.push_back({
"IMAGETYP",
"Dark Frame",
"Frame Type"});
2140 double min_val, max_val;
2141 getMinMax(&min_val, &max_val, targetChip);
2143 fitsKeywords.push_back({
"DATAMIN", min_val, 6,
"Minimum value"});
2144 fitsKeywords.push_back({
"DATAMAX", max_val, 6,
"Maximum value"});
2155 if (!std::isnan(effectiveFocalLength))
2156 fitsKeywords.push_back({
"FOCALLEN", effectiveFocalLength, 3,
"Focal Length (mm)"});
2158 if (!std::isnan(effectiveAperture))
2159 fitsKeywords.push_back({
"APTDIA", effectiveAperture, 3,
"Telescope diameter (mm)"});
2161 if (!std::isnan(MPSAS))
2163 fitsKeywords.push_back({
"MPSAS", MPSAS, 6,
"Sky Quality (mag per arcsec^2)"});
2166 if (!std::isnan(RotatorAngle))
2168 fitsKeywords.push_back({
"ROTATANG", RotatorAngle, 3,
"Rotator angle in degrees"});
2171 if (!std::isnan(RotatorPosition))
2173 fitsKeywords.push_back({
"ROTSTEPS", RotatorPosition, 0,
"Rotator absolute position in steps"});
2178 if (FocuserPos != -1)
2180 fitsKeywords.push_back({
"FOCUSPOS", FocuserPos,
"Focus position in steps"});
2182 if (!std::isnan(FocuserTemp))
2184 fitsKeywords.push_back({
"FOCUSTEM", FocuserTemp, 3,
"Focuser temperature in degrees C"});
2188 if (!std::isnan(effectiveFocalLength))
2190 double pixScale = subPixSize1 / effectiveFocalLength * 206.3 * subBinX;
2191 fitsKeywords.push_back({
"SCALE", pixScale, 6,
"arcsecs per pixel"});
2195 if ( targetChip->getFrameType() == CCDChip::LIGHT_FRAME && !std::isnan(RA) && !std::isnan(Dec) && (std::isnan(J2000RA)
2196 || std::isnan(J2000DE) || !J2000Valid) )
2200 epochPos.declination = Dec;
2205 J2000RA = J2000Pos.rightascension;
2206 J2000DE = J2000Pos.declination;
2210 if ( targetChip->getFrameType() == CCDChip::LIGHT_FRAME && !std::isnan(J2000RA) && !std::isnan(J2000DE) )
2212 if (!std::isnan(Latitude) && !std::isnan(Longitude))
2217 J2000Pos.declination = J2000DE;
2224 IGeographicCoordinates observer;
2225 observer.latitude = Latitude;
2226 observer.longitude = Longitude;
2229 Azimuth = horizontalPos.
azimuth;
2231 Airmass = ln_get_airmass(Altitude, 750);
2234 char ra_str[32] = {0}, de_str[32] = {0};
2236 fs_sexa(ra_str, J2000RA, 2, 360000);
2237 fs_sexa(de_str, J2000DE, 2, 360000);
2239 char * raPtr = ra_str, *dePtr = de_str;
2240 while (*raPtr !=
'\0')
2246 while (*dePtr !=
'\0')
2253 if (!std::isnan(Latitude) && !std::isnan(Longitude))
2255 fitsKeywords.push_back({
"SITELAT", Latitude, 6,
"Latitude of the imaging site in degrees"});
2256 fitsKeywords.push_back({
"SITELONG", Longitude, 6,
"Longitude of the imaging site in degrees"});
2258 if (!std::isnan(Airmass))
2261 fitsKeywords.push_back({
"AIRMASS", Airmass, 6,
"Airmass"});
2262 fitsKeywords.push_back({
"OBJCTAZ", Azimuth, 6,
"Azimuth of center of image in Degrees"});
2263 fitsKeywords.push_back({
"OBJCTALT", Altitude, 6,
"Altitude of center of image in Degrees"});
2265 fitsKeywords.push_back({
"OBJCTRA", ra_str,
"Object J2000 RA in Hours"});
2266 fitsKeywords.push_back({
"OBJCTDEC", de_str,
"Object J2000 DEC in Degrees"});
2268 fitsKeywords.push_back({
"RA", J2000RA * 15, 6,
"Object J2000 RA in Degrees"});
2269 fitsKeywords.push_back({
"DEC", J2000DE, 6,
"Object J2000 DEC in Degrees"});
2275 fitsKeywords.push_back({
"PIERSIDE",
"WEST",
"West, looking East"});
2278 fitsKeywords.push_back({
"PIERSIDE",
"EAST",
"East, looking West"});
2283 fitsKeywords.push_back({
"EQUINOX", 2000,
"Equinox"});
2286 if (WorldCoordSP[WCS_ENABLE].getState() ==
ISS_ON && m_ValidCCDRotation && !std::isnan(effectiveFocalLength))
2288 double J2000RAHours = J2000RA * 15;
2289 fitsKeywords.push_back({
"CRVAL1", J2000RAHours, 10,
"CRVAL1"});
2290 fitsKeywords.push_back({
"CRVAL2", J2000DE, 10,
"CRVAL1"});
2292 char radecsys[8] =
"FK5";
2293 char ctype1[16] =
"RA---TAN";
2294 char ctype2[16] =
"DEC--TAN";
2296 fitsKeywords.push_back({
"RADECSYS", radecsys,
"RADECSYS"});
2297 fitsKeywords.push_back({
"CTYPE1", ctype1,
"CTYPE1"});
2298 fitsKeywords.push_back({
"CTYPE2", ctype2,
"CTYPE2"});
2300 double crpix1 = subW / subBinX / 2.0;
2301 double crpix2 = subH / subBinY / 2.0;
2303 fitsKeywords.push_back({
"CRPIX1", crpix1, 10,
"CRPIX1"});
2304 fitsKeywords.push_back({
"CRPIX2", crpix2, 10,
"CRPIX2"});
2306 double secpix1 = subPixSize1 / effectiveFocalLength * 206.3 * subBinX;
2307 double secpix2 = subPixSize2 / effectiveFocalLength * 206.3 * subBinY;
2309 fitsKeywords.push_back({
"SECPIX1", secpix1, 10,
"SECPIX1"});
2310 fitsKeywords.push_back({
"SECPIX2", secpix2, 10,
"SECPIX2"});
2312 double degpix1 = secpix1 / 3600.0;
2313 double degpix2 = secpix2 / 3600.0;
2315 fitsKeywords.push_back({
"CDELT1", degpix1, 10,
"CDELT1"});
2316 fitsKeywords.push_back({
"CDELT2", degpix2, 10,
"CDELT2"});
2319 double rotation = 360 - CCDRotationNP[0].getValue();
2323 fitsKeywords.push_back({
"CROTA1", rotation, 10,
"CROTA1"});
2324 fitsKeywords.push_back({
"CROTA2", rotation, 10,
"CROTA2"});
2339 fitsKeywords.push_back({
"DATE-OBS", exposureStartTime,
"UTC start date of observation"});
2342 for (
auto &record : m_CustomFITSKeywords)
2343 fitsKeywords.push_back(record.second);
2345 fitsKeywords.push_back(FITSRecord(
"Generated by INDI"));
2351void CCD::fits_update_key_s(fitsfile * fptr,
int type, std::string name,
void * p, std::string explanation,
2355 fits_update_key(fptr,
type, name.c_str(), p,
const_cast<char *
>(explanation.c_str()), status);
2364 setCurrentPollingPeriod(getPollingPeriod());
2367 std::thread(&CCD::ExposureCompletePrivate,
this, targetChip).detach();
2375bool CCD::ExposureCompletePrivate(
CCDChip * targetChip)
2381 snprintf(exposureStartTime,
sizeof(exposureStartTime),
"%s", targetChip->
getExposureStartTime());
2392 if (processFastExposure(targetChip) ==
false)
2395 bool sendImage = (UploadSP[UPLOAD_CLIENT].getState() ==
ISS_ON || UploadSP[UPLOAD_BOTH].getState() ==
ISS_ON);
2396 bool saveImage = (UploadSP[UPLOAD_LOCAL].getState() ==
ISS_ON || UploadSP[UPLOAD_BOTH].getState() ==
ISS_ON);
2400 sendImage = saveImage =
false;
2402 if (sendImage || saveImage)
2404 if (EncodeFormatSP[FORMAT_FITS].getState() ==
ISS_ON)
2411 long naxis = targetChip->
getNAxis();
2419 switch (targetChip->
getBPP())
2423 img_type = BYTE_IMG;
2427 byte_type = TUSHORT;
2428 img_type = USHORT_IMG;
2433 img_type = ULONG_IMG;
2441 nelements = naxes[0] * naxes[1];
2451 std::unique_lock<std::mutex> guard(ccdBufferLock);
2454 uint32_t size = 8640 + nelements * (targetChip->
getBPP() / 8);
2458 fits_report_error(stderr, status);
2459 fits_get_errstatus(status, error_status);
2466 fits_create_img(fptr, img_type, naxis, naxes, &status);
2470 fits_report_error(stderr, status);
2471 fits_get_errstatus(status, error_status);
2477 std::vector<FITSRecord> fitsKeywords;
2481 for (
auto &keyword : fitsKeywords)
2484 switch(keyword.type())
2489 fits_write_comment(fptr, keyword.comment().c_str(), &key_status);
2492 fits_update_key_str(fptr, keyword.key().c_str(), keyword.valueString().c_str(), keyword.comment().c_str(), &key_status);
2495 fits_update_key_lng(fptr, keyword.key().c_str(), keyword.valueInt(), keyword.comment().c_str(), &key_status);
2498 fits_update_key_dbl(fptr, keyword.key().c_str(), keyword.valueDouble(), keyword.decimal(), keyword.comment().c_str(),
2504 fits_get_errstatus(key_status, error_status);
2505 LOGF_ERROR(
"FITS key %s Error: %s", keyword.key().c_str(), error_status);
2509 fits_write_img(fptr, byte_type, 1, nelements, targetChip->
getFrameBuffer(), &status);
2513 fits_report_error(stderr, status);
2514 fits_get_errstatus(status, error_status);
2535 else if (EncodeFormatSP[FORMAT_XISF].getState() ==
ISS_ON)
2537 std::vector<FITSRecord> fitsKeywords;
2543 AutoCNumeric locale;
2544 LibXISF::Image image;
2545 LibXISF::XISFWriter xisfWriter;
2547 for (
auto &keyword : fitsKeywords)
2549 image.addFITSKeyword({keyword.key().c_str(), keyword.valueString().c_str(), keyword.comment().c_str()});
2550 image.addFITSKeywordAsProperty(keyword.key().c_str(), keyword.valueString());
2555 targetChip->
getNAxis() == 2 ? 1 : 3);
2556 switch(targetChip->
getBPP())
2559 image.setSampleFormat(LibXISF::Image::UInt8);
2562 image.setSampleFormat(LibXISF::Image::UInt16);
2565 image.setSampleFormat(LibXISF::Image::UInt32);
2574 case CCDChip::LIGHT_FRAME:
2575 image.setImageType(LibXISF::Image::Light);
2577 case CCDChip::BIAS_FRAME:
2578 image.setImageType(LibXISF::Image::Bias);
2580 case CCDChip::DARK_FRAME:
2581 image.setImageType(LibXISF::Image::Dark);
2583 case CCDChip::FLAT_FRAME:
2584 image.setImageType(LibXISF::Image::Flat);
2588 if (targetChip->SendCompressed)
2590 if(LibXISF::DataBlock::CompressionCodecSupported(LibXISF::DataBlock::ZSTD))
2591 image.setCompression(LibXISF::DataBlock::ZSTD);
2593 image.setCompression(LibXISF::DataBlock::LZ4);
2594 image.setByteshuffling(targetChip->
getBPP() / 8);
2598 image.setColorFilterArray({2, 2, BayerTP[2].getText()});
2602 image.setColorSpace(LibXISF::Image::RGB);
2605 std::unique_lock<std::mutex> guard(ccdBufferLock);
2606 std::memcpy(image.imageData(), targetChip->
getFrameBuffer(), image.imageDataSize());
2607 xisfWriter.writeImage(image);
2609 LibXISF::ByteArray xisfFile;
2610 xisfWriter.save(xisfFile);
2611 bool rc =
uploadFile(targetChip, xisfFile.data(), xisfFile.size(), sendImage, saveImage);
2618 catch (LibXISF::Error &error)
2630 std::unique_lock<std::mutex> guard(ccdBufferLock);
2643 if (FastExposureToggleSP[INDI_ENABLED].getState() !=
ISS_ON)
2646 UploadComplete(targetChip);
2653bool CCD::uploadFile(CCDChip * targetChip,
const void * fitsData,
size_t totalBytes,
bool sendImage,
2656 uint8_t * compressedData =
nullptr;
2658 DEBUGF(Logger::DBG_DEBUG,
"Uploading file. Ext: %s, Size: %d, sendImage? %s, saveImage? %s",
2659 targetChip->getImageExtension(), totalBytes, sendImage ?
"Yes" :
"No", saveImage ?
"Yes" :
"No");
2663 targetChip->FitsBP[0].setBlob(
const_cast<void *
>(fitsData));
2664 targetChip->FitsBP[0].setBlobLen(totalBytes);
2665 std::string format =
"." + std::string(targetChip->getImageExtension());
2666 targetChip->FitsBP[0].setFormat(format);
2667 FILE * fp =
nullptr;
2669 std::string prefix = UploadSettingsTP[UPLOAD_PREFIX].getText();
2670 std::string directory = UploadSettingsTP[UPLOAD_DIR].getText();
2675 if (
const char * home = getenv(
"HOME"))
2678 int maxIndex = getFileIndex(directory, prefix,
2679 targetChip->FitsBP[0].getFormat());
2683 LOGF_ERROR(
"Error iterating directory %s. %s", directory.c_str(),
2690 auto now = std::chrono::system_clock::now();
2691 std::time_t time = std::chrono::system_clock::to_time_t(now);
2692 std::tm* now_tm = std::localtime(&time);
2693 long long timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
2695 std::stringstream stream;
2697 stream << std::setfill(
'0')
2698 << std::put_time(now_tm,
"%FT%H-%M-")
2699 << std::setw(2) << (timestamp / 1000) % 60 <<
'.'
2700 << std::setw(3) << timestamp % 1000;
2702 prefix = std::regex_replace(prefix, std::regex(
"ISO8601"), stream.str());
2704 char indexString[8];
2705 snprintf(indexString, 8,
"%03d", maxIndex);
2706 std::string prefixIndex = indexString;
2708 prefix = std::regex_replace(prefix, std::regex(
"XXX"), prefixIndex);
2711 std::string imageFileName = directory +
"/" + prefix + std::string(
2712 targetChip->FitsBP[0].getFormat());
2714 fp = fopen(imageFileName.c_str(),
"w");
2717 LOGF_ERROR(
"Unable to save image file (%s). %s", imageFileName.c_str(), strerror(
errno));
2722 auto len = targetChip->FitsBP[0].getBlobLen();
2723 auto buffer =
static_cast<char *
>(targetChip->FitsBP[0].getBlob());
2724 for (
int nr = 0; nr < len; nr += n)
2725 n = fwrite(buffer + nr, 1, len - nr, fp);
2730 FileNameTP[0].setText(imageFileName);
2732 LOGF_INFO(
"Image saved to %s", imageFileName.c_str());
2733 FileNameTP.setState(
IPS_OK);
2737 if (targetChip->SendCompressed && EncodeFormatSP[FORMAT_XISF].getState() !=
ISS_ON)
2739 if (EncodeFormatSP[FORMAT_FITS].getState() ==
ISS_ON && !strcmp(targetChip->getImageExtension(),
"fits"))
2743 size_t compressedBytes = 0;
2745 if (
fp_pack_data_to_data(
reinterpret_cast<const char *
>(fitsData), totalBytes, &compressedData, &compressedBytes, fpvar,
2748 free(compressedData);
2749 LOG_ERROR(
"Error: Ran out of memory compressing image");
2753 targetChip->FitsBP[0].setBlob(compressedData);
2754 targetChip->FitsBP[0].setBlobLen(compressedBytes);
2755 std::string format =
"." + std::string(targetChip->getImageExtension()) +
".fz";
2756 targetChip->FitsBP[0].setFormat(format);
2760 uLong compressedBytes =
sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
2761 compressedData =
new uint8_t[compressedBytes];
2763 if (fitsData ==
nullptr || compressedData ==
nullptr)
2766 delete [] compressedData;
2767 LOG_ERROR(
"Error: Ran out of memory compressing image");
2771 int r = compress2(compressedData, &compressedBytes, (
const Bytef *)fitsData, totalBytes, 9);
2775 LOG_ERROR(
"Error: Failed to compress image");
2776 delete [] compressedData;
2780 targetChip->FitsBP[0].setBlob(compressedData);
2781 targetChip->FitsBP[0].setBlobLen(compressedBytes);
2782 std::string format =
"." + std::string(targetChip->getImageExtension()) +
".z";
2783 targetChip->FitsBP[0].setFormat(format);
2789 targetChip->FitsBP[0].setBlob(
const_cast<void *
>(fitsData));
2790 targetChip->FitsBP[0].setBlobLen(totalBytes);
2791 std::string format =
"." + std::string(targetChip->getImageExtension());
2792 targetChip->FitsBP[0].setFormat(format);
2795 targetChip->FitsBP[0].setSize(totalBytes);
2796 targetChip->FitsBP.setState(
IPS_OK);
2800 auto start = std::chrono::high_resolution_clock::now();
2801 targetChip->FitsBP.apply();
2802 auto end = std::chrono::high_resolution_clock::now();
2803 std::chrono::duration<double> diff = end - start;
2804 LOGF_DEBUG(
"BLOB transfer took %g seconds", diff.count());
2808 delete [] compressedData;
2810 DEBUG(Logger::DBG_DEBUG,
"Upload complete");
2818bool CCD::processFastExposure(
CCDChip * targetChip)
2821 if (FastExposureToggleSP[INDI_ENABLED].getState() ==
ISS_ON)
2827 if (FastExposureCountNP[0].getValue() > 1)
2829 if (UploadSP[UPLOAD_LOCAL].getState() !=
ISS_ON)
2831 if (FastExposureCountNP.getState() !=
IPS_BUSY)
2833 FastExposureToggleStartup = std::chrono::system_clock::now();
2837 auto end = std::chrono::system_clock::now();
2839 m_UploadTime = (std::chrono::duration_cast<std::chrono::milliseconds>(end - FastExposureToggleStartup)).count() / 1000.0 -
2841 LOGF_DEBUG(
"Image download and upload/save took %.3f seconds.", m_UploadTime);
2843 FastExposureToggleStartup = end;
2847 FastExposureCountNP.setState(
IPS_BUSY);
2848 FastExposureCountNP[0].setValue(FastExposureCountNP[0].getValue() - 1);
2849 FastExposureCountNP.apply();
2851 if (UploadSP[UPLOAD_LOCAL].getState() ==
ISS_ON || m_UploadTime < duration)
2853 if (StartExposure(duration))
2854 PrimaryCCD.ImageExposureNP.setState(
IPS_BUSY);
2856 PrimaryCCD.ImageExposureNP.setState(
IPS_ALERT);
2857 if (duration * 1000 < getCurrentPollingPeriod())
2858 setCurrentPollingPeriod(duration * 950);
2862 LOGF_ERROR(
"Rapid exposure not possible since upload time is %.2f seconds while exposure time is %.2f seconds.",
2865 PrimaryCCD.ImageExposureNP.setState(
IPS_ALERT);
2866 PrimaryCCD.ImageExposureNP.apply();
2867 FastExposureCountNP[0].setValue(1);
2868 FastExposureCountNP.setState(
IPS_IDLE);
2869 FastExposureCountNP.apply();
2877 FastExposureCountNP.setState(
IPS_IDLE);
2878 FastExposureCountNP.apply();
2888void CCD::SetCCDParams(
int x,
int y,
int bpp,
float xf,
float yf)
2890 PrimaryCCD.setResolution(x, y);
2891 PrimaryCCD.setFrame(0, 0, x, y);
2893 PrimaryCCD.setBin(1, 1);
2894 PrimaryCCD.setPixelSize(xf, yf);
2895 PrimaryCCD.setBPP(bpp);
2901void CCD::SetGuiderParams(
int x,
int y,
int bpp,
float xf,
float yf)
2903 capability |= CCD_HAS_GUIDE_HEAD;
2905 GuideCCD.setResolution(x, y);
2906 GuideCCD.setFrame(0, 0, x, y);
2907 GuideCCD.setPixelSize(xf, yf);
2908 GuideCCD.setBPP(bpp);
2914bool CCD::saveConfigItems(FILE * fp)
2916 DefaultDevice::saveConfigItems(fp);
2918 ActiveDeviceTP.save(fp);
2920 UploadSettingsTP.save(fp);
2921 FastExposureToggleSP.save(fp);
2923 PrimaryCCD.CompressSP.save(fp);
2925 if (PrimaryCCD.getCCDInfo().getPermission() !=
IP_RO)
2926 PrimaryCCD.getCCDInfo().save(fp);
2928 if (!CaptureFormatSP.isEmpty())
2929 CaptureFormatSP.save(fp);
2930 if (!EncodeFormatSP.isEmpty())
2931 EncodeFormatSP.save(fp);
2934 TemperatureRampNP.save(fp);
2938 GuideCCD.CompressSP.save(fp);
2939 GuideCCD.ImageBinNP.save(fp);
2942 if (CanSubFrame() && PrimaryCCD.ImageFrameNP[CCDChip::FRAME_W].getValue() > 0)
2943 PrimaryCCD.ImageFrameNP.save(fp);
2946 PrimaryCCD.ImageBinNP.save(fp);
2952 Streamer->saveConfigItems(fp);
2955 DSP->saveConfigItems(fp);
2957 ScopeInfoNP.save(fp);
2968 LOG_ERROR(
"The CCD does not support guiding.");
2978 LOG_ERROR(
"The CCD does not support guiding.");
2988 LOG_ERROR(
"The CCD does not support guiding.");
2998 LOG_ERROR(
"The CCD does not support guiding.");
3005void CCD::getMinMax(
double *
min,
double *
max,
CCDChip * targetChip)
3010 double lmin = 0, lmax = 0;
3012 switch (targetChip->
getBPP())
3017 lmin = lmax = imageBuffer[0];
3019 for (i = 0; i < imageHeight; i++)
3020 for (j = 0; j < imageWidth; j++)
3022 ind = (i * imageWidth) + j;
3023 if (imageBuffer[ind] < lmin)
3024 lmin = imageBuffer[ind];
3025 else if (imageBuffer[ind] > lmax)
3026 lmax = imageBuffer[ind];
3033 uint16_t * imageBuffer =
reinterpret_cast<uint16_t*
>(targetChip->
getFrameBuffer());
3034 lmin = lmax = imageBuffer[0];
3036 for (i = 0; i < imageHeight; i++)
3037 for (j = 0; j < imageWidth; j++)
3039 ind = (i * imageWidth) + j;
3040 if (imageBuffer[ind] < lmin)
3041 lmin = imageBuffer[ind];
3042 else if (imageBuffer[ind] > lmax)
3043 lmax = imageBuffer[ind];
3050 uint32_t * imageBuffer =
reinterpret_cast<uint32_t*
>(targetChip->
getFrameBuffer());
3051 lmin = lmax = imageBuffer[0];
3053 for (i = 0; i < imageHeight; i++)
3054 for (j = 0; j < imageWidth; j++)
3056 ind = (i * imageWidth) + j;
3057 if (imageBuffer[ind] < lmin)
3058 lmin = imageBuffer[ind];
3059 else if (imageBuffer[ind] > lmax)
3060 lmax = imageBuffer[ind];
3074 std::stringstream s;
3075 std::regex_replace(std::ostreambuf_iterator<char>(s), input.begin(), input.end(), std::regex(pattern), replace);
3082int CCD::getFileIndex(
const std::string &dir,
const std::string &prefix,
const std::string &ext)
3089 LOG_ERROR(
"Upload directory is not set. Please configure the upload directory.");
3093 DIR * dpdf =
nullptr;
3094 struct dirent * epdf =
nullptr;
3095 std::vector<std::string> files = std::vector<std::string>();
3097 std::string prefixIndex = prefix;
3104 if (stat(dir.c_str(), &st) == -1)
3106 if (
errno == ENOENT)
3108 LOGF_INFO(
"Creating directory %s...", dir.c_str());
3110 LOGF_ERROR(
"Error creating directory %s (%s)", dir.c_str(), strerror(
errno));
3114 LOGF_ERROR(
"Couldn't stat directory %s: %s", dir.c_str(), strerror(
errno));
3119 dpdf = opendir(dir.c_str());
3120 if (dpdf !=
nullptr)
3122 while ((epdf = readdir(dpdf)))
3125 if (strcmp(epdf->d_name,
".") == 0 || strcmp(epdf->d_name,
"..") == 0)
3128 if (prefixIndex.empty() || strstr(epdf->d_name, prefixIndex.c_str()))
3129 files.push_back(epdf->d_name);
3136 for (uint32_t i = 0; i < files.size(); i++)
3140 std::string file = files.at(i);
3141 std::size_t start = file.find_last_of(
"_");
3142 std::size_t end = file.find_last_of(
".");
3143 if (start != std::string::npos && end != std::string::npos && end > start)
3145 index = atoi(file.substr(start + 1, end - start - 1).c_str());
3146 if (index > maxIndex)
3152 return (maxIndex + 1);
3160 GuiderInterface::GuideComplete(axis);
3166bool CCD::StartStreaming()
3168 LOG_ERROR(
"Streaming is not supported.");
3175bool CCD::StopStreaming()
3177 LOG_ERROR(
"Streaming is not supported.");
3185void CCD::checkTemperatureTarget()
3187 if (TemperatureNP.getState() ==
IPS_BUSY)
3189 if (std::abs(m_TargetTemperature - TemperatureNP[0].getValue()) <= TemperatureRampNP[RAMP_THRESHOLD].getValue())
3191 TemperatureNP.setState(
IPS_OK);
3192 m_TemperatureCheckTimer.stop();
3193 TemperatureNP.apply();
3198 else if (m_TargetTemperature > TemperatureNP[0].getValue())
3200 double rampSlope = TemperatureRampNP[RAMP_SLOPE].getValue();
3204 double tempDelta = std::abs(m_TargetTemperature - m_InitialRampTemperature);
3205 long minTimeToTarget_ms =
static_cast<long>((tempDelta / rampSlope) * 60000);
3206 if (m_TemperatureElapsedTimer.elapsed() < minTimeToTarget_ms)
3209 if (m_TemperatureElapsedTimer.elapsed() >= 60000)
3211 double nextTemperature = std::min(m_TargetTemperature, TemperatureNP[0].getValue() + rampSlope);
3212 m_TemperatureElapsedTimer.restart();
3213 SetTemperature(nextTemperature);
3221 if (std::abs(TemperatureNP[0].getValue() - m_TemperatureStabilizationValue) <= TemperatureRampNP[RAMP_THRESHOLD].getValue())
3224 if (m_TemperatureStabilizationTimer.elapsed() >= 120000)
3226 LOGF_INFO(
"Temperature stabilized at %.2f°C (target %.2f°C not reachable, likely limited by ambient temperature).",
3227 TemperatureNP[0].getValue(), m_TargetTemperature);
3228 TemperatureNP.setState(
IPS_OK);
3229 m_TemperatureCheckTimer.stop();
3230 TemperatureNP.apply();
3236 m_TemperatureStabilizationValue = TemperatureNP[0].getValue();
3237 m_TemperatureStabilizationTimer.restart();
3241 else if (TemperatureRampNP[RAMP_SLOPE].getValue() > 0 && m_TemperatureElapsedTimer.elapsed() >= 60000)
3243 double nextTemperature = 0;
3245 if (m_TargetTemperature < TemperatureNP[0].getValue())
3247 nextTemperature = std::max(m_TargetTemperature, TemperatureNP[0].getValue() - TemperatureRampNP[RAMP_SLOPE].getValue());
3252 nextTemperature = std::min(m_TargetTemperature, TemperatureNP[0].getValue() + TemperatureRampNP[RAMP_SLOPE].getValue());
3255 m_TemperatureElapsedTimer.restart();
3256 SetTemperature(nextTemperature);
3267 auto pos = std::find_if(m_CaptureFormats.begin(), m_CaptureFormats.end(), [format](
auto & oneFormat)
3269 return format.name == oneFormat.name;
3271 if (pos != m_CaptureFormats.end())
3275 auto count = CaptureFormatSP.size();
3276 CaptureFormatSP.resize(count + 1);
3278 const bool isOn = (format.
name == m_ConfigCaptureFormatName) || (m_ConfigCaptureFormatName.empty() && format.
isDefault);
3280 m_CaptureFormats.push_back(format);
3286bool CCD::SetCaptureFormat(uint8_t index)
const char * getDeviceName() const
INDI::PropertyText getText(const char *name) 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
char * getImageExtension()
bool finishFITSFile(int &status)
Finish any pending write to fits file.
float getPixelSizeY() const
getPixelSizeY Get vertical pixel size in microns.
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
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 extension
size_t * fitsMemorySizePointer()
uint8_t * getFrameBuffer()
getFrameBuffer Get raw frame buffer of the CCD chip.
void setExposureFailed()
setExposureFailed Alert the client that the exposure failed.
int getSubW() const
getSubW Get the width of the frame
int getFrameBufferSize() const
getFrameBufferSize Get allocated frame buffer size to hold the CCD image frame.
void ** fitsMemoryBlockPointer()
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.
fitsfile ** fitsFilePointer()
bool openFITSFile(uint32_t size, int &status)
openFITSFile Allocate memory buffer for internal FITS file structure and open
static constexpr const char * WCS_TAB
double m_InitialRampTemperature
virtual bool StartExposure(float duration)
Start exposing primary CCD chip.
INDI::PropertySwitch UploadSP
INDI::PropertySwitch WorldCoordSP
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.
double m_TemperatureStabilizationValue
INDI::PropertyNumber EqNP
Properties.
INDI::Timer m_TemperatureCheckTimer
static constexpr const char * GUIDE_CONTROL_TAB
Group Names.
INDI::PropertyNumber CCDRotationNP
virtual void activeDevicesUpdated()
activeDevicesUpdated Inform children that ActiveDevices property was updated so they can snoop on the...
INDI::PropertyNumber FastExposureCountNP
INDI::PropertyText ActiveDeviceTP
ActiveDeviceTP defines 4 devices the camera driver can listen to (snoop) for properties of interest s...
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.
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.
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...
INDI::PropertyNumber TemperatureNP
TemperatureNP Camera Temperature in Celcius.
virtual int SetTemperature(double temperature)
Set CCD temperature.
INDI::PropertyNumber ScopeInfoNP
INDI::PropertyNumber J2000EqNP
J200EqNP Snoop property to read the equatorial J2000 coordinates of the mount. ActiveDeviceTP defines...
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 FastExposureToggleSP
INDI::PropertySwitch EncodeFormatSP
Specifies Driver image encoding format (FITS, Native, JPG, ..etc)
virtual bool SetCaptureFormat(uint8_t index)
SetCaptureFormat Set Active Capture format.
virtual bool initProperties() override
Initialize 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.
INDI::PropertyText BayerTP
BayerTP Bayer pattern offset and type.
char exposureStartTime[MAXINDINAME]
virtual bool UpdateGuiderFrameType(CCDChip::CCD_FRAME fType)
CCD calls this function when Guide frame type is updated by the client.
INDI::PropertyText UploadSettingsTP
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual void checkTemperatureTarget()
checkTemperatureTarget Checks the current temperature against target temperature and calculates the n...
INDI::PropertyText FileNameTP
FileNameTP File name of locally-saved images. By default, images are uploaded to the client but when ...
std::vector< std::string > FilterNames
virtual void SetGuiderParams(int x, int y, int bpp, float xf, float yf)
Setup CCD parameters for guide head CCD. Child classes call this function to update CCD parameters.
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...
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. All 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.
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 parameters 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
INDI::ElapsedTimer m_TemperatureStabilizationTimer
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...
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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()
Initialize properties initial state and value. The child class must implement this function.
void syncDriverInfo()
syncDriverInfo sends the current driver information to the client.
uint32_t getDriverInterface() const
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Process the client newBLOB command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
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().
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void initProperties(const char *groupName)
Initialize guider properties. It is recommended to call this function within initProperties() of your...
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Call this function whenever client updates GuideNSNP or GuideWSP properties in the primary device....
void setDeviceName(const char *name)
void setState(IPState state)
WidgetView< T > * findWidgetByName(const char *name) const
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
const char * getName() const
bool isNameMatch(const char *otherName) const
bool snoop(XMLEle *root)
Inspect the root XML and snoop the property if it exists in the root element.
void setPermission(IPerm permission)
IPerm getPermission() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
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 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.
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 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 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.
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 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,...
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 ...
#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.
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
std::vector< uint8_t > buffer
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)
void replace_all(std::string &subject, const std::string &search, const std::string &replace)
int mkpath(std::string s, mode_t mode)
@ error
throw a parse_error exception in case of a tag
void uploadFile(const char *filename)
void addFITSKeywords(fitsfile *fptr, IMAGE_INFO *image_info)