Ejemplo n.º 1
0
  /**
   * 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();
  }
Ejemplo n.º 2
0
  /**
   * 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();
  }
Ejemplo n.º 3
0
    /** 
     * @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();
    }
Ejemplo n.º 4
0
  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();
  }
Ejemplo n.º 5
0
  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();
  }
Ejemplo n.º 6
0
    // 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();
    }
Ejemplo n.º 7
0
  /**
   * 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();
  }
Ejemplo n.º 8
0
  /**
   * 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();
  }
Ejemplo n.º 9
0
  /** 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;
  }
Ejemplo n.º 10
0
  /**
   * 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();
  }
Ejemplo n.º 11
0
  /**
   * 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();
  }
Ejemplo n.º 12
0
  /**
   * @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();
  }
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
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