USGS

Isis 3.0 Application Source Code Reference

Home

cisscal.cpp

Go to the documentation of this file.
00001 #include "Isis.h"
00002 #include <algorithm>// sort, unique
00003 #include <cmath>
00004 #include <cstdlib>
00005 #include <iterator>//unique
00006 #include <iostream> // unique
00007 #include <QString>
00008 #include <sstream>
00009 #include <vector>
00010 #include "Brick.h"
00011 #include "Buffer.h"
00012 #include "Camera.h"
00013 #include "CisscalFile.h"
00014 #include "CissLabels.h"
00015 #include "Cube.h"
00016 #include "DarkCurrent.h"
00017 #include "FileName.h"
00018 #include "LeastSquares.h"
00019 #include "NumericalApproximation.h"
00020 #include "PolynomialUnivariate.h"
00021 #include "ProcessByLine.h"
00022 #include "Preference.h"
00023 #include "PvlGroup.h"
00024 #include "SpecialPixel.h"
00025 #include "Stretch.h"
00026 #include "Table.h"
00027 #include "UserInterface.h"
00028 #include "IException.h"
00029 #include "IString.h"
00030 
00031 using namespace Isis;
00032 using namespace std;
00033 
00034 // Working functions and parameters
00035 namespace gbl {
00036   //global methods
00037   void BitweightCorrect(Buffer &in);
00038   void Calibrate(vector<Buffer *> &in, vector<Buffer *> &out);
00039   void ComputeBias();
00040   void CopyInput(Buffer &in);
00041   void CreateBitweightStretch(FileName bitweightTable);
00042   FileName FindBitweightFile();
00043   vector<double> OverclockFit();
00044   void Linearize();
00045   void FindDustRingParameters();
00046   FileName FindFlatFile();
00047   void FindCorrectionFactors();
00048   void DNtoElectrons();
00049   void FindShutterOffset();
00050   void DivideByAreaPixel();
00051   void FindEfficiencyFactor(QString fluxunits);
00052   QString GetCalibrationDirectory(QString calibrationType);
00053 
00054   //global variables
00055   CissLabels *cissLab;
00056   Cube *incube;
00057   PvlGroup calgrp;
00058   Stretch stretch;
00059   int numberOfOverclocks;
00060   vector <double> bias;
00061   vector <vector <double> > bitweightCorrected;
00062   //dark subtraction variables
00063   vector <vector <double> > dark_DN;
00064   // flatfield variables
00065   FileName dustFile;
00066   FileName mottleFile;
00067   double strengthFactor;
00068   bool dustCorrection, mottleCorrection, flatCorrection;
00069   //DN to Flux variables
00070   double trueGain;
00071   bool divideByExposure;
00072   Brick *offset;
00073   double solidAngle;
00074   double opticsArea;
00075   double sumFactor;
00076   double efficiencyFactor;
00077   //correction factor variables
00078   double polarizationFactor;
00079   double correctionFactor;
00080 }
00081 
00082 void IsisMain() {
00083   // Initialize Globals
00084   UserInterface &ui = Application::GetUserInterface();
00085   gbl::cissLab = new CissLabels(ui.GetFileName("FROM"));
00086   gbl::incube = NULL;
00087   gbl::stretch.ClearPairs();
00088   gbl::numberOfOverclocks = 0;
00089   gbl::bias.clear();
00090   gbl::dustFile = "";
00091   gbl::mottleFile = "";
00092   gbl::strengthFactor = 1.0;
00093   gbl::dustCorrection = false;
00094   gbl::mottleCorrection = false;
00095   gbl::flatCorrection = false;
00096   gbl::trueGain = 1.0;
00097   gbl::divideByExposure = false;
00098   gbl::offset = NULL; // initialize pointer to null or 0?
00099   gbl::solidAngle = 1.0;
00100   gbl::opticsArea = 1.0;
00101   gbl::sumFactor  = 1.0;
00102   gbl::efficiencyFactor = 1.0;
00103   gbl::polarizationFactor = 1.0;
00104   gbl::correctionFactor = 1.0;
00105 
00106   // Set up our ProcessByLine objects
00107   // we will take 2 passes through the input cube
00108   ProcessByLine firstpass;
00109   ProcessByLine secondpass;
00110   // for the first pass, use the input cube.
00111   gbl::incube = firstpass.SetInputCube("FROM"); // CopyInput() or BitweightCorrect() parameter in
00112   // for the second pass, set input cube to "FROM" due to requirements of
00113   // ProcessByLine that there be at least 1 input buffer before setting the
00114   // output buffer, however this cube is not used in the Calibrate method,
00115   // instead the bitweightCorrected vector is used as the initial values before
00116   // the rest of the calibration steps are performed.
00117   gbl::incube = secondpass.SetInputCube("FROM"); // Calibrate() parameter in[0]
00118   // we need to set output cube at the beginning of the program to check right
00119   // away for CubeCustomization IsisPreference and throw an error, if necessary.
00120   Cube *outcube = secondpass.SetOutputCube("TO"); // Calibrate() parameter out[0]
00121 
00122   // resize 2dimensional vectors
00123   gbl::bitweightCorrected.resize(gbl::incube->sampleCount());
00124   gbl::dark_DN.resize(gbl::incube->sampleCount());
00125   for(unsigned int i = 0; i < gbl::bitweightCorrected.size(); i++) {
00126     gbl::bitweightCorrected[i].resize(gbl::incube->lineCount());
00127     gbl::dark_DN[i].resize(gbl::incube->lineCount());
00128   }
00129 
00130   // Add the radiometry group
00131   gbl::calgrp.SetName("Radiometry");
00132 
00133   // The first ProcessByLine pass will either compute bitweight values or copy input values
00134 
00135   // BITWEIGHT CORRECTION
00136   gbl::calgrp += PvlKeyword("BitweightCorrectionPerformed", "Yes");
00137   gbl::calgrp.FindKeyword("BitweightCorrectionPerformed").AddComment("Bitweight Correction Parameters");
00138   // Bitweight correction is not applied to Lossy-compressed or Table-converted images
00139   if(gbl::cissLab->CompressionType() == "Lossy") {
00140     gbl::calgrp.FindKeyword("BitweightCorrectionPerformed").SetValue("No: Lossy compressed");
00141     gbl::calgrp += PvlKeyword("BitweightFile", "Not applicable: No bitweight correction");
00142     firstpass.Progress()->SetText("Lossy compressed: skip bitweight correction as insignificant.\nCopying input image...");
00143     firstpass.StartProcess(gbl::CopyInput);
00144     firstpass.EndProcess();
00145   }
00146   else if(gbl::cissLab->DataConversionType() == "Table") {
00147     gbl::calgrp.FindKeyword("BitweightCorrectionPerformed").SetValue("No: Table converted");
00148     gbl::calgrp += PvlKeyword("BitweightFile", "Not applicable: No bitweight correction");
00149     firstpass.Progress()->SetText("Table converted: skip bitweight correction as insignificant.\nCopying input image...");
00150     firstpass.StartProcess(gbl::CopyInput);
00151     firstpass.EndProcess();
00152   }
00153   // Skip bitweight correction for GainState==0, there is no data for this case,
00154   // see ground calibration report 5.1.9 Uneven Bit Weighting
00155   else if(gbl::cissLab->GainState() == 0) {
00156     gbl::calgrp.FindKeyword("BitweightCorrectionPerformed").SetValue("No: No bitweight calibration file for GainState 0.");
00157     gbl::calgrp += PvlKeyword("BitweightFile", "Not applicable: No bitweight correction.");
00158     firstpass.Progress()->SetText("No bitweight calibration file for GainState 0: skip bitweight correction.\nCopying input image...");
00159     firstpass.StartProcess(gbl::CopyInput);
00160     firstpass.EndProcess();
00161   }
00162   else { // Bitweight correction
00163     FileName bitweightFile = gbl::FindBitweightFile();
00164     if(!bitweightFile.fileExists()) { // bitweight file not found, stop calibration
00165       throw IException(IException::Io,
00166                        "Unable to calibrate image. BitweightFile ***"
00167                        + bitweightFile.expanded() + "*** not found.", _FILEINFO_);
00168     }
00169     else {
00170       gbl::calgrp += PvlKeyword("BitweightFile", bitweightFile.expanded());
00171       gbl::CreateBitweightStretch(bitweightFile);
00172       firstpass.Progress()->SetText("Computing bitweight correction...");
00173       firstpass.StartProcess(gbl::BitweightCorrect);
00174       firstpass.EndProcess();
00175     }
00176   }// THIS ENDS FIRST PROCESS
00177 
00178   // Compute global values needed for the rest of the calibration steps
00179 
00180   //Subtract bias (debias)
00181   gbl::ComputeBias();
00182 
00183   //Dark current subtraction
00184   try {
00185     DarkCurrent dark(*gbl::cissLab);
00186     gbl::dark_DN = dark.ComputeDarkDN();
00187     gbl::calgrp += PvlKeyword("DarkSubtractionPerformed", "Yes");
00188     gbl::calgrp.FindKeyword("DarkSubtractionPerformed").AddComment("Dark Current Subtraction Parameters");
00189     gbl::calgrp += PvlKeyword("DarkParameterFile", dark.DarkParameterFile().expanded());
00190     if(gbl::cissLab->NarrowAngle()) {
00191       gbl::calgrp += PvlKeyword("BiasDistortionTable", dark.BiasDistortionTable().expanded());
00192     }
00193     else {
00194       gbl::calgrp += PvlKeyword("BiasDistortionTable", "ISSWA: No bias distortion table used");
00195     }
00196   }
00197   catch(IException &e) { // cannot perform dark current, stop calibration
00198     e.print();
00199     throw IException(e, IException::Unknown,
00200                      "Unable to calibrate image. Dark current calculations failed.",
00201                      _FILEINFO_);
00202   }
00203 
00204   //Linearity Correction
00205   gbl::Linearize();
00206 
00207   //Dust Ring Correction
00208   gbl::FindDustRingParameters();
00209   //Flat Field Correction
00210   FileName flatFile = gbl::FindFlatFile();
00211 
00212   //DN to Flux Correction
00213   gbl::DNtoElectrons();
00214   gbl::FindShutterOffset();
00215   gbl::DivideByAreaPixel();
00216   gbl::FindEfficiencyFactor(ui.GetString("UNITS"));
00217 
00218   //Correction Factor
00219   // Set the remaining necessary input cube files for second pass
00220   CubeAttributeInput att;
00221   gbl::FindCorrectionFactors();
00222   if(gbl::flatCorrection) { // Calibrate() parameter in[1]
00223     secondpass.SetInputCube(flatFile.expanded(), att);
00224   }
00225   if(gbl::dustCorrection) { // Calibrate() parameter in[2]
00226     secondpass.SetInputCube(gbl::dustFile.expanded(), att);
00227   }
00228   if(gbl::mottleCorrection) { // Calibrate() parameter in[3]
00229     secondpass.SetInputCube(gbl::mottleFile.expanded(), att);
00230   }
00231   // this pass will call the Calibrate method
00232   secondpass.Progress()->SetText("Calibrating image...");
00233   outcube->putGroup(gbl::calgrp);
00234   secondpass.StartProcess(gbl::Calibrate);
00235   secondpass.EndProcess();
00236   gbl::calgrp.Clear();
00237   return;
00238 
00239 } //END MAIN
00240 
00241 /**
00242  * This calibration method runs through all calibration steps.
00243  * It takes a vector of input buffers that contains the
00244  * input image and, if needed, the flat field image, the
00245  * dustring correction image, and the mottle correction image.
00246  * The vector of output buffers will only contain one element:
00247  * the output image.
00248  *
00249  * @param in Vector of pointers to input buffers for the second
00250  *           process in IsisMain()
00251  * @param out Vector of pointers to output buffer.
00252  * @internal
00253  *   @history 2008-11-05 Jeannie Walldren - Original version
00254  *   @history 2009-05-27 Jeannie Walldren - Added polarization
00255  *            factor correction.  Updated ConstOffset value per
00256  *            new idl cisscal version, 3.6.
00257  */
00258 void gbl::Calibrate(vector<Buffer *> &in, vector<Buffer *> &out) {
00259   Buffer *flat = 0;
00260   Buffer *dustCorr = 0;
00261   Buffer *mottleCorr = 0;
00262   if(gbl::flatCorrection) {
00263     flat = in[1];
00264   }
00265   if(gbl::dustCorrection) {
00266     dustCorr = in[2];
00267     if(gbl::mottleCorrection) {
00268       mottleCorr = in[3];
00269     }
00270   }
00271   Buffer &outLine = *out[0];
00272   //get line index of output
00273   int lineIndex = outLine.Line() - 1;
00274   for(unsigned int sampIndex = 0; sampIndex < gbl::bitweightCorrected.size(); sampIndex++) {
00275     if(IsValidPixel(gbl::bitweightCorrected[sampIndex][lineIndex])) {
00276 
00277       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00278       // STEP 1) set output to bitweight corrected values
00279       outLine[sampIndex] = bitweightCorrected[sampIndex][lineIndex];
00280 
00281       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00282       // STEP 2)  remove bias (debias)
00283       if(gbl::numberOfOverclocks) {
00284         outLine[sampIndex] = outLine[sampIndex] - gbl::bias[lineIndex];
00285       }
00286       else {
00287         outLine[sampIndex] = outLine[sampIndex] - gbl::bias[0];
00288       }
00289 
00290       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00291       // idl cisscal step "REMOVE 2-HZ NOISE" skipped
00292       // -- this is more of a filter than calibration
00293 
00294       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00295       // STEP 3) remove dark current
00296       outLine[sampIndex] = outLine[sampIndex] - gbl::dark_DN[sampIndex][lineIndex];
00297 
00298       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00299       // idl cisscal step "ANTI-BLOOMING CORRECTION" skipped
00300       // -- this is more of a filter than calibration
00301 
00302       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00303       // STEP 4)  linearity correction (linearize)
00304       if(outLine[sampIndex] < 0) {
00305         outLine[sampIndex] = (outLine[sampIndex]) * (gbl::stretch.Map(0));
00306       }
00307       else {
00308         outLine[sampIndex] = (outLine[sampIndex]) * (gbl::stretch.Map((int) outLine[sampIndex]));
00309       }
00310 
00311       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00312       // STEP 5)  flatfield correction
00313       // 5a1: dust ring correction
00314       if(gbl::dustCorrection) {
00315         outLine[sampIndex] = (outLine[sampIndex]) * ((*dustCorr)[sampIndex]);
00316         // 5a2: mottle correction
00317         if(gbl::mottleCorrection) {
00318           outLine[sampIndex] = (outLine[sampIndex]) * (1 - ((gbl::strengthFactor) * ((*mottleCorr)[sampIndex]) / 1000.0));
00319         }
00320       }
00321       // 5b: divide by flats
00322       if(gbl::flatCorrection) {
00323         outLine[sampIndex] = outLine[sampIndex] / ((*flat)[sampIndex]);
00324       }
00325 
00326       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00327       // STEP 6)  convert DN to flux
00328       // 6a DN to Electrons
00329       outLine[sampIndex] = outLine[sampIndex] * gbl::trueGain;
00330       // 6b Divide By Exposure Time
00331       //  JPL confirm that these values must be subtracted thus:
00332       if(gbl::divideByExposure) {
00333         double exposureTime = gbl::cissLab->ExposureDuration() - (*gbl::offset)[gbl::offset->Index(sampIndex+1, 1, 1)];
00334         // IDL documentation:
00335         //  Need to develop way to subtract constant offset discussed in
00336         //  section 4.3 of Ground Calibration Report. Right now just use 1 ms
00337         //  for all cases.
00338         //  CORRECTION: New analysis of Vega images points to a value more like
00339         //  2.85 ms (correct to within about 0.05 ms for the NAC, less certain
00340         //  for the WAC. Use this until better data available. (12/1/2005 - BDK)
00341         //  UPDATE: Used azimuthal ring scans to pin down WAC to around 1.8 ms.
00342         //  (1/18/2006 - BDK)
00343         double ConstOffset;
00344         if(gbl::cissLab->InstrumentId() == "ISSNA") {
00345           ConstOffset = 2.85;
00346         }
00347         else {
00348           ConstOffset = 1.8;
00349         }
00350         exposureTime = exposureTime - ConstOffset;
00351         outLine[sampIndex] = outLine[sampIndex] * 1000 / exposureTime;  // 1000 to scale ms to seconds
00352       }
00353       // 6c Divide By Area Pixel
00354       outLine[sampIndex] = outLine[sampIndex] * gbl::sumFactor / (gbl::solidAngle * gbl::opticsArea);
00355       // 6d Divide By Efficiency
00356       outLine[sampIndex] = outLine[sampIndex] / gbl::efficiencyFactor;
00357 
00358       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00359       // STEP 7)  correction factors
00360       outLine[sampIndex] = outLine[sampIndex] / (gbl::correctionFactor * gbl::polarizationFactor);
00361 
00362       ///////////////////////////////////////////////////////////////////////////////////////////////////////////
00363     }
00364     else {
00365       outLine[sampIndex] = bitweightCorrected[sampIndex][lineIndex];
00366     }
00367   }
00368   return;
00369 }
00370 
00371 
00372 //=====4 Bitweight Methods=======================================================================//
00373 // These methods are modelled after IDL CISSCAL's cassimg_bitweightcorrect.pro
00374 
00375 /**
00376  * The purpose of this method is to copy the input to output if
00377  * no bitweight correction occurs.
00378  *
00379  * @param in Input buffer for the first process in IsisMain()
00380  * @internal
00381  *   @history 2008-11-05 Jeannie Walldren - Original version
00382  *   @history 2008-12-22 Jeannie Walldren - Fixed bug in calls
00383  *            to resize() method.
00384  *   @history 2009-01-26 Jeannie Walldren - Moved resizing of
00385  *            2-dimensional vectors to main method
00386  */
00387 void gbl::CopyInput(Buffer &in) {
00388   // find line index
00389   int lineIndex = in.Line() - 1;
00390   for(int sampIndex = 0; sampIndex < in.size(); sampIndex++) {
00391     // assign input value to image vector
00392     gbl::bitweightCorrected[sampIndex][lineIndex] = in[sampIndex];
00393   }
00394   return;
00395 }
00396 
00397 /**
00398  * This method is modelled after IDL CISSCAL's
00399  * cassimg_bitweightcorrect.pro.  The purpose is to correct the
00400  * image for uneven bit weights.  This is done using one of
00401  * several tables developed from the ground calibration
00402  * exercises table depends on InstrumentId, GainModeId, and
00403  * OpticsTemperature.
00404  *
00405  * @param in Input buffer for the first process in IsisMain()
00406  * @internal
00407  *   @history 2008-11-05 Jeannie Walldren - Original version
00408  *   @history 2009-01-26 Jeannie Walldren - Moved resizing of
00409  *            2-dimensional vectors to main method
00410  */
00411 void gbl::BitweightCorrect(Buffer &in) {
00412   // find line index
00413   int lineIndex = in.Line() - 1;
00414   // loop through samples of this line
00415   for(int sampIndex = 0; sampIndex < in.size(); sampIndex++) {
00416     // map bitweight corrected image output values to buffer input values
00417     if(IsValidPixel(in[sampIndex])) {
00418       gbl::bitweightCorrected[sampIndex][lineIndex] = gbl::stretch.Map(in[sampIndex]);
00419     }
00420     //Handle special pixels
00421     else {
00422       gbl::bitweightCorrected[sampIndex][lineIndex] = in[sampIndex];
00423     }
00424   }
00425   return;
00426 }
00427 
00428 
00429 /**
00430  * This method sets up the strech for the conversion from file.
00431  * It is used by the BitweightCorrect() method to map LUT
00432  * values.
00433  *
00434  * @param bitweightTable Name of the bitweight table for this
00435  *                       image.
00436  *
00437  * @internal
00438  *   @history 2008-11-05 Jeannie Walldren - Original version
00439  */
00440 void gbl::CreateBitweightStretch(FileName bitweightTable) {
00441   CisscalFile *stretchPairs = new CisscalFile(bitweightTable.expanded());
00442   // Create the stretch pairs
00443   double stretch1 = 0, stretch2;
00444   gbl::stretch.ClearPairs();
00445   for(int i = 0; i < stretchPairs->LineCount(); i++) {
00446     QString line;
00447     stretchPairs->GetLine(line);
00448     line = line.simplified().trimmed();
00449 
00450     QStringList tokens = line.split(QRegExp("[, ]"), QString::SkipEmptyParts);
00451     foreach (QString token, tokens) {
00452       stretch2 = toDouble(token);
00453       gbl::stretch.AddPair(stretch1, stretch2);
00454       stretch1 = stretch1 + 1.0;
00455     }
00456   }
00457   stretchPairs->Close();
00458   return;
00459 }
00460 
00461 /**
00462  * This method is modelled after IDL CISSCAL's
00463  * cassimg_bitweightcorrect.pro.  The purpose is to find the
00464  * look up table file name for this image.
00465  *
00466  *  The table to be used depends on:
00467  *    Camera    NAC or WAC
00468  *    GainState  1, 2 or 3 <=> GainModeId 95, 29, or 12 Optics temp.  -10, +5 or
00469  *     +25
00470  *
00471  * @return <B>FileName</B> Name of the bitweight file
00472  *
00473  * @internal
00474  *   @history 2008-11-05 Jeannie Walldren - Original version
00475  */
00476 FileName gbl::FindBitweightFile() {
00477   // Get the directory where the CISS bitweight directory is
00478   QString bitweightName;
00479 
00480   if(gbl::cissLab->NarrowAngle()) {
00481     bitweightName += "nac";
00482   }
00483   else {
00484     bitweightName += "wac";
00485   }
00486   QString gainState(toString(gbl::cissLab->GainState()));
00487   bitweightName = bitweightName + "g" + gainState;
00488 
00489   if(gbl::cissLab->FrontOpticsTemp() < -5.0) {
00490     bitweightName += "m10_bwt.tab";
00491   }
00492   else if(gbl::cissLab->FrontOpticsTemp() < 25.0) {
00493     bitweightName += "p5_bwt.tab";
00494   }
00495   else {
00496     bitweightName += "p25_bwt.tab";
00497   }
00498   return FileName(gbl::GetCalibrationDirectory("bitweight") + bitweightName);
00499 }
00500 //=====End Bitweight Methods=====================================================================//
00501 
00502 //=====2 Debias Methods============================================================================//
00503 //These methods are modelled after IDL CISSCAL's cassimg_debias.pro
00504 
00505 /**
00506  * This method is modelled after IDL CISSCAL's
00507  *  cassimg_debias.pro.  The purpose is to compute the bias
00508  *  (zero-exposure DN level of CCD chip) to be subtracted in the
00509  *  Calibrate() method.
00510  *  There are two ways to do this
00511  *      1. (DEFAULT) using overclocked pixel array taken out of
00512  *         binary line prefix
00513  *      2. subtract BiasMeanStrip value found in labels
00514  *
00515  *  @internal
00516  *   @history 2008-11-05 Jeannie Walldren - Original version
00517  *   @history 2009-05-27 Jeannie Walldren - Commented out table
00518  *            if-statement as done idl cisscal version 3.6.
00519  */
00520 void gbl::ComputeBias() {
00521   gbl::calgrp += PvlKeyword("BiasSubtractionPerformed", "Yes");
00522   gbl::calgrp.FindKeyword("BiasSubtractionPerformed").AddComment("Bias Subtraction Parameters");
00523 
00524   QString fsw(gbl::cissLab->FlightSoftwareVersion());
00525   double flightSoftwareVersion;
00526 
00527   if(fsw == "Unknown") {
00528     flightSoftwareVersion = 0.0;// cassimg_readlabels.pro sets this to 1.3, we treat this as 1.2???
00529   }
00530   else {
00531     flightSoftwareVersion = toDouble(fsw);
00532   }
00533   //  check overclocked pixels exist
00534   if(gbl::cissLab->CompressionType() != "Lossy") {
00535     if(flightSoftwareVersion < 1.3) { // (1.2=CAS-ISS2 or Unknown=0.0=CAS-ISS)
00536       gbl::numberOfOverclocks = 1;
00537     }
00538     else { //if(1.3=CAS-ISS3 or 1.4=CAS-ISS4)
00539       gbl::numberOfOverclocks = 2;
00540     }
00541     gbl::calgrp += PvlKeyword("BiasSubtractionMethod", "Overclock fit");
00542   }
00543   // otherwise overclocked pixels are invalid and must use bias strip mean where possible
00544   else { // overclocks array is corrupt for lossy images (see cassimg_readvic.pro)
00545 #if 0
00546     // 2009-04-27 Jeannie Walldren
00547     //   following code comment out in new idl cisscal version, 3.6:
00548     if(gbl::cissLab->DataConversionType() == "Table") { // Lossy + Table = no debias
00549       gbl::calgrp.FindKeyword("BiasSubtractionPerformed").SetValue("No: Table converted and Lossy compressed");
00550       gbl::calgrp += PvlKeyword("BiasSubtractionMethod", "Not applicable: No bias subtraction");
00551       gbl::calgrp += PvlKeyword("NumberOfOverclocks", "Not applicable: No bias subtraction");
00552       gbl::bias.resize(1);
00553       gbl::bias[0] = 0.0;
00554       return;
00555     }
00556 #endif
00557 
00558     // according to SIS if 1.2 or 1.3 and Lossy, ignore bias strip mean - invalid data
00559     if(flightSoftwareVersion <= 1.3) { // Lossy + 1.2 or 1.3 = no debias
00560       gbl::calgrp.FindKeyword("BiasSubtractionPerformed").SetValue("No: Lossy compressed on CAS-ISS2 or CAS-ISS3");
00561       gbl::calgrp += PvlKeyword("BiasSubtractionMethod", "Not applicable: No bias subtraction");
00562       gbl::calgrp += PvlKeyword("NumberOfOverclocks", "Not applicable: No bias subtraction");
00563       gbl::bias.resize(1);
00564       gbl::bias[0] = 0.0;
00565       return;
00566     }
00567     gbl::calgrp += PvlKeyword("BiasSubtractionMethod", "Bias strip mean");
00568     gbl::numberOfOverclocks = 0;//overclocks array is corrupt for lossy images
00569   }
00570   //Choose bias subtraction method
00571   if(gbl::numberOfOverclocks) {        // use overclocked pixels as default
00572     gbl::bias = gbl::OverclockFit();
00573   }
00574   else {  // use BiasStripMean in image label if can't use overclock
00575     gbl::bias.resize(1);
00576     gbl::bias[0] = gbl::cissLab->BiasStripMean();
00577   }
00578   gbl::calgrp += PvlKeyword("NumberOfOverclocks", toString(gbl::numberOfOverclocks));
00579   return;
00580 }
00581 
00582 /**
00583  * This method is modelled after IDL CISSCAL's
00584  * cassimg_define.pro method, CassImg::OverclockAvg().  This
00585  * access function computes line-averaged overclocked pixel
00586  * values and returns a linear fit of these values.
00587  *
00588  * @return <B>vector <double> </B> Results of linear fit
00589  *
00590  *  @internal
00591  *   @history 2008-11-05 Jeannie Walldren - Original version
00592  */
00593 vector<double> gbl::OverclockFit() {
00594   vector< vector <double> > overclocks;
00595   //  Read overclocked info from table saved during ciss2isis
00596   //  table should have 3 columns:
00597   //     -col 3 is the "average" of the overclocked pixels
00598   //       -if there are 2 overclocks, columns 1 and 2 contain them
00599   //       -otherwise column 1 is all null and we use column 2
00600   Table overClkTable("ISS Prefix Pixels");
00601   gbl::incube->read(overClkTable);
00602   for(int i = 0; i < overClkTable.Records(); i++) {
00603     overclocks.push_back(overClkTable[i]["OverclockPixels"]);
00604   }
00605 
00606   vector<double> overclockfit, eqn, avg;
00607   PolynomialUnivariate poly(1);
00608   LeastSquares lsq(poly);
00609   //get overclocked averages
00610   for(unsigned int i = 0; i < overclocks.size(); i++) {
00611     avg.push_back(overclocks[i][2]);
00612   }
00613   if(avg[0] > 2 * avg[1]) {
00614     avg[0] = avg[1];
00615   }
00616 
00617   // initialize eqn
00618   eqn.push_back(0.0);
00619   for(unsigned int i = 0; i < avg.size(); i++) {
00620     //  if avg is a special pixel, we must change to integer values so we don't throw off the linear fit
00621     if(avg[i] == Isis::NULL2) {
00622       avg[i] = 0;
00623     }
00624     if(avg[i] == Isis::HIGH_REPR_SAT2) {
00625       if(gbl::cissLab->DataConversionType() == "Table") {
00626         avg[i] = 4095;
00627       }
00628       else {
00629         avg[i] = 255;
00630       }
00631     }
00632     eqn[0] = (double)(i + 1);
00633     //  add to least squares variable
00634     lsq.AddKnown(eqn, avg[i]);
00635   }
00636   // solve linear fit
00637   lsq.Solve(LeastSquares::QRD);
00638   for(unsigned int i = 0; i < overclocks.size(); i++) {
00639     eqn[0] = (double)(i + 1);
00640     overclockfit.push_back(lsq.Evaluate(eqn));
00641 
00642   }
00643   //return a copy of the vector of linear fitted overclocks
00644   // this will be used as the bias
00645   return overclockfit;
00646 }
00647 //=====End Debias Methods========================================================================//
00648 
00649 
00650 //=====Subtract Dark Methods=====================================================================//
00651 // THESE ARE CONTAINED IN THE CLASS DARKCURRENT
00652 //=====End Subtract Dark Methods=================================================================//
00653 
00654 
00655 //=====1 Linearize Methods=========================================================================//
00656 //This method is modelled after IDL CISSCAL's cassimg_linearise.pro
00657 
00658 /**
00659  * This method is modelled after IDL CISSCAL's
00660  * cassimg_linearise.pro.  The purpose is to correct the image
00661  * for non-linearity.
00662  *
00663  *  @internal
00664  *   @history 2008-11-05 Jeannie Walldren - Original version
00665  */
00666 void gbl::Linearize() {
00667 //  These are the correction factor tables from the referenced documents
00668 //  For each gain state there are a list of DNs where measurements
00669 //    were performed and the corresponding correction factors C
00670 //  The correction is then performed as DN'=DN*Cdn
00671 //  Where Cdn is an interpolation for C from the tabulated values
00672 
00673   QString lut;
00674   int gainState = gbl::cissLab->GainState();
00675   if(gbl::cissLab->NarrowAngle()) {
00676     switch(gainState) {
00677       case 0:
00678         lut = "NAC0.lut";
00679         break;
00680       case 1:
00681         lut = "NAC1.lut";
00682         break;
00683       case 2:
00684         lut = "NAC2.lut";
00685         break;
00686       case 3:
00687         lut = "NAC3.lut";
00688         break;
00689       default:
00690         throw IException(IException::Unknown,
00691                          "Input file contains invalid GainState. See Software Interface Specification (SIS), Version 1.1, page 86.",
00692                          _FILEINFO_);
00693     }
00694   }
00695   else {
00696     switch(gainState) {
00697       case 0:
00698         lut = "WAC0.lut";
00699         break;
00700       case 1:
00701         lut = "WAC1.lut";
00702         break;
00703       case 2:
00704         lut = "WAC2.lut";
00705         break;
00706       case 3:
00707         lut = "WAC3.lut";
00708         break;
00709       default:
00710         throw IException(IException::Unknown,
00711                          "Input file contains invalid GainState. See Software Interface Specification (SIS), Version 1.1, page 86.",
00712                          _FILEINFO_);
00713     }
00714   }
00715   vector <double> DN_VALS, C_VALS;
00716   // Get the directory where the CISS linearize directory is.
00717   FileName linearLUT(gbl::GetCalibrationDirectory("linearize") + lut);
00718   if(!linearLUT.fileExists()) {
00719     throw IException(IException::Io,
00720                      "Unable to calibrate image. LinearityCorrectionTable ***"
00721                      + linearLUT.expanded() + "*** not found.", _FILEINFO_);
00722   }
00723   gbl::calgrp += PvlKeyword("LinearityCorrectionPerformed", "Yes");
00724   gbl::calgrp.FindKeyword("LinearityCorrectionPerformed").AddComment("Linearity Correction Parameters");
00725   gbl::calgrp += PvlKeyword("LinearityCorrectionTable", linearLUT.expanded());
00726 
00727   TextFile *pairs = new TextFile(linearLUT.expanded());
00728   for(int i = 0; i < pairs->LineCount(); i++) {
00729     QString line;
00730     pairs->GetLine(line, true);
00731     line = line.simplified();
00732     QStringList tokens = line.split(" ");
00733     DN_VALS.push_back(toDouble(tokens.takeFirst()));
00734     C_VALS.push_back(toDouble(tokens.takeFirst()));
00735   }
00736   pairs->Close();
00737 
00738   //  ASSUMPTION: C will not change significantly over fractional DN
00739   //  If this is not the case, then can perform simple second interpolation
00740   //  between DNs while mapping LUT onto the image
00741   NumericalApproximation linearInterp(NumericalApproximation::Linear);
00742   for(unsigned int i = 0; i < DN_VALS.size(); i++) {
00743     linearInterp.AddData(DN_VALS[i], C_VALS[i]);
00744   }
00745 
00746   // Create the stretch pairs
00747   gbl::stretch.ClearPairs();
00748   for(unsigned int i = 0; i < 4096; i++) {
00749     double j = linearInterp.Evaluate((double) i);
00750     gbl::stretch.AddPair(i, j);
00751   }
00752   //  Map LUT onto image, defending against out-of-range DN values
00753   return;
00754 }
00755 //=====End Linearize Methods=====================================================================//
00756 
00757 
00758 //=====2 Flatfield Methods=========================================================================//
00759 // These methods are modelled after IDL CISSCAL's cassimg_dustringcorrect.pro and cassimg_dividebyflats.pro
00760 
00761 /**
00762  * This method is modelled after IDL CISSCAL's
00763  * cassimg_dustringcorrect.pro.  The purpose is to find the
00764  * files and value needed to perform dustring correction and
00765  * mottle correction: dustFile, mottleFile, strengthFactor.
00766  *
00767  * @internal
00768  *   @history 2008-11-05 Jeannie Walldren - Original version
00769  *   @history 2009-05-27 Jeannie Walldren - Changed to read
00770  *            effective wavelength from 5th column of file
00771  *            (rather than 3rd) since effective wavelength file
00772  *            updated with new idl cisscal version, 3.6.  Added
00773  *            effective wavelength to calibration group in
00774  *            labels.
00775  */
00776 void gbl::FindDustRingParameters() {
00777   //  No dustring or mottle correction for WAC
00778   if(gbl::cissLab->WideAngle()) {
00779     gbl::dustCorrection = false;
00780     gbl::mottleCorrection = false;
00781     gbl::calgrp += PvlKeyword("DustRingCorrectionPerformed", "No: ISSWA");
00782     gbl::calgrp.FindKeyword("DustRingCorrectionPerformed").AddComment("DustRing Correction Parameters");
00783     gbl::calgrp += PvlKeyword("DustRingFile", "Not applicable: No dustring correction");
00784     gbl::calgrp += PvlKeyword("MottleCorrectionPerformed", "No: dustring correction");
00785     gbl::calgrp += PvlKeyword("MottleFile", "Not applicable: No dustring correction");
00786     gbl::calgrp += PvlKeyword("EffectiveWavelengthFile", "Not applicable: No dustring correction");
00787     gbl::calgrp += PvlKeyword("StrengthFactor", "Not applicable: No dustring correction");
00788     return;
00789   }
00790 
00791   // dustring correct for NAC
00792   gbl::dustCorrection = true;
00793   gbl::calgrp += PvlKeyword("DustRingCorrectionPerformed", "Yes");
00794   gbl::calgrp.FindKeyword("DustRingCorrectionPerformed").AddComment("DustRing Correction Parameters");
00795   // get name of dust file
00796   gbl::dustFile = (gbl::GetCalibrationDirectory("dustring") + "nac_dustring_venus."
00797                    + gbl::cissLab->InstrumentModeId() + ".cub");
00798   if(!gbl::dustFile.fileExists()) { // dustring file not found, stop calibration
00799     throw IException(IException::Io,
00800                      "Unable to calibrate image. DustRingFile ***"
00801                      + gbl::dustFile.expanded() + "*** not found.", _FILEINFO_);
00802   }
00803   gbl::calgrp += PvlKeyword("DustRingFile", gbl::dustFile.expanded());
00804 
00805   // No mottle correct for summation mode other than 1
00806   if(gbl::cissLab->SummingMode() != 1) {
00807     gbl::mottleCorrection = false;
00808     gbl::calgrp += PvlKeyword("MottleCorrectionPerformed", "No: Summing mode is " + toString(gbl::cissLab->SummingMode()));
00809     gbl::calgrp += PvlKeyword("MottleFile", "Not applicable: No mottle correction");
00810     gbl::calgrp += PvlKeyword("EffectiveWavelengthFile", "Not applicable: No mottle correction");
00811     gbl::calgrp += PvlKeyword("StrengthFactor", "Not applicable: No mottle correction");
00812     return;
00813   }
00814 
00815   // No Mottling correction for images before sclk=1444733393: (i.e., 2003-286T10:28:04)
00816   if(gbl::cissLab->ImageNumber() < 1455892746) {
00817     gbl::mottleFile = "";
00818     gbl::mottleCorrection = false;
00819     gbl::calgrp += PvlKeyword("MottleCorrectionPerformed", "No: Image before 2003-286T10:28:04");
00820     gbl::calgrp += PvlKeyword("MottleFile", "Not applicable: No mottle correction");
00821     gbl::calgrp += PvlKeyword("EffectiveWavelengthFile", "Not applicable: No mottle correction");
00822     gbl::calgrp += PvlKeyword("StrengthFactor", "Not applicable: No mottle correction");
00823     return;
00824   }
00825 
00826   // Mottling correction for full images after 2003-286T10:28:04
00827   gbl::mottleFile = (gbl::GetCalibrationDirectory("duString") + "nac_mottle_1444733393.full.cub");
00828   if(!gbl::mottleFile.fileExists()) { // mottle file not found, stop calibration
00829     throw IException(IException::Io,
00830                      "Unable to calibrate image. MottleFile ***"
00831                      + gbl::mottleFile.expanded() + "*** not found.", _FILEINFO_);
00832   }
00833   gbl::mottleCorrection = true;
00834   gbl::calgrp += PvlKeyword("MottleCorrectionPerformed", "Yes");
00835   gbl::calgrp += PvlKeyword("MottleFile", gbl::mottleFile.expanded());
00836 
00837   // determine strength factor, need effective wavelength of filter
00838   vector <int> filterIndex(2);
00839   filterIndex[0] = gbl::cissLab->FilterIndex()[0];
00840   filterIndex[1] = gbl::cissLab->FilterIndex()[1];
00841   if(filterIndex[0] == 17 && filterIndex[1] == 18) { //filter combo CL1/CL2
00842     filterIndex[0] = -1;
00843   }
00844   if((filterIndex[0] < 17 && filterIndex[1] < 17) || (filterIndex[0] >= 17 && filterIndex[1] >= 17)) {
00845     gbl::strengthFactor = 0.0;
00846     // use effective wavelength to estimate strength factor:
00847     FileName effectiveWavelength(gbl::GetCalibrationDirectory("efficiency") + "na_effwl.tab");
00848     if(!effectiveWavelength.fileExists()) { // effectivewavelength file not found, stop calibration
00849       throw IException(IException::Io,
00850                        "Unable to calibrate image. EffectiveWavelengthFile ***"
00851                        + effectiveWavelength.expanded() + "*** not found.", _FILEINFO_);
00852     }
00853     gbl::calgrp += PvlKeyword("EffectiveWavelengthFile", effectiveWavelength.expanded());
00854     CisscalFile *effwlDB = new CisscalFile(effectiveWavelength.expanded());
00855     QString col1, col2, col3, col4, col5;
00856     double effwl;
00857     for(int i = 0; i < effwlDB->LineCount(); i++) {
00858       QString line;
00859       effwlDB->GetLine(line);
00860       line = line.simplified();
00861 
00862       QStringList cols = line.split(" ");
00863 
00864       col1 = cols.takeFirst();
00865       if(col1 == gbl::cissLab->FilterName()[0]) {
00866         col2 = cols.takeFirst();
00867         if(col2 == gbl::cissLab->FilterName()[1]) {
00868           col3 = cols.takeFirst();  // central wavelength of filter combo
00869           col4 = cols.takeFirst();  // full-width at half-maximum (FWHM) of filter combo
00870           col5 = cols.takeFirst();  // effective wavelength
00871           if(col5 == "") {
00872             //       Couldn't find a match in the database
00873             gbl::calgrp.FindKeyword("MottleCorrectionPerformed").SetValue("Yes: EffectiveWavelengthFile contained no factor for filter combination, used strengthFactor of 1.0");
00874             gbl::strengthFactor = 1.0;
00875           }
00876           else {
00877             effwl = toDouble(col5);
00878             gbl::calgrp += PvlKeyword("EffectiveWavelength", toString(effwl));
00879             gbl::strengthFactor = 1.30280 - 0.000717552 * effwl;
00880           }
00881           break;
00882         }
00883         else {
00884           continue;
00885         }
00886       }
00887       else {
00888         continue;
00889       }
00890     }
00891     effwlDB->Close();
00892     if(gbl::strengthFactor == 0.0) {
00893       gbl::calgrp.FindKeyword("MottleCorrectionPerformed").SetValue("Yes: EffectiveWavelengthFile contained no factor for filter combination, used strengthFactor of 1.0");
00894       gbl::strengthFactor = 1.0;
00895     }
00896   }
00897   else {//if(filterIndex[0] > 17 || filterIndex[1] > 17 )
00898     gbl::calgrp += PvlKeyword("EffectiveWavelengthFile", "No effective wavelength file used");
00899     switch(filterIndex[0]) {
00900       case 0:
00901         gbl::strengthFactor = 1.119;
00902         break;
00903       case 1:
00904         gbl::strengthFactor = 1.186;
00905         break;
00906       case 3:
00907         gbl::strengthFactor = 1.00;
00908         break;
00909       case 6:
00910         gbl::strengthFactor = 0.843;
00911         break;
00912       case 8:
00913         gbl::strengthFactor = 0.897;
00914         break;
00915       case 10:
00916         gbl::strengthFactor = 0.780;
00917         break;
00918       case -1:
00919         gbl::strengthFactor = 0.763;
00920         break;
00921       default:
00922         switch(filterIndex[1]) {
00923           case 2:
00924             gbl::strengthFactor = 1.069;
00925             break;
00926           case 4:
00927             gbl::strengthFactor = 0.833;
00928             break;
00929           case 5:
00930             gbl::strengthFactor = 0.890;
00931             break;
00932           case 7:
00933             gbl::strengthFactor = 0.997;
00934             break;
00935           case 9:
00936             gbl::strengthFactor = 0.505;
00937             break;
00938           case 11:
00939             gbl::strengthFactor = 0.764;
00940             break;
00941           case 12:
00942             gbl::strengthFactor = 0.781;
00943             break;
00944           case 13:
00945             gbl::strengthFactor = 0.608;
00946             break;
00947           case 14:
00948             gbl::strengthFactor = 0.789;
00949             break;
00950           case 15:
00951             gbl::strengthFactor = 0.722;
00952             break;
00953           case 16:
00954             gbl::strengthFactor = 0.546;
00955             break;
00956           default:
00957             throw IException(IException::Unknown,
00958                              "Input file contains invalid FilterName. See Software Interface Specification (SIS) Appendix A, Table 8.2.",
00959                              _FILEINFO_);
00960         }
00961     }
00962   }
00963   gbl::calgrp += PvlKeyword("StrengthFactor", toString(gbl::strengthFactor));
00964   return;
00965 }
00966 
00967 /**
00968  * This method is modelled after IDL CISSCAL's
00969  * cassimg_dividebyflats.pro.  The purpose is to find the flat
00970  * field file needed to correct the image for sensitivity
00971  * variations across the field by dividing by flat field image.
00972  *
00973  * @return <B>FileName</B> Name of the flat file for this image.
00974  * @internal
00975  *   @history 2008-11-05 Jeannie Walldren - Original version
00976  */
00977 FileName gbl::FindFlatFile() {
00978   //  There is a text database file in the slope files directory
00979   //   that maps filter combinations (and camera temperature)
00980   //   to the corresponding slope field files.
00981   // according to slope_info.txt, slope_db_1 is original, slope_db_2 is the best and slope_db_3 is newest but has some issues
00982   FileName flatFile;
00983   FileName slopeDatabaseName(gbl::GetCalibrationDirectory("slope") + "slope_db_2.tab");
00984   if(!slopeDatabaseName.fileExists()) { // slope database not found, stop calibration
00985     throw IException(IException::Io,
00986                      "Unable to calibrate image. SlopeDataBase ***"
00987                      + slopeDatabaseName.expanded() + "*** not found.", _FILEINFO_);
00988   }
00989   gbl::calgrp += PvlKeyword("FlatfieldCorrectionPerformed", "Yes");
00990   gbl::calgrp.FindKeyword("FlatfieldCorrectionPerformed").AddComment("Flatfield Correction Parameters");
00991   gbl::calgrp += PvlKeyword("SlopeDataBase", slopeDatabaseName.expanded());
00992   gbl::flatCorrection = true;
00993 
00994   // Find the best-match flat file
00995   //  Choose a nominal optics temp name as per ISSCAL
00996   QString frontOpticsTemp("");
00997   if(gbl::cissLab->FrontOpticsTemp() < -5.0) {
00998     frontOpticsTemp += "m10";
00999   }
01000   else if(gbl::cissLab->FrontOpticsTemp() < 25.0) {
01001     frontOpticsTemp += "p5";
01002   }
01003   else {
01004     frontOpticsTemp += "p25";
01005   }
01006   //  Require match for instrument, temperature range name, Filter1, filter2
01007   CisscalFile *slopeDB = new CisscalFile(slopeDatabaseName.expanded());
01008   QString col1, col2, col3, col4, col5, col6, col7, col8;
01009   for(int i = 0; i < slopeDB->LineCount(); i++) {
01010     QString line;
01011     slopeDB->GetLine(line);  //assigns value to line
01012     line = line.simplified();
01013     QStringList cols = line.split(" ");
01014     cols.replaceInStrings(QRegExp("(^'|'$)"), "");
01015 
01016     col1 = cols.takeFirst();
01017     if(col1 == gbl::cissLab->InstrumentId()) {
01018       col2 = cols.takeFirst();
01019       if((col2 == frontOpticsTemp) || (gbl::cissLab->WideAngle())) {
01020         col3 = cols.takeFirst();
01021         if(col3 == gbl::cissLab->FilterName()[0]) {
01022           col4 = cols.takeFirst();
01023           if(col4 == gbl::cissLab->FilterName()[1]) {
01024             col5 = cols.takeFirst();// col5 = gainstate (not used)
01025             col6 = cols.takeFirst();// col6 = antiblooming state (not used)
01026             col7 = cols.takeFirst();// col7 = file number (not used)
01027             col8 = cols.takeFirst();// col8 = slope file name
01028             break;
01029           }
01030           else {
01031             continue;
01032           }
01033         }
01034         else {
01035           continue;
01036         }
01037       }
01038       else {
01039         continue;
01040       }
01041     }
01042     else {
01043       continue;
01044     }
01045   }
01046   slopeDB->Close();
01047 
01048   if(col8 == "") {  // error in slope database, stop calibration
01049     // Couldn't find a match in the database
01050     throw IException(IException::Io,
01051                      "Unable to calibrate image. SlopeDataBase contained no factor for combination:"
01052                      + gbl::cissLab->InstrumentId()  + ":" + frontOpticsTemp + ":"
01053                      + gbl::cissLab->FilterName()[0] + ":" + gbl::cissLab->FilterName()[1] + ".",
01054                      _FILEINFO_);
01055   }
01056   //Column 8 contains version of slopefile from which our flatfiles are derived
01057   int j = col8.indexOf(".");
01058   //attatch version number to "flat" by skipping
01059   // the first 5 characters("SLOPE") and skipping
01060   // any thing after "." ("IMG")
01061   col8 = "flat" + col8.mid(5, (j - 5) + 1);
01062   flatFile = (gbl::GetCalibrationDirectory("slope/flat") + col8
01063               + gbl::cissLab->InstrumentModeId() + ".cub");
01064   gbl::calgrp += PvlKeyword("FlatFile", flatFile.expanded());
01065   if(!flatFile.fileExists()) { // flat file not found, stop calibration
01066     throw IException(IException::Io,
01067                      "Unable to calibrate image. FlatFile ***"
01068                      + flatFile.expanded() + "*** not found.", _FILEINFO_);
01069   }
01070   return flatFile;
01071 }
01072 //=====End Flatfield Methods=====================================================================//
01073 
01074 //=====5 Convert DN to Flux Methods================================================================//
01075 // These methods are modelled after IDL CISSCAL's cassimg_dntoelectrons.pro, cassimg_dividebyexpot.pro,
01076 // cassimg_dividebyareapixel.pro, cassimg_dividebyefficiency.pro
01077 
01078 /**
01079  * This method is modelled after IDL CISSCAL's
01080  * cassimg_dntoelectrons.pro.  The purpose is to find the true
01081  * gain needed to multiply image by gain constant (convert DN to
01082  * electrons).
01083  *
01084  *  @internal
01085  *   @history 2008-11-05 Jeannie Walldren - Original version
01086  *   @history 2009-05-27 Jeannie Walldren - Removed return
01087  *            statement and added else statement so that the
01088  *            TrueGain keyword is added in any case.
01089  */
01090 void gbl::DNtoElectrons() {
01091   gbl::calgrp += PvlKeyword("DNtoFluxPerformed", "Yes");
01092   gbl::calgrp.FindKeyword("DNtoFluxPerformed").AddComment("DN to Flux Parameters");
01093   gbl::calgrp += PvlKeyword("DNtoElectrons", "Yes");
01094   //  Gain used for an image is documented by the GainModID attribute
01095   //  of the image. Nominal values are as follow:
01096   //
01097   //  Attribute  Gain  Usual  Nominal Gain
01098   //   Value  state    mode  (e- per DN)
01099   //  "1400K"    0      SUM4      215
01100   //   "400K"    1      SUM2       95
01101   //   "100K"    2      FULL       29
01102   //    "40K"    3      FULL       12
01103   if(gbl::cissLab->NarrowAngle()) {
01104     switch(gbl::cissLab->GainState()) {
01105       case 0:
01106         gbl::trueGain = 30.27 / 0.135386;
01107         break;
01108       case 1:
01109         gbl::trueGain = 30.27 / 0.309569;
01110         break;
01111       case 2:
01112         gbl::trueGain = 30.27 / 1.0;
01113         break;
01114       case 3:
01115         gbl::trueGain = 30.27 / 2.357285;
01116         break;
01117       default: // invalid gainstate, unable to convert DN to electrons, stop calibration
01118         throw IException(IException::Unknown,
01119                          "Input file contains invalid GainState. See Software Interface Specification (SIS), Version 1.1, page 86.",
01120                          _FILEINFO_);
01121     }
01122   }
01123   else {
01124     switch(gbl::cissLab->GainState()) {
01125       case 0:
01126         gbl::trueGain = 27.68 / 0.125446;
01127         break;
01128       case 1:
01129         gbl::trueGain = 27.68 / 0.290637;
01130         break;
01131       case 2:
01132         gbl::trueGain = 27.68 / 1.0;
01133         break;
01134       case 3:
01135         gbl::trueGain = 27.68 / 2.360374;
01136         break;
01137       default: // invalid gainstate, unable to convert DN to electrons, stop calibration
01138         throw IException(IException::Unknown,
01139                          "Input file contains invalid GainState. See Software Interface Specification (SIS), Version 1.1, page 86.",
01140                          _FILEINFO_);
01141     }
01142   }
01143   gbl::calgrp += PvlKeyword("TrueGain", toString(gbl::trueGain));
01144   return;
01145 }
01146 
01147 /**
01148  * This method is modelled after IDL CISSCAL's
01149  * cassimg_dividebyexpot.pro.  The purpose is to find the
01150  * shutter offset needed to divide a Cassini image by corrected
01151  * exposure time, correcting for shutter offset effects (sample
01152  * dependency of actual exposure time).
01153  *
01154  * @internal
01155  *   @history 2008-11-05 Jeannie Walldren - Original version
01156  */
01157 void gbl::FindShutterOffset() {
01158   //  Don't do this for zero-exposure images!
01159   if(gbl::cissLab->ExposureDuration() == 0.0) { // exposuretime = 0, stop calibration
01160     throw IException(IException::Unknown,
01161                      "Unable to calibrate image.  Cannot divide by exposure time for zero exposure image.",
01162                      _FILEINFO_);
01163   }
01164   gbl::calgrp += PvlKeyword("DividedByExposureTime", "Yes");
01165   gbl::divideByExposure = true;
01166   //  Define whereabouts of shutter offset files
01167   QString offsetFileName("");
01168   if(gbl::cissLab->NarrowAngle()) {
01169     offsetFileName += (gbl::GetCalibrationDirectory("offset") + "nacfm_so_");
01170   }
01171   else {
01172     offsetFileName += (gbl::GetCalibrationDirectory("offset") + "wacfm_so_");
01173   }
01174   if(gbl::cissLab->FrontOpticsTemp() < -5.0) {
01175     offsetFileName += "m10.";
01176   }
01177   else if(gbl::cissLab->FrontOpticsTemp() < 25.0) {
01178     offsetFileName += "p5.";
01179   }
01180   else {
01181     offsetFileName += "p25.";
01182   }
01183   offsetFileName += (gbl::cissLab->InstrumentModeId() + ".cub");
01184   FileName shutterOffsetFile(offsetFileName);
01185   if(!shutterOffsetFile.fileExists()) { // shutter offset file not found, stop calibration
01186     throw IException(IException::Io,
01187                      "Unable to calibrate image. ShutterOffsetFile ***"
01188                      + shutterOffsetFile.expanded() + "*** not found.", _FILEINFO_);
01189   }
01190   gbl::calgrp += PvlKeyword("ShutterOffsetFile", shutterOffsetFile.expanded());
01191   Cube offsetCube;
01192   offsetCube.open(shutterOffsetFile.expanded());
01193   gbl::offset = new Brick(gbl::incube->sampleCount(), 1, 1, offsetCube.pixelType());
01194   gbl::offset->SetBasePosition(1, 1, 1);
01195   offsetCube.read(*gbl::offset);
01196   offsetCube.close();
01197   return;
01198   // Pixel value is now flux (electrons per second)
01199 }
01200 
01201 /**
01202  * This method is modelled after IDL CISSCAL's
01203  *  cassimg_dividebyareapixel.pro.  The purpose is to find the
01204  *  values needed to normalise the image by dividing by area of
01205  *  optics and by solid angle subtended by a pixel.
01206  *
01207  * @internal
01208  *   @history 2008-11-05 Jeannie Walldren - Original version
01209  */
01210 void gbl::DivideByAreaPixel() {
01211   //  These values as per ISSCAL
01212   //  SolidAngle is (FOV of Optics) / (Number of Pixels)
01213   //  OpticsArea is (Diameter of Primary Mirror)^2 * Pi/4
01214   //  We will adjust here for the effects of SUM modes
01215   //  (which effectively give pixels of 4 or 16 times normal size)
01216 
01217   gbl::calgrp += PvlKeyword("DividedByAreaPixel", "Yes");
01218   if(gbl::cissLab->NarrowAngle()) {
01219     gbl::solidAngle = 3.6 * pow(10.0, -11.0);
01220     gbl::opticsArea = 264.84;
01221   }
01222   else {
01223     gbl::solidAngle = 3.6 * pow(10.0, -9);
01224     gbl::opticsArea = 29.32;
01225   }
01226   //  Normalize summed images to real pixels
01227 
01228   // sumFactor is the inverse of the square of the summing mode,
01229   // it was expressed in IDL as the following:
01230   //       [gbl::sumFactor = (gbl::incube->sampleCount()/1024.0)*(gbl::incube->lineCount()/1024.0);]
01231   gbl::sumFactor = 1 / pow(gbl::cissLab->SummingMode(), 2.0);
01232   gbl::calgrp += PvlKeyword("SolidAngle", toString(gbl::solidAngle));
01233   gbl::calgrp += PvlKeyword("OpticsArea", toString(gbl::opticsArea));
01234   gbl::calgrp += PvlKeyword("SumFactor", toString(gbl::sumFactor));
01235   return;
01236 }
01237 
01238 /**
01239  * This method is modelled after IDL CISSCAL's
01240  * cassimg_dividebyefficiency.pro. The purpose is to find the
01241  * efficiency factor for the given flux units. This value is
01242  * used to correct the image for filter and CCD efficiency.
01243  *
01244  * @b Note: For "I/F", The results diverge from the IDL results
01245  * due to differences in the way they calculate solar distance.
01246  * However, the DN results are still within 0.2% after we divide
01247  * by efficiency factor.
01248  *
01249  * @internal
01250  *   @history 2008-11-05 Jeannie Walldren - Original version of
01251  *            FindEfficiencyFactor_IoverF() and
01252  *            FindEfficiencyFactor_IntensityUnits() written.
01253  *   @history 2009-02-12 Jeannie Walldren - Modified
01254  *            FindEfficiencyFactor_IoverF() code to make a
01255  *            second attempt to find the planet if the
01256  *            Isis::Camera class fails to find it at the center
01257  *            point of the cube. Previously, if the target was
01258  *            not found, an exception was thrown. Now the
01259  *            SubSpacecraftPoint() method from Isis::Camera
01260  *            class is used to try to locate the target before
01261  *            throwing the exception.
01262  *   @history 2009-05-27 Jeannie Walldren - Joined
01263  *            FindEfficiencyFactor_IoverF() and
01264  *            FindEfficiencyFactor_IntensityUnits() into
01265  *            FindEfficiencyFactor() per new idl cisscal
01266  *            version, 3.6, updates. This version no longer uses
01267  *            the effic_db (now called omega0) to calculate the
01268  *            efficiency factor for intensity units.  Now, the
01269  *            method is not far off from the I/F method.
01270  */
01271 
01272 void gbl::FindEfficiencyFactor(QString fluxunits) {
01273 
01274   gbl::calgrp += PvlKeyword("DividedByEfficiency", "Yes");
01275   gbl::calgrp += PvlKeyword("EfficiencyFactorMethod", fluxunits);
01276   vector<double> lambda; // lambda will contain all wavelength vectors used
01277 
01278   //--- 1) CREATE LINEAR APPROXIMATION FROM SYSTEM TRANSMISSION FILE ----------
01279   // find system transmission file (T0*T1*T2*QE)
01280 
01281   FileName transfile(gbl::GetCalibrationDirectory("efficiency/systrans")
01282                      + gbl::cissLab->InstrumentId().toLower()
01283                      + gbl::cissLab->FilterName()[0].toLower()
01284                      + gbl::cissLab->FilterName()[1].toLower() + "_systrans.tab");
01285   if(!transfile.fileExists()) { // transmission file not found, stop calibration
01286     throw IException(IException::Io,
01287                      "Unable to calibrate image. TransmissionFile ***"
01288                      + transfile.expanded() + "*** not found.", _FILEINFO_);
01289   }
01290   // read in system transmission to find transmitted wavelength and flux
01291   gbl::calgrp += PvlKeyword("TransmissionFile", transfile.expanded());
01292   CisscalFile *trans = new CisscalFile(transfile.expanded());
01293   vector<double> wavelengthT, transmittedFlux;
01294   double x, y;
01295   for(int i = 0; i < trans->LineCount(); i++) {
01296     QString line;
01297     trans->GetLine(line);  //assigns value to line
01298     line = line.simplified().trimmed();
01299     if(line == "") {
01300       break;
01301     }
01302     x = toDouble(line.split(" ")[0]);
01303     y = toDouble(line.split(" ")[1]);
01304     wavelengthT.push_back(x);
01305     transmittedFlux.push_back(y);
01306   }
01307   trans->Close();
01308   // wavelength and flux are in descending order, reverse to ascending order
01309   if(wavelengthT[0] > wavelengthT.back()) {
01310     reverse(wavelengthT.begin(), wavelengthT.end());
01311     reverse(transmittedFlux.begin(), transmittedFlux.end());
01312   }
01313   lambda = wavelengthT;
01314   // Create Linear approximation from the transmitted data
01315   NumericalApproximation newtrans(NumericalApproximation::Linear);
01316   for(unsigned int i = 0; i < transmittedFlux.size(); i++) {
01317     newtrans.AddData(wavelengthT[i], transmittedFlux[i]);
01318   }
01319 
01320   //--- 2) CREATE LINEAR APPROXIMATION FROM QUANTUM EFFICIENCY FILE -----------
01321   // find quantum efficiency file
01322   FileName qecorrfile;
01323   if(gbl::cissLab->NarrowAngle()) {
01324     qecorrfile = gbl::GetCalibrationDirectory("correction") + "nac_qe_correction.tab";
01325   }
01326   else {
01327     qecorrfile = gbl::GetCalibrationDirectory("correction") + "wac_qe_correction.tab";
01328   }
01329   if(!qecorrfile.fileExists()) { // quantum efficiency file not found, stop calibration
01330     throw IException(IException::Io,
01331                      "Unable to calibrate image. QuantumEfficiencyFile ***"
01332                      + qecorrfile.expanded() + "*** not found.", _FILEINFO_);
01333   }
01334   // read qe file to find qe wavelength and correction
01335   gbl::calgrp += PvlKeyword("QuantumEfficiencyFile", qecorrfile.expanded());
01336   CisscalFile *qeCorr = new CisscalFile(qecorrfile.expanded());
01337   vector<double> wavelengthQE, qecorrection;
01338   for(int i = 0; i < qeCorr->LineCount(); i++) {
01339     QString line;
01340     qeCorr->GetLine(line);  //assigns value to line
01341     line = line.simplified().trimmed();
01342     if(line == "") {
01343       break;
01344     }
01345     x = toDouble(line.split(" ").first());
01346     y = toDouble(line.split(" ").last());
01347     wavelengthQE.push_back(x);
01348     qecorrection.push_back(y);
01349     lambda.push_back(x);
01350   }
01351   qeCorr->Close();
01352   // wavelength and qecorr are in descending order, reverse to ascending order
01353   if(wavelengthQE[0] > wavelengthQE.back()) {
01354     reverse(wavelengthQE.begin(), wavelengthQE.end());
01355     reverse(qecorrection.begin(), qecorrection.end());
01356   }
01357   // Create Linear approximation from the qe data
01358   NumericalApproximation newqecorr(NumericalApproximation::Linear);
01359   for(unsigned int i = 0; i < qecorrection.size(); i++) {
01360     newqecorr.AddData(wavelengthQE[i], qecorrection[i]);
01361   }
01362 
01363 
01364   // these variables will be defined in the if-statement
01365   QString units;
01366   double minlam, maxlam;
01367   vector<double> fluxproduct1, fluxproduct2;
01368 
01369   if(fluxunits == "INTENSITY") {
01370     gbl::calgrp += PvlKeyword("SpectralFile", "Not applicable: Intensity Units chosen");
01371     gbl::calgrp += PvlKeyword("SolarDistance", "Not applicable: Intensity Units chosen");
01372     //--- 3a) SORT AND MAKE LAMBDA UNIQUE, REMOVE OUTLIERS, -------------------
01373     //---     FIND FLUX PRODUCTS TO BE INTERPOLATED ---------------------------
01374     units = "phot/cm^2/s/nm/ster";
01375 
01376     // lambda domain min is the largest of the minimum wavelength values (rounded up)
01377     minlam = ceil(max(wavelengthT.front(), wavelengthQE.front()));
01378     // lambda domain max is the smallest of the maximum wavelength values (rounded down)
01379     maxlam = floor(min(wavelengthT.back(), wavelengthQE.back()));
01380 
01381     // NumericalApproximation requires lambda to be sorted
01382     sort(lambda.begin(), lambda.end());
01383     // NumericalApproximation requires lambda to be unique
01384     vector<double>::iterator it = unique(lambda.begin(), lambda.end());
01385     lambda.resize(it - lambda.begin());
01386 
01387     // remove any values that fall below minlam
01388     while(lambda[0] < minlam) {
01389       lambda.erase(lambda.begin());
01390     }
01391 
01392     // remove any values that fall above maxlam
01393     while(lambda[lambda.size()-1] > maxlam) {
01394       lambda.erase(lambda.end() - 1);
01395     }
01396 
01397     for(unsigned int i = 0; i < lambda.size(); i++) {
01398       double a = newtrans.Evaluate(lambda[i]);
01399       double b = newqecorr.Evaluate(lambda[i]);
01400       fluxproduct1.push_back(a * b);
01401     }
01402     fluxproduct2 = fluxproduct1;
01403   }
01404   else {// if(fluxunits == "I/F") {
01405     //--- 3b) CALCULATE SOLAR DISTANCE, ---------------------------------------
01406     // ---    CREATE LINEAR APPROXIMATION FROM SPECTRAL FILE ------------------
01407     //---     SORT AND MAKE LAMBDA UNIQUE, REMOVE OUTLIERS, -------------------
01408     //---     FIND FLUX PRODUCTS TO BE INTERPOLATED ---------------------------
01409 
01410     units = "I/F";
01411 
01412     // find spectral file
01413     FileName specfile(gbl::GetCalibrationDirectory("efficiency") + "solarflux.tab");
01414     if(!specfile.fileExists()) { // spectral file not found, stop calibration
01415       throw IException(IException::Io,
01416                        "Unable to calibrate image using I/F. SpectralFile ***"
01417                        + specfile.expanded() + "*** not found.", _FILEINFO_);
01418     }
01419     gbl::calgrp += PvlKeyword("SpectralFile", specfile.expanded());
01420 
01421     // get distance from sun (AU):
01422     double angstromsToNm = 10.0;
01423     double distFromSun = 0;
01424     try {
01425       Camera *cam = gbl::incube->camera();
01426       bool camSuccess = cam->SetImage(gbl::incube->sampleCount() / 2, gbl::incube->lineCount() / 2);
01427       if(!camSuccess) {// the camera was unable to find the planet at the center of the image
01428         double lat, lon;
01429         // find values for lat/lon directly below spacecraft
01430         cam->subSpacecraftPoint(lat, lon);
01431         // use these values to set the ground coordinates
01432         cam->SetUniversalGround(lat, lon);
01433       }
01434       distFromSun = cam->SolarDistance();
01435     }
01436     catch(IException &e) { // unable to get solar distance, can't divide by efficiency, stop calibration
01437       throw IException(IException::Unknown,
01438                        "Unable to calibrate image using I/F. Cannot calculate Solar Distance using Isis::Camera object.",
01439                        _FILEINFO_);
01440     }
01441     if(distFromSun <= 0) { // solar distance <= 0, can't divide by efficiency, stop calibration
01442       throw IException(IException::Unknown,
01443                        "Unable to calibrate image using I/F. Solar Distance calculated is less than or equal to 0.",
01444                        _FILEINFO_);
01445     }
01446     gbl::calgrp += PvlKeyword("SolarDistance", toString(distFromSun));
01447 
01448     // read spectral file to find wavelength and flux
01449     CisscalFile *spectral = new CisscalFile(specfile.expanded());
01450     vector<double> wavelengthF, flux;
01451     for(int i = 0; i < spectral->LineCount(); i++) {
01452       QString line;
01453       spectral->GetLine(line);  //assigns value to line
01454       line = line.simplified().trimmed();
01455       if(line == "") {
01456         break;
01457       }
01458       x = toDouble(line.split(" ").first()) / angstromsToNm;
01459       y = toDouble(line.split(" ").last()) * angstromsToNm;
01460       wavelengthF.push_back(x);
01461       flux.push_back(y);
01462       lambda.push_back(x);
01463     }
01464     spectral->Close();
01465     // wavelength is in descending order, reverse to ascending order
01466     if(wavelengthF[0] > wavelengthF.back()) {
01467       reverse(wavelengthF.begin(), wavelengthF.end());
01468       reverse(flux.begin(), flux.end());
01469     }
01470     // Create Linear Approximation
01471     NumericalApproximation newflux(NumericalApproximation::Linear);
01472     for(unsigned int i = 0; i < flux.size(); i++) {
01473       newflux.AddData(wavelengthF[i], flux[i]);
01474     }
01475 
01476     // lambda domain min is the largest of the minimum wavelength values (rounded up)
01477     minlam = ceil(max(wavelengthF.front(), max(wavelengthT.front(), wavelengthQE.front())));
01478     // lambda domain max is the smallest of the maximum wavelength values (rounded down)
01479     maxlam = floor(min(wavelengthF.back(), min(wavelengthT.back(), wavelengthQE.back())));
01480 
01481     // NumericalApproximation requires lambda to be sorted
01482     sort(lambda.begin(), lambda.end());
01483     // NumericalApproximation requires lambda to be unique
01484     vector<double>::iterator it = unique(lambda.begin(), lambda.end());
01485     lambda.resize(it - lambda.begin());
01486 
01487     // remove any values that fall below minlam
01488     while(lambda[0] < minlam) {
01489       lambda.erase(lambda.begin());
01490     }
01491 
01492     // remove any values that fall above maxlam
01493     while(lambda[lambda.size()-1] > maxlam) {
01494       lambda.erase(lambda.end() - 1);
01495     }
01496 
01497     for(unsigned int i = 0; i < lambda.size(); i++) {
01498       double a = newtrans.Evaluate(lambda[i]);
01499       double b = newqecorr.Evaluate(lambda[i]);
01500       double c = newflux.Evaluate(lambda[i]) / (Isis::PI * pow(distFromSun, 2.0));
01501       fluxproduct1.push_back(a * b * c);
01502       fluxproduct2.push_back(a * b);
01503     }
01504   }
01505 
01506   //--- 4) CALCULATE EFFICIENCY FACTOR AND TOTAL EFFICIENCY -------------------
01507   //---    USING LAMBDA AND FLUX PRODUCTS -------------------------------------
01508   NumericalApproximation spline1(NumericalApproximation::CubicNatural);
01509   NumericalApproximation spline2(NumericalApproximation::CubicNatural);
01510   spline1.AddData(lambda, fluxproduct1);
01511   spline2.AddData(lambda, fluxproduct2);
01512   gbl::efficiencyFactor = spline1.BoolesRule(spline1.DomainMinimum(), spline1.DomainMaximum());
01513   double efficiency = spline2.BoolesRule(spline2.DomainMinimum(), spline2.DomainMaximum());
01514   gbl::calgrp += PvlKeyword("EfficiencyFactor", toString(gbl::efficiencyFactor), units);
01515   gbl::calgrp += PvlKeyword("TotalEfficiency", toString(efficiency));
01516 
01517   // Cannot divide by 0.0
01518   if(gbl::efficiencyFactor == 0) {
01519     throw IException(IException::Unknown,
01520                      "Unable to calibrate image using I/F.  Cannot divide by efficiency factor of 0.",
01521                      _FILEINFO_);
01522   }
01523   return;
01524 }
01525 
01526 
01527 //=====End DN to Flux Methods====================================================================//
01528 
01529 
01530 //=====1 Correction Factors Methods================================================================//
01531 
01532 /**
01533  *  This method is modelled after IDL CISSCAL's
01534  *  cassimg_correctionfactors.pro.  The purpose is to find the
01535  *  correction factor, i.e. the value used to correct the image
01536  *  for ad-hoc factors.
01537  *
01538  * @internal
01539  *   @history 2008-11-05 Jeannie Walldren - Original version
01540  *   @history 2009-05-27 Jeannie Walldren - Renamed from
01541  *            FindCorrectionFactor since code was added to find
01542  *            the polarization correction factor, when
01543  *            available.
01544  */
01545 void gbl::FindCorrectionFactors() {
01546   QString filter1 = gbl::cissLab->FilterName()[0];
01547   QString filter2 = gbl::cissLab->FilterName()[1];
01548   // check if polarized filters
01549   if(filter1 == "IRP0" || filter1 == "P120" || filter1 == "P60" || filter1 == "P0"
01550       || filter2 == "IRP90" || filter2 == "IRP0") {
01551     FileName polarizationFactorFile(gbl::GetCalibrationDirectory("correction") + "pol_correction.tab");
01552     if(!polarizationFactorFile.fileExists()) { // correction factor file not found, stop calibration
01553       throw IException(IException::Io,
01554                        "Unable to calibrate image. PolarizationFactorFile ***"
01555                        + polarizationFactorFile.expanded() + "*** not found.", _FILEINFO_);
01556     }
01557     gbl::calgrp += PvlKeyword("PolarizationFactorPerformed", "Yes");
01558     gbl::calgrp.FindKeyword("PolarizationFactorPerformed").AddComment("Correction Factor Parameters");
01559     gbl::calgrp += PvlKeyword("PolarizationFactorFile", polarizationFactorFile.expanded());
01560     CisscalFile *polFact = new CisscalFile(polarizationFactorFile.expanded());
01561     gbl::polarizationFactor = 0.0;
01562     QString col1, col2, col3, col4;
01563     for(int i = 0; i < polFact->LineCount(); i++) {
01564       QString line;
01565       polFact->GetLine(line);  //assigns value to line
01566       line = line.simplified().trimmed();
01567       QStringList cols = line.split(" ");
01568       col1 = cols.takeFirst();
01569       if(col1 == gbl::cissLab->InstrumentId()) {
01570         col2 = cols.takeFirst();
01571         if(col2 == gbl::cissLab->FilterName()[0]) {
01572           col3 = cols.takeFirst();
01573           if(col3 == gbl::cissLab->FilterName()[1]) {
01574             col4 = cols.takeFirst();
01575             if(col4 == "") {
01576               gbl::polarizationFactor = 1.0;
01577               // dividing by polarization factor of 1.0 implies this correction is not performed
01578               gbl::calgrp.FindKeyword("PolarizationFactorPerformed").SetValue("No: PolarizationFactorFile contained no factor for filter combination");
01579             }
01580             else {
01581               gbl::polarizationFactor = toDouble(col4);
01582             }
01583             break;
01584           }
01585           else {
01586             continue;
01587           }
01588         }
01589         else {
01590           continue;
01591         }
01592       }
01593       else {
01594         continue;
01595       }
01596     }
01597     polFact->Close();
01598 
01599     // if no factor was found for instrument ID and filter combination
01600     if(gbl::polarizationFactor == 0.0) {
01601       gbl::polarizationFactor = 1.0;
01602       // dividing by polarization factor of 1.0 implies this correction is not performed
01603       gbl::calgrp.FindKeyword("PolarizationFactorPerformed").SetValue("No: PolarizationFactorFile contained no factor for filter combination");
01604     }
01605     else {
01606       // polarization factor is defined such that they are applied with the correction factor for the related CLR/Filter pair
01607       if(gbl::cissLab->InstrumentId() == "ISSNA") {
01608         filter1 = "CL1";
01609       }
01610       if(gbl::cissLab->InstrumentId() == "ISSWA") {
01611         filter2 = "CL2";
01612       }
01613     }
01614     gbl::calgrp += PvlKeyword("PolarizationFactor", toString(gbl::polarizationFactor));
01615 
01616   }
01617   else {
01618     // no polarization correction - gbl::polarizationFactor already initialized to 1 in main()
01619     gbl::calgrp += PvlKeyword("PolarizationFactorPerformed", "No");
01620     gbl::calgrp.FindKeyword("PolarizationFactorPerformed").AddComment("Correction Factor Parameters");
01621   }
01622   // Get the directory where the CISS calibration directories are.
01623   FileName correctionFactorFile(gbl::GetCalibrationDirectory("correction") + "correctionfactors_qecorr.tab");
01624   if(!correctionFactorFile.fileExists()) { // correction factor file not found, stop calibration
01625     throw IException(IException::Io,
01626                      "Unable to calibrate image. CorrectionFactorFile ***"
01627                      + correctionFactorFile.expanded() + "*** not found.", _FILEINFO_);
01628   }
01629   gbl::calgrp += PvlKeyword("CorrectionFactorPerformed", "Yes");
01630   gbl::calgrp += PvlKeyword("CorrectionFactorFile", correctionFactorFile.expanded());
01631   CisscalFile *corrFact = new CisscalFile(correctionFactorFile.expanded());
01632   gbl::correctionFactor = 0.0;
01633   QString col1, col2, col3, col4;
01634   for(int i = 0; i < corrFact->LineCount(); i++) {
01635     QString line;
01636     corrFact->GetLine(line);  //assigns value to line
01637     line = line.simplified().trimmed();
01638     QStringList cols = line.split(" ");
01639     col1 = cols.takeFirst();
01640     if(col1 == gbl::cissLab->InstrumentId()) {
01641       col2 = cols.takeFirst();
01642       if(col2 == filter1) {
01643         col3 = cols.takeFirst();
01644         if(col3 == filter2) {
01645           col4 = cols.takeFirst();
01646           if(col4 == "") {
01647             gbl::correctionFactor = 1.0;
01648             // dividing by correction factor of 1.0 implies this correction is not performed
01649             gbl::calgrp.FindKeyword("CorrectionFactorPerformed").SetValue("No: CorrectionFactorFile contained no factor for filter combination");
01650           }
01651           else {
01652             gbl::correctionFactor = toDouble(col4);
01653           }
01654           break;
01655         }
01656         else {
01657           continue;
01658         }
01659       }
01660       else {
01661         continue;
01662       }
01663     }
01664     else {
01665       continue;
01666     }
01667   }
01668   corrFact->Close();
01669 
01670   // if no factor was found for instrument ID and filter combination
01671   if(gbl::correctionFactor == 0.0) {
01672     gbl::correctionFactor = 1.0;
01673     // dividing by correction factor of 1.0 implies this correction is not performed
01674     gbl::calgrp.FindKeyword("CorrectionFactorPerformed").SetValue("No: CorrectionFactorFile contained no factor for filter combination");
01675   }
01676   gbl::calgrp += PvlKeyword("CorrectionFactor", toString(gbl::correctionFactor));
01677   return;
01678 }
01679 //=====End Correction Factor Methods=============================================================//
01680 
01681 /**
01682  * This method returns an QString containing the path of a
01683  * Cassini calibration directory
01684  *
01685  * @param calibrationType
01686  * @return <b>IString</b> Path of the calibration directory
01687  *
01688  * @internal
01689  *   @history 2008-11-05 Jeannie Walldren - Original version
01690  */
01691 QString gbl::GetCalibrationDirectory(QString calibrationType) {
01692   // Get the directory where the CISS calibration directories are.
01693   PvlGroup &dataDir = Preference::Preferences().FindGroup("DataDirectory");
01694   QString missionDir = (QString) dataDir["Cassini"];
01695   return missionDir + "/calibration/" + calibrationType + "/";
01696 }
01697