|
Instrument Neutral Distributed Interface INDI
0.9
|
00001 /* 00002 INDI Developers Manual 00003 Tutorial #3 00004 00005 "Simple CCD Simulator" 00006 00007 In this tutorial, we employ a BLOB property to send a sample FITS file to the client. 00008 00009 Refer to README, which contains instruction on how to build this driver, and use it 00010 with an INDI-compatible client. 00011 00012 */ 00013 00019 #include <stdio.h> 00020 #include <stdlib.h> 00021 #include <string.h> 00022 #include <stdarg.h> 00023 #include <math.h> 00024 #include <unistd.h> 00025 #include <time.h> 00026 #include <fcntl.h> 00027 #include <signal.h> 00028 #include <errno.h> 00029 #include <sys/stat.h> 00030 #include <sys/types.h> 00031 #include <sys/socket.h> 00032 #include <netinet/in.h> 00033 #include <netdb.h> 00034 #include <zlib.h> 00035 00036 /* INDI Core headers */ 00037 #include "indidevapi.h" 00038 #include "indicom.h" 00039 #include "eventloop.h" 00040 #include "base64.h" 00041 00042 #define mydev "CCD Simulator" 00043 #define COMM_GROUP "Main Control" 00044 00045 /* Handy macro */ 00046 #define currentTemp TemperatureN[0].value 00047 00048 void uploadFile(const char* filename); 00049 void ISPoll(void * p); 00050 00051 static double targetTemp = 0; 00052 00053 /*INDI controls */ 00054 00055 /* Connect/Disconnect */ 00056 static ISwitch PowerS[] = {{"CONNECT" , "Connect" , ISS_OFF, 0, 0},{"DISCONNECT", "Disconnect", ISS_ON, 0, 0}}; 00057 static ISwitchVectorProperty PowerSP = { mydev, "CONNECTION" , "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE, PowerS, NARRAY(PowerS), "", 0}; 00058 00059 /* Exposure time */ 00060 static INumber ExposeTimeN[] = {{ "CCD_EXPOSURE_VALUE", "Duration (s)", "%5.2f", 0., 36000., .5, 1., 0, 0, 0}}; 00061 static INumberVectorProperty ExposeTimeNP = { mydev, "CCD_EXPOSURE_REQUEST", "Expose", COMM_GROUP, IP_RW, 60, IPS_IDLE, ExposeTimeN, NARRAY(ExposeTimeN), "", 0}; 00062 00063 /* Temperature control */ 00064 static INumber TemperatureN[] = { {"CCD_TEMPERATURE_VALUE", "Temperature", "%+06.2f", -30., 40., 1., 0., 0, 0, 0}}; 00065 static INumberVectorProperty TemperatureNP = { mydev, "CCD_TEMPERATURE", "Temperature (C)", COMM_GROUP, IP_RW, 60, IPS_IDLE, TemperatureN, NARRAY(TemperatureN), "", 0}; 00066 00067 /* BLOB for sending image */ 00068 static IBLOB imagePrimaryB = {"CCD1", "Primary", "", 0, 0, 0, 0, 0, 0, 0}; 00069 static IBLOBVectorProperty imagePrimaryBP = {mydev, "CCD1", "", COMM_GROUP, 00070 IP_RO, 0, IPS_IDLE, &imagePrimaryB, 1, "", 0}; 00071 00072 static IBLOB imageGuideB = {"CCD2", "Guide", "", 0, 0, 0, 0, 0, 0, 0}; 00073 static IBLOBVectorProperty imageGuideBP = {mydev, "CCD2", "", COMM_GROUP, 00074 IP_RO, 0, IPS_IDLE, &imageGuideB, 1, "", 0}; 00075 00076 void ISGetProperties (const char *dev) 00077 { 00078 00079 if (dev && strcmp (mydev, dev)) 00080 return; 00081 00082 /* COMM_GROUP */ 00083 IDDefSwitch(&PowerSP, NULL); 00084 IDDefNumber(&ExposeTimeNP, NULL); 00085 IDDefNumber(&TemperatureNP, NULL); 00086 IDDefBLOB(&imagePrimaryBP, NULL); 00087 IDDefBLOB(&imageGuideBP, NULL); 00088 00089 IEAddTimer(1000, ISPoll, NULL); 00090 00091 } 00092 00093 /* Note that we must define ISNewBLOB and ISSnoopDevice even if we don't use them, otherwise, the driver will NOT compile */ 00094 void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) {} 00095 void ISSnoopDevice (XMLEle *root) {} 00096 00097 00098 void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) 00099 { 00100 /* ignore if not ours */ 00101 if (dev && strcmp (dev, mydev)) 00102 return; 00103 00104 /* Connection */ 00105 if (!strcmp (name, PowerSP.name)) 00106 { 00107 IUUpdateSwitch(&PowerSP, states, names, n); 00108 PowerSP.s = IPS_OK; 00109 if (PowerS[0].s == ISS_ON) 00110 IDSetSwitch(&PowerSP, "CCD Simulator is online."); 00111 else 00112 IDSetSwitch(&PowerSP, "CCD Simulator is offline."); 00113 return; 00114 } 00115 00116 } 00117 00118 void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n) 00119 { 00120 /* ignore if not ours */ 00121 if (dev && strcmp (mydev, dev)) 00122 return; 00123 00124 /* suppress warning */ 00125 n=n; dev=dev; name=name; names=names; texts=texts; 00126 00127 } 00128 00129 00130 void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n) 00131 { 00132 00133 /* Exposure time */ 00134 if (!strcmp (ExposeTimeNP.name, name)) 00135 { 00136 00137 /* Let's make sure that our simulator is online */ 00138 if (PowerS[0].s != ISS_ON) 00139 { 00140 ExposeTimeNP.s = IPS_IDLE; 00141 IDSetNumber(&ExposeTimeNP, "CCD Simulator is offline."); 00142 return; 00143 } 00144 00145 IDLog("Sending BLOB FITS...\n"); 00146 00147 ExposeTimeNP.s = IPS_BUSY; 00148 ExposeTimeN[0].value = values[0]; 00149 00150 return; 00151 } 00152 00153 /* Temperature */ 00154 if (!strcmp (TemperatureNP.name, name)) 00155 { 00156 00157 /* Let's make sure that our simulator is online */ 00158 if (PowerS[0].s != ISS_ON) 00159 { 00160 TemperatureNP.s = IPS_IDLE; 00161 IDSetNumber(&TemperatureNP, "CCD Simulator is offline"); 00162 return; 00163 } 00164 00165 targetTemp = values[0]; 00166 00167 /* If the requested temperature is the current CCD chip temperature, then return */ 00168 if (targetTemp == TemperatureN[0].value) 00169 { 00170 TemperatureNP.s = IPS_OK; 00171 IDSetNumber(&TemperatureNP, NULL); 00172 return; 00173 } 00174 00175 /* Otherwise, set property state to busy */ 00176 TemperatureNP.s = IPS_BUSY; 00177 IDSetNumber(&TemperatureNP, "Setting CCD temperature to %g", targetTemp); 00178 00179 return; 00180 } 00181 00182 } 00183 00184 /* Open a data file, compress its content, and send it via our BLOB property */ 00185 void uploadFile(const char* filename) 00186 { 00187 FILE * fitsFile; 00188 unsigned char *fitsData, *compressedData; 00189 int r=0; 00190 unsigned int i =0, nr = 0; 00191 uLongf compressedBytes=0; 00192 uLong totalBytes; 00193 struct stat stat_p; 00194 00195 /* #1 Let's get the file size */ 00196 if ( -1 == stat (filename, &stat_p)) 00197 { 00198 IDLog(" Error occoured attempting to stat file.\n"); 00199 return; 00200 } 00201 00202 /* #2 Allocate memory for raw and compressed data */ 00203 totalBytes = stat_p.st_size; 00204 fitsData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes); 00205 compressedData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes + totalBytes / 64 + 16 + 3); 00206 00207 if (fitsData == NULL || compressedData == NULL) 00208 { 00209 IDLog("Error! low memory. Unable to initialize fits buffers.\n"); 00210 return; 00211 } 00212 00213 /* #3 Open the FITS file */ 00214 fitsFile = fopen(filename, "r"); 00215 00216 if (fitsFile == NULL) 00217 return; 00218 00219 /* #4 Read file from disk */ 00220 for (i=0; i < totalBytes; i+= nr) 00221 { 00222 nr = fread(fitsData + i, 1, totalBytes - i, fitsFile); 00223 00224 if (nr <= 0) 00225 { 00226 IDLog("Error reading temporary FITS file.\n"); 00227 return; 00228 } 00229 } 00230 00231 compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3; 00232 00233 /* #5 Compress raw data */ 00234 r = compress2(compressedData, &compressedBytes, fitsData, totalBytes, 9); 00235 if (r != Z_OK) 00236 { 00237 /* this should NEVER happen */ 00238 IDLog("internal error - compression failed: %d\n", r); 00239 return; 00240 } 00241 00242 /* #6 Send it */ 00243 00244 if (!strcmp(filename, "ngc1316o.fits")) 00245 { 00246 imagePrimaryB.blob = compressedData; 00247 imagePrimaryB.bloblen = compressedBytes; 00248 imagePrimaryB.size = totalBytes; 00249 strcpy(imagePrimaryB.format, ".fits.z"); 00250 00251 /* #7 Set BLOB state to Ok */ 00252 imagePrimaryBP.s = IPS_OK; 00253 IDSetBLOB (&imagePrimaryBP, NULL); 00254 } 00255 else 00256 { 00257 imageGuideB.blob = compressedData; 00258 imageGuideB.bloblen = compressedBytes; 00259 imageGuideB.size = totalBytes; 00260 strcpy(imageGuideB.format, ".fits.z"); 00261 00262 /* #7 Set BLOB state to Ok */ 00263 imageGuideBP.s = IPS_OK; 00264 IDSetBLOB (&imageGuideBP, NULL); 00265 } 00266 00267 /* #8 Set Exposure status to Ok */ 00268 ExposeTimeNP.s = IPS_OK; 00269 IDSetNumber(&ExposeTimeNP, "Sending FITS..."); 00270 00271 /* #9 Let's never forget to free our memory */ 00272 free (fitsData); 00273 free (compressedData); 00274 00275 } 00276 00277 /* Poll device status and update accordingly */ 00278 void ISPoll(void * p) 00279 { 00280 00281 /* We need to check the status of properties that we want to watch. */ 00282 00283 /* #1 CCD Exposure */ 00284 switch (ExposeTimeNP.s) 00285 { 00286 case IPS_IDLE: 00287 case IPS_OK: 00288 case IPS_ALERT: 00289 break; 00290 00291 /* If an exposure state is busy, decrement the exposure value until it's zero (done exposing). */ 00292 case IPS_BUSY: 00293 ExposeTimeN[0].value--; 00294 00295 /* Are we done yet? */ 00296 if (ExposeTimeN[0].value <= 0) 00297 { 00298 ExposeTimeN[0].value = 0; 00299 00300 /* Let's set the state of OK and report that to the client */ 00301 ExposeTimeNP.s = IPS_OK; 00302 00303 /* Upload a sample FITS file */ 00304 uploadFile("ngc1316o.fits"); 00305 uploadFile("x0cj010ct_d0h.fit"); 00306 00307 IDSetNumber(&ExposeTimeNP, NULL); 00308 break; 00309 } 00310 00311 IDSetNumber(&ExposeTimeNP, NULL); 00312 break; 00313 } 00314 00315 /* #2 CCD Temperature */ 00316 switch (TemperatureNP.s) 00317 { 00318 case IPS_IDLE: 00319 case IPS_OK: 00320 case IPS_ALERT: 00321 break; 00322 00323 case IPS_BUSY: 00324 /* If target temperature is higher, then increase current CCD temperature */ 00325 if (currentTemp < targetTemp) 00326 currentTemp++; 00327 /* If target temperature is lower, then decrese current CCD temperature */ 00328 else if (currentTemp > targetTemp) 00329 currentTemp--; 00330 /* If they're equal, stop updating */ 00331 else 00332 { 00333 TemperatureNP.s = IPS_OK; 00334 IDSetNumber(&TemperatureNP, "Target temperature reached."); 00335 00336 break; 00337 } 00338 00339 IDSetNumber(&TemperatureNP, NULL); 00340 break; 00341 } 00342 00343 /* Keep polling alive */ 00344 IEAddTimer (1000, ISPoll, NULL); 00345 00346 }
1.7.6.1