USGS

Isis 3.0 Application Source Code Reference

Home

vimscal.cpp

Go to the documentation of this file.
00001 #include "Isis.h"
00002 
00003 #include <vector>
00004 #include <stdio.h>
00005 
00006 #include <QFile>
00007 
00008 #include "Camera.h"
00009 #include "EndianSwapper.h"
00010 #include "IException.h"
00011 #include "IString.h"
00012 #include "LeastSquares.h"
00013 #include "LineManager.h"
00014 #include "PolynomialUnivariate.h"
00015 #include "ProcessByLine.h"
00016 #include "ProgramLauncher.h"
00017 #include "Statistics.h"
00018 #include "Table.h"
00019 #include "UserInterface.h"
00020 
00021 using namespace Isis;
00022 using namespace std;
00023 
00024 //! map from <sample, band> to dark correction value
00025 map< pair<int, int>, double > sampleBasedDarkCorrections;
00026 
00027 //! map from <line, band> to dark correction value
00028 map< pair<int, int>, double > lineBasedDarkCorrections;
00029 
00030 //! specific energy corrections for each band of the cube
00031 vector<double> specificEnergyCorrections;
00032 
00033 //! list of temp files that need deleted
00034 vector<QString> tempFiles;
00035 
00036 //! solar remove coefficient
00037 double solarRemoveCoefficient;
00038 
00039 //! Output in I/F units
00040 bool iof;
00041 
00042 void calculateDarkCurrent(Cube *);
00043 void calculateVisDarkCurrent(Cube *);
00044 void calculateIrDarkCurrent(Cube *);
00045 
00046 void chooseFlatFile(Cube *, ProcessByLine *);
00047 
00048 void calculateSpecificEnergy(Cube *);
00049 void calculateSolarRemove(Cube *, ProcessByLine *);
00050 
00051 void calibrate(vector<Buffer *> &in, vector<Buffer *> &out);
00052 
00053 QString createCroppedFile(Cube *icube, QString cubeFileName, bool flatFile = false);
00054 void GetOffsets(const Pvl &lab, int &finalSampOffset, int &finalLineOffset);
00055 
00056 // This is the results group
00057 PvlGroup calibInfo;
00058 
00059 void IsisMain() {
00060   UserInterface &ui = Application::GetUserInterface();
00061 
00062   tempFiles.clear();
00063   specificEnergyCorrections.clear();
00064   sampleBasedDarkCorrections.clear();
00065   lineBasedDarkCorrections.clear();
00066   solarRemoveCoefficient = 1.0;
00067   iof = (ui.GetString("UNITS") == "IOF");
00068 
00069   calibInfo = PvlGroup("Results");
00070 
00071   ProcessByLine p;
00072   Cube *icube = p.SetInputCube("FROM");
00073 
00074   bool isVims = true;
00075 
00076   try {
00077     isVims = (icube->label()->FindGroup("Instrument",
00078               Pvl::Traverse)["InstrumentId"][0] == "VIMS");
00079   }
00080   catch(IException &e) {
00081     isVims = false;
00082   }
00083 
00084   if(!isVims) {
00085     QString msg = "The input cube [" + QString(ui.GetAsString("FROM")) + "] is not a Cassini VIMS cube";
00086     throw IException(IException::User, msg, _FILEINFO_);
00087   }
00088 
00089   if(icube->label()->FindObject("IsisCube").HasGroup("AlphaCube")) {
00090     QString msg = "The input cube [" + QString(ui.GetAsString("FROM")) + "] has had its dimensions modified and can not be calibrated";
00091     throw IException(IException::User, msg, _FILEINFO_);
00092   }
00093 
00094   // done first since it's likely to cause an error if one exists
00095   calculateSolarRemove(icube, &p);
00096 
00097   if(ui.GetBoolean("DARK"))
00098     calculateDarkCurrent(icube);
00099 
00100   chooseFlatFile(icube, &p);
00101   calculateSpecificEnergy(icube);
00102 
00103   calibInfo += PvlKeyword("OutputUnits", ((iof) ? "I/F" : "Specific Energy"));
00104 
00105   Application::Log(calibInfo);
00106 
00107   p.SetOutputCube("TO");
00108   p.StartProcess(calibrate);
00109   p.EndProcess();
00110 
00111   for(unsigned int i = 0; i < tempFiles.size(); i++) {
00112     QFile::remove(tempFiles[i]);
00113   }
00114 
00115   tempFiles.clear();
00116 }
00117 
00118 /**
00119  * This applies the calculated calibration coefficients to the file.
00120  *
00121  * @param inBuffers
00122  * @param outBuffers
00123  */
00124 void calibrate(vector<Buffer *> &inBuffers, vector<Buffer *> &outBuffers) {
00125   Buffer *inBuffer = inBuffers[0];
00126   Buffer *flatFieldBuffer = inBuffers[1];
00127   Buffer *solarRemoveBuffer = NULL; // this is optional
00128 
00129   if(inBuffers.size() > 2) {
00130     inBuffer = inBuffers[0];
00131     solarRemoveBuffer = inBuffers[1];
00132     flatFieldBuffer = inBuffers[2];
00133   }
00134 
00135   Buffer *outBuffer = outBuffers[0];
00136 
00137   for(int i = 0; i < inBuffer->size(); i++) {
00138     (*outBuffer)[i] = (*inBuffer)[i];
00139 
00140     if(IsSpecial((*outBuffer)[i])) continue;
00141 
00142     map< pair<int, int>, double>::iterator darkCorrection =
00143       sampleBasedDarkCorrections.find(pair<int, int>(i + 1, inBuffer->Band()));
00144 
00145     if(darkCorrection != sampleBasedDarkCorrections.end()) {
00146       (*outBuffer)[i] -= darkCorrection->second;
00147     }
00148 
00149     darkCorrection = lineBasedDarkCorrections.find(pair<int, int>(inBuffer->Line(), inBuffer->Band()));
00150 
00151     if(darkCorrection != lineBasedDarkCorrections.end()) {
00152       if(!IsSpecial(darkCorrection->second))
00153         (*outBuffer)[i] -= darkCorrection->second;
00154       else
00155         (*outBuffer)[i] = Null;
00156     }
00157 
00158     if(!IsSpecial((*flatFieldBuffer)[i]) && !IsSpecial((*outBuffer)[i])) {
00159       (*outBuffer)[i] /= (*flatFieldBuffer)[i];
00160     }
00161 
00162     if(inBuffer->Band() <= (int)specificEnergyCorrections.size() && !IsSpecial((*outBuffer)[i])) {
00163       (*outBuffer)[i] *= specificEnergyCorrections[inBuffer->Band()-1];
00164     }
00165 
00166     if(iof && solarRemoveBuffer && !IsSpecial((*outBuffer)[i])) {
00167       (*outBuffer)[i] = (*outBuffer)[i] / ((*solarRemoveBuffer)[i] / solarRemoveCoefficient) * Isis::PI;
00168     }
00169   }
00170 }
00171 
00172 /**
00173  * This calculates the values necessary to convert from
00174  * specific energy to I/F. A cube is used as part of the
00175  * equation (which probably* just contains a vector of values)
00176  * so p->SetInputCube(...) will be called with the appropriate
00177  * filename.
00178  *
00179  * @param icube
00180  * @param p
00181  */
00182 void calculateSolarRemove(Cube *icube, ProcessByLine *p) {
00183   UserInterface &ui = Application::GetUserInterface();
00184   if(ui.GetString("UNITS") != "IOF") return;
00185 
00186   Camera *cam = NULL;
00187 
00188   try {
00189     cam = icube->camera();
00190   }
00191   catch(IException &e) {
00192     QString msg = "Unable to create a camera model from [" +
00193                   icube->fileName() + "]. Please run "
00194                   "spiceinit on this file";
00195     throw IException(e, IException::Unknown, msg, _FILEINFO_);
00196   }
00197 
00198 
00199   solarRemoveCoefficient = -1;
00200 
00201   // try center first
00202   if(cam->SetImage(icube->sampleCount() / 2, icube->lineCount() / 2)) {
00203     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00204   }
00205 
00206   // try 4 corners
00207   if(solarRemoveCoefficient < 0 && cam->SetImage(1.0, 1.0)) {
00208     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00209   }
00210 
00211   if(solarRemoveCoefficient < 0 && cam->SetImage(icube->sampleCount(), 1.0)) {
00212     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00213   }
00214 
00215   if(solarRemoveCoefficient < 0 && cam->SetImage(icube->sampleCount(),
00216       icube->lineCount())) {
00217     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00218   }
00219 
00220   if(solarRemoveCoefficient < 0 && cam->SetImage(1.0, icube->lineCount())) {
00221     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00222   }
00223 
00224   // try center of 4 edges
00225   if(solarRemoveCoefficient < 0 && cam->SetImage(icube->sampleCount() / 2, 1.0)) {
00226     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00227   }
00228 
00229   if(solarRemoveCoefficient < 0 && cam->SetImage(icube->sampleCount(),
00230       icube->lineCount() / 2)) {
00231     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00232   }
00233 
00234   if(solarRemoveCoefficient < 0 && cam->SetImage(icube->sampleCount() / 2,
00235       icube->lineCount())) {
00236     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00237   }
00238 
00239   if(solarRemoveCoefficient < 0 && cam->SetImage(1.0, icube->lineCount() / 2)) {
00240     solarRemoveCoefficient = cam->SolarDistance() * cam->SolarDistance();
00241   }
00242 
00243   // Default to original vimscal's solar distance if we can't find a target
00244   if(solarRemoveCoefficient < 0) {
00245     solarRemoveCoefficient = 81.595089;
00246     /*
00247     QString msg = "Unable to project image at four corners, center of edges or ";
00248     msg += "at center. The solar distance can not be calculated, try using";
00249     msg += " [UNITS=SPECENERGY] on [";
00250     msg += icube->FileName() + "]";
00251     throw iException::Message(iException::Camera, msg, _FILEINFO_);
00252     */
00253   }
00254 
00255   bool vis = (icube->label()->
00256               FindGroup("Instrument", Pvl::Traverse)["Channel"][0] != "IR");
00257 
00258   QString attributes;
00259 
00260   // vis is bands 1-96, ir is bands 97-352 in this calibration file
00261   if(vis) {
00262     attributes = "+1-96";
00263   }
00264   else {
00265     attributes = "+97-352";
00266   }
00267 
00268   CubeAttributeInput iatt(attributes);
00269 
00270   FileName solarFileName("$cassini/calibration/vims/solar_v????.cub");
00271   solarFileName = solarFileName.highestVersion();
00272 
00273   p->SetInputCube(createCroppedFile(icube, solarFileName.expanded()), iatt);
00274 }
00275 
00276 
00277 /**
00278  * This calculates the coefficients for specific energy corrections
00279  */
00280 void calculateSpecificEnergy(Cube *icube) {
00281   PvlGroup &inst = icube->label()->FindGroup("Instrument", Pvl::Traverse);
00282   bool vis = (inst["Channel"][0] != "IR");
00283 
00284   double coefficient = 1.0;
00285 
00286   if(inst["GainMode"][0] == "HIGH") {
00287     coefficient /= 2;
00288   }
00289 
00290   if(vis && inst["SamplingMode"][0] == "HI-RES") {
00291     coefficient *= 3;
00292   }
00293 
00294   if(vis) {
00295     coefficient /= toDouble(inst["ExposureDuration"][1]) / 1000.0;
00296   }
00297   else {
00298     coefficient /= (toDouble(inst["ExposureDuration"][0])) / 1000.0 - 0.004;
00299   }
00300 
00301   QString specEnergyFile = "$cassini/calibration/vims/";
00302 
00303   if(vis) {
00304     specEnergyFile += "vis_perf_v????.cub";
00305   }
00306   else {
00307     specEnergyFile += "ir_perf_v????.cub";
00308   }
00309 
00310   QString waveCalFile = "$cassini/calibration/vims/wavecal_v????.cub";
00311 
00312   FileName specEnergyFileName(specEnergyFile);
00313   specEnergyFileName = specEnergyFileName.highestVersion();
00314 
00315   FileName waveCalFileName(waveCalFile);
00316   waveCalFileName = waveCalFileName.highestVersion();
00317 
00318   Cube specEnergyCube;
00319   specEnergyCube.open(specEnergyFileName.expanded());
00320 
00321   Cube waveCalCube;
00322   waveCalCube.open(waveCalFileName.expanded());
00323 
00324   LineManager specEnergyMgr(specEnergyCube);
00325   LineManager waveCalMgr(waveCalCube);
00326 
00327   for(int i = 0; i < icube->bandCount(); i++) {
00328     Statistics specEnergyStats;
00329     Statistics waveCalStats;
00330 
00331     if(vis) {
00332       specEnergyMgr.SetLine(1, i + 1);
00333       waveCalMgr.SetLine(1, i + 1);
00334     }
00335     else {
00336       specEnergyMgr.SetLine(1, i + 1);
00337       // ir starts at band 97
00338       waveCalMgr.SetLine(1, i + 96 + 1);
00339     }
00340 
00341     specEnergyCube.read(specEnergyMgr);
00342     waveCalCube.read(waveCalMgr);
00343 
00344     specEnergyStats.AddData(specEnergyMgr.DoubleBuffer(), specEnergyMgr.size());
00345     waveCalStats.AddData(waveCalMgr.DoubleBuffer(), waveCalMgr.size());
00346 
00347     double bandCoefficient = coefficient * specEnergyStats.Average() * waveCalStats.Average();
00348 
00349     specificEnergyCorrections.push_back(bandCoefficient);
00350   }
00351 }
00352 
00353 /**
00354  * This decides if we have a VIS or IR dark current correction and calls the
00355  * appropriate method.
00356  *
00357  * @param currCube
00358  */
00359 void calculateDarkCurrent(Cube *icube) {
00360   PvlGroup &inst = icube->label()->FindGroup("Instrument", Pvl::Traverse);
00361   bool vis = (inst["Channel"][0] != "IR");
00362 
00363   calibInfo += PvlKeyword("Vis", ((vis) ? "true" : "false"));
00364 
00365   if(vis) {
00366     calculateVisDarkCurrent(icube);
00367   }
00368   else {
00369     calculateIrDarkCurrent(icube);
00370   }
00371 }
00372 
00373 /**
00374  * This populates darkCorrections with the result of the equation:
00375  *   dark = a + x * b
00376  * for each line,band. a, b are from the "vis_*_dark_model.tab" files
00377  * and x is the ExposureDuration.
00378  *
00379  * @param icube
00380  */
00381 void calculateVisDarkCurrent(Cube *icube) {
00382   PvlGroup &inst = icube->label()->FindGroup("Instrument", Pvl::Traverse);
00383 
00384   // This is the dark current corrections for VIS
00385   bool hires = ((inst["SamplingMode"][0] == "HIGH") || (inst["SamplingMode"][0] == "HI-RES"));
00386   QString calFile = "$cassini/calibration/vims/vis_";
00387 
00388   if(hires) {
00389     calFile += "hires";
00390   }
00391   else {
00392     calFile += "lowres";
00393   }
00394 
00395   calFile += "_dark_model_v????.tab";
00396 
00397   FileName calFileName(calFile);
00398   calFileName = calFileName.highestVersion();
00399 
00400   calibInfo += PvlKeyword("DarkCurrentFile", calFileName.originalPath() + "/" + calFileName.name());
00401 
00402   calFile = calFileName.expanded();
00403 
00404   EndianSwapper swapper("LSB");
00405 
00406   FILE *calFilePtr = fopen(calFile.toAscii().data(), "r");
00407 
00408   double visExposure = toDouble(inst["ExposureDuration"][1]);
00409 
00410   int sampleOffset, lineOffset;
00411   GetOffsets(*icube->label(), sampleOffset, lineOffset);
00412 
00413   /**
00414    * Reading in one parameter at a time:
00415    *   parameter 1 = constant coefficient
00416    *   parameter 2 = exposure coefficient
00417    *   param1 + param2*exposure = dark correction
00418    *
00419    * Do byte swapping where necessary.
00420    */
00421   for(int parameter = 1; parameter <= 2; parameter ++) {
00422     for(int band = 1; band <= 96; band++) {
00423       for(int sample = 1; sample <= 64; sample++) {
00424         float calData;
00425 
00426         if(fread(&calData, sizeof(calData), 1, calFilePtr) != 1) {
00427           // error!
00428           QString msg = "Error reading file [" + calFile + "]";
00429           throw IException(IException::Io, msg, _FILEINFO_);
00430         }
00431 
00432         int associatedSample = sample - sampleOffset + 1;
00433 
00434         calData = swapper.Float(&calData);
00435         pair<int, int> index = pair<int, int>(associatedSample, band);
00436 
00437         map< pair<int, int>, double>::iterator pos = sampleBasedDarkCorrections.find(index);
00438         if(pos == sampleBasedDarkCorrections.end()) {
00439           sampleBasedDarkCorrections.insert(pair< pair<int, int>, double>(index, calData));
00440         }
00441         else {
00442           (*pos).second = (*pos).second + visExposure * calData;
00443         }
00444       }
00445     }
00446   }
00447 
00448   fclose(calFilePtr);
00449 
00450   // If spectral summing is on, go in sets of 8 and set darks to the average
00451   /*
00452   PvlGroup &archive = icube->Label()->FindGroup("Archive", Pvl::Traverse);
00453   if(archive["SpectralSummingFlag"][0] == "ON") {
00454     for(int band = 1; band <= 96; band += 8) {
00455       for(int sample = 1; sample <= 64; sample++) {
00456         Statistics stats;
00457 
00458         // we're calculating an average of 8 of these values through the bands
00459         for(int avgIndex = 0; avgIndex < 8; avgIndex ++) {
00460           // this wont go out of bounds as long as we have 8 as the increment
00461           pair<int,int> index = pair<int,int>(sample, band+avgIndex);
00462           stats.AddData(sampleBasedDarkCorrections.find(index)->second);
00463         }
00464 
00465         // now set the values to the average
00466         for(int setIndex = 0; setIndex < 8; setIndex ++) {
00467           pair<int,int> index = pair<int,int>(sample, band+setIndex);
00468           if(!setIndex) printf("Changing %.4f to %.4f\n", sampleBasedDarkCorrections.find(index)->second, stats.Average());
00469           sampleBasedDarkCorrections.find(index)->second = stats.Average();
00470         }
00471       }
00472     }
00473   }*/
00474 }
00475 
00476 /**
00477  * This calculates the dark current corrections for IR. If
00478  * IRDARKAVG is false, then it translates the sideplane data
00479  * into the lineBasedDarkCorrections map directly and does nothing further
00480  * with the data. Otherwise, this will apply a least squares linear
00481  * fit (the original script did chi-squared, but this is okay) for
00482  * each band and uses the points on the line instead of the sideplane
00483  * data directly.
00484  *
00485  * @param icube
00486  */
00487 void calculateIrDarkCurrent(Cube *icube) {
00488   UserInterface &ui = Application::GetUserInterface();
00489 
00490   bool found = false;
00491 
00492   // verify if IR we have sideplane data
00493   for(int obj = 0; !found && obj < icube->label()->Objects(); obj++) {
00494     PvlObject &object = icube->label()->Object(obj);
00495 
00496     if(object.Name() != "Table") continue;
00497 
00498     if(object.HasKeyword("Name") && object["Name"][0] == "SideplaneIr") found = true;
00499   }
00500 
00501   if(!found) {
00502     calibInfo += PvlKeyword("SideplaneCorrection", "None");
00503     return;
00504   }
00505 
00506   Table sideplane("SideplaneIr", ui.GetFileName("FROM"));
00507 
00508   // If spectal summing is on OR compressor_id isnt N/A then
00509   //   just return.
00510   PvlGroup &archive = icube->label()->FindGroup("Archive", Pvl::Traverse);
00511 
00512   // If dark subtracted (compressorid is valid) and cant do linear
00513   //   correction (spectral editing flag on) then do not do dark
00514   if(archive["CompressorId"][0] != "N/A" &&
00515       archive["SpectralEditingFlag"][0] == "ON") {
00516     calibInfo += PvlKeyword("SideplaneCorrection", "None");
00517     return;
00518   }
00519 
00520   // If subtracted (compressor id is valid) and dont do linear then return
00521   if(archive["CompressorId"][0] != "N/A" && ui.GetBoolean("IRORIGDARK") == true) {
00522     calibInfo += PvlKeyword("SideplaneCorrection", "None");
00523     return;
00524   }
00525 
00526   if(archive["SpectralSummingFlag"][0] == "ON") return;
00527 
00528   // Insert the sideplane data into our lineBasedDarkCorrections map (line,band to correction)
00529   for(int line = 1; line <= icube->lineCount(); line++) {
00530     for(int band = 1; band <= icube->bandCount(); band++) {
00531       pair<int, int> index = pair<int, int>(line, band);
00532       int value = (int)sideplane[(line-1)*icube->bandCount() + (band-1)][2];
00533 
00534       if(value != 57344)
00535         lineBasedDarkCorrections.insert(pair< pair<int, int>, double>(index, value));
00536       else
00537         lineBasedDarkCorrections.insert(pair< pair<int, int>, double>(index, Null));
00538     }
00539   }
00540 
00541   if(ui.GetBoolean("IRORIGDARK") == true) {
00542     calibInfo += PvlKeyword("SideplaneCorrection", "Sideplane");
00543     return;
00544   }
00545 
00546   // do linear fits
00547   for(int band = 1; band <= icube->bandCount(); band++) {
00548     PolynomialUnivariate basis(1);
00549     LeastSquares lsq(basis);
00550 
00551     for(int line = 1; line <= icube->lineCount(); line++) {
00552       pair<int, int> index = pair<int, int>(line, band);
00553       map< pair<int, int>, double>::iterator val = lineBasedDarkCorrections.find(index);
00554 
00555       if(val != lineBasedDarkCorrections.end()) {
00556         vector<double> input;
00557         input.push_back(line);
00558         double expected = val->second;
00559 
00560         if(!IsSpecial(expected))
00561           lsq.AddKnown(input, expected);
00562       }
00563     }
00564 
00565     if(lsq.Rows() == 0) return;
00566     lsq.Solve();
00567 
00568     double coefficients[2] = {
00569       basis.Coefficient(0),
00570       basis.Coefficient(1)
00571     };
00572 
00573     for(int line = 1; line <= icube->lineCount(); line++) {
00574       pair<int, int> index = pair<int, int>(line, band);
00575 
00576       map< pair<int, int>, double>::iterator val = lineBasedDarkCorrections.find(index);
00577       if(val != lineBasedDarkCorrections.end()) {
00578         double currentDark = val->second;
00579 
00580         if(!IsSpecial(currentDark)) {
00581           double newDark = coefficients[0] + line * coefficients[1];
00582 
00583           // initial dark applied by compressor
00584           if(archive["CompressorId"][0] != "N/A") {
00585             // input is in (dn-dark) units
00586             // (dn-dark) - (fit-dark) = dn-fit
00587             newDark -= currentDark;
00588           }
00589 
00590           val->second = newDark;
00591         }
00592       }
00593     }
00594   }
00595 
00596   if(archive["CompressorId"][0] != "N/A") {
00597     calibInfo += PvlKeyword("SideplaneCorrection", "Fit Delta");
00598   }
00599   else {
00600     calibInfo += PvlKeyword("SideplaneCorrection", "Fit");
00601   }
00602 }
00603 
00604 /**
00605  * This calls p->SetInputCube with the appropriate flat file needed for
00606  * icube.
00607  *
00608  * @param icube
00609  * @param p
00610  */
00611 void chooseFlatFile(Cube *icube, ProcessByLine *p) {
00612   PvlGroup &inst = icube->label()->FindGroup("Instrument", Pvl::Traverse);
00613   bool vis = (inst["Channel"][0] != "IR");
00614   bool hires = ((inst["SamplingMode"][0] == "HIGH") || (inst["SamplingMode"][0] == "HI-RES"));
00615 
00616   QString calFile = "$cassini/calibration/vims/flatfield/";
00617 
00618   if(vis) {
00619     calFile += "vis_";
00620   }
00621   else {
00622     calFile += "ir_";
00623   }
00624 
00625   if(hires) {
00626     calFile += "hires_flatfield_v????.cub";
00627   }
00628   else {
00629     calFile += "flatfield_v????.cub";
00630   }
00631 
00632   FileName calibrationFileName(calFile);
00633   calibrationFileName = calibrationFileName.highestVersion();
00634 
00635   calibInfo += PvlKeyword("FlatFile", calibrationFileName.originalPath() + "/" + calibrationFileName.name());
00636 
00637   CubeAttributeInput iatt;
00638   p->SetInputCube(createCroppedFile(icube, calibrationFileName.expanded(), true), iatt);
00639 }
00640 
00641 /**
00642  * This makes our calibration files match the input cube described
00643  * by the swath keywords.
00644  *
00645  * @param icube
00646  * @param cubeFileName
00647  *
00648  * @return QString
00649  */
00650 QString createCroppedFile(Cube *icube, QString cubeFileName, bool flatFile) {
00651   int sampOffset = 1;
00652   int lineOffset = 1;
00653 
00654   if(flatFile) {
00655     GetOffsets(*icube->label(), sampOffset, lineOffset);
00656   }
00657 
00658 
00659   QString appArgs = "from=" + cubeFileName + " ";
00660   appArgs += "sample=" + toString(sampOffset) + " ";
00661   appArgs += "line=" + toString(lineOffset) + " ";
00662   appArgs += "nsamples=" + toString(icube->sampleCount()) + " ";
00663   appArgs += "nlines=" + toString(icube->lineCount()) + " ";
00664 
00665   FileName tempFile("$TEMPORARY/tmp_" + FileName(cubeFileName).baseName() +
00666                     "_" + FileName(icube->fileName()).name());
00667 
00668   appArgs += "to=" + tempFile.expanded();
00669 
00670   ProgramLauncher::RunIsisProgram("crop", appArgs);
00671   tempFiles.push_back(tempFile.expanded());
00672   return tempFile.expanded();
00673 }
00674 
00675 void GetOffsets(const Pvl &lab, int &finalSampOffset, int &finalLineOffset) {
00676   const PvlGroup &inst = lab.FindGroup("Instrument", Pvl::Traverse);
00677 
00678   //  Get sample/line offsets
00679   int sampOffset = inst ["XOffset"];
00680   int lineOffset = inst ["ZOffset"];
00681 
00682   //  Get swath width/length which will be image size unless occultation image
00683   int swathWidth = inst ["SwathWidth"];
00684   int swathLength = inst ["SwathLength"];
00685 
00686   bool vis = (inst["Channel"][0] != "IR");
00687   finalSampOffset = sampOffset;
00688   finalLineOffset = lineOffset;
00689 
00690   QString samplingMode = QString(inst["SamplingMode"]).toUpper();
00691   if(vis) {
00692     if(samplingMode == "NORMAL") {
00693       finalSampOffset = sampOffset - 1;
00694       finalLineOffset = lineOffset - 1;
00695     }
00696     else if(samplingMode == "HI-RES") {
00697       finalSampOffset = (3 * ((sampOffset - 1) + swathWidth / 2)) - swathWidth / 2;
00698       finalLineOffset = (3 * (lineOffset + swathLength / 2)) - swathLength / 2;
00699     }
00700     else {
00701       QString msg = "Unsupported sampling mode [" + samplingMode + "]";
00702       throw IException(IException::Io, msg, _FILEINFO_);
00703     }
00704   }
00705   else {
00706     if(samplingMode == "NORMAL") {
00707       finalSampOffset = sampOffset - 1;
00708       finalLineOffset = lineOffset - 1;
00709     }
00710     else if(samplingMode == "HI-RES") {
00711       finalSampOffset = 2 * ((sampOffset - 1) + ((swathWidth - 1) / 4));
00712       finalLineOffset = lineOffset - 1;
00713     }
00714     else if(samplingMode == "NYQUIST") {
00715       QString msg = "Cannot process NYQUIST (undersampled) mode ";
00716       throw IException(IException::Unknown, msg, _FILEINFO_);
00717     }
00718     else {
00719       QString msg = "Unsupported sampling mode [" + samplingMode + "]";
00720       throw IException(IException::Unknown, msg, _FILEINFO_);
00721     }
00722   }
00723 
00724   finalSampOffset ++;
00725   finalLineOffset ++;
00726 }