/** * Constructs an Apollo Metric Camera object using the image labels. * * @param lab Pvl label from an Apollo Metric image. * * @internal * @history 2011-01-14 Travis Addair - Added new CK/SPK accessor methods. * @history 2011-05-03 Jeannie Walldren - Added documentation. */ ApolloMetricCamera::ApolloMetricCamera(Pvl &lab) : FramingCamera(lab) { NaifStatus::CheckErrors(); // Get the camera characteristics SetFocalLength(); SetPixelPitch(); // Setup detector map new CameraDetectorMap(this); // Setup focal plane map CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this, naifIkCode()); focalMap->SetDetectorOrigin(ParentSamples() / 2.0, ParentLines() / 2.0); QString ppKey("INS" + toString(naifIkCode()) + "_PP"); QString odkKey("INS" + toString(naifIkCode()) + "_OD_K"); QString decenterKey("INS" + toString(naifIkCode()) + "_DECENTER"); new ApolloMetricDistortionMap(this, getDouble(ppKey, 0), getDouble(ppKey, 1), getDouble(odkKey, 0), getDouble(odkKey, 1), getDouble(odkKey, 2), getDouble(decenterKey, 0), getDouble(decenterKey, 1), getDouble(decenterKey, 2)); // Setup the ground and sky map new CameraGroundMap(this); new CameraSkyMap(this); const PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse); // The Spacecraft Name should be either Apollo 15, 16, or 17. The name // itself could be formatted any number of ways, but the number contained // in the name should be unique between the missions QString spacecraft = inst.findKeyword("SpacecraftName")[0]; if (spacecraft.contains("15")) { p_ckFrameId = -915240; p_ckReferenceId = 1400015; p_spkTargetId = -915; } else if (spacecraft.contains("16")) { p_ckFrameId = -916240; p_ckReferenceId = 1400016; p_spkTargetId = -916; } else if (spacecraft.contains("17")) { p_ckFrameId = -917240; p_ckReferenceId = 1400017; p_spkTargetId = -917; } else { QString msg = "File does not appear to be an Apollo image"; throw IException(IException::User, msg, _FILEINFO_); } // Create a cache and grab spice info since it does not change for // a framing camera (fixed spacecraft position and pointing) // Convert the start time to et setTime((QString)inst["StartTime"]); LoadCache(); NaifStatus::CheckErrors(); }
/** * Constructor for the Themis Vis Camera Model * * @param lab Pvl label from an Odyssey Themis VIS image. * * @throws IException::User - The image does not appear to be a Themis * VIS image * @internal * @history 2010-08-04 Jeannie Walldren - Added NAIF error check. */ ThemisVisCamera::ThemisVisCamera(Pvl &lab) : PushFrameCamera(lab) { NaifStatus::CheckErrors(); // Set up the camera characteristics // LoadFrameMounting("M01_SPACECRAFT","M01_THEMIS_VIS"); // Changed Focal Length from 203.9 (millimeters????) to 202.059, per request from // Christopher Edwards ([email protected]) at ASU, on 2/18/11. SetFocalLength(202.059); SetPixelPitch(0.009); PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse); // make sure it is a themis vis image if(inst["InstrumentId"][0] != "THEMIS_VIS") { string msg = "The image does not appear to be a Themis Vis Image"; throw IException(IException::User, msg, _FILEINFO_); } // Get necessary variables p_exposureDur = inst["ExposureDuration"]; p_interframeDelay = inst["InterframeDelay"]; int sumMode = inst["SpatialSumming"]; // Get the start and end time double et; QString stime = inst["SpacecraftClockCount"]; et = getClockTime(stime).Et(); double offset = inst["SpacecraftClockOffset"]; p_etStart = et + offset - ((p_exposureDur / 1000.0) / 2.0); p_nframes = inst["NumFramelets"]; // Get the keywords from labels PvlGroup &bandBin = lab.findGroup("BandBin", Pvl::Traverse); PvlKeyword &orgBand = bandBin["OriginalBand"]; for(int i = 0; i < orgBand.size(); i++) { p_originalBand.push_back(toInt(orgBand[i])); } // Setup detector map double frameRate = p_interframeDelay; PushFrameCameraDetectorMap *dmap = new PushFrameCameraDetectorMap(this, p_etStart, frameRate, 192); dmap->SetDetectorSampleSumming(sumMode); dmap->SetDetectorLineSumming(sumMode); // Setup focal plane map CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this, naifIkCode()); focalMap->SetDetectorOrigin(512.5, 512.5); // Setup distortion map new ThemisVisDistortionMap(this); // Setup the ground and sky map bool evenFramelets = (inst["Framelets"][0] == "Even"); new PushFrameCameraGroundMap(this, evenFramelets); new CameraSkyMap(this); LoadCache(); NaifStatus::CheckErrors(); }
/** * @internal * @history 2008-11-05 Jeannie Walldren - Replaced reference * to MocLabels IsWideAngleRed() with MocLabels * WideAngleRed(). */ MocWideAngleCamera::MocWideAngleCamera (Isis::Pvl &lab) : LineScanCamera(lab) { // See if we have a moc camera MocLabels *moclab = new Isis::Mgs::MocLabels(lab); double lineRate = moclab->LineRate(); double csum = moclab->CrosstrackSumming(); double dsum = moclab->DowntrackSumming(); double ss = moclab->FirstLineSample(); bool isRed = moclab->WideAngleRed(); // Set up the camera info from ik/iak kernels // LoadEulerMounting(); SetFocalLength(); SetPixelPitch(); if ( PixelPitch() == 1) { throw iException::Message(iException::User, "Cube file needs to be spiceinit'd with updated iak",_FILEINFO_); } InstrumentRotation()->SetTimeBias(-1.15); // Get the start time from labels Isis::PvlGroup &inst = lab.FindGroup ("Instrument",Isis::Pvl::Traverse); string stime = inst["SpacecraftClockCount"]; SpiceDouble etStart; scs2e_c (NaifSpkCode(),stime.c_str(),&etStart); // Setup detector map MocWideAngleDetectorMap *detectorMap = new MocWideAngleDetectorMap(this,etStart,lineRate,moclab); detectorMap->SetDetectorSampleSumming(csum); detectorMap->SetDetectorLineSumming(dsum); detectorMap->SetStartingDetectorSample(ss); // Setup focal plane map CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this,NaifIkCode()); if (isRed) { focalMap->SetDetectorOrigin(1674.65,0.0); focalMap->SetDetectorOffset(0.0,6.7785); } else { focalMap->SetDetectorOrigin(1688.58,0.0); focalMap->SetDetectorOffset(0.0,-0.8486); } // Setup distortion map new MocWideAngleDistortionMap(this,isRed); // Setup the ground and sky map new LineScanCameraGroundMap(this); new LineScanCameraSkyMap(this); LoadCache(); }
HiresCamera::HiresCamera (Pvl &lab) : FramingCamera(lab) { // Get the camera characteristics iString filter = (string)(lab.FindGroup("BandBin", Pvl::Traverse))["FilterName"]; filter = filter.UpCase(); SetFocalLength (); SetPixelPitch (); // Get the start time in et PvlGroup inst = lab.FindGroup ("Instrument",Pvl::Traverse); string stime = inst["StartTime"]; double time; str2et_c(stime.c_str(),&time); // Do not correct time for center of the exposure duration. This is because // the kernels were built to accept the start times of the images. // time += ((double)inst["ExposureDuration"] / 1000.0) / 2.0; // Add half // exposure duration in milliseconds // Setup detector map new CameraDetectorMap(this); // Setup focal plane map CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this,NaifIkCode()); focalMap->SetDetectorOrigin ( Spice::GetDouble("INS" + (iString)(int)NaifIkCode() + "_BORESIGHT_SAMPLE"), Spice::GetDouble("INS" + (iString)(int)NaifIkCode() + "_BORESIGHT_LINE")); // Setup distortion map new CameraDistortionMap(this); // Setup the ground and sky map new CameraGroundMap(this); new CameraSkyMap(this); SetEphemerisTime(time); LoadCache(); }
IssWACamera::IssWACamera (Pvl &lab) : FramingCamera(lab) { PvlGroup bandBin = lab.FindGroup ("BandBin",Pvl::Traverse); // Get the camera characteristics iString key = string("INS"+(iString)(int)NaifIkCode()+"_")+ (string)bandBin["FilterName"] + "_FOCAL_LENGTH"; key = key.Convert("/",'_'); double focalLength = Spice::GetDouble(key); SetFocalLength (focalLength); SetPixelPitch (); InstrumentRotation()->SetFrame(Spice::GetInteger("INS_"+(iString)(int)NaifIkCode()+"_FRAME_ID")); // Get the start time in et PvlGroup inst = lab.FindGroup ("Instrument",Pvl::Traverse); string stime = inst["StartTime"]; double et; str2et_c(stime.c_str(),&et); double exposureDuration = (double)inst["ExposureDuration"] /1000.0; et += exposureDuration / 2.0; // Setup detector map int summingMode = inst["SummingMode"]; CameraDetectorMap *detectorMap = new CameraDetectorMap(this); detectorMap->SetDetectorLineSumming(summingMode); detectorMap->SetDetectorSampleSumming(summingMode); // Setup focal plane map CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this,NaifIkCode()); focalMap->SetDetectorOrigin (Spice::GetDouble("INS" + (iString)(int)NaifIkCode() + "_BORESIGHT_SAMPLE"), Spice::GetDouble("INS" + (iString)(int)NaifIkCode() + "_BORESIGHT_LINE")); // Setup distortion map double k1 = Spice::GetDouble("INS" + (iString)(int)NaifIkCode() + "_K1"); new RadialDistortionMap(this, k1); // Setup the ground and sky map new CameraGroundMap(this); new CameraSkyMap(this); SetEphemerisTime(et); LoadCache(); }
// constructors LoHighCamera::LoHighCamera (Pvl &lab) : FramingCamera(lab) { // Get the Instrument label information needed to define the camera for this frame PvlGroup inst = lab.FindGroup ("Instrument",Pvl::Traverse); iString spacecraft = (string)inst["SpacecraftName"]; iString instId = (string)inst["InstrumentId"]; // Turn off the aberration corrections for the instrument position object InstrumentPosition()->SetAberrationCorrection("NONE"); // Get the camera characteristics SetFocalLength (); SetPixelPitch (); // Get the start time in et string stime = inst["StartTime"]; double time; str2et_c(stime.c_str(),&time); // Setup focal plane map LoCameraFiducialMap fid( inst, NaifIkCode()); // Setup detector map new CameraDetectorMap(this); // Setup focalplane map CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this,NaifIkCode()); // Try (0.,0.) focalMap->SetDetectorOrigin(0.0,0.0); // Setup distortion map LoHighDistortionMap *distortionMap = new LoHighDistortionMap(this); distortionMap->SetDistortion(NaifIkCode()); // Setup the ground and sky map new CameraGroundMap(this); new CameraSkyMap(this); SetEphemerisTime(time); LoadCache(); }
/** * Creates a Mariner10 Camera Model * * @param lab Pvl label from a Mariner 10 image. * * @throw iException::User - "File does not appear to be a Mariner 10 image. * Invalid InstrumentId." * @throw iException::Programmer - "Unable to create distortion map." * @internal * @history 2011-05-03 Jeannie Walldren - Added NAIF error check. * */ Mariner10Camera::Mariner10Camera(Pvl &lab) : FramingCamera(lab) { NaifStatus::CheckErrors(); // Turn off the aberration corrections for instrument position object instrumentPosition()->SetAberrationCorrection("NONE"); instrumentRotation()->SetFrame(-76000); // Set camera parameters SetFocalLength(); SetPixelPitch(); PvlGroup inst = lab.findGroup("Instrument", Pvl::Traverse); // Get utc start time QString stime = inst["StartTime"]; iTime startTime; startTime.setUtc((QString)inst["StartTime"]); setTime(startTime); // Setup detector map new CameraDetectorMap(this); // Setup focal plane map, and detector origin CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this, naifIkCode()); QString ikernKey = "INS" + toString((int)naifIkCode()) + "_BORESIGHT_SAMPLE"; double sampleBoresight = getDouble(ikernKey); ikernKey = "INS" + toString((int)naifIkCode()) + "_BORESIGHT_LINE"; double lineBoresight = getDouble(ikernKey); focalMap->SetDetectorOrigin(sampleBoresight, lineBoresight); // Setup distortion map which is dependent on encounter, use start time // MOON: 1973-11-08T03:16:26.350 QString spacecraft = (QString)inst["SpacecraftName"]; QString instId = (QString)inst["InstrumentId"]; QString cam; if(instId == "M10_VIDICON_A") { cam = "a"; } else if(instId == "M10_VIDICON_B") { cam = "b"; } else { QString msg = "File does not appear to be a Mariner10 image. InstrumentId [" + instId + "] is invalid Mariner 10 value."; throw IException(IException::User, msg, _FILEINFO_); } QString fname = FileName("$mariner10/reseaus/mar10" + cam + "MasterReseaus.pvl").expanded(); try { new ReseauDistortionMap(this, lab, fname); } catch(IException &e) { string msg = "Unable to create distortion map."; throw IException(e, IException::Programmer, msg, _FILEINFO_); } // Setup the ground and sky map new CameraGroundMap(this); new CameraSkyMap(this); LoadCache(); NaifStatus::CheckErrors(); }
/** * Creates a Hirise Camera Model * * @param lab Pvl label from the iamge * @internal * @history 2011-05-03 Jeannie Walldren - Added NAIF error check. */ HiriseCamera::HiriseCamera(Cube &cube) : LineScanCamera(cube) { NaifStatus::CheckErrors(); // Setup camera characteristics from instrument and frame kernel SetFocalLength(); SetPixelPitch(); //LoadFrameMounting("MRO_SPACECRAFT", "MRO_HIRISE_OPTICAL_AXIS"); instrumentRotation()->SetFrame(-74690); // Get required keywords from instrument group Pvl &lab = *cube.label(); PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse); int tdiMode = inst["Tdi"]; double binMode = inst["Summing"]; int chan = inst["ChannelNumber"]; int cpmm = inst["CpmmNumber"]; double deltaLineTimerCount = inst["DeltaLineTimerCount"]; QString stime = inst["SpacecraftClockStartCount"]; // Convert CPMM number to CCD number static int cpmm2ccd[] = {0, 1, 2, 3, 12, 4, 10, 11, 5, 13, 6, 7, 8, 9}; int ccd = cpmm2ccd[cpmm]; // Compute the line rate, convert to seconds, and multiply by the // downtrack summing double unBinnedRate = (74.0 + (deltaLineTimerCount / 16.0)) / 1000000.0; double lineRate = unBinnedRate * binMode; // Convert the spacecraft clock count to ephemeris time SpiceDouble et; // The -74999 is the code to select the transformation from // high-precision MRO SCLK to ET et = getClockTime(stime, -74999).Et(); // Adjust the start time so that it is the effective time for // the first line in the image file. Note that on 2006-03-29, this // time is now subtracted as opposed to adding it. The computed start // time in the EDR is at the first serial line. et -= unBinnedRate * (((double) tdiMode / 2.0) - 0.5); // Effective observation // time for all the TDI lines used for the // first line before doing binning et += unBinnedRate * (((double) binMode / 2.0) - 0.5); // Effective observation time of the first line // in the image file, which is possibly binned // Compute effective line number within the CCD (in pixels) for the // given TDI mode. // This is the "centered" 0-based line number, where line 0 is the // center of the detector array and line numbers decrease going // towards the serial readout. Line number +64 sees a spot // on the ground before line number 0 or -64. double ccdLine_c = -64.0 + ((double) tdiMode / 2.0); // Setup detector map for transform of image pixels to detector position // CameraDetectorMap *detectorMap = // new LineScanCameraDetectorMap(this,et,lineRate); LineScanCameraDetectorMap *detectorMap = new LineScanCameraDetectorMap(this, et, lineRate); detectorMap->SetDetectorSampleSumming(binMode); detectorMap->SetDetectorLineSumming(binMode); if(chan == 0) { detectorMap->SetStartingDetectorSample(1025.0); } // Setup focal plane map for transform of detector position to // focal plane x/y. This will read the appropriate CCD // transformation coefficients from the instrument kernel CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this, -74600 - ccd); focalMap->SetDetectorOrigin(1024.5, 0.0); focalMap->SetDetectorOffset(0.0, ccdLine_c); // Setup distortion map. This will read the optical distortion // coefficients from the instrument kernel CameraDistortionMap *distortionMap = new CameraDistortionMap(this); distortionMap->SetDistortion(naifIkCode()); // Setup the ground and sky map to transform undistorted focal // plane x/y to lat/lon or ra/dec respectively. new LineScanCameraGroundMap(this); new LineScanCameraSkyMap(this); LoadCache(); NaifStatus::CheckErrors(); }
/** Compute undistorted focal plane coordinate from ra/dec * * @param ra right ascension in degrees * @param dec declination in degrees * * @return conversion was successful * @todo what happens if we are looking behind the focal plane????? * @todo what happens if we are looking parallel to the focal plane?? * @todo can lookC[2] == zero imply parallel * @todo can this all be solved by restricting the physical size of * the focal plane? */ bool LineScanCameraSkyMap::SetSky(const double ra, const double dec) { // Get beginning bounding time and offset for iterative loop p_camera->Sensor::setTime(p_camera->Spice::cacheStartTime()); p_camera->Sensor::SetRightAscensionDeclination(ra, dec); double lookC[3]; p_camera->Sensor::LookDirection(lookC); double ux = p_camera->FocalLength() * lookC[0] / lookC[2]; double uy = p_camera->FocalLength() * lookC[1] / lookC[2]; CameraDistortionMap *distortionMap = p_camera->DistortionMap(); if(!distortionMap->SetUndistortedFocalPlane(ux, uy)) return false; double dx = distortionMap->FocalPlaneX(); double dy = distortionMap->FocalPlaneY(); CameraFocalPlaneMap *focalMap = p_camera->FocalPlaneMap(); if(!focalMap->SetFocalPlane(dx, dy)) return false; double startOffset = focalMap->DetectorLineOffset() - focalMap->DetectorLine(); // Get ending bounding time and offset for iterative loop p_camera->Sensor::setTime(p_camera->Spice::cacheEndTime()); p_camera->Sensor::SetRightAscensionDeclination(ra, dec); p_camera->Sensor::LookDirection(lookC); ux = p_camera->FocalLength() * lookC[0] / lookC[2]; uy = p_camera->FocalLength() * lookC[1] / lookC[2]; if(!distortionMap->SetUndistortedFocalPlane(ux, uy)) return false; dx = distortionMap->FocalPlaneX(); dy = distortionMap->FocalPlaneY(); if(!focalMap->SetFocalPlane(dx, dy)) return false; double endOffset = focalMap->DetectorLineOffset() - focalMap->DetectorLine(); // Make sure we are in the image if((startOffset < 0.0) && (endOffset < 0.0)) return false; if((startOffset > 0.0) && (endOffset > 0.0)) return false; // Get everything ordered for iteration double fl, fh, xl, xh; if(startOffset < endOffset) { fl = startOffset; fh = endOffset; xl = p_camera->Spice::cacheStartTime().Et(); xh = p_camera->Spice::cacheEndTime().Et(); } else { fl = endOffset; fh = startOffset; xl = p_camera->Spice::cacheEndTime().Et(); xh = p_camera->Spice::cacheStartTime().Et(); } // Iterate to find the time at which the instrument imaged the ground point LineScanCameraDetectorMap *detectorMap = (LineScanCameraDetectorMap *) p_camera->DetectorMap(); double timeTol = detectorMap->LineRate() / 10.0; for(int j = 0; j < 30; j++) { double etGuess = xl + (xh - xl) * fl / (fl - fh); p_camera->Sensor::setTime(etGuess); p_camera->Sensor::SetRightAscensionDeclination(ra, dec); p_camera->Sensor::LookDirection(lookC); ux = p_camera->FocalLength() * lookC[0] / lookC[2]; uy = p_camera->FocalLength() * lookC[1] / lookC[2]; if(!distortionMap->SetUndistortedFocalPlane(ux, uy)) return false; dx = distortionMap->FocalPlaneX(); dy = distortionMap->FocalPlaneY(); if(!focalMap->SetFocalPlane(dx, dy)) return false; double f = focalMap->DetectorLineOffset() - focalMap->DetectorLine(); double delTime; if(f < 0.0) { delTime = xl - etGuess; xl = etGuess; fl = f; } else { delTime = xh - etGuess; xh = etGuess; fh = f; } // See if we converged on the point so set up the undistorted // focal plane values and return if(fabs(delTime) < timeTol || f == 0.0) { p_focalPlaneX = ux; p_focalPlaneY = uy; return true; } } return false; }
/** * Constructs a Clementine HiresCamera object using the image labels. * * @param lab Pvl label from a Clementine HIRES image. * * @internal * @history 2011-05-03 Jeannie Walldren - Added NAIF error check. Added * call to ShutterOpenCloseTimes() method. Changed * centertime to add half exposure duration to start * time to maintain consistency with other Clementine * models. */ NirCamera::NirCamera(Cube &cube) : FramingCamera(cube) { NaifStatus::CheckErrors(); // Get the camera characteristics Pvl &lab = *cube.label(); QString filter = (QString)(lab.findGroup("BandBin", Pvl::Traverse))["FilterName"]; filter = filter.toUpper(); if(filter.compare("A") == 0) { SetFocalLength(2548.2642 * 0.038); } else if(filter.compare("B") == 0) { SetFocalLength(2530.8958 * 0.038); } else if(filter.compare("C") == 0) { SetFocalLength(2512.6589 * 0.038); } else if(filter.compare("D") == 0) { SetFocalLength(2509.0536 * 0.038); } else if(filter.compare("E") == 0) { SetFocalLength(2490.7378 * 0.038); } else if(filter.compare("F") == 0) { SetFocalLength(2487.8694 * 0.038); } SetPixelPitch(); // Get the start time in et PvlGroup inst = lab.findGroup("Instrument", Pvl::Traverse); // set variables startTime and exposureDuration double et = iTime((QString)inst["StartTime"]).Et(); // divide exposure duration keyword value by 1000 to convert to seconds double exposureDuration = ((double) inst["ExposureDuration"]) / 1000.0; pair<iTime, iTime> shuttertimes = ShutterOpenCloseTimes(et, exposureDuration); /************************************************************************ * The following line was uncommented to maintain consistency within all * clementine camera models. Not sure why the following was originally * commented out: * 2010-08-05 Jeannie Walldren ***********************************************************************/ // Do not correct time for center of the exposure duration. This is because // the kernels were built to accept the start times of the images. iTime centerTime = shuttertimes.first.Et() + exposureDuration / 2.0; // Setup detector map new CameraDetectorMap(this); // Setup focal plane map CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this, naifIkCode()); focalMap->SetDetectorOrigin( Spice::getDouble("INS" + toString(naifIkCode()) + "_BORESIGHT_SAMPLE"), Spice::getDouble("INS" + toString(naifIkCode()) + "_BORESIGHT_LINE")); // Setup distortion map new RadialDistortionMap(this, -0.0006364); // Setup the ground and sky map new CameraGroundMap(this); new CameraSkyMap(this); setTime(centerTime); LoadCache(); NaifStatus::CheckErrors(); }
/** * Constructor for the LRO NAC Camera Model * * @param lab Pvl Label to create the camera model from * * @internal * @history 2011-05-03 Jeannie Walldren - Added NAIF error check. */ LroNarrowAngleCamera::LroNarrowAngleCamera(Cube &cube) : LineScanCamera(cube) { NaifStatus::CheckErrors(); // Set up the camera info from ik/iak kernels SetFocalLength(); SetPixelPitch(); double constantTimeOffset = 0.0, additionalPreroll = 0.0, additiveLineTimeError = 0.0, multiplicativeLineTimeError = 0.0; QString ikernKey = "INS" + toString(naifIkCode()) + "_CONSTANT_TIME_OFFSET"; constantTimeOffset = getDouble(ikernKey); ikernKey = "INS" + toString(naifIkCode()) + "_ADDITIONAL_PREROLL"; additionalPreroll = getDouble(ikernKey); ikernKey = "INS" + toString(naifIkCode()) + "_ADDITIVE_LINE_ERROR"; additiveLineTimeError = getDouble(ikernKey); ikernKey = "INS" + toString(naifIkCode()) + "_MULTIPLI_LINE_ERROR"; multiplicativeLineTimeError = getDouble(ikernKey); // Get the start time from labels Pvl &lab = *cube.label(); PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse); QString stime = inst["SpacecraftClockPrerollCount"]; SpiceDouble etStart; if(stime != "NULL") { etStart = getClockTime(stime).Et(); } else { etStart = iTime((QString)inst["PrerollTime"]).Et(); } // Get other info from labels double csum = inst["SpatialSumming"]; double lineRate = (double) inst["LineExposureDuration"] / 1000.0; double ss = inst["SampleFirstPixel"]; ss += 1.0; lineRate *= 1.0 + multiplicativeLineTimeError; lineRate += additiveLineTimeError; etStart += additionalPreroll * lineRate; etStart += constantTimeOffset; setTime(etStart); // Setup detector map LineScanCameraDetectorMap *detectorMap = new LineScanCameraDetectorMap(this, etStart, lineRate); detectorMap->SetDetectorSampleSumming(csum); detectorMap->SetStartingDetectorSample(ss); // Setup focal plane map CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this, naifIkCode()); // Retrieve boresight location from instrument kernel (IK) (addendum?) ikernKey = "INS" + toString(naifIkCode()) + "_BORESIGHT_SAMPLE"; double sampleBoreSight = getDouble(ikernKey); ikernKey = "INS" + toString(naifIkCode()) + "_BORESIGHT_LINE"; double lineBoreSight = getDouble(ikernKey); focalMap->SetDetectorOrigin(sampleBoreSight, lineBoreSight); focalMap->SetDetectorOffset(0.0, 0.0); // Setup distortion map LroNarrowAngleDistortionMap *distMap = new LroNarrowAngleDistortionMap(this); distMap->SetDistortion(naifIkCode()); // Setup the ground and sky map new LineScanCameraGroundMap(this); new LineScanCameraSkyMap(this); LoadCache(); NaifStatus::CheckErrors(); }
/** * @brief Initialize the MDIS camera model for NAC and WAC * * This constructor reads the Messenger/MDIS instrument addendum for many of * its default parameters. * * This camera model does not support subframes of jailbar imaging modes. * An exception is thrown in those cases. * * @param lab Pvl label from a Messenger MDIS image. * * @throws iException::User - "Subframe imaging mode is not supported" * @throws iException::User - "Jail bar observations are not supported" * @throws iException::User - "New MDIS/NAC distortion model invalidates * previous SPICE - you must rerun spiceinit to get new * kernels" * @internal * @history 2011-05-03 Jeannie Walldren - Added NAIF error check. * */ MdisCamera::MdisCamera(Pvl &lab) : FramingCamera(lab) { NaifStatus::CheckErrors(); // Set up detector constants const int MdisWac(-236800); // const int MdisNac(-236820); PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse); // Clarification on MDIS subframe image mode provides us the ability to // support this mode now. The entire MDIS frame is geometrically valid // but only portions of the full frame actually contain image data. The // portions outside subframes should be NULL and not interfere in // downstream processing, such as mosaics. #if defined(MDIS_SUBFRAMES_UNSUPPORTED) int subFrameMode = inst["SubFrameMode"]; if(subFrameMode != 0) { string msg = "Subframe imaging mode is not supported!"; throw iException::Message(iException::User, msg, _FILEINFO_); } #endif // According to the MDIS team, this is nothing to be concerned with and // should be treated as other normal observations. So the test to // disallow it has been effectively removed 2007-09-05 (KJB). #if defined(MDIS_JAILBARS_UNSUPPORTED) int jailBars = inst["JailBars"]; if(jailBars != 0) { string msg = "Jail bar observations are not currently supported!"; throw iException::Message(iException::Programmer, msg, _FILEINFO_); } #endif // Determine filter number. Only conditional code required for // NAC and WAC support! int filterNumber(0); // Default appropriate for MDIS-NAC if(naifIkCode() == MdisWac) { PvlGroup &bandBin = lab.findGroup("BandBin", Pvl::Traverse); filterNumber = bandBin["Number"]; } // Set up instrument and filter code strings QString ikCode = toString(naifIkCode()); int fnCode(naifIkCode() - filterNumber); QString filterCode = toString(fnCode); QString ikernKey; // Fetch the frame translations from the instrument kernels ikernKey = "INS" + ikCode + "_REFERENCE_FRAME"; QString baseFrame = getString(ikernKey); ikernKey = "INS" + filterCode + "_FRAME"; QString ikFrame = getString(ikernKey); // Set up the camera info from ik/iak kernels // Turns out (2008-01-17) the WAC has different focal lengths for // each filter. Added to the instrument kernel (IAK) on this date. // Add temperature dependant focal length SetFocalLength(computeFocalLength(filterCode, lab)); SetPixelPitch(); // Removed by Jeff Anderson. The refactor of the SPICE class // uses frames always so this is no longer needed // LoadFrameMounting(baseFrame, ikFrame, false); // Get the start time from labels as the starting image time plus half // the exposure duration (in <MS>) to get pointing attitude. // !!NOTE: The ephemeris time MUST be set prior to creating the // cache (CreateCache) because the kernels are all unloaded // after the cache is done and this operation will fail!! QString stime = inst["SpacecraftClockCount"]; double exposureDuration = ((double) inst["ExposureDuration"]) / 1000.0;// divide by 1000 to convert to seconds iTime etStart = getClockTime(stime); // Setup camera detector map CameraDetectorMap *detMap = new CameraDetectorMap(this); // Setup focal plane map, and detector origin for the instrument that // may have a filter (WAC only!). CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this, fnCode); // Retrieve boresight location from instrument kernel (IK) (addendum?) ikernKey = "INS" + ikCode + "_BORESIGHT_SAMPLE"; double sampleBoreSight = getDouble(ikernKey); ikernKey = "INS" + ikCode + "_BORESIGHT_LINE"; double lineBoreSight = getDouble(ikernKey); // Apply the boresight focalMap->SetDetectorOrigin(sampleBoreSight, lineBoreSight); // Determine summing. MDIS has two sources of summing or binning. // One is performed in the FPU and the in the MP, post-observation, // on-board after coming out of the FPGAs, where the FPU binning is // performed. The FPU binning was programmed incorrectly and the // actual pixels from the detector are peculiar. Hence, I have // designed this camera model such that the offsets can be managed // external to the code. See the MDIS instrument kernel addendum // in $ISIS3DATA/messenger/kernels/iak/mdisAddendum???.ti for the // offsets for *each* detector. Note that an offset is only applied // when FPU binning is performed. int fpuBinMode = inst["FpuBinningMode"]; int pixelBinMode = inst["PixelBinningMode"]; int summing = ((pixelBinMode == 0) ? 1 : pixelBinMode); // FPU binning was performed, retrieve the FPU binning offsets and // apply them to the focal plane mapping. if(fpuBinMode == 1) { #if defined(USE_FPU_BINNING_OFFSETS) ikernKey = "INS" + ikCode + "_FPUBIN_START_SAMPLE"; double fpuStartingSample = getDouble(ikernKey); detMap->SetStartingDetectorSample(fpuStartingSample); ikernKey = "INS" + ikCode + "_FPUBIN_START_LINE"; double fpuStartingLine = getDouble(ikernKey); detMap->SetStartingDetectorLine(fpuStartingLine); #endif summing *= 2; } // Set summing/binning modes as an accumulation of FPU and MP binning. detMap->SetDetectorLineSumming(summing); detMap->SetDetectorSampleSumming(summing); // Setup distortion map. As of 2007/12/06, we now have an actual model. // Note that this model supports distinct distortion for each WAC filter. // See $ISIS3DATA/messenger/kernels/iak/mdisAddendumXXX.ti or possibly // $ISIS3DATA/messenger/kernels/ik/msgr_mdis_vXXX.ti for the *_OD_K // parameters. // NAC has a new implementation of its distortion contributed by // Scott Turner and Lillian Nguyen at JHUAPL. // (2010/10/06) The WAC now uses the same disortion model implementation. // Valid Taylor Series parameters are in versions msgr_mdis_v120.ti IK // and above. Note fnCode works for NAC as well as long as // filterNumber stays at 0 for the NAC only! try { TaylorCameraDistortionMap *distortionMap = new TaylorCameraDistortionMap(this); distortionMap->SetDistortion(fnCode); } catch(IException &ie) { string msg = "New MDIS NAC/WAC distortion models will invalidate previous " "SPICE - you may need to rerun spiceinit to get new kernels"; throw IException(ie, IException::User, msg, _FILEINFO_); } // Setup the ground and sky map new CameraGroundMap(this); new CameraSkyMap(this); // Create a cache and grab spice info since it does not change for // a framing camera (fixed spacecraft position and pointing) after, // of course applying the gimble offset which is handled in the SPICE // kernels (thank you!). Note this was done automagically in the // SetEpheremisTime call above. IMPORTANT that it be done prior to // creating the cache since all kernels are unloaded, essentially // clearing the pool and whacking the frames definitions, required to iTime centerTime = etStart + (exposureDuration / 2.0); setTime(centerTime); LoadCache(); NaifStatus::CheckErrors(); }
void IsisMain() { // Use a regular Process Process p; // Get user parameters and error check UserInterface &ui = Application::GetUserInterface(); QString from = ui.GetFileName("FROM"); QString to = FileName(ui.GetFileName("TO")).expanded(); //TO DO: UNCOMMENT THIS LINE ONCE HRSC IS WORKING IN SS // double HRSCNadirCenterTime = ui.GetDouble("HRSC_NADIRCENTERTIME"); // Open input cube and Make sure this is a lev1 image (ie, not map projected) Cube cube; cube.open(from); if (cube.isProjected()) { QString msg = "Input images is a map projected cube ... not a level 1 image"; throw IException(IException::User, msg, _FILEINFO_); } // Initialize the camera Cube *input = p.SetInputCube("FROM"); Pvl *cubeHeader = input->label(); Camera *cam = input->camera(); CameraDetectorMap *detectorMap = cam->DetectorMap(); CameraFocalPlaneMap *focalMap = cam->FocalPlaneMap(); CameraDistortionMap *distortionMap = cam->DistortionMap(); CameraGroundMap *groundMap = cam->GroundMap(); // Make sure the image contains the InstrumentPointing (aka CK) blob/table PvlGroup test = cube.label()->findGroup("Kernels", Pvl::Traverse); QString InstrumentPointing = (QString) test["InstrumentPointing"]; if (InstrumentPointing != "Table") { QString msg = "Input image does not contain needed SPICE blobs...run spiceinit with attach=yes."; throw IException(IException::User, msg, _FILEINFO_); } // Open output line scanner keyword file ofstream toStrm; toStrm.open(to.toAscii().data(), ios::trunc); if (toStrm.bad()) { QString msg = "Unable to open output TO file"; throw IException(IException::User, msg, _FILEINFO_); } // Get required keywords from instrument and band groups PvlGroup inst = cube.label()->findGroup("Instrument", Pvl::Traverse); QString instrumentId = (QString) inst["InstrumentId"]; bool isMocNA = false; //TO DO: UNCOMMENT THIS LINES ONCE MOC IS WORKING IN SS // bool isMocWARed = false; bool isHiRise = false; bool isCTX = false; bool isLroNACL = false; bool isLroNACR = false; bool isHRSC = false; //TO DO: UNCOMMENT THESE LINE ONCE MOC IS WORKING IN SS // if (instrumentId == "MOC") { // PvlGroup band = cube.label()->findGroup("BandBin", Pvl::Traverse); // QString filter = (QString) band["FilterName"]; // // if (strcmp(filter.toAscii().data(), "BROAD_BAND") == 0) // isMocNA = true; // else if (strcmp(filter.toAscii().data(), "RED") == 0) // isMocWARed = true; // else if (strcmp(filter.toAscii().data(), "BLUE") == 0) { // QString msg = "MOC WA Blue filter images not supported for Socet Set mapping"; // throw IException(IException::User, msg, _FILEINFO_); // } // } // else if (instrumentId == "IdealCamera") { //TO DO: DELETE THIS LINE ONCE MOC IS WORKING IN SS if (instrumentId == "IdealCamera") { PvlGroup orig = cube.label()->findGroup("OriginalInstrument", Pvl::Traverse); QString origInstrumentId = (QString) orig["InstrumentId"]; if (origInstrumentId == "HIRISE") { isHiRise = true; } else { QString msg = "Unsupported instrument: " + origInstrumentId; throw IException(IException::User, msg, _FILEINFO_); } } else if (instrumentId == "HIRISE") { isHiRise = true; } else if (instrumentId == "CTX") { isCTX = true; } else if (instrumentId == "NACL") { isLroNACL = true; } else if (instrumentId == "NACR") { isLroNACR = true; } //TO DO: UNCOMMENT THIS LINE ONCE HRSC IS WORKING IN SS // else if (instrumentId == "HRSC") isHRSC = true; else { QString msg = "Unsupported instrument: " + instrumentId; throw IException(IException::User, msg, _FILEINFO_); } int ikCode = cam->naifIkCode(); // Get Focal Length. // NOTE: // For MOC Wide Angle, cam->focal_length returns the focal length // in pixels, so we must convert from pixels to mm using the PIXEL_SIZE // of 0.007 mm gotten from $ISIS3DATA/mgs/kernels/ik/moc20.ti. (The // PIXEL_PITCH value gotten from cam->PixelPitch is 1.0 since the // focal length used by ISIS in this case is in pixels) // For reference: the MOC WA blue filter pixel size needs an adjustment // of 1.000452 (see p_scale in MocWideAngleDistortionMap.cpp), so that // the final blue filter pixel size = (0.007 / 1.000452) // // For all other cameras, cam->focal_length returns the focal // length in mm, as needed by Socet Set double focal = cam->FocalLength(); // focal length returned in mm //TO DO: UNCOMMENT THESE LINES ONCE HRSC and MOC IS WORKING IN SS // if (isMocWARed) // focal = focal * 0.007; // pixel to mm conversion // else if (isHRSC) // { // switch (ikCode) { // case -41219: //S1: fwd stereo // focal = 184.88; // break; // case -41218: //IR: infra-red // focal = 181.57; // break; // case -41217: //P1: fwd photo // focal = 179.16; // break; // case -41216: // GREEN // focal = 175.31; // break; // case -41215: // NADIR // focal = 175.01; // break; // case -41214: // BLUE // focal = 175.53; // break; // case -41213: // P2: aft photo // focal = 179.19; // break; // case -41212: // RED // focal = 181.77; // break; // case -41211: // S2: aft stereo // focal = 184.88; // break; // default: // break; // } // } // Get instrument summing modes int csum = (int) detectorMap->SampleScaleFactor(); int dsum = (int) detectorMap->LineScaleFactor(); if (isLroNACL || isLroNACR || isHRSC) dsum = csum; // Calculate location of boresight in image space, these are zero-based values // // Note: For MOC NA, the boresight is at the image center // For MOC WA, MRO HiRISE, MRO CTX, LRO_NACL, LRO_NACR and HRSC the // boresight is not at the detector center, but the boresight is at the // center of a NOPROJ'ED MRO HIRISE image // Get line/samp of boresight pixel in detector space (summing == 1) focalMap->SetFocalPlane(0.0, 0.0); double detectorBoresightSample = focalMap->DetectorSample(); double detectorBoresightLine = focalMap->DetectorLine(); // Convert sample of boresight pixel in detector into image space // (summing, etc., is accounted for.) detectorMap->SetDetector(detectorBoresightSample, detectorBoresightLine); double boresightSample = detectorMap->ParentSample(); // Set Atmospheric correction coefficients to 0 double atmco[4] = {0.0, 0.0, 0.0, 0.0}; // Get totalLines, totalSamples and account for summed images int totalLines = cube.lineCount(); int totalSamples = cube.sampleCount(); // Get the Interval Time in seconds and calculate // scan duration in seconds double scanDuration = 0.0; double intTime = 0.0; //TO DO: UNCOMMENT THESE LINES ONCE HRSC IS WORKING IN SS // int numIntTimes = 0.0; // vector<LineRateChange> lineRates; // if (isHRSC) { // numIntTimes = GetHRSCLineRates(&cube, lineRates, totalLines, HRSCNadirCenterTime); // if (numIntTimes == 1) { // LineRateChange lrc = lineRates.at(0); // intTime = lrc.GetLineScanRate(); // } // if (numIntTimes <= 0) { // QString msg = "HRSC: Invalid number of scan times"; // throw IException(IException::Programmer, msg, _FILEINFO_); // } // else // scanDuration = GetHRSCScanDuration(lineRates, totalLines); // } // else { // // TO DO: indent the following two lines when HRSC is working in SS intTime = detectorMap->LineRate(); //LineRate is in seconds scanDuration = intTime * totalLines; //TO DO: UNCOMMENT THIS LINE ONCE HRSC IS WORKING IN SS // } // For reference, this is the code if calculating interval time // via LineExposureDuration keyword off image labels: // // if (isMocNA || isMocWARed) // intTime = exposureDuration * (double) dsum / 1000.0; // else if (isHiRise) // intTime = exposureDuration * (double) dsum / 1000000.0; // Get along and cross scan pixel size for NA and WA sensors. // NOTE: // 1) The MOC WA pixel size is gotten from moc20.ti and is 7 microns // HRSC pixel size is from the Instrument Addendum file // 2) For others, cam->PixelPitch() returns the pixel pitch (size) in mm. double alongScanPxSize = 0.0; double crossScanPxSize = 0.0; //TO DO: UNCOMMENT THESE LINES ONCE MOC IS WORKING IN SS // if (isMocWARed || isHRSC) { // alongScanPxSize = csum * 0.007; // crossScanPxSize = dsum * 0.007; // } // else { // // TO DO: indent the following 24 lines when HRSC is working in SS crossScanPxSize = dsum * cam->PixelPitch(); // Get the ephemeris time, ground position and undistorted focal plane X // coordinate at the center line/samp of image cam->SetImage(cube.sampleCount() / 2.0, cube.lineCount() / 2.0); double tMid = cam->time().Et(); const double latCenter = cam->UniversalLatitude(); const double lonCenter = cam->UniversalLongitude(); const double radiusCenter = cam->LocalRadius().meters(); double uXCenter = distortionMap->UndistortedFocalPlaneX(); // from the ground position at the image center, increment the ephemeris // time by the line rate and map the ground position into the sensor in // undistorted focal plane coordinates cam->setTime(iTime(tMid + intTime)); double uX, uY; groundMap->GetXY(latCenter, lonCenter, radiusCenter, &uX, &uY); // the along scan pixel size is the difference in focal plane X coordinates alongScanPxSize = abs(uXCenter - uX); //TO DO: UNCOMMENT THIS LINE ONCE MOC and HRSC IS WORKING IN SS // } // Now that we have totalLines, totalSamples, alongScanPxSize and // crossScanPxSize, fill the Interior Orientation Coefficient arrays double ioCoefLine[10]; double ioCoefSample[10]; for (int i = 0; i <= 9; i++) { ioCoefLine[i] = 0.0; ioCoefSample[i] = 0.0; } ioCoefLine[0] = totalLines / 2.0; ioCoefLine[1] = 1.0 / alongScanPxSize; ioCoefSample[0] = totalSamples / 2.0; ioCoefSample[2] = 1.0 / crossScanPxSize; // Update the Rectification Terms found in the base sensor class double rectificationTerms[6]; rectificationTerms[0] = totalLines / 2.0; rectificationTerms[1] = 0.0; rectificationTerms[2] = 1.0; rectificationTerms[3] = totalSamples / 2.0; rectificationTerms[4] = 1.0; rectificationTerms[5] = 0.0; // Fill the triangulation parameters array double triParams[18]; for (int i = 0; i <= 17; i++) triParams[i] = 0.0; triParams[15] = focal; // Set the Center Ground Point at the SOCET Set image, in radians double centerGp[3]; double radii[3] = {0.0, 0.0, 0.0}; Distance Dradii[3]; cam->radii(Dradii); radii[0] = Dradii[0].kilometers(); radii[1] = Dradii[1].kilometers(); radii[2] = Dradii[2].kilometers(); cam->SetImage(boresightSample, totalLines / 2.0); centerGp[0] = DEG2RAD * TProjection::ToPlanetographic(cam->UniversalLatitude(), radii[0], radii[2]); centerGp[1] = DEG2RAD * TProjection::To180Domain(cam->UniversalLongitude()); centerGp[2] = 0.0; //**** NOTE: in the import_pushbroom SOCET SET program, centerGp[2] will be set to the SS //**** project's gp_origin_z // Now get keyword values that depend on ephemeris data. // First get the ephemeris time and camera Lat Lon at image center line, boresight sample. double centerLine = double(totalLines) / 2.0; cam->SetImage(boresightSample, centerLine); //set to boresight of image double etCenter = cam->time().Et(); // Get the sensor position at the image center in ographic lat, // +E lon domain 180 coordinates, radians, height in meters double sensorPosition[3] = {0.0, 0.0, 0.0}; double ocentricLat, e360Lon; cam->subSpacecraftPoint(ocentricLat, e360Lon); sensorPosition[0] = DEG2RAD * TProjection::ToPlanetographic(ocentricLat, radii[0], radii[2]); sensorPosition[1] = DEG2RAD * TProjection::To180Domain(e360Lon); sensorPosition[2] = cam->SpacecraftAltitude() * 1000.0; // Build the ephem data. If the image label contains the InstrumentPosition // table, use it as a guide for number and spacing of Ephem points. // Otherwise (i.e, for dejittered HiRISE images), the number and spacing of // ephem points based on hardcoded dtEphem value // Using the InstrumentPosition table as a guide build the ephem data QList< QList<double> > ephemPts; QList< QList<double> > ephemRates; PvlGroup kernels = cube.label()->findGroup("Kernels", Pvl::Traverse); QString InstrumentPosition = (QString) kernels["InstrumentPosition"]; int numEphem = 0; // number of ephemeris points double dtEphem = 0.0; // delta time of ephemeris points, seconds if (InstrumentPosition == "Table") { // Labels contain SPK blob // set up Ephem pts/rates number and spacing Table tablePosition("InstrumentPosition", cubeHeader->fileName()); numEphem = tablePosition.Records(); // increase the number of ephem nodes by 20%. This is somewhat random but // generally intended to compensate for having equally time spaced nodes // instead of of the potentially more efficient placement used by spiceinit numEphem = int(double(numEphem) * 1.2); // if numEphem calcutated from SPICE blobs is too sparse for SOCET Set, // mulitiply it by a factor of 30 // (30X was settled upon emperically. In the future, make this an input parameter) if (numEphem <= 10) numEphem = tablePosition.Records() * 30; // make the number of nodes odd numEphem = (numEphem % 2) == 1 ? numEphem : numEphem + 1; // SOCET has a max number of ephem pts of 10000, and we're going to add twenty... if (numEphem > 10000 - 20) numEphem = 9979; dtEphem = scanDuration / double(numEphem); //build the tables of values double et = etCenter - (((numEphem - 1) / 2) * dtEphem); for (int i = 0; i < numEphem; i++) { cam->setTime(iTime(et)); SpiceRotation *bodyRot = cam->bodyRotation(); vector<double> pos = bodyRot->ReferenceVector(cam->instrumentPosition()->Coordinate()); //TO DO: UNCOMMENT THE FOLLOWING LINE WHEN VELOCITY BLOBS ARE CORRECT IN ISIS //vector<double> vel = bodyRot->ReferenceVector(cam->instrumentPosition()->Velocity()); //Add the ephemeris position and velocity to their respective lists, in meters and meters/sec QList<double> ephemPt; QList<double> ephemRate; ephemPts.append(ephemPt << pos[0] * 1000 << pos[1] * 1000 << pos[2] * 1000); //TO DO: UNCOMMENT THE FOLLOWING LINE WHEN VELOCITY BLOBS ARE CORRECT IN ISIS //ephemRates.append(ephemRate << vel[0] * 1000 << vel[1] * 1000 << vel[2] * 1000); et += dtEphem; } //TO DO: WHEN VELOCITY BLOBS ARE CORRECT IN ISIS, linearlly interpolate 10 nodes rather than 11 // (need 11 now for computation of velocity at first and last ephemeris point) // linearlly interpolate 11 additional nodes before line 1 (SOCET requires this) for (int i = 0; i < 11; i++) { double vec[3] = {0.0, 0.0, 0.0}; vec[0] = ephemPts[0][0] + (ephemPts[0][0] - ephemPts[1][0]); vec[1] = ephemPts[0][1] + (ephemPts[0][1] - ephemPts[1][1]); vec[2] = ephemPts[0][2] + (ephemPts[0][2] - ephemPts[1][2]); QList<double> ephemPt; ephemPts.prepend (ephemPt << vec[0] << vec[1] << vec[2]); //TO DO: UNCOMMENT THE FOLLOWING LINES WHEN VELOCITY BLOBS ARE CORRECT IN ISIS //vec[0] = ephemRates[0][0] + (ephemRates[0][0] - ephemRates[1][0]); //vec[1] = ephemRates[0][1] + (ephemRates[0][1] - ephemRates[1][1]); //vec[2] = ephemRates[0][2] + (ephemRates[0][2] - ephemRates[1][2]); //QList<double> ephemRate; //ephemRates.prepend (ephemRate << vec[0] << vec[1] << vec[2]); } //TO DO: WHEN VELOCITY BLOBS ARE CORRECT IN ISIS, linearlly interpolate 10 nodes rather than 11 // (need 11 now for computation of velocity at first and last ephemeris point) // linearlly interpolate 11 additional nodes after the last line (SOCET requires this) for (int i = 0; i < 11; i++) { double vec[3] = {0.0, 0.0, 0.0}; int index = ephemPts.size() - 1; vec[0] = ephemPts[index][0] + (ephemPts[index][0] - ephemPts[index - 1][0]); vec[1] = ephemPts[index][1] + (ephemPts[index][1] - ephemPts[index - 1][1]); vec[2] = ephemPts[index][2] + (ephemPts[index][2] - ephemPts[index - 1][2]); QList<double> ephemPt; ephemPts.append(ephemPt << vec[0] << vec[1] << vec[2]); //TO DO: UNCOMMENT THE FOLLOWING LINES WHEN VELOCITY BLOBS ARE CORRECT IN ISIS //vec[0] = ephemRates[index][0] + (ephemRates[index][0] - ephemRates[index - 1][0]); //vec[1] = ephemRates[index][1] + (ephemRates[index][1] - ephemRates[index - 1][1]); //vec[2] = ephemRates[index][2] + (ephemRates[index][2] - ephemRates[index - 1][2]); //QList<double> ephemRate; //ephemRates.append(ephemRate << vec[0] << vec[1] << vec[2]); } numEphem += 20; //TO DO: DELETE THE FOLLOWING LINES WHEN VELOCITY BLOBS ARE CORRECT IN ISIS // Compute the spacecraft velocity at each ephemeris point double deltaTime = 2.0 * dtEphem; for (int i = 0; i < numEphem; i++) { double vec[3] = {0.0, 0.0, 0.0}; vec[0] = (ephemPts[i+2][0] - ephemPts[i][0]) / deltaTime; vec[1] = (ephemPts[i+2][1] - ephemPts[i][1]) / deltaTime; vec[2] = (ephemPts[i+2][2] - ephemPts[i][2]) / deltaTime; QList<double> ephemRate; ephemRates.append(ephemRate << vec[0] << vec[1] << vec[2]); } } else { // Calculate the number of ephemeris points that are needed, based on the // value of dtEphem (Delta-Time-Ephemeris). SOCET SET needs the ephemeris // points to exceed the image range for interpolation. For now, attempt a // padding of 10 ephemeris points on either side of the image. if (isMocNA || isHiRise || isCTX || isLroNACL || isLroNACR || isHRSC) // Try increment of every 300 image lines dtEphem = 300 * intTime; // Make this a user definable increment? else // Set increment for WA images to one second dtEphem = 1.0; // Pad by 10 ephem pts on each side of the image numEphem = (int)(scanDuration / dtEphem) + 20; // if numEphem is even, make it odd so that the number of ephemeris points // is equal on either side of T_CENTER if ((numEphem % 2) == 0) numEphem++; //TO DO: DELETE THE FOLLOWING LINE WHEN VELOCITY BLOBS ARE CORRECT IN ISIS numEphem = numEphem + 2; // Add two for calcuation of velocity vectors... // Find the ephemeris time for the first ephemeris point, and from that, get // to_ephem needed by SOCET (to_ephem is relative to etCenter) double et = etCenter - (((numEphem - 1) / 2) * dtEphem); for (int i = 0; i < numEphem; i++) { cam->setTime(iTime(et)); SpiceRotation *bodyRot = cam->bodyRotation(); vector<double> pos = bodyRot->ReferenceVector(cam->instrumentPosition()->Coordinate()); //TO DO: UNCOMMENT THE FOLLOWING LINE WHEN VELOCITY BLOBS ARE CORRECT IN ISIS //vector<double> vel = bodyRot->ReferenceVector(cam->instrumentPosition()->Velocity()); //Add the ephemeris position and velocity to their respective lists, in meters and meters/sec QList<double> ephemPt; QList<double> ephemRate; ephemPts.append(ephemPt << pos[0] * 1000 << pos[1] * 1000 << pos[2] * 1000); //TO DO: UNCOMMENT THE FOLLOWING LINE WHEN VELOCITY BLOBS ARE CORRECT IN ISIS //ephemRates.append(ephemRate << vel[0] * 1000 << vel[1] * 1000 << vel[2] * 1000); et += dtEphem; } //TO DO: DELETE THE FOLLOWING LINES WHEN VELOCITY BLOBS ARE CORRECT IN ISIS // Compute the spacecraft velocity at each ephemeris point // (We must do this when blobs are not attached because the Spice Class // stores in memory the same data that would be in a blob...even when reading NAIF kernels) double deltaTime = 2.0 * dtEphem; numEphem = numEphem - 2; // set numEphem back to the number we need output for (int i = 0; i < numEphem; i++) { double vec[3] = {0.0, 0.0, 0.0}; vec[0] = (ephemPts[i+2][0] - ephemPts[i][0]) / deltaTime; vec[1] = (ephemPts[i+2][1] - ephemPts[i][1]) / deltaTime; vec[2] = (ephemPts[i+2][2] - ephemPts[i][2]) / deltaTime; QList<double> ephemRate; ephemRates.append(ephemRate << vec[0] << vec[1] << vec[2]); } } //update ephem stats double etFirstEphem = etCenter - (((numEphem - 1) / 2) * dtEphem); double t0Ephem = etFirstEphem - etCenter; // Using the intrumentPointing table as a guide build the quarternions // for simplicity sake we'll leave the mountingAngles as identity // and store the complete rotation from body fixed to camera in the // quarternions //set up quaternions number and spacing Table tablePointing("InstrumentPointing", cubeHeader->fileName()); //number of quaternions int numQuaternions = tablePointing.Records(); // increase the number of quaternions nodes by 20%. This is somewhat random but // generally intended to compensate for having equally time spaced nodes // instead of of the potentially more efficient placement used by spiceinit numQuaternions = (int)(numQuaternions * 1.2); // if numQuaternions calcutated from SPICE blobs is too sparse for SOCET Set, // mulitiply it by a factor of 30 // (30X was settled upon emperically. In the future, make this an input parameter) if (numQuaternions <= 10) numQuaternions = tablePointing.Records() * 30; //make the number of nodes odd numQuaternions = (numQuaternions % 2) == 1 ? numQuaternions : numQuaternions + 1; // SOCET has a max number of quaternions of 20000, and we're going to add twenty... if (numQuaternions > 20000 - 20) numQuaternions = 19179; double dtQuat = scanDuration / double(numQuaternions); // build the tables of values QList< QList<double> > quaternions; double et = etCenter - (((numQuaternions - 1) / 2) * dtQuat); for (int i = 0; i < numQuaternions; i++) { cam->setTime(iTime(et)); vector<double> j2000ToBodyFixedMatrixVector = cam->bodyRotation()->Matrix(); vector<double> j2000ToCameraMatrixVector = cam->instrumentRotation()->Matrix(); double quaternion[4] = {0.0, 0.0, 0.0, 0.0}; double j2000ToBodyFixedRotationMatrix[3][3], //rotation from J2000 to target (aka body, planet) j2000ToCameraRotationMatrix[3][3], //rotation from J2000 to spacecraft cameraToBodyFixedRotationMatrix[3][3]; //rotation from camera to target // reformat vectors to 3x3 rotation matricies for (int j = 0; j < 3; j++) { for (int k = 0; k < 3; k++) { j2000ToBodyFixedRotationMatrix[j][k] = j2000ToBodyFixedMatrixVector[3 * j + k]; j2000ToCameraRotationMatrix[j][k] = j2000ToCameraMatrixVector[3 * j + k]; } } // get the quaternion mxmt_c(j2000ToBodyFixedRotationMatrix, j2000ToCameraRotationMatrix, cameraToBodyFixedRotationMatrix); m2q_c(cameraToBodyFixedRotationMatrix, quaternion); // add the quaternion to the list of quaternions QList<double> quat; quaternions.append(quat << quaternion[1] << quaternion[2] << quaternion[3] << quaternion[0]); //note also that the order is changed to match socet et += dtQuat; } // linearlly interpolate 10 additional nodes before the first quaternion (SOCET requires this) for (int i = 0; i < 10; i++) { double vec[4] = {0.0, 0.0, 0.0, 0.0}; vec[0] = quaternions[0][0] + (quaternions[0][0] - quaternions[1][0]); vec[1] = quaternions[0][1] + (quaternions[0][1] - quaternions[1][1]); vec[2] = quaternions[0][2] + (quaternions[0][2] - quaternions[1][2]); vec[3] = quaternions[0][3] + (quaternions[0][3] - quaternions[1][3]); QList<double> quat; quaternions.prepend (quat << vec[0] << vec[1] << vec[2] << vec[3]); } // linearlly interpolate 10 additional nodes after the last quaternion (SOCET requires this) for (int i = 0; i < 10; i++) { double vec[4] = {0.0, 0.0, 0.0, 0.0}; int index = quaternions.size() - 1; vec[0] = quaternions[index][0] + (quaternions[index][0] - quaternions[index - 1][0]); vec[1] = quaternions[index][1] + (quaternions[index][1] - quaternions[index - 1][1]); vec[2] = quaternions[index][2] + (quaternions[index][2] - quaternions[index - 1][2]); vec[3] = quaternions[index][3] + (quaternions[index][3] - quaternions[index - 1][3]); QList<double> quat; quaternions.append(quat << vec[0] << vec[1] << vec[2] << vec[3]); } //update quaternions stats numQuaternions += 20; //ephemeris time of the first quarternion double et0Quat = etCenter - (((numQuaternions - 1) / 2) * dtQuat); //quadrtic time of the first quarternion double qt0Quat = et0Quat - etCenter; //query remaing transformation parameters from Camera Classes //transformation to distortionless focal plane double zDirection = distortionMap->ZDirection(); //transformation from DistortionlessFocalPlane to FocalPlane vector<double> opticalDistCoefs = distortionMap->OpticalDistortionCoefficients(); // For instruments with less than 3 distortion coefficients, set the // unused ones to 0.0 opticalDistCoefs.resize(3, 0); //transformation from focal plane to detector const double *iTransS = focalMap->TransS(); const double *iTransL = focalMap->TransL(); double detectorSampleOrigin = focalMap->DetectorSampleOrigin(); double detectorLineOrigin = focalMap->DetectorLineOrigin(); //transformation from dectector to cube double startingSample = detectorMap->AdjustedStartingSample(); double startingLine = detectorMap->AdjustedStartingLine(); double sampleSumming = detectorMap->SampleScaleFactor(); double etStart = ((LineScanCameraDetectorMap *)detectorMap)->StartTime(); double lineOffset = focalMap->DetectorLineOffset(); // We are done with computing keyword values, so output the Line Scanner // Keyword file. // This is the SOCET SET base sensor class keywords portion of support file: toStrm.setf(ios::scientific); toStrm << "RECTIFICATION_TERMS" << endl; toStrm << " " << setprecision(14) << rectificationTerms[0] << " " << rectificationTerms[1] << " " << rectificationTerms[2] << endl; toStrm << " " << rectificationTerms[3] << " " << rectificationTerms[4] << " " << rectificationTerms[5] << endl; toStrm << "GROUND_ZERO "; toStrm << centerGp[0] << " " << centerGp[1] << " " << centerGp[2] << endl; toStrm << "LOAD_PT "; toStrm << centerGp[0] << " " << centerGp[1] << " " << centerGp[2] << endl; toStrm << "COORD_SYSTEM 1" << endl; toStrm << "IMAGE_MOTION 0" << endl; // This is the line scanner sensor model portion of support file: toStrm << "SENSOR_TYPE USGSAstroLineScanner" << endl; toStrm << "SENSOR_MODE UNKNOWN" << endl; toStrm << "FOCAL " << focal << endl; toStrm << "ATMCO"; for (int i = 0; i < 4; i++) toStrm << " " << atmco[i]; toStrm << endl; toStrm << "IOCOEF_LINE"; for (int i = 0; i < 10; i++) toStrm << " " << ioCoefLine[i]; toStrm << endl; toStrm << "IOCOEF_SAMPLE"; for (int i = 0; i < 10; i++) toStrm << " " << ioCoefSample[i]; toStrm << endl; toStrm << "ABERR 0" << endl; toStrm << "ATMREF 0" << endl; toStrm << "PLATFORM 1" << endl; toStrm << "SOURCE_FLAG 1" << endl; toStrm << "SINGLE_EPHEMERIDE 0" << endl; //Note, for TRI_PARAMETERS, we print the first element separate from the rest so that the array //starts in the first column. Otherwise, SOCET Set will treat the array as a comment toStrm << "TRI_PARAMETERS" << endl; toStrm << triParams[0]; for (int i = 1; i < 18; i++) toStrm << " " << triParams[i]; toStrm << endl; toStrm << setprecision(25) << "T_CENTER "; double tCenter = 0.0; //TO DO: UNCOMMENT THESE LINES ONCE HRSC IS WORKING IN SS // if (isHRSC) { // tCenter = etCenter - HRSCNadirCenterTime; // toStrm << tCenter << endl; // } // else toStrm << tCenter << endl; toStrm << "DT_EPHEM " << dtEphem << endl; toStrm << "T0_EPHEM "; //TO DO: UNCOMMENT THESE LINES ONCE HRSC IS WORKING IN SS // if (isHRSC) { // double t = tCenter + t0Ephem; // toStrm << t << endl; // } // else toStrm << t0Ephem << endl; toStrm << "NUMBER_OF_EPHEM " << numEphem << endl; toStrm << "EPHEM_PTS" << endl; //TO DO: DELETE THE FOLLOWING LINE WHEN VELOCITY BLOBS ARE CORRECT IN ISIS for (int i = 1; i <= numEphem; i++) { //TO DO: UNCOMMENT THE FOLLOWING LINE WHEN VELOCITY BLOBS ARE CORRECT IN ISIS //for (int i = 0; i < numEphem; i++) { toStrm << " " << ephemPts[i][0]; toStrm << " " << ephemPts[i][1]; toStrm << " " << ephemPts[i][2] << endl; } toStrm << "\n\nEPHEM_RATES" << endl; for (int i = 0; i < numEphem; i++) { toStrm << " " << ephemRates[i][0]; toStrm << " " << ephemRates[i][1]; toStrm << " " << ephemRates[i][2] << endl; } toStrm << "\n\nDT_QUAT " << dtQuat << endl; toStrm << "T0_QUAT " << qt0Quat << endl; toStrm << "NUMBER_OF_QUATERNIONS " << numQuaternions << endl; toStrm << "QUATERNIONS" << endl; for (int i = 0; i < numQuaternions; i++) { toStrm << " " << quaternions[i][0]; toStrm << " " << quaternions[i][1]; toStrm << " " << quaternions[i][2]; toStrm << " " << quaternions[i][3] << endl; } toStrm << "\n\nSCAN_DURATION " << scanDuration << endl; // UNCOMMENT toStrm << "\nNUMBER_OF_INT_TIMES " << numIntTimes << endl; // // if (isHRSC) { // toStrm << "INT_TIMES" << endl; // for (int i = 0; i < numIntTimes; i++) { // LineRateChange lr = lineRates.at(i); // toStrm << " " << lr.GetStartEt(); // toStrm << " " << lr.GetLineScanRate(); // toStrm << " " << lr.GetStartLine() << endl; // } // } // else toStrm << "INT_TIME " << intTime << endl; toStrm << "\nALONG_SCAN_PIXEL_SIZE " << alongScanPxSize << endl; toStrm << "CROSS_SCAN_PIXEL_SIZE " << crossScanPxSize << endl; toStrm << "\nCENTER_GP"; for (int i = 0; i < 3; i++) toStrm << " " << centerGp[i]; toStrm << endl; toStrm << "SENSOR_POSITION"; for (int i = 0; i < 3; i++) toStrm << " " << sensorPosition[i]; toStrm << endl; toStrm << "MOUNTING_ANGLES"; double mountingAngles[3] = {0.0, 0.0, 0.0}; for (int i = 0; i < 3; i++) toStrm << " " << mountingAngles[i]; toStrm << endl; toStrm << "\nTOTAL_LINES " << totalLines << endl; toStrm << "TOTAL_SAMPLES " << totalSamples << endl; toStrm << "\n\n\n" << endl; toStrm << "IKCODE " << ikCode << endl; toStrm << "ISIS_Z_DIRECTION " << zDirection << endl; toStrm << "OPTICAL_DIST_COEF"; for (int i = 0; i < 3; i++) toStrm << " " << opticalDistCoefs[i]; toStrm << endl; toStrm << "ITRANSS"; for (int i = 0; i < 3; i++) toStrm << " " << iTransS[i]; toStrm << endl; toStrm << "ITRANSL"; for (int i = 0; i < 3; i++) toStrm << " " << iTransL[i]; toStrm << endl; toStrm << "DETECTOR_SAMPLE_ORIGIN " << detectorSampleOrigin << endl; toStrm << "DETECTOR_LINE_ORIGIN " << detectorLineOrigin << endl; toStrm << "DETECTOR_LINE_OFFSET " << lineOffset << endl; toStrm << "DETECTOR_SAMPLE_SUMMING " << sampleSumming << endl; toStrm << "STARTING_SAMPLE " << startingSample << endl; toStrm << "STARTING_LINE " << startingLine << endl; toStrm << "STARTING_EPHEMERIS_TIME " << setprecision(25) << etStart << endl; toStrm << "CENTER_EPHEMERIS_TIME " << etCenter << endl; } // end main
void IsisMain() { // Use a regular Process Process p; UserInterface &ui = Application::GetUserInterface(); QString from = ui.GetFileName("FROM"); QString to = FileName(ui.GetFileName("TO")).expanded(); QString socetProject = ui.GetString("SS_PROJECT"); QString socetImageLocation = ui.GetString("SS_IMG_LOC"); QString socetInputDataPath = ui.GetString("SS_INPUT_PATH"); QString socetCameraCalibrationPath = ui.GetString("SS_CAM_CALIB_PATH"); // Open input cube and make sure this is a lev1 image (ie, not map projected) Cube cube; cube.open(from); if (cube.isProjected()) { QString msg = QString("You can only create a SOCET Set Framing Camera or FrameOffAxis settings " "file for level 1 images. The input image [%1] is a map projected, level " "2, cube.").arg(from); throw IException(IException::User, msg, _FILEINFO_); } // Initialize the camera Cube *input = p.SetInputCube("FROM"); Camera *cam = input->camera(); CameraDetectorMap *detectorMap = cam->DetectorMap(); CameraFocalPlaneMap *focalMap = cam->FocalPlaneMap(); // Make sure the image contains the SPICE blobs/tables PvlGroup test = cube.label()->findGroup("Kernels", Pvl::Traverse); QString instrumentPointing = (QString) test["InstrumentPointing"]; if (instrumentPointing != "Table") { QString msg = QString("Input image [%1] does not contain needed SPICE blobs. Please run " "spiceinit on the image with attach=yes.").arg(from); throw IException(IException::User, msg, _FILEINFO_); } // Set the image at the boresight pixel to get the ephemeris time and SPICE data at that image // location double detectorSampleOrigin = focalMap->DetectorSampleOrigin(); double detectorLineOrigin = focalMap->DetectorSampleOrigin(); cam->SetImage(detectorSampleOrigin, detectorLineOrigin); double et = cam->time().Et(); Spice spice(*input); spice.setTime(et); // Get required keywords from instrument and band groups PvlGroup inst = cube.label()->findGroup("Instrument", Pvl::Traverse); QString instrumentId = (QString) inst["InstrumentId"]; QString spacecraftName = (QString) inst["SpacecraftName"]; // Compensate for noproj altering cube labels if (instrumentId == "IdealCamera") { PvlGroup orig = cube.label()->findGroup("OriginalInstrument", Pvl::Traverse); instrumentId = (QString) orig["InstrumentId"]; spacecraftName = (QString) orig["SpacecraftName"]; } // Get sensor position and orientation (opk) angles double ographicCamPos[3] = {0.0, 0.0, 0.0}; double omegaPhiKappa[3] = {0.0, 0.0, 0.0}; double isisFocalPlane2SocetPlateTranspose[3][3] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}; getCamPosOPK(spice, spacecraftName, et, cam, ographicCamPos, omegaPhiKappa,isisFocalPlane2SocetPlateTranspose); // Determine the SOCET Set camera calibration file QString socetCamFile = socetCameraCalibrationPath; if (spacecraftName == "VIKING_ORBITER_1") { if (instrumentId == "VISUAL_IMAGING_SUBSYSTEM_CAMERA_A") { socetCamFile += "VIK1A.cam"; } else { socetCamFile += "VIK1B.cam"; } } else if (spacecraftName == "VIKING_ORBITER_2") { if (instrumentId == "VISUAL_IMAGING_SUBSYSTEM_CAMERA_A") { socetCamFile += "VIK2A.cam"; } else { socetCamFile += "VIK2B.cam"; } } //----------------------------------------.------------- //TO DO: Uncomment these lines when MEX SRC is supported //----------------------------------------.------------- // // Mars Express // else if (spacecraftName == "MARS_EXPRESS") { // socetCamFile += "SRC.cam"; // } //----------------------------------------------------- //TO DO: Uncomment these lines when Themis is supported //----------------------------------------------------- // // THEMIS VIS images (MARS Odyssey) // else if (spacecraftName == "MARS_ODYSSEY") { // socetCamFile += "THEMIS_VIS_F3.cam"; // } //----------------------------------------------------- //TO DO: Uncomment these lines when Apollo is supported //----------------------------------------------------- // else if (spacecraftName == "APOLLO 15") { // socetCamFile += "Apollo15_M_ASU.cam"; // } // else if (spacecraftName == "APOLLO 16") { // socetCamFile += "Apollo16_M_ASU.cam"; // } // else if (spacecraftName == "APOLLO 17") { // socetCamFile += "Apollo17_M_ASU.cam"; // } else if (spacecraftName == "Galileo Orbiter") { //Check if this image was aquired with the cover on or off iTime removeCoverDate("1994/04/01 00:00:00"); iTime imageDate((QString) inst["StartTime"]); if (imageDate < removeCoverDate) { socetCamFile += "Galileo_SSI_Cover.cam"; } else { socetCamFile += "Galileo_SSI.cam"; } } else if (spacecraftName == "Cassini-Huygens") { // Get the image filter and replace "/" with "_" PvlGroup bandBin = cube.label()->findGroup("BandBin", Pvl::Traverse); QString filter = (QString) bandBin["FilterName"]; filter.replace("/", "_"); socetCamFile += "Cassini_ISSNA_"; socetCamFile += filter; socetCamFile += ".cam"; } else if (spacecraftName == "Messenger") { if (instrumentId == "MDIS-NAC") { socetCamFile += "MDIS_NAC.cam"; } else { socetCamFile += "MDIS_WAC.cam"; } } else if (spacecraftName == "CLEMENTINE 1") { if (instrumentId == "UVVIS") { socetCamFile += "ClemUVVIS.cam"; } } // Throw exception for unsupported camera else { QString msg = QString("The ISIS to SOCET Set translation of input image [%1] is currently " "not supported for instrument [%2].").arg(from).arg(instrumentId); throw IException(IException::User, msg, _FILEINFO_); } // For THEMIS VIS, Galileo SSI, Cassini ISS get the image summation mode int summation = 1; //----------------------------------------------------- //TO DO: Uncomment these lines when Themis is supported //----------------------------------------------------- // if (spacecraftName == "MARS_ODYSSEY") { // try { // summation = (int) detectorMap->SampleScaleFactor(); // } // catch (IException &e) { // QString msg = "Error reading SpatialSumming from Instrument label"; // throw IException(IException::User, msg, _FILEINFO_); // } // } if (spacecraftName == "Galileo Orbiter") { try { summation = (int) detectorMap->SampleScaleFactor(); } catch (IException &e) { QString msg = "Error reading Summing from Instrument label"; throw IException(IException::User, msg, _FILEINFO_); } } if (spacecraftName == "Cassini-Huygens") { try { summation = (int) detectorMap->SampleScaleFactor(); } catch (IException &e) { QString msg = "Error reading Summing from Instrument label"; throw IException(IException::User, msg, _FILEINFO_); } } // Get NL/NS of image and calculate the size in x/y dimensions, in mm // Note: for THEMIS VIS, Galileo SSI and Cassini ISS summed images, calculate the size of the full // resolution image because our "isis2socet" scripts will enlarge the summed image for import into // Socet Set double pixelSize = 1.0 / cam->PixelPitch(); int numLines = cube.lineCount(); int numSamples = cube.sampleCount(); if (summation > 1) { // For Themis VIS, Galileo SSI, Cassini ISS: numLines *= summation; numSamples *= summation; } double sizeX = numSamples / pixelSize; double sizeY = numLines / pixelSize; // Make sure the Socet Set project name has the .prj extension if (socetProject.endsWith(".prj", Qt::CaseInsensitive) == FALSE) socetProject += ".prj"; // Find cube base name w/o extensions & establish the Socet Set support file name // Note: I'm using the QFileInfo class because the baseName method in the ISIS // FileName class only strips the last extension, and we need the core name // of the file without any extensions, or path QString baseName = QFileInfo(from).baseName(); QString socetSupFile = baseName + ".sup"; // Open and write to the SOCET Set Framing Camera settings file keywords and values // If this is a Messenger image, add the temperature-dependent focal length so as to overrride // the nominal focal lenghth stored in the SOCET Set camera calibration files ofstream toStrm; toStrm.open (to.toAscii().data(), ios::trunc); if (toStrm.bad()) { QString msg = "Unable to open output settings file"; throw IException(IException::User, msg, _FILEINFO_); } toStrm << "setting_file 1.1\n"; toStrm << "multi_frame.project " << socetProject << endl; toStrm << "multi_frame.cam_calib_filename " << socetCamFile << endl; toStrm << "multi_frame.create_files IMAGE_AND_SUPPORT\n"; toStrm << "multi_frame.atmos_ref 0\n"; toStrm << "multi_frame.auto_min YES\n"; toStrm << "multi_frame.digital_cam NO\n"; toStrm << "multi_frame.input_image_filename " << socetInputDataPath + baseName + ".raw" << endl; toStrm << "multi_frame.output_format img_type_vitec\n"; toStrm << "multi_frame.output_name " << socetSupFile << endl; toStrm << "multi_frame.output_location " << socetImageLocation << endl; toStrm << "multi_frame.cam_loc_ang_sys OPK\n"; toStrm << "multi_frame.cam_loc_ang_units UNIT_DEGREES\n"; toStrm << "multi_frame.cam_loc_xy_units UNIT_DEGREES\n"; if (spacecraftName == "Messenger") { // Overide the nominal focal length in the SOCET SET camera calibration file with the // Temperature Dependent Focal Length used in ISIS double focalLength = cam->FocalLength(); toStrm << "multi_frame.cam_loc_focal " << setprecision(17) << focalLength << endl; } toStrm << "multi_frame.cam_loc_y_or_lat " << setprecision(17) << ographicCamPos[0] << endl; toStrm << "multi_frame.cam_loc_x_or_lon " << ographicCamPos[1] << endl; toStrm << "multi_frame.cam_loc_elev " << ographicCamPos[2] << endl; toStrm << "multi_frame.cam_loc_omega " << omegaPhiKappa[0] << endl; toStrm << "multi_frame.cam_loc_phi " << omegaPhiKappa[1] << endl; toStrm << "multi_frame.cam_loc_kappa " << omegaPhiKappa[2] << endl; toStrm << "multi_frame.img_size_lines " << numLines << endl; toStrm << "multi_frame.img_size_samps " << numSamples << endl; toStrm << "multi_frame.sizex " << setprecision(6) << sizeX << endl; toStrm << "multi_frame.sizey " << sizeY << endl; toStrm << "multi_frame.orientation 1\n"; // Furthermore, if this is a Messenger image, get the needed keywords values needed for the // USGSAstro FrameOffAxis *support* file, and add them to the output settings file. // During frame import in SOCET Set, these values will be ignored, but then later accessed by // the USGSAstro import_frame SOCET Set program. // // Note: Summed Messenger images are handled in the FrameOffAxis sensor model, so no need to // account for enlarging Messenger images in the "socet2isis" scripts if (spacecraftName == "Messenger") { double originalHalfLines = numLines / 2.0; double originalHalfSamples = numSamples / 2.0; // Set the lens distortion coefficients // Note: These values were calculated for SOCET Set by Orrin Thomas in an MSExcel spreadsheet, // and are hardcoded here QString lenscoX; QString lenscoY; if (instrumentId == "MDIS-WAC") { lenscoX = QString("1.0913499678359500E-06 1.0000181809155400E+00 5.2705094712778700E-06 " "7.3086112844249500E-05 -2.1503011755973800E-06 -3.5311655893430800E-08 " "-5.3312743384716000E-06 -1.4642661005550900E-07 -5.4770856997706100E-06 " "-1.2364567692453900E-07 0.0000000000000000E+00 0.0000000000000000E+00 " "0.0000000000000000E+00 0.0000000000000000E+00 0.0000000000000000E+00"); lenscoY = QString("-4.8524316760252900E-08 -5.2704844291112000E-06 1.0000181808487100E+00 " "2.4702140905559800E-09 7.3084305868732200E-05 -2.1478354889239300E-06 " "1.2364567791040000E-07 -5.4663905009059100E-06 1.4516772126792600E-07 " "-5.3419626374895400E-06 0.0000000000000000E+00 0.0000000000000000E+00 " "0.0000000000000000E+00 0.0000000000000000E+00 0.0000000000000000E+00"); } else { //MDIS-NAC lens distortion coefficients: lenscoX = QString("-0.000000000000005 0.997948053760188 0.000000000000000 0.000000000000000 " "0.000542184519158 0.000000000000000 -0.000007008182254 0.000000000000000 " "-0.000006526474815 0.000000000000000 0.000000000000000 0.000000000000000 " "0.000000000000000 0.000000000000000 0.000000000000000"); lenscoY = QString("-0.000003746900328 0.000000000000000 0.999999575428613 -0.000880501428960 " "0.000000000000000 -0.000332760373453 0.000000000000000 -0.000008067196812 " "0.000000000000000 -0.000007553955548 0.000000000000000 0.000000000000000 " "0.000000000000000 0.000000000000000 0.000000000000000"); } // Get the image summation double sampleSumming = (int) detectorMap->SampleScaleFactor(); double lineSumming = (int) detectorMap->LineScaleFactor(); // Get the Starting Detector Line/Sample double startingSample = detectorMap->AdjustedStartingSample(); double startingLine = detectorMap->AdjustedStartingLine(); // Get the image plane corrdinates to pixel coordinates transformation matrices const double *iTransS = focalMap->TransS(); const double *iTransL = focalMap->TransL(); // Because of the options for applying light-time correction, capture the pertinent // ISIS keywords as a record to be stored in the settingsfile // Note: these values will not go into the Socet Set support file) QString ikCode; QString swapObserverTarget; QString lightTimeCorrection; QString ltSurfaceCorrect; PvlObject naifKeywordsObject = cube.label()->findObject("NaifKeywords"); if (instrumentId == "MDIS-NAC") { ikCode = "236820"; swapObserverTarget = (QString) naifKeywordsObject["INS-236820_SWAP_OBSERVER_TARGET"]; lightTimeCorrection = (QString) naifKeywordsObject["INS-236820_LIGHTTIME_CORRECTION"]; ltSurfaceCorrect = (QString) naifKeywordsObject["INS-236820_LT_SURFACE_CORRECT"]; } else { ikCode = "236800"; swapObserverTarget = (QString) naifKeywordsObject["INS-236800_SWAP_OBSERVER_TARGET"]; lightTimeCorrection = (QString) naifKeywordsObject["INS-236800_LIGHTTIME_CORRECTION"]; ltSurfaceCorrect = (QString) naifKeywordsObject["INS-236800_LT_SURFACE_CORRECT"]; } toStrm << "\nSENSOR_TYPE FrameOffAxis" << endl; toStrm << "USE_LENS_DISTORTION 1" << endl; toStrm << "ORIGINAL_HALF_LINES " << originalHalfLines << endl; toStrm << "ORIGINAL_HALF_SAMPLES " << originalHalfSamples << endl; toStrm << "LENSCOX " << lenscoX << endl; toStrm << "LENSCOY " << lenscoY << endl; toStrm << "SAMPLE_SUMMING " << sampleSumming << endl; toStrm << "LINE_SUMMING " << lineSumming << endl; toStrm << "STARTING_DETECTOR_SAMPLE " << setprecision(17) << startingSample << endl; toStrm << "STARTING_DETECTOR_LINE " << startingLine << endl; toStrm << "SAMPLE_BORESIGHT " << detectorSampleOrigin << endl; toStrm << "LINE_BORESIGHT " << detectorLineOrigin << endl; toStrm << "INS_ITRANSS"; for (int i = 0; i < 3; i++) toStrm << " " << setprecision(14) << iTransS[i]; toStrm << endl; toStrm << "INS_ITRANSL"; for (int i = 0; i < 3; i++) toStrm << " " << iTransL[i]; toStrm << endl; toStrm << "M_SOCET2ISIS_FOCALPLANE " << setprecision(2) << isisFocalPlane2SocetPlateTranspose[0][0] << " " << isisFocalPlane2SocetPlateTranspose[0][1] << " " << isisFocalPlane2SocetPlateTranspose[0][2] << endl; toStrm << " " << isisFocalPlane2SocetPlateTranspose[1][0] << " " << isisFocalPlane2SocetPlateTranspose[1][1] << " " << isisFocalPlane2SocetPlateTranspose[1][2] << endl; toStrm << " " << isisFocalPlane2SocetPlateTranspose[2][0] << " " << isisFocalPlane2SocetPlateTranspose[2][1] << " " << isisFocalPlane2SocetPlateTranspose[2][2] << endl; toStrm << "INS-" << ikCode << "_SWAP_OBSERVER_TARGET = '" << swapObserverTarget << "'\n"; toStrm << "INS-" << ikCode << "_LIGHTTIME_CORRECTION = '" << lightTimeCorrection << "'\n"; toStrm << "INS-" << ikCode << "_LT_SURFACE_CORRECT = '" << ltSurfaceCorrect <<"'\n"; } } //End IsisMain