From 4a13290e70bb57a8e8da7b73bb0bfe9a6b8978a6 Mon Sep 17 00:00:00 2001 From: Stuart Sides <ssides@usgs.gov> Date: Sun, 12 Apr 2020 13:51:41 -0700 Subject: [PATCH] Add support for Mars Express HRSC SRC instrument (#3826) * Add support for MacOS 10.14, needed for make base tests * Add support for MEX SRC instrument * Fixs found by PR review * Fixed PR issues * Tweaking translation * Added exposure duration * Adding in the SRC model * Now with lat/lon, and test updates * Adding Mex SRC Camera unitest * Fix spelling in application docs. Co-authored-by: Kristin <kberry@usgs.gov> --- .../serialnumbers/MexSrcSerialNumber.trn | 42 +++ isis/appdata/translations/Instruments.trn | 1 + .../src/mex/apps/hrsc2isis/MexHrscArchive.trn | 25 +- .../src/mex/apps/hrsc2isis/MexHrscBandBin.trn | 27 +- .../mex/apps/hrsc2isis/MexHrscInstrument.trn | 57 ++-- .../src/mex/apps/hrsc2isis/MexHrscKernels.trn | 21 ++ isis/src/mex/apps/hrsc2isis/hrsc2isis.xml | 51 ++-- isis/src/mex/apps/hrsc2isis/main.cpp | 283 ++++++++++-------- .../mex/apps/hrsc2isis/tsts/default/Makefile | 2 + .../mex/apps/hrsc2isis/tsts/srcImage/Makefile | 15 +- .../mex/objs/MexHrscSrcCamera/Camera.plugin | 6 + .../MexHrscSrcCamera/MexHrscSrcCamera.cpp | 129 ++++++++ .../objs/MexHrscSrcCamera/MexHrscSrcCamera.h | 86 ++++++ .../MexHrscSrcCamera/MexHrscSrcCamera.truth | 34 +++ .../mex/objs/MexHrscSrcCamera/unitTest.cpp | 133 ++++++++ 15 files changed, 689 insertions(+), 223 deletions(-) create mode 100644 isis/appdata/serialnumbers/MexSrcSerialNumber.trn create mode 100644 isis/src/mex/apps/hrsc2isis/MexHrscKernels.trn create mode 100644 isis/src/mex/objs/MexHrscSrcCamera/Camera.plugin create mode 100644 isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.cpp create mode 100644 isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.h create mode 100644 isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.truth create mode 100644 isis/src/mex/objs/MexHrscSrcCamera/unitTest.cpp diff --git a/isis/appdata/serialnumbers/MexSrcSerialNumber.trn b/isis/appdata/serialnumbers/MexSrcSerialNumber.trn new file mode 100644 index 0000000000..712e82c239 --- /dev/null +++ b/isis/appdata/serialnumbers/MexSrcSerialNumber.trn @@ -0,0 +1,42 @@ +Group = Keyword1 + Auto + InputKey = SpacecraftName + InputGroup = "IsisCube,Instrument" + InputPosition = (IsisCube, Instrument) + OutputName = Keyword1 + OutputPosition = (Group, SerialNumberKeywords) + Translation = (MEX, "MARS EXPRESS") + Translation = (MEX, MARS_EXPRESS) + Translation = (*, *) +End_Group + +Group = Keyword2 + Auto + InputKey = SpacecraftClockStartCount + InputGroup = "IsisCube,Instrument" + InputPosition = (IsisCube, Instrument) + OutputName = Keyword2 + OutputPosition = (Group, SerialNumberKeywords) + Translation = (*, *) +End_Group + +Group = Keyword3 + Auto + InputKey = InstrumentId + InputGroup = "IsisCube,Instrument" + InputPosition = (IsisCube, Instrument) + OutputName = Keyword3 + OutputPosition = (Group, SerialNumberKeywords) + Translation = (*, *) +End_Group + +Group = Keyword4 + Auto + InputKey = DetectorId + InputGroup = "IsisCube,Archive" + InputPosition = (IsisCube, Archive) + OutputName = Keyword4 + OutputPosition = (Group, SerialNumberKeywords) + Translation = (*, *) +End_Group +End diff --git a/isis/appdata/translations/Instruments.trn b/isis/appdata/translations/Instruments.trn index b45ca98bd1..4bf42fade9 100644 --- a/isis/appdata/translations/Instruments.trn +++ b/isis/appdata/translations/Instruments.trn @@ -108,6 +108,7 @@ Group = InstrumentName # MEX Translation = (Hrsc, HRSC) + Translation = (Src, SRC) # Messenger Translation = (Mdis, MDIS-NAC) diff --git a/isis/src/mex/apps/hrsc2isis/MexHrscArchive.trn b/isis/src/mex/apps/hrsc2isis/MexHrscArchive.trn index 892406a68e..857bf5ff75 100644 --- a/isis/src/mex/apps/hrsc2isis/MexHrscArchive.trn +++ b/isis/src/mex/apps/hrsc2isis/MexHrscArchive.trn @@ -1,25 +1,6 @@ -# -# The group names listed here are the native names used by -# applications to get the foreign names and values -# -# There is only one level of groups the groups are not -# allowed to be nested -# -# The Group names can NOT be repeated. -# -# InputGroup is a comma delimited list of objects and/or -# groups in the foreign label. Traversing this list will -# lead to the correct level to find the foreign keyword. -# -# InputKey is the keyword within the group which holds -# the information. -# -# InputDefault is the value used if there is no value for -# the keyword -# -# Translation is the native and corresponding foreign values. -# Translation may be repeated as needed. -# +# Translations for HRSC Stereo and SRC instrument. +# ISIS cube label Archive group + Group = DataSetId Auto InputKey = DATA_SET_ID diff --git a/isis/src/mex/apps/hrsc2isis/MexHrscBandBin.trn b/isis/src/mex/apps/hrsc2isis/MexHrscBandBin.trn index 2a9de3010c..f611c2532e 100644 --- a/isis/src/mex/apps/hrsc2isis/MexHrscBandBin.trn +++ b/isis/src/mex/apps/hrsc2isis/MexHrscBandBin.trn @@ -1,32 +1,12 @@ -# -# The group names listed here are the native names used by -# applications to get the foreign names and values -# -# There is only one level of groups the groups are not -# allowed to be nested -# -# The Group names can NOT be repeated. -# -# InputGroup is a comma delimited list of objects and/or -# groups in the foreign label. Traversing this list will -# lead to the correct level to find the foreign keyword. -# -# InputKey is the keyword within the group which holds -# the information. -# -# InputDefault is the value used if there is no value for -# the keyword -# -# Translation is the native and corresponding foreign values. -# Translation may be repeated as needed. -# +# Translations for HRSC Stereo and SRC instrument. +# ISIS cube label BandBin group + Group = FilterWidth Auto InputKey = BANDWIDTH OutputName = Width OutputPosition = (Group, BandBin) Translation = (*, *) - Translation = ("--- Illegal Value ---", *) End_Group Group = FilterCenter @@ -35,6 +15,5 @@ Group = FilterCenter OutputName = Center OutputPosition = (Group, BandBin) Translation = (*, *) - Translation = ("--- Illegal Value ---", *) End_Group End diff --git a/isis/src/mex/apps/hrsc2isis/MexHrscInstrument.trn b/isis/src/mex/apps/hrsc2isis/MexHrscInstrument.trn index 8f2633f3fd..038fc8850b 100644 --- a/isis/src/mex/apps/hrsc2isis/MexHrscInstrument.trn +++ b/isis/src/mex/apps/hrsc2isis/MexHrscInstrument.trn @@ -1,25 +1,6 @@ -# -# The group names listed here are the native names used by -# applications to get the foreign names and values -# -# There is only one level of groups the groups are not -# allowed to be nested -# -# The Group names can NOT be repeated. -# -# InputGroup is a comma delimited list of objects and/or -# groups in the foreign label. Traversing this list will -# lead to the correct level to find the foreign keyword. -# -# InputKey is the keyword within the group which holds -# the information. -# -# InputDefault is the value used if there is no value for -# the keyword -# -# Translation is the native and corresponding foreign values. -# Translation may be repeated as needed. -# +# Translations for HRSC Stereo and SRC instrument. +# ISIS cube label Instrument group + Group = SpacecraftName Auto InputKey = INSTRUMENT_HOST_NAME @@ -31,10 +12,20 @@ End_Group Group = InstrumentId Auto - InputKey = INSTRUMENT_ID + InputKey = DETECTOR_ID OutputName = InstrumentId OutputPosition = (Group, Instrument) - Translation = (*, *) + Translation = (HRSC, "MEX_HRSC_HEAD") + Translation = (HRSC, "MEX_HRSC_S2") + Translation = (HRSC, "MEX_HRSC_RED") + Translation = (HRSC, "MEX_HRSC_P2") + Translation = (HRSC, "MEX_HRSC_BLUE") + Translation = (HRSC, "MEX_HRSC_NADIR") + Translation = (HRSC, "MEX_HRSC_GREEN") + Translation = (HRSC, "MEX_HRSC_P1") + Translation = (HRSC, "MEX_HRSC_IR") + Translation = (HRSC, "MEX_HRSC_S1") + Translation = (SRC, "MEX_HRSC_SRC") End_Group Group = StartTime @@ -88,6 +79,24 @@ Group = TargetName Translation = (Cal, CAL) End_Group +Group = Summing + Auto + Optional + InputKey = MACROPIXEL_SIZE + OutputName = Summing + OutputPosition = (Group, Instrument) + Translation = (*, *) +End_Group + +Group = ExpousreDuration + Auto + Optional + InputKey = EXPOSURE_DURATION + OutputName = ExposureDuration + OutputPosition = (Group, Instrument) + Translation = (*, *) +End_Group + Group = FocalPlaneTemperature Auto Optional diff --git a/isis/src/mex/apps/hrsc2isis/MexHrscKernels.trn b/isis/src/mex/apps/hrsc2isis/MexHrscKernels.trn new file mode 100644 index 0000000000..5b880398fe --- /dev/null +++ b/isis/src/mex/apps/hrsc2isis/MexHrscKernels.trn @@ -0,0 +1,21 @@ +# Translations for HRSC Stereo and SRC instrument. +# ISIS cube label Kernels group + +Group = NaifIkCode + Auto + InputKey = DETECTOR_ID + OutputName = NaifIkCode + OutputPosition = (Group, Kernels) + Translation = (-41210, "MEX_HRSC_HEAD") + Translation = (-41211, "MEX_HRSC_S2") + Translation = (-41212, "MEX_HRSC_RED") + Translation = (-41213, "MEX_HRSC_P2") + Translation = (-41214, "MEX_HRSC_BLUE") + Translation = (-41215, "MEX_HRSC_NADIR") + Translation = (-41216, "MEX_HRSC_GREEN") + Translation = (-41217, "MEX_HRSC_P1") + Translation = (-41218, "MEX_HRSC_IR") + Translation = (-41219, "MEX_HRSC_S1") + Translation = (-41220, "MEX_HRSC_SRC") +End_Group + diff --git a/isis/src/mex/apps/hrsc2isis/hrsc2isis.xml b/isis/src/mex/apps/hrsc2isis/hrsc2isis.xml index dad2d188be..83d8227b15 100644 --- a/isis/src/mex/apps/hrsc2isis/hrsc2isis.xml +++ b/isis/src/mex/apps/hrsc2isis/hrsc2isis.xml @@ -5,18 +5,26 @@ <brief>Import HRSC image</brief> <description> - This program imports Mars Express HRSC files. This works by first - determining whether or not the input file has prefix data. If there is - prefix data, the prefix data is collected and used to look for "gaps" - HRSC - files can give us a time and exposure duration for each line, we look for - where the time + exposure duration =/= next line's time. We populate a - table (LineScanTimes) with the prefix data and lineInFile with whether or - not a gap should be inserted. For all files, we now process the data and if - there were gaps, they will be put in their proper places. This is a two-pass - system for files with prefix data, one-pass for files without. The Isis2 - equivalent to this program is mex2isis.pl. It is worth noting that - regardless of the input file's byte order, the prefix data byte order is - always LSB. + <p> + This program imports Mars Express HRSC files. This includes both the HRSC + Stereo and the SRC instruments with procesing level of "1" or "2" (not "3"). + The program populates all label keywords necessary to create the corresponding camera + models. + </p> + <p> + For the stereo instrument there is always prefix data. The + prefix data is collected and used to look for "gaps" aka missing lines. The + line prefix data for HRSC + files contain time and exposure duration for each line. We look for occurrences + where the time + exposure duration is not equal to the next line's time to find missing + lines. This program uses two passes, the first reads the PDS file into a cube, the + second inserts lines where gaps were identified. + </p> + <p> + For the SRC instrument, this program reads the PDS file and converts it to an ISIS cube. The + PDS file for the SRC instrument is much simpler than the stereo instrument files and requires + no special processing. + </p> </description> <history> @@ -32,14 +40,17 @@ </change> <change name="Steven Lambright" date="2008-07-28"> Updated to support the HRSC camera model. The Kernels group now has the IK code - properly added, the summing keyword is propagated, the times table is now compressed - into only having entries on time changes, and the times no longer have a 'Z' at the end - of them once imported. + properly added, the summing keyword is propagated, the times table is now compressed + into only having entries on time changes, and the times no longer have a 'Z' at the end + of them once imported. </change> <change name="Adam Paquette" date="2016-10-05"> Updated the error messages thrown when hrsc2isis trys to ingest an image that is not an HRSC image, and when hrsc2isis trys to ingest a map projected image. Fixes #4259. </change> + <change name="Stuart Sides" date="2020-03-31"> + Added ability to read HRSC SRC images + </change> </history> <oldName> @@ -57,7 +68,7 @@ <type>filename</type> <fileMode>input</fileMode> <brief> - Input HRSC Image + Input HRSC Stereo or SRC Image </brief> <description> Use this parameter to select the PDS filename. This file @@ -68,7 +79,7 @@ binary image data. </description> <filter> - *.lbl *.img + *.lbl *.LBL *.img *.IMG </filter> </parameter> @@ -90,10 +101,10 @@ <examples> <example> - <brief>Import an HRSC image with gaps</brief> + <brief>Import an HRSC stereo instrument image with gaps</brief> <description> - This example covers importing an HRSC image and shows what happens when - gaps are found. + This example covers importing an HRSC stereo instrument image and shows + what happens when gaps are found. </description> <terminalInterface> <commandLine> from=h0279_0000_re2.img to=h0279_0000_re2.cub</commandLine> diff --git a/isis/src/mex/apps/hrsc2isis/main.cpp b/isis/src/mex/apps/hrsc2isis/main.cpp index bff78f365e..573a177f86 100644 --- a/isis/src/mex/apps/hrsc2isis/main.cpp +++ b/isis/src/mex/apps/hrsc2isis/main.cpp @@ -11,93 +11,146 @@ using namespace std; using namespace Isis; -void IgnoreData(Isis::Buffer &buf) {}; +void ImportHrscStereoImage(ProcessImportPds &p, Pvl &label); +void ImportHrscSrcImage(ProcessImportPds &p, Pvl &label); + +void IgnoreData(Isis::Buffer &buf); void WriteOutput(Isis::Buffer &buf); + void TranslateHrscLabels(Pvl &inLabels, Pvl &outLabel); Cube *outCube = NULL; long numLinesSkipped = 0; std::vector< bool > lineInFile; -/** - * This program imports Mars Express HRSC files - * - * This works by first determining whether or not the input file - * has prefix data. - * - * If there is prefix data, a StartProcess is called with the - * IgnoreData() function callback in order to get the import class to - * collect prefix data. Once the prefix data is populated, we go ahead - * and look for "gaps" - HRSC files can give us a time and exposure duration - * for each line, we look for where the time + exposure duration != next line's time. - * We populate a table (LineScanTimes) with the prefix data and lineInFile with whether or not - * a gap should be inserted. - * - * For all files, we now process the data using the WriteOutput callback. If there were gaps, - * WriteOutput will put them in their proper places. Finally, we translate the labels and put - * the LineScanTimes (if necessary) in the output cube. - * - * This is a two-pass system for files with prefix data, one-pass for files without. - * - * The Isis2 equivalent to this program is mex2isis.pl. It is worth noting that regardless of the - * input file's byte order, the prefix data byte order is always LSB. - */ + void IsisMain() { UserInterface &ui = Application::GetUserInterface(); - try { - Pvl temp(ui.GetFileName("FROM")); - // Check for HRSC file - if(temp["INSTRUMENT_ID"][0] != "HRSC") throw IException(); + ProcessImportPds p; + Pvl label; + p.SetPdsFile(ui.GetFileName("FROM"), "", label); + + // Decide if the file is an HRSC image or something else + if (label["INSTRUMENT_ID"][0] != "HRSC") { + IString msg = "File [" + ui.GetFileName("FROM") + "] with [INSTRUMENT_ID = " + + label["INSTRUMENT_ID"][0] + + "] does not appear to be a Mars Express HRSC image. " + + "Consider using pds2isis to import the image."; + throw IException(IException::User, msg, _FILEINFO_); + } + + // Decide if the file is an HRSC SRC, HRSC Stereo (S2) or something else + bool isSrcFile; + if (label["DETECTOR_ID"][0] == "MEX_HRSC_SRC") { + isSrcFile = true; + } + else if ((label["DETECTOR_ID"][0] == "MEX_HRSC_S2") || + (label["DETECTOR_ID"][0] == "MEX_HRSC_RED") || + (label["DETECTOR_ID"][0] == "MEX_HRSC_P2") || + (label["DETECTOR_ID"][0] == "MEX_HRSC_BLUE") || + (label["DETECTOR_ID"][0] == "MEX_HRSC_NADIR") || + (label["DETECTOR_ID"][0] == "MEX_HRSC_GREEN") || + (label["DETECTOR_ID"][0] == "MEX_HRSC_P1") || + (label["DETECTOR_ID"][0] == "MEX_HRSC_IR") || + (label["DETECTOR_ID"][0] == "MEX_HRSC_S1")) { + isSrcFile = false; } - catch(IException &e) { - IString msg = "File [" + ui.GetFileName("FROM") + - "] does not appear to be a Mars Express HRSC image."; + else { + QString msg = "File [" + ui.GetFileName("FROM"); + msg += "] does not appear to be a Mars Express stereo or SRC file. Label keyword [DETECTOR_ID = "; + msg += label["DETECTOR_ID"][0] + "] is not recognized."; throw IException(IException::User, msg, _FILEINFO_); } - ProcessImportPds p; - Pvl label; - lineInFile.clear(); - numLinesSkipped = 0; + // This program is setup to work with Mex HRSC processing level 1 and 2 only. + // Not level 3 (Mapped) + if ((int)label["PROCESSING_LEVEL_ID"] >= 3) { + QString msg = "File [" + ui.GetFileName("FROM"); + msg += "] has keyword [PROCESSING_LEVEL_ID = " + label["PROCESSING_LEVEL_ID"][0] + "]"; + msg += " and can not be read by this program."; + throw IException(IException::User, msg, _FILEINFO_); + } - p.SetPdsFile(ui.GetFileName("FROM"), "", label); + // The processing for Stereo and SRC are significantly different. Call the + // appropriate processing function + if (isSrcFile) { + ImportHrscSrcImage(p, label); + } + else { + ImportHrscStereoImage(p, label); + } - CubeAttributeOutput outAtt(ui.GetFileName("TO")); - outCube = new Cube(); - outCube->setByteOrder(outAtt.byteOrder()); - outCube->setFormat(outAtt.fileFormat()); - outCube->setLabelsAttached(outAtt.labelAttachment() == AttachedLabel); +} - /** - * Isis2 mex2isis.pl: - * if (index($detector_id,"MEX_HRSC_SRC") < 0 && - * $processing_level_id < 3) - */ - bool isSrcFile = (label["DETECTOR_ID"][0] != "MEX_HRSC_SRC"); - bool mapProjRdr = ((int)label["PROCESSING_LEVEL_ID"] >= 3); +// Import a PDS3, HRSC, SRC Camera image. +void ImportHrscSrcImage(ProcessImportPds &p, Pvl &label) { - try { - if (!isSrcFile) { - QString msg = "File [" + ui.GetFileName("FROM"); - msg += "] is SRC data and cannot be read."; - throw IException(IException::User, msg, _FILEINFO_); - } + outCube = p.SetOutputCube("TO"); + p.StartProcess(); - if (mapProjRdr) { - QString msg = "File [" + ui.GetFileName("FROM"); - msg += "] is map projected and cannot be read."; - throw IException(IException::User, msg, _FILEINFO_); - } + Pvl otherLabels; + TranslateHrscLabels(label, otherLabels); + + if (otherLabels.hasGroup("Instrument") && + (otherLabels.findGroup("Instrument").keywords() > 0)) { + outCube->putGroup(otherLabels.findGroup("Instrument")); } - catch (IException &e) { - QString msg = "File cannot be read by hrsc2isis, use pds2isis."; - throw IException(e, IException::User, msg, _FILEINFO_); + if (otherLabels.hasGroup("BandBin") && + (otherLabels.findGroup("BandBin").keywords() > 0)) { + outCube->putGroup(otherLabels.findGroup("BandBin")); } + if (otherLabels.hasGroup("Archive") && + (otherLabels.findGroup("Archive").keywords() > 0)) { + outCube->putGroup(otherLabels.findGroup("Archive")); + } + + if (otherLabels.hasGroup("Kernels") && + (otherLabels.findGroup("Kernels").keywords() > 0)) { + outCube->putGroup(otherLabels.findGroup("Kernels")); + } + + p.EndProcess(); +} + + +/** + * This function reads Mars Express HRSC Stereo files + * + * First pass through the file is a Process called with the IgnoreData() function in order to + * get the import class to collect prefix data. Once the prefix data is populated, we look for + * "gaps" - HRSC files with prefix data give us a time and exposure duration for each line, we + * look for where the time + exposure duration != next line's time. We populate a table + * (LineScanTimes) with the prefix data and lineInFile with whether or not a missing line should + * be inserted. + * + * Second pass through the file is a Process called with the WriteOutput function. If there were + * gaps, WriteOutput will add lines at the appropriate positions in the output cube. Finally, we + * translate the labels and put the LineScanTimes (if necessary) in the output cube. + * + * This is a two-pass system for files with prefix data, one-pass for files without. + * + * NOTE: Regardless of the input file's byte order IMAGE-SAMPLE_TYPE, the prefix data byte order + * is always LSB. + */ +void ImportHrscStereoImage(ProcessImportPds &p, Pvl &label) { + + UserInterface &ui = Application::GetUserInterface(); + + lineInFile.clear(); + numLinesSkipped = 0; + + CubeAttributeOutput outAtt(ui.GetFileName("TO")); + outCube = new Cube(); + + outCube->setByteOrder(outAtt.byteOrder()); + outCube->setFormat(outAtt.fileFormat()); + outCube->setLabelsAttached(outAtt.labelAttachment() == AttachedLabel); + TableField ephTimeField("EphemerisTime", TableField::Double); TableField expTimeField("ExposureTime", TableField::Double); TableField lineStartField("LineStart", TableField::Integer); @@ -115,6 +168,7 @@ void IsisMain() { p.Progress()->SetText("Reading Prefix Data"); p.StartProcess(IgnoreData); + // Get the prefix data from the Process // The prefix data is always in LSB format, regardless of the overall file format EndianSwapper swapper("LSB"); @@ -122,23 +176,23 @@ void IsisMain() { std::vector<double> exposureTimes; std::vector< std::vector<char *> > prefix = p.DataPrefix(); - for(int line = 0; line < p.Lines(); line++) { + for (int line = 0; line < p.Lines(); line++) { double ephTime = swapper.Double((double *)prefix[0][line]); double expTime = swapper.Float((float *)(prefix[0][line] + 8)) / 1000.0; - if(line > 0) { + if (line > 0) { /** * We know how many skipped lines with this equation. We take the - * difference in the current line and the last line's time, which will - * ideally be equal to the last line's exposure duration. We divide this by - * the last line's exposure duration, and the result is the 1-based count of - * how many exposures there were between the last line and the current line. - * We subtract one in order to remove the known exposure, and the remaining should - * be the 1-based count of how many lines were skipped. Add 0.5 to round up. + * difference in the current line and the last line's time, which will + * ideally be equal to the last line's exposure duration. We divide this by + * the last line's exposure duration, and the result is the 1-based count of + * how many exposures there were between the last line and the current line. + * We subtract one in order to remove the known exposure, and the remaining should + * be the 1-based count of how many lines were skipped. Add 0.5 to round up. */ int skippedLines = (int)((ephTime - ephemerisTimes.back()) / exposureTimes.back() - 1.0 + 0.5); - for(int i = 0; i < skippedLines; i++) { + for (int i = 0; i < skippedLines; i++) { ephemerisTimes.push_back(ephemerisTimes.back() + exposureTimes.back()); exposureTimes.push_back(exposureTimes.back()); lineInFile.push_back(false); @@ -151,8 +205,8 @@ void IsisMain() { } double lastExp = 0.0; - for(unsigned int i = 0; i < ephemerisTimes.size(); i++) { - if(lastExp != exposureTimes[i]) { + for (unsigned int i = 0; i < ephemerisTimes.size(); i++) { + if (lastExp != exposureTimes[i]) { lastExp = exposureTimes[i]; timesRecord[0] = ephemerisTimes[i]; timesRecord[1] = exposureTimes[i]; @@ -169,33 +223,27 @@ void IsisMain() { outCube->write(timesTable); - // Get as many of the other labels as we can + // Translate the PDS labels into ISIS labels and add them to the cube Pvl otherLabels; - //p.TranslatePdsLabels (otherLabels); TranslateHrscLabels(label, otherLabels); - if(otherLabels.hasGroup("Mapping") && - (otherLabels.findGroup("Mapping").keywords() > 0)) { - outCube->putGroup(otherLabels.findGroup("Mapping")); - } - - if(otherLabels.hasGroup("Instrument") && + if (otherLabels.hasGroup("Instrument") && (otherLabels.findGroup("Instrument").keywords() > 0)) { outCube->putGroup(otherLabels.findGroup("Instrument")); } - if(otherLabels.hasGroup("BandBin") && + if (otherLabels.hasGroup("BandBin") && (otherLabels.findGroup("BandBin").keywords() > 0)) { outCube->putGroup(otherLabels.findGroup("BandBin")); } - if(otherLabels.hasGroup("Archive") && + if (otherLabels.hasGroup("Archive") && (otherLabels.findGroup("Archive").keywords() > 0)) { outCube->putGroup(otherLabels.findGroup("Archive")); } - if(otherLabels.hasGroup("Kernels") && + if (otherLabels.hasGroup("Kernels") && (otherLabels.findGroup("Kernels").keywords() > 0)) { outCube->putGroup(otherLabels.findGroup("Kernels")); } @@ -208,32 +256,49 @@ void IsisMain() { lineInFile.clear(); } + +// Processing function called by ProcessImportPds:StartProcess for each HRSC Stereo instrumennt +// line. It ignores the image data and returns. This is used to get the ProcessImportPds object +// to collect the prefix bytes. +void IgnoreData(Isis::Buffer &buf) { + return; +} + + +// Processing function called by ProcessImportPds:StartProcess for each HRSC Stereo instrument +// line in the IMG file. void WriteOutput(Isis::Buffer &buf) { + LineManager outLines(*outCube); - if(lineInFile.size()) { - for(int i = 0; i < outLines.size(); i++) { + if (lineInFile.size()) { + for (int i = 0; i < outLines.size(); i++) { outLines[i] = Isis::Null; } - while(!lineInFile[(buf.Line()+numLinesSkipped) % lineInFile.size()]) { + while (!lineInFile[(buf.Line()+numLinesSkipped) % lineInFile.size()]) { outLines.SetLine(buf.Line() + numLinesSkipped, buf.Band()); outCube->write(outLines); - numLinesSkipped ++; + numLinesSkipped++; } } outLines.SetLine(buf.Line() + numLinesSkipped, buf.Band()); - // outLines.Copy(buf); doesn't work because the raw buffers don't match - for(int i = 0; i < outLines.size(); i++) + for (int i = 0; i < outLines.size(); i++) { outLines[i] = buf[i]; + } outCube->write(outLines); + + return; } + +// Translate HRSC Stereo lables into ISIS labels void TranslateHrscLabels(Pvl &inLabels, Pvl &outLabel) { - // Get the directory where the MRO HiRISE translation tables are. + + // Get the directory where the translation tables are. QString transDir = "$ISISROOT/appdata/translations/"; // Translate the Instrument group @@ -241,13 +306,6 @@ void TranslateHrscLabels(Pvl &inLabels, Pvl &outLabel) { PvlToPvlTranslationManager instrumentXlater(inLabels, transFile.expanded()); instrumentXlater.Auto(outLabel); - if(inLabels.hasKeyword("MACROPIXEL_SIZE")) { - outLabel.findGroup("Instrument", Pvl::Traverse) += PvlKeyword("Summing", inLabels["MACROPIXEL_SIZE"][0]); - } - else { - outLabel.findGroup("Instrument", Pvl::Traverse) += PvlKeyword("Summing", "1"); - } - // Remove 'Z' from times QString startTime = outLabel.findGroup("Instrument", Pvl::Traverse)["StartTime"][0]; startTime = startTime.mid(0, startTime.size() - 1); @@ -267,30 +325,9 @@ void TranslateHrscLabels(Pvl &inLabels, Pvl &outLabel) { PvlToPvlTranslationManager archiveXlater(inLabels, transFile.expanded()); archiveXlater.Auto(outLabel); - std::map<QString, int> naifIkCodes; - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_HEAD", -41210)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_S2", -41211)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_RED", -41212)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_P2", -41213)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_BLUE", -41214)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_NADIR", -41215)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_GREEN", -41216)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_P1", -41217)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_IR", -41218)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_S1", -41219)); - naifIkCodes.insert(std::pair<QString, int>("MEX_HRSC_SRC", -41220)); - - QString key = outLabel.findGroup("Archive", Pvl::Traverse)["DetectorId"]; - int ikCode = naifIkCodes[key]; - - if(ikCode < -41220 || ikCode > -41210) { - QString msg = "Unrecognized Detector ID ["; - msg += key; - msg += "]"; - throw IException(IException::Unknown, msg, _FILEINFO_); - } + // Translate the Kernels group + transFile = transDir + "MexHrscKernels.trn"; + PvlToPvlTranslationManager kernelsXlater(inLabels, transFile.expanded()); + kernelsXlater.Auto(outLabel); - PvlGroup kerns("Kernels"); - kerns += PvlKeyword("NaifIkCode", toString(ikCode)); - outLabel.addGroup(kerns); } diff --git a/isis/src/mex/apps/hrsc2isis/tsts/default/Makefile b/isis/src/mex/apps/hrsc2isis/tsts/default/Makefile index 2c56d55b61..57b0e9b62d 100644 --- a/isis/src/mex/apps/hrsc2isis/tsts/default/Makefile +++ b/isis/src/mex/apps/hrsc2isis/tsts/default/Makefile @@ -6,6 +6,7 @@ commands: $(APPNAME) FROM=$(INPUT)/h0279_0000_re2.img TO=$(OUTPUT)/h0279_0000_re2.cub > /dev/null; tabledump FROM=$(OUTPUT)/h0279_0000_re2.cub TO=$(OUTPUT)/h0279_0000_re2.txt NAME=LineScanTimes > /dev/null; catlab FROM=$(OUTPUT)/h0279_0000_re2.cub to=$(OUTPUT)/h0279_0000_re2.pvl > /dev/null; + if [ `$(APPNAME) FROM=$(INPUT)/h1580_0008_sr3.img \ TO=$(OUTPUT)/junk.cub \ >& $(OUTPUT)/error_message_temp.txt` ]; \ @@ -13,3 +14,4 @@ commands: fi; $(SED) 's+\[/.*/input/+\[input/+' $(OUTPUT)/error_message_temp.txt > $(OUTPUT)/error.txt; $(RM) $(OUTPUT)/error_message_temp.txt; + diff --git a/isis/src/mex/apps/hrsc2isis/tsts/srcImage/Makefile b/isis/src/mex/apps/hrsc2isis/tsts/srcImage/Makefile index cddfcf70f5..b4b9cbdfae 100644 --- a/isis/src/mex/apps/hrsc2isis/tsts/srcImage/Makefile +++ b/isis/src/mex/apps/hrsc2isis/tsts/srcImage/Makefile @@ -2,13 +2,8 @@ APPNAME =hrsc2isis include $(ISISROOT)/make/isismake.tsts -commands:# TEST A: Check that a projected HRSC image fails to be read - echo -e "Test hrsc2isis with an image that has SRC data:" > $(OUTPUT)/error_message_temp.txt; - if [ `$(APPNAME) \ - FROM=$(INPUT)/h2862_0006_sr2.img \ - TO=$(OUTPUT)/temp.cub \ - 2>> $(OUTPUT)/error_message_temp.txt > /dev/null` ]; \ - then true; \ - fi; - $(SED) 's+\[/.*/input/+\[input/+' $(OUTPUT)/error_message_temp.txt > $(OUTPUT)/error_message.txt; - $(RM) $(OUTPUT)/error_message_temp.txt; +commands: + $(APPNAME) FROM=$(INPUT)/h2862_0006_sr2.img \ + TO=$(OUTPUT)/h2862_0006_sr2.cub \ + > /dev/null + diff --git a/isis/src/mex/objs/MexHrscSrcCamera/Camera.plugin b/isis/src/mex/objs/MexHrscSrcCamera/Camera.plugin new file mode 100644 index 0000000000..495ae95857 --- /dev/null +++ b/isis/src/mex/objs/MexHrscSrcCamera/Camera.plugin @@ -0,0 +1,6 @@ +Group = MarsExpress/Src + Version = 1 + Library = MexHrscSrcCamera + Routine = MexHrscSrcCameraPlugin +EndGroup + diff --git a/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.cpp b/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.cpp new file mode 100644 index 0000000000..dbd8d86ec6 --- /dev/null +++ b/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.cpp @@ -0,0 +1,129 @@ +/** + * @file + * + * Unless noted otherwise, the portions of Isis written by the USGS are public + * domain. See individual third-party library and package descriptions for + * intellectual property information,user agreements, and related information. + * + * Although Isis has been used by the USGS, no warranty, expressed or implied, + * is made by the USGS as to the accuracy and functioning of such software + * and related material nor shall the fact of distribution constitute any such + * warranty, and no responsibility is assumed by the USGS in connection + * therewith. + * + * For additional information, launch + * $ISISROOT/doc//documents/Disclaimers/Disclaimers.html in a browser or see + * the Privacy & Disclaimers page on the Isis website, + * http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on + * http://www.usgs.gov/privacy.html. + */ + +#include "MexHrscSrcCamera.h" + +#include <QString> + +#include "CameraDetectorMap.h" +#include "CameraDistortionMap.h" +#include "CameraFocalPlaneMap.h" +#include "CameraGroundMap.h" +#include "CameraSkyMap.h" +#include "IString.h" +#include "iTime.h" +#include "NaifStatus.h" + +using namespace std; + +namespace Isis { + /** + * Constructs an Mex HRSC SRC Framing Camera object. + * + * @param lab Pvl label from a Mex HRSC SRC Framing Camera image. + * + * @author Stuart Sides + * + * @internal + */ + + MexHrscSrcCamera::MexHrscSrcCamera(Cube &cube) : FramingCamera(cube) { + m_instrumentNameLong = "Super Resolution Channel"; + m_instrumentNameShort = "SRC"; + m_spacecraftNameLong = "Mars Express"; + m_spacecraftNameShort = "MEX"; + + NaifStatus::CheckErrors(); + + SetFocalLength(Spice::getDouble("INS" + toString(naifIkCode()) + "_FOCAL_LENGTH")); + + // For setting the pixel pitch, the Naif keyword PIXEL_SIZE is used instead of the ISIS + // default of PIXEL_PITCH, so set the value directly. + QString pp = "INS" + toString(naifIkCode()) + "_PIXEL_SIZE"; + double pixelPitch = Spice::getDouble(pp); + pixelPitch /= 1000.0; + SetPixelPitch(pixelPitch); + + // SRC doesn't appear to use any summing modes + CameraDetectorMap *detectorMap = new CameraDetectorMap(this); + detectorMap->SetDetectorSampleSumming(1); + detectorMap->SetDetectorLineSumming(1); + + // Setup focal plane map. The class will read data from the instrument addendum kernel to pull + // out the affine transforms from detector samp,line to focal plane x,y. + CameraFocalPlaneMap *focalMap = new CameraFocalPlaneMap(this, naifIkCode()); + + // The boresight position recorded in the IK is zero-based and therefore needs to be adjusted + // for ISIS + double boresightSample = Spice::getDouble("INS" + toString(naifIkCode()) + "_CCD_CENTER",0) + 1.0; + double boresightLine = Spice::getDouble("INS" + toString(naifIkCode()) + "_CCD_CENTER",1) + 1.0; + focalMap->SetDetectorOrigin(boresightSample,boresightLine); + + // The distortion is documented as near 1 pixel at the corners. This is less than the + // point spread, so zero distortion is used + new CameraDistortionMap(this); + + // Setup the ground and sky map + new CameraGroundMap(this); + new CameraSkyMap(this); + + // The observation start time and clock count for SRC are based on the center of the exposure. + Pvl &lab = *cube.label(); + PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse); + QString clockCount = inst["SpacecraftClockStartCount"]; + double et = getClockTime(clockCount).Et(); + double exposureDuration = (double)inst["ExposureDuration"] / 1000.0; + + pair<iTime, iTime> startStop = ShutterOpenCloseTimes(et, exposureDuration); + setTime(et); + + // Internalize all the NAIF SPICE information into memory. + LoadCache(); + NaifStatus::CheckErrors(); + } + + /** + * Returns the shutter open and close times. + * + * @param time The SpacecraftClockStartCount converted to ephemeris time. + * @param exposureDuration ExposureDuration keyword value from the labels, converted to + * seconds. + * + * @return @b pair < @b iTime, @b iTime > The first value is the shutter + * open time and the second is the shutter close time. + * + */ + pair<iTime, iTime> MexHrscSrcCamera::ShutterOpenCloseTimes(double time, + double exposureDuration) { + return FramingCamera::ShutterOpenCloseTimes(time - exposureDuration / 2.0, exposureDuration); + } +} + +/** + * This is the function that is called in order to instantiate a MexHrscSrcCamera + * object. + * + * @param lab Cube labels + * + * @return Isis::Camera* MexHrscSrcCamera + */ +extern "C" Isis::Camera *MexHrscSrcCameraPlugin(Isis::Cube &cube) { + return new Isis::MexHrscSrcCamera(cube); +} diff --git a/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.h b/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.h new file mode 100644 index 0000000000..ea62aa707c --- /dev/null +++ b/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.h @@ -0,0 +1,86 @@ +#ifndef MexHrscSrcCamera_h +#define MexHrscSrcCamera_h +/** + * @file + * + * Unless noted otherwise, the portions of Isis written by the USGS are public + * domain. See individual third-party library and package descriptions for + * intellectual property information,user agreements, and related information. + * + * Although Isis has been used by the USGS, no warranty, expressed or implied, + * is made by the USGS as to the accuracy and functioning of such software + * and related material nor shall the fact of distribution constitute any such + * warranty, and no responsibility is assumed by the USGS in connection + * therewith. + * + * For additional information, launch + * $ISISROOT/doc//documents/Disclaimers/Disclaimers.html in a browser or see + * the Privacy & Disclaimers page on the Isis website, + * http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on + * http://www.usgs.gov/privacy.html. + */ + +#include "FramingCamera.h" + +#include <QString> + +namespace Isis { + /** + * This is the camera model for the Mex HRSC SRC Framing Camera + * + * @ingroup SpiceInstrumentsAndCameras + * @ingroup Mex + * + * @author 2020-04-02 Stuart Sides + * + * @internal + * @history 2020-04-01 Stuart Sides - Initial version + + */ + class MexHrscSrcCamera : public FramingCamera { + public: + //! Create a MexHrscSrcCamera object + MexHrscSrcCamera(Cube &cube); + + //! Destroys the MexHrscSrcCamera object + ~MexHrscSrcCamera() {}; + + /** + * Reimplemented from FrameCamera + * + * @param time Start time of the observation + * @param exposureDuration The exposure duration of the observation + * + * @return std::pair<iTime,iTime> The start and end times of the observation + */ + virtual std::pair <iTime, iTime> ShutterOpenCloseTimes(double time, + double exposureDuration); + + /** + * CK frame ID - - Instrument Code from spacit run on CK + * + * @return @b int The appropriate instrument code for the "Camera-matrix" + * Kernel Frame ID + */ + virtual int CkFrameId() const { return (-41001); } + + + /** + * CK Reference ID - J2000 + * + * @return @b int The appropriate instrument code for the "Camera-matrix" + * Kernel Reference ID + */ + virtual int CkReferenceId() const { return (1); } + + + /** + * SPK Reference ID - J2000 + * + * @return @b int The appropriate instrument code for the Spacecraft + * Kernel Reference ID + */ + virtual int SpkReferenceId() const { return (1); } + }; +}; +#endif diff --git a/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.truth b/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.truth new file mode 100644 index 0000000000..2d5da1ad26 --- /dev/null +++ b/isis/src/mex/objs/MexHrscSrcCamera/MexHrscSrcCamera.truth @@ -0,0 +1,34 @@ +Unit Test for MexHrscSrcCamera... +FileName: H0010_0023_SR2.cub +CK Frame: -41220 + +Kernel IDs: +CK Frame ID = -41001 +CK Reference ID = 1 +SPK Target ID = -41 +SPK Reference ID = 1 + +Spacecraft Name Long: Mars Express +Spacecraft Name Short: MEX +Instrument Name Long: Super Resolution Channel +Instrument Name Short: SRC + +For upper left corner ... +DeltaSample = 0.000000000 +DeltaLine = 0.000000000 + +For upper right corner ... +DeltaSample = 0.000000000 +DeltaLine = 0.000000000 + +For lower left corner ... +DeltaSample = 0.000000000 +DeltaLine = 0.000000000 + +For lower right corner ... +DeltaSample = 0.000000000 +DeltaLine = 0.000000000 + +For center pixel position ... +Latitude OK +Longitude OK diff --git a/isis/src/mex/objs/MexHrscSrcCamera/unitTest.cpp b/isis/src/mex/objs/MexHrscSrcCamera/unitTest.cpp new file mode 100644 index 0000000000..0c1f27eebc --- /dev/null +++ b/isis/src/mex/objs/MexHrscSrcCamera/unitTest.cpp @@ -0,0 +1,133 @@ +/** + * @file + * + * Unless noted otherwise, the portions of Isis written by the USGS are public + * domain. See individual third-party library and package descriptions for + * intellectual property information,user agreements, and related information. + * + * Although Isis has been used by the USGS, no warranty, expressed or implied, + * is made by the USGS as to the accuracy and functioning of such software + * and related material nor shall the fact of distribution constitute any such + * warranty, and no responsibility is assumed by the USGS in connection + * therewith. + * + * For additional information, launch + * $ISISROOT/doc//documents/Disclaimers/Disclaimers.html in a browser or see + * the Privacy & Disclaimers page on the Isis website, + * http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on + * http://www.usgs.gov/privacy.html. + */ +#include <cmath> +#include <iomanip> +#include <iostream> + +#include "Camera.h" +#include "CameraFactory.h" +#include "FileName.h" +#include "IException.h" +#include "Preference.h" +#include "Pvl.h" + +using namespace std; +using namespace Isis; + +void TestLineSamp(Camera *cam, double samp, double line); +/** + * @internal + * + * @history 2020-04-09 Stuart Sides - Original version. + */ +int main(void) { + Preference::Preferences(true); + + cout << "Unit Test for MexHrscSrcCamera..." << endl; + + try { + // These should be lat/lon at center of image. To obtain these numbers for a new cube/camera, + // set both the known lat and known lon to zero and copy the unit test output "Latitude off by: " + // and "Longitude off by: " values directly into these variables. + double knownLat = -9.3335948038633116; + double knownLon = 90.4734324741402531; + + Cube c("$mex/testData/H0010_0023_SR2.cub", "r"); + Camera *cam = CameraFactory::Create(c); + cout << "FileName: " << FileName(c.fileName()).name() << endl; + cout << "CK Frame: " << cam->instrumentRotation()->Frame() << endl << endl; + cout.setf(std::ios::fixed); + cout << setprecision(9); + + // Test kernel IDs + cout << "Kernel IDs: " << endl; + cout << "CK Frame ID = " << cam->CkFrameId() << endl; + cout << "CK Reference ID = " << cam->CkReferenceId() << endl; + cout << "SPK Target ID = " << cam->SpkTargetId() << endl; + cout << "SPK Reference ID = " << cam->SpkReferenceId() << endl << endl; + + // Test name methods + cout << "Spacecraft Name Long: " << cam->spacecraftNameLong() << endl; + cout << "Spacecraft Name Short: " << cam->spacecraftNameShort() << endl; + cout << "Instrument Name Long: " << cam->instrumentNameLong() << endl; + cout << "Instrument Name Short: " << cam->instrumentNameShort() << endl << endl; + + // Test all four corners to make sure the conversions are right + cout << "For upper left corner ..." << endl; + TestLineSamp(cam, 1.0, 1.0); + + cout << "For upper right corner ..." << endl; + TestLineSamp(cam, cam->Samples(), 1.0); + + cout << "For lower left corner ..." << endl; + TestLineSamp(cam, 1.0, cam->Lines()); + + cout << "For lower right corner ..." << endl; + TestLineSamp(cam, cam->Samples(), cam->Lines()); + + double samp = cam->Samples() / 2.0; + double line = cam->Lines() / 2.0; + cout << "For center pixel position ..." << endl; + + if (!cam->SetImage(samp, line)) { + cout << "ERROR" << endl; + return 0; + } + + if (fabs(cam->UniversalLatitude() - knownLat) < 1.81E-5) { + cout << "Latitude OK" << endl; + } + else { + cout << setprecision(16) << "Latitude off by: " << cam->UniversalLatitude() - knownLat << endl; + } + + if (fabs(cam->UniversalLongitude() - knownLon) < 1.4E-6) { + cout << "Longitude OK" << endl; + } + else { + cout << setprecision(16) << "Longitude off by: " << cam->UniversalLongitude() - knownLon << endl; + } + } + catch (IException &e) { + e.print(); + } +} + +void TestLineSamp(Camera *cam, double samp, double line) { + bool success = cam->SetImage(samp, line); + + if (success) { + success = cam->SetUniversalGround(cam->UniversalLatitude(), cam->UniversalLongitude()); + } + + if (success) { + double deltaSamp = samp - cam->Sample(); + double deltaLine = line - cam->Line(); + if (fabs(deltaSamp) < 0.008) deltaSamp = 0.0; + if (fabs(deltaLine) < 0.008) deltaLine = 0.0; + cout << "DeltaSample = " << deltaSamp << endl; + cout << "DeltaLine = " << deltaLine << endl << endl; + } + else { + cout << "DeltaSample = ERROR" << endl; + cout << "DeltaLine = ERROR" << endl << endl; + } +} + -- GitLab