USGS

Isis 3.0 Application Source Code Reference

Home

gllssical.cpp

Go to the documentation of this file.
00001 #include "Isis.h"
00002 #include "ProcessByLine.h"
00003 #include "Buffer.h"
00004 #include "SpecialPixel.h"
00005 #include "TextFile.h"
00006 #include "Camera.h"
00007 
00008 using namespace Isis;
00009 using namespace std;
00010 
00011 /**
00012  * Shutter file:
00013  * The shutter on the galileo SSI camera took from
00014  * about 1ms at the top of the camera to about 1.5ms
00015  * at the bottom due to friction. The shutter offset file in
00016  * isis 2 is rotated 90 degrees.
00017  *
00018  * Some of the values and equations in the program were verified
00019  *   using the book "In Orbit at Jupiter, Contributions of the Galileo
00020  *   Science Team," Section H Part I.
00021  */
00022 
00023 // Globals
00024 vector<double> weight;
00025 double scaleFactor0; // Entire scale factor that e is multiplied by
00026 double scaleFactor; // A1 or A2 in the equation, depending on units
00027 double dcScaleFactor;
00028 double exposureDuration;
00029 bool eightBitDarkCube; // Is the dark cube of type unsigned byte?
00030 bool iof; // Determines output units (i.e. I/F or radiance)
00031 double s1;
00032 double s2;
00033 double rsun;
00034 double cubeConversion;
00035 double gainConversion;
00036 
00037 void Calibrate(vector<Buffer *> &in, vector<Buffer *> &out);
00038 FileName FindDarkFile(Cube *icube);
00039 FileName FindGainFile(Cube *icube);
00040 FileName FindShutterFile(Cube *icube);
00041 FileName ReadWeightTable(Cube *icube);
00042 FileName GetScaleFactorFile();
00043 int getGainModeID(Cube *icube);
00044 void calculateScaleFactor0(Cube *icube, Cube *gaincube);
00045 
00046 void IsisMain() {
00047   // Initialize Globals
00048   weight.clear();
00049   dcScaleFactor = 0.0;
00050   exposureDuration = 0.0;
00051 
00052   UserInterface &ui = Application::GetUserInterface();
00053 
00054   // Set up our ProcessByLine
00055   ProcessByLine p;
00056   Cube *icube = p.SetInputCube("FROM");
00057 
00058   Isis::CubeAttributeInput inAtt1;
00059   FileName darkFileName = FindDarkFile(icube);
00060   Cube *darkcube = p.SetInputCube(darkFileName.expanded(), inAtt1);
00061   dcScaleFactor = toDouble(darkcube->group("Instrument")["PicScale"][0]);
00062 
00063   Isis::CubeAttributeInput inAtt2;
00064   FileName gainFileName = FindGainFile(icube);
00065   Cube *gaincube = p.SetInputCube(gainFileName.expanded(), inAtt2);
00066 
00067   Isis::CubeAttributeInput inAtt3;
00068   p.SetInputCube(FindShutterFile(icube).expanded(), inAtt3, Isis::AllMatchOrOne);
00069 
00070   Cube *ocube = p.SetOutputCube("TO");
00071 
00072   (ui.GetString("UNITS") == "IOF") ? iof = true : iof = false;
00073   scaleFactor = ui.GetDouble("SCALE");
00074 
00075   if(ui.GetBoolean("BITWEIGHTING")) {
00076     ReadWeightTable(icube);
00077   }
00078   else {
00079     for(int i = 0; i < 256; i++) {
00080       weight.push_back(i);
00081     }
00082   }
00083 
00084   calculateScaleFactor0(icube, gaincube);
00085 
00086   exposureDuration = toDouble(icube->group("Instrument")["ExposureDuration"][0]) * 1000;
00087 
00088   if(darkcube->pixelType() == Isis::UnsignedByte) {
00089     eightBitDarkCube = true;
00090   }
00091   else {
00092     eightBitDarkCube = false;
00093   }
00094 
00095   p.StartProcess(Calibrate);
00096 
00097   PvlGroup calibrationLog("RadiometricCalibration");
00098   calibrationLog.AddKeyword(PvlKeyword("From", ui.GetFileName("FROM")));
00099 
00100   FileName shutterFileName = FindShutterFile(icube);
00101   calibrationLog.AddKeyword(PvlKeyword("DarkCurrentFile", darkFileName.originalPath() + "/" +
00102                                        darkFileName.name()));
00103   calibrationLog.AddKeyword(PvlKeyword("GainFile", gainFileName.originalPath() + "/" +
00104                                        gainFileName.name()));
00105   calibrationLog.AddKeyword(PvlKeyword("ShutterFile", shutterFileName.originalPath() + "/" +
00106                                        shutterFileName.name()));
00107   calibrationLog.AddKeyword(PvlKeyword("ScaleFactor", toString(scaleFactor)));
00108   calibrationLog.AddKeyword(PvlKeyword("OutputUnits", iof ? "I/F" : "Radiance"));
00109   if (iof) {
00110     calibrationLog.AddKeyword(PvlKeyword("S1", toString(s1), "I/F per Ft-Lambert"));
00111     calibrationLog.AddKeyword(PvlKeyword("RSUN", toString(rsun), "(Planet-Sun range)/5.2 A.U."));
00112     calibrationLog.AddKeyword(PvlKeyword("Scale", toString(scaleFactor), "I/F units per DN"));
00113     calibrationLog.AddKeyword(PvlKeyword("GC", toString(cubeConversion), "Cube gain conversion"));
00114     calibrationLog.AddKeyword(PvlKeyword("GG", toString(gainConversion), "Gain file gain conversion"));
00115     calibrationLog.AddKeyword(PvlKeyword("IOF-SCALE0", toString(scaleFactor0), "(S1/Scale)*(GC/GG)/RSUN**2"));
00116   }
00117   else {
00118     calibrationLog.AddKeyword(PvlKeyword("S2", toString(s2), "Nanowatts per Ft-Lambert"));
00119     calibrationLog.AddKeyword(PvlKeyword("Scale", toString(scaleFactor),
00120                                          "Nanowatts/cm**2/steradian/nanometer/DN"));
00121     calibrationLog.AddKeyword(PvlKeyword("GC", toString(cubeConversion), "Cube gain conversion"));
00122     calibrationLog.AddKeyword(PvlKeyword("GG", toString(gainConversion), "Gain file gain conversion"));
00123     calibrationLog.AddKeyword(PvlKeyword("Radiance-SCALE0", toString(scaleFactor0), "(S2/Scale)*(GC/GG)"));
00124   }
00125 
00126   ocube->putGroup(calibrationLog);
00127   Application::Log(calibrationLog);
00128   p.EndProcess();
00129 }
00130 
00131 void Calibrate(vector<Buffer *> &in, vector<Buffer *> &out) {
00132   Buffer &inputFile   = *in[0];
00133   Buffer &darkFile    = *in[1];
00134   Buffer &gainFile    = *in[2];
00135   Buffer &shutterFile = *in[3];
00136   Buffer &outputFile  = *out[0];
00137 
00138   /*
00139    * Calculate this part of the radiometric correction equation:
00140    *
00141    * scaleFactor0 / (t-to)
00142    */
00143   double scale = scaleFactor0 / (exposureDuration - shutterFile[0]);
00144 
00145   for(int samp = 0; samp < inputFile.size(); samp++) {
00146     // Some shutter files are only a single sample. Others may match the number
00147     // of samples in the cube.
00148     int shutterIndex = (shutterFile.size() == 1) ? 0 : samp;
00149 
00150     // Don't do anything to special pixels.
00151     if(IsSpecial(inputFile[samp])) {
00152       outputFile[samp] = inputFile[samp];
00153       continue;
00154     }
00155 
00156     if(IsSpecial(darkFile[samp]) || IsSpecial(gainFile[samp]) || IsSpecial(shutterFile[shutterIndex])) {
00157       outputFile[samp] = Isis::Null;
00158       continue;
00159     }
00160 
00161     /*
00162      * Calculate this part of the equation:
00163      *   e = z(d - dc)
00164      */
00165     double dn = weight[(unsigned long)(inputFile[samp])];
00166 
00167     double dc;
00168     if (eightBitDarkCube) {
00169       dc = weight[(unsigned long)(darkFile[samp])];
00170     }
00171     else {
00172       dc = (1.0 / dcScaleFactor) * darkFile[samp];
00173     }
00174 
00175     double e = gainFile[samp] * (dn - dc);
00176     double r = e * scale;
00177 
00178     // The following behavior does not match the Isis 2 version of this app.
00179     // In the Isis 2 version, negative I/F were kept in the output, which
00180     // doesn't make sense.
00181     if(r >= 0 || !iof) {
00182       outputFile[samp] = r;
00183     }
00184     else {
00185       outputFile[samp] = Isis::Lrs;
00186     }
00187   }
00188 }
00189 
00190 FileName FindDarkFile(Cube *icube) {
00191   QString file = "$galileo/calibration/gll_dc.sav";
00192 
00193   TextFile darkFile(file);
00194   darkFile.SetComment("C");
00195   QString data;
00196 
00197   /**
00198    * The dark current table requires the following information to match:
00199    *   Mission,Frame Mode ID,Gain State ID,Frame Rate ID,Extended Exposure,Readout Mode,Image Number
00200    * So let's grab the information we need from the image labels first
00201    */
00202   int gainModeId = getGainModeID(icube);
00203 
00204   int frameRateId = 0;
00205   /**
00206    * Frame rate code
00207    * 1 = 2 1/3 (sec)
00208    * 2 = 8 2/3
00209    * 3 = 30 1/3
00210    * 4 = 60 2/3
00211    * 5 = 15 1/6
00212    */
00213   if((int)(toDouble(icube->group("Instrument")["FrameDuration"][0])) == 2) frameRateId = 1;
00214   if((int)(toDouble(icube->group("Instrument")["FrameDuration"][0])) == 8) frameRateId = 2;
00215   if((int)(toDouble(icube->group("Instrument")["FrameDuration"][0])) == 30) frameRateId = 3;
00216   if((int)(toDouble(icube->group("Instrument")["FrameDuration"][0])) == 60) frameRateId = 4;
00217   if((int)(toDouble(icube->group("Instrument")["FrameDuration"][0])) == 15) frameRateId = 5;
00218 
00219   int exposureTypeId = (icube->group("Instrument")["ExposureType"][0] == "NORMAL") ? 0 : 1;
00220 
00221   // We have what we need from the image label, now go through the text file that is our table line by line
00222   // looking for a match.
00223   while(darkFile.GetLine(data)) {
00224     data = data.simplified().trimmed();
00225 
00226     QStringList tokens = data.split(" ");
00227     QString mission = (tokens.count()? tokens.takeFirst() : "");
00228     if(mission != "GALILEO") {
00229       continue;
00230     }
00231 
00232     QString frameMode = (tokens.count()? tokens.takeFirst() : "");
00233     if(frameMode.at(0) != icube->group("Instrument")["FrameModeId"][0].at(0)) {
00234       continue;
00235     }
00236 
00237     QString gainState = (tokens.count()? tokens.takeFirst() : "");
00238     if(toInt(gainState) != gainModeId) {
00239       continue;
00240     }
00241 
00242     QString frameRate = (tokens.count()? tokens.takeFirst() : "");
00243     if(frameRateId != toInt(frameRate)) {
00244       continue;
00245     }
00246 
00247     QString tableExposureTypeId = (tokens.count()? tokens.takeFirst() : "");
00248     if(toInt(tableExposureTypeId) != exposureTypeId) {
00249       continue;
00250     }
00251 
00252     QString readout = (tokens.count()? tokens.takeFirst() : "");
00253     if(readout.at(0) != icube->group("Instrument")["ReadoutMode"][0].at(0)) {
00254       continue;
00255     }
00256 
00257     int minImageNum = toInt(tokens.takeFirst());
00258     int maxImageNum = toInt(tokens.takeFirst());
00259 
00260     int imageNumber = (int)(toDouble(icube->group("Instrument")["SpacecraftClockStartCount"]) * 100 + 0.5);
00261     QString telemetry = icube->group("Instrument")["TelemetryFormat"][0];
00262     if(imageNumber > 99757701 && imageNumber < 159999999) {
00263       if((telemetry == "AI8" && (gainState == "1" || gainState == "2")) ||
00264           (telemetry == "IM4" && (gainState == "3" || gainState == "4"))) {
00265         imageNumber = 160000001;
00266       }
00267       else {
00268         imageNumber = 1;
00269       }
00270     }
00271 
00272     if(imageNumber < minImageNum || imageNumber > maxImageNum) {
00273       continue;
00274     }
00275 
00276     // By process of elimination, we found the dark current file successfully. Return it.
00277     return FileName("$galileo/calibration/darkcurrent/" + tokens.takeFirst() + ".cub");
00278   }
00279 
00280   throw IException(IException::Unknown, "Dark current file could not be determined.", _FILEINFO_);
00281 }
00282 
00283 FileName FindGainFile(Cube *icube) {
00284   QString file = "$galileo/calibration/gll_gain.sav";
00285 
00286   TextFile gainFile(file);
00287   gainFile.SetComment("C");
00288   QString data;
00289 
00290   int imageNumber = (int)((double)icube->group("Instrument")["SpacecraftClockStartCount"] * 100 + 0.5);
00291 
00292   while(gainFile.GetLine(data)) {
00293     data = data.simplified().trimmed();
00294 
00295     QStringList tokens = data.split(" ");
00296 
00297     QString mission = tokens.count()? tokens.takeFirst() : "";
00298     if(mission != "GALILEO") {
00299       continue;
00300     }
00301 
00302     /**
00303      * Filter codes
00304      * 0=clear,1=green,2=red,3=violet,4=7560,5=9680,6=7270,7=8890
00305      */
00306     QString filter = icube->group("BandBin")["FilterNumber"][0];
00307     if(filter != tokens.takeFirst()) {
00308       continue;
00309     }
00310 
00311     QString frameMode = tokens.count()? tokens.takeFirst() : "";
00312     if(frameMode.at(0) != icube->group("Instrument")["FrameModeId"][0].at(0)) {
00313       continue;
00314     }
00315 
00316     int minImageNum = toInt(tokens.takeFirst());
00317     int maxImageNum = toInt(tokens.takeFirst());
00318     if(imageNumber < minImageNum || imageNumber > maxImageNum) {
00319       continue;
00320     }
00321 
00322     return FileName("$galileo/calibration/gain/" + tokens.takeFirst() + ".cub");
00323   }
00324 
00325   throw IException(IException::Unknown, "Gain file could not be determined.", _FILEINFO_);
00326 }
00327 
00328 FileName ReadWeightTable(Cube *icube) {
00329   QString file = "$galileo/calibration/weightTables_v???.sav";
00330 
00331   FileName weightFile(file);
00332   weightFile = weightFile.highestVersion();
00333   Pvl weightTables(weightFile.expanded());
00334   QString group = QString("FrameMode") + icube->group("Instrument")["FrameModeId"][0].at(0);
00335   PvlGroup &frameGrp = weightTables.FindGroup(group);
00336   QString keyword = QString("GainState") + ((getGainModeID(icube) < 3) ? QString("12") : QString("34"));
00337 
00338   for(int i = 0; i < frameGrp[keyword].Size(); i++) {
00339     weight.push_back(toDouble(frameGrp[keyword][i]));
00340   }
00341 
00342   return weightFile;
00343 }
00344 
00345 int getGainModeID(Cube *icube) {
00346   int gainModeId = 0;
00347   /**
00348    * Gain mode ID code
00349    * 1 = 400,000
00350    * 2 = 100,000
00351    * 3 = 40,000
00352    * 4 = 10,000
00353    */
00354   if((int)toDouble(icube->group("Instrument")["GainModeId"][0]) == 4E5) {
00355     gainModeId = 1;
00356   }
00357   else if((int)toDouble(icube->group("Instrument")["GainModeId"][0]) == 1E5) {
00358     gainModeId = 2;
00359   }
00360   else if((int)toDouble(icube->group("Instrument")["GainModeId"][0]) == 4E4) {
00361     gainModeId = 3;
00362   }
00363   else if((int)toDouble(icube->group("Instrument")["GainModeId"][0]) == 1E4) {
00364     gainModeId = 4;
00365   }
00366   else {
00367     throw IException(IException::Unknown,
00368                      "Invalid value for Gain Mode ID [" +
00369                      icube->group("Instrument")["GainModeId"][0] +
00370                      "].", _FILEINFO_);
00371   }
00372 
00373   return gainModeId;
00374 }
00375 
00376 /*
00377  * Calculates scaleFactor0, which is:
00378  *
00379  *         S1       K
00380  *      -------- * --- (D/5.2)**2
00381  *         A1       Ko
00382  *
00383  * if output units are in I/F, or:
00384  *
00385  *         S2       K
00386  *      -------- * ---
00387  *         A2       Ko
00388  *
00389  * if output units are in radiance.
00390  */
00391 void calculateScaleFactor0(Cube *icube, Cube *gaincube) {
00392   Pvl conversionFactors(GetScaleFactorFile().expanded());
00393   PvlKeyword fltToRef, fltToRad;
00394 
00395   for(int grp = 0; grp < conversionFactors.Groups(); grp++) {
00396     PvlGroup currGrp = conversionFactors.Group(grp);
00397 
00398     // Match target name
00399     if(currGrp.HasKeyword("TargetName")) {
00400       if(!icube->group("Archive")["CalTargetCode"][0].startsWith(currGrp["TargetName"][0])) {
00401         continue;
00402       }
00403     }
00404 
00405     // Match MinimumEncounter
00406     if(currGrp.HasKeyword("MinimumTargetName")) {
00407       try {
00408         if((int)currGrp["MinimumTargetName"] >
00409             (int)toInt(icube->group("Archive")["CalTargetCode"][0].mid(0, 2))) {
00410           continue;
00411         }
00412       }
00413       catch(IException &) {
00414         continue;
00415       }
00416     }
00417 
00418     fltToRef = currGrp["FloatToRef"];
00419     fltToRad = currGrp["FloatToRad"];
00420   }
00421 
00422   int filterNumber = toInt(icube->group("BandBin")["FilterNumber"][0]);
00423 
00424   if(fltToRef.Size() == 0) {
00425     throw IException(IException::Unknown,
00426                      "Unable to find matching reflectance and radiance values for target [" +
00427                      icube->group("Instrument")["TargetName"][0] + "] in [" +
00428                      GetScaleFactorFile().expanded() + "]",
00429                      _FILEINFO_);
00430   }
00431 
00432   s1 = toDouble(fltToRef[filterNumber]);
00433   s2 = toDouble(fltToRad[filterNumber]);
00434   cubeConversion = toDouble( conversionFactors["GainRatios"][getGainModeID(icube)-1]);
00435   gainConversion = toDouble(conversionFactors["GainRatios"][getGainModeID(gaincube)-1]);
00436 
00437   if (iof) {
00438     Camera *cam = icube->camera();
00439     bool camSuccess = cam->SetImage(icube->sampleCount() / 2, icube->lineCount() / 2);
00440 
00441     if(!camSuccess) {
00442       throw IException(IException::Unknown,
00443                        "Unable to calculate the Solar Distance on [" +
00444                        icube->fileName() + "]", _FILEINFO_);
00445     }
00446 
00447 
00448     rsun = cam->SolarDistance() / 5.2;
00449     /*
00450      * We are calculating I/F, so scaleFactor0 is:
00451      *
00452      *         S1       K
00453      *      -------- * --- (D/5.2)**2
00454      *         A1       Ko
00455      */
00456     scaleFactor0 = (s1 * (cubeConversion / gainConversion) * pow(rsun, 2)) / (scaleFactor);
00457   }
00458   else {
00459     /*
00460      * We are calculating radiance, so scaleFactor0 is:
00461      *
00462      *         S2       K
00463      *      -------- * ---
00464      *         A2       Ko
00465      */
00466     scaleFactor0 = (s2 / scaleFactor) * (cubeConversion / gainConversion);
00467   }
00468 }
00469 
00470 FileName GetScaleFactorFile() {
00471   QString file = "$galileo/calibration/conversionFactors_v???.sav";
00472   FileName scaleFactor(file);
00473   scaleFactor = scaleFactor.highestVersion();
00474   return scaleFactor;
00475 }
00476 
00477 FileName FindShutterFile(Cube *icube) {
00478   QString file = "$galileo/calibration/shutter/calibration.so02";
00479   file += icube->group("Instrument")["FrameModeId"][0].at(0);
00480   file += ".cub";
00481   FileName shutterFile(file);
00482   return shutterFile;
00483 }