Instrument Neutral Distributed Interface INDI  0.9
tutorial_three.c
Go to the documentation of this file.
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 }