From 77cf5d9e2f4f6e6632c7f9706eaeb570e2439653 Mon Sep 17 00:00:00 2001 From: Tim Giroux <62255438+tgiroux@users.noreply.github.com> Date: Mon, 7 Feb 2022 09:11:52 -0700 Subject: [PATCH] ciss2isis - Set validmax global to input label value (#4589) * set validmax to inputlabel value * Converted ciss2isis app and added gtests * Addressed PR feedback and updated test * Added test data for ciss2isis tests Co-authored-by: acpaquette --- isis/notebooks/crop_cassiniIss.ipynb | 385 +++++++++++++++ isis/src/cassini/apps/ciss2isis/ciss2isis.cpp | 449 ++++++++++++++++++ isis/src/cassini/apps/ciss2isis/ciss2isis.h | 19 + isis/src/cassini/apps/ciss2isis/main.cpp | 448 +---------------- .../apps/ciss2isis/tsts/narrowAngle/Makefile | 10 - .../apps/ciss2isis/tsts/wideAngle/Makefile | 10 - isis/tests/FunctionalTestsCiss2isis.cpp | 272 +++++++++++ .../data/ciss2isis/N1472853667_1.cropped.img | Bin 0 -> 14672 bytes .../data/ciss2isis/N1472853667_1.cropped.lbl | 111 +++++ .../data/ciss2isis/W1472855646_5.cropped.img | Bin 0 -> 26936 bytes .../data/ciss2isis/W1472855646_5.cropped.lbl | 111 +++++ 11 files changed, 1357 insertions(+), 458 deletions(-) create mode 100644 isis/notebooks/crop_cassiniIss.ipynb create mode 100644 isis/src/cassini/apps/ciss2isis/ciss2isis.cpp create mode 100644 isis/src/cassini/apps/ciss2isis/ciss2isis.h delete mode 100644 isis/src/cassini/apps/ciss2isis/tsts/narrowAngle/Makefile delete mode 100644 isis/src/cassini/apps/ciss2isis/tsts/wideAngle/Makefile create mode 100644 isis/tests/FunctionalTestsCiss2isis.cpp create mode 100644 isis/tests/data/ciss2isis/N1472853667_1.cropped.img create mode 100644 isis/tests/data/ciss2isis/N1472853667_1.cropped.lbl create mode 100644 isis/tests/data/ciss2isis/W1472855646_5.cropped.img create mode 100644 isis/tests/data/ciss2isis/W1472855646_5.cropped.lbl diff --git a/isis/notebooks/crop_cassiniIss.ipynb b/isis/notebooks/crop_cassiniIss.ipynb new file mode 100644 index 0000000000..36849f925b --- /dev/null +++ b/isis/notebooks/crop_cassiniIss.ipynb @@ -0,0 +1,385 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "b6c4ed37", + "metadata": {}, + "outputs": [], + "source": [ + "import pvl\n", + "import struct\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import datetime\n", + "import os\n", + "import binascii" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e45c31fe", + "metadata": {}, + "outputs": [], + "source": [ + "label_file = \"/Users/acpaquette/repos/ISIS3/build/N1472853667_1.LBL\"" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4eccdc86", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PVLModule([\n", + " ('PDS_VERSION_ID', 'PDS3')\n", + " ('RECORD_TYPE', 'FIXED_LENGTH')\n", + " ('RECORD_BYTES', 1048)\n", + " ('FILE_RECORDS', 1028)\n", + " ('^IMAGE_HEADER', ['N1472853667_1.IMG', 1])\n", + " ('^TELEMETRY_TABLE', ['N1472853667_1.IMG', 4])\n", + " ('^LINE_PREFIX_TABLE', ['N1472853667_1.IMG', 5])\n", + " ('^IMAGE', ['N1472853667_1.IMG', 5])\n", + " ('ANTIBLOOMING_STATE_FLAG', 'ON')\n", + " ('BIAS_STRIP_MEAN', 17.25049)\n", + " ('CALIBRATION_LAMP_STATE_FLAG', 'N/A')\n", + " ('COMMAND_FILE_NAME', 'trigger_6510_6.ioi')\n", + " ('COMMAND_SEQUENCE_NUMBER', 6510)\n", + " ('DARK_STRIP_MEAN', 0.0)\n", + " ('DATA_CONVERSION_TYPE', 'TABLE')\n", + " ('DATA_SET_ID', 'CO-S-ISSNA/ISSWA-2-EDR-V1.0')\n", + " ('DELAYED_READOUT_FLAG', 'NO')\n", + " ('DESCRIPTION', 'Incomplete product finalized due to truncated lines.')\n", + " ('DETECTOR_TEMPERATURE', Quantity(value=-89.243546, units='DEGC'))\n", + " ('EARTH_RECEIVED_START_TIME',\n", + " datetime.datetime(2004, 9, 3, 9, 54, 37, 232000, tzinfo=datetime.timezone.utc))\n", + " ('EARTH_RECEIVED_STOP_TIME',\n", + " datetime.datetime(2004, 9, 3, 9, 57, 12, 966000, tzinfo=datetime.timezone.utc))\n", + " ('ELECTRONICS_BIAS', 112)\n", + " ('EXPECTED_MAXIMUM', [0.756496, 1.9487])\n", + " ('EXPECTED_PACKETS', 519)\n", + " ('EXPOSURE_DURATION', 220000.0)\n", + " ('FILTER_NAME', ['CL1', 'CL2'])\n", + " ('FILTER_TEMPERATURE', -0.468354)\n", + " ('FLIGHT_SOFTWARE_VERSION_ID', '1.3')\n", + " ('GAIN_MODE_ID', '12 ELECTRONS PER DN')\n", + " ('IMAGE_MID_TIME',\n", + " datetime.datetime(2004, 9, 2, 21, 34, 26, 410000, tzinfo=datetime.timezone.utc))\n", + " ('IMAGE_NUMBER', '1472853667')\n", + " ('IMAGE_OBSERVATION_TYPE', frozenset({'SCIENCE'}))\n", + " ('IMAGE_TIME',\n", + " datetime.datetime(2004, 9, 2, 21, 36, 16, 410000, tzinfo=datetime.timezone.utc))\n", + " ('INSTRUMENT_DATA_RATE', 182.783997)\n", + " ('INSTRUMENT_HOST_NAME', 'CASSINI ORBITER')\n", + " ('INSTRUMENT_ID', 'ISSNA')\n", + " ('INSTRUMENT_MODE_ID', 'FULL')\n", + " ('INSTRUMENT_NAME', 'IMAGING SCIENCE SUBSYSTEM NARROW ANGLE')\n", + " ('INST_CMPRS_PARAM', ['N/A', 'N/A', 'N/A', 'N/A'])\n", + " ('INST_CMPRS_RATE', [3.6, 4.333807])\n", + " ('INST_CMPRS_RATIO', 1.845952)\n", + " ('INST_CMPRS_TYPE', 'LOSSLESS')\n", + " ('LIGHT_FLOOD_STATE_FLAG', 'ON')\n", + " ('METHOD_DESC', 'ISSPT2.5.3;Saturn-Ering;ISS_00ARI_DIFFUSRNG003_PRIME_4')\n", + " ('MISSING_LINES', 511)\n", + " ('MISSING_PACKET_FLAG', 'NO')\n", + " ('MISSION_NAME', 'CASSINI-HUYGENS')\n", + " ('MISSION_PHASE_NAME', 'TOUR PRE-HUYGENS')\n", + " ('OBSERVATION_ID', 'ISS_00ARI_DIFFUSRNG003_PRIME')\n", + " ('OPTICS_TEMPERATURE', [0.712693, 1.905708])\n", + " ('ORDER_NUMBER', 2)\n", + " ('PARALLEL_CLOCK_VOLTAGE_INDEX', 9)\n", + " ('PREPARE_CYCLE_INDEX', 12)\n", + " ('PRODUCT_CREATION_TIME',\n", + " datetime.datetime(2004, 9, 3, 10, 39, 14, tzinfo=datetime.timezone.utc))\n", + " ('PRODUCT_ID', '1_N1472853667.118')\n", + " ('PRODUCT_VERSION_TYPE', 'FINAL')\n", + " ('READOUT_CYCLE_INDEX', 10)\n", + " ('RECEIVED_PACKETS', 576)\n", + " ('SENSOR_HEAD_ELEC_TEMPERATURE', 1.633024)\n", + " ('SEQUENCE_ID', 'S03')\n", + " ('SEQUENCE_NUMBER', 2)\n", + " ('SEQUENCE_TITLE', 'ISS_00ARI_DIFFUSRNG003_PRIME')\n", + " ('SHUTTER_MODE_ID', 'NACONLY')\n", + " ('SHUTTER_STATE_ID', 'ENABLED')\n", + " ('SOFTWARE_VERSION_ID', 'ISS 9.00 02-05-2004')\n", + " ('SPACECRAFT_CLOCK_CNT_PARTITION', 1)\n", + " ('SPACECRAFT_CLOCK_START_COUNT', '1472853447.118')\n", + " ('SPACECRAFT_CLOCK_STOP_COUNT', '1472853667.118')\n", + " ('START_TIME',\n", + " datetime.datetime(2004, 9, 2, 21, 32, 36, 410000, tzinfo=datetime.timezone.utc))\n", + " ('STOP_TIME',\n", + " datetime.datetime(2004, 9, 2, 21, 36, 16, 410000, tzinfo=datetime.timezone.utc))\n", + " ('TARGET_DESC', 'Saturn-Ering')\n", + " ('TARGET_LIST', 'N/A')\n", + " ('TARGET_NAME', 'SATURN')\n", + " ('TELEMETRY_FORMAT_ID', 'UNK')\n", + " ('VALID_MAXIMUM', [9896, 4095])\n", + " ('IMAGE_HEADER',\n", + " {'BYTES': 3144,\n", + " 'HEADER_TYPE': 'VICAR2',\n", + " 'INTERCHANGE_FORMAT': 'ASCII',\n", + " 'RECORDS': 1,\n", + " '^DESCRIPTION': 'VICAR2.TXT'})\n", + " ('TELEMETRY_TABLE',\n", + " {'COLUMN': {'BYTES': 987,\n", + " 'DATA_TYPE': 'MSB_UNSIGNED_INTEGER',\n", + " 'NAME': 'NULL_PADDING',\n", + " 'START_BYTE': 61},\n", + " 'COLUMNS': 2,\n", + " 'INTERCHANGE_FORMAT': 'BINARY',\n", + " 'ROWS': 1,\n", + " 'ROW_BYTES': 1048,\n", + " '^STRUCTURE': 'TLMTAB.FMT'})\n", + " ('LINE_PREFIX_TABLE',\n", + " {'COLUMNS': 7,\n", + " 'INTERCHANGE_FORMAT': 'BINARY',\n", + " 'ROWS': 1024,\n", + " 'ROW_BYTES': 24,\n", + " 'ROW_SUFFIX_BYTES': 1024,\n", + " '^LINE_PREFIX_STRUCTURE': 'PREFIX2.FMT'})\n", + " ('IMAGE',\n", + " {'LINES': 1024,\n", + " 'LINE_PREFIX_BYTES': 24,\n", + " 'LINE_SAMPLES': 1024,\n", + " 'SAMPLE_BITS': 8,\n", + " 'SAMPLE_TYPE': 'SUN_INTEGER'})\n", + "])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pvl_label = pvl.load(label_file)\n", + "img_file = os.path.join(os.path.split(label_file)[0], pvl_label[\"^IMAGE\"][0])\n", + "pvl_label" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "616e7622", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "with open(img_file, 'rb') as fp:\n", + " byte_position = ((pvl_label[\"^IMAGE\"][1] - 1) * pvl_label[\"RECORD_BYTES\"])\n", + " b_original_header = fp.read(byte_position)\n", + " b_image_data = fp.read()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "10f0680b", + "metadata": {}, + "outputs": [], + "source": [ + "lut_list = [1, 3, 5, 7, 9, 11, 14, 16, 19, 22, 25, 28, 31, 35, 38, 42, 46, 49, 53, 57,\n", + "61, 65, 70, 74, 79, 83, 88, 93, 98, 103, 108, 113, 119, 125, 130, 136, 142,\n", + "148, 154, 160, 166, 173, 179, 186, 193, 199, 206, 213, 221, 228, 236, 243,\n", + "251, 259, 267, 275, 283, 291, 299, 307, 316, 324, 333, 342, 351, 360, 369,\n", + "379, 388, 398, 407, 417, 427, 437, 447, 457, 467, 478, 488, 499, 509, 520,\n", + "531, 542, 554, 565, 576, 588, 600, 611, 623, 635, 647, 659, 672, 684, 697,\n", + "709, 722, 735, 747, 761, 774, 787, 801, 814, 828, 842, 856, 870, 884, 898,\n", + "912, 926, 941, 955, 970, 985, 1000, 1015, 1030, 1045, 1061, 1076, 1092, 1108,\n", + "1124, 1140, 1156, 1172, 1188, 1204, 1221, 1237, 1254, 1271, 1288, 1305, 1322,\n", + "1339, 1357, 1374, 1392, 1410, 1428, 1446, 1464, 1482, 1500, 1518, 1537, 1555,\n", + "1574, 1593, 1612, 1631, 1650, 1669, 1689, 1708, 1728, 1748, 1768, 1788, 1808,\n", + "1828, 1848, 1868, 1889, 1909, 1930, 1951, 1971, 1993, 2014, 2035, 2057, 2078,\n", + "2100, 2121, 2143, 2165, 2187, 2209, 2232, 2254, 2276, 2299, 2322, 2344, 2367,\n", + "2390, 2413, 2437, 2460, 2484, 2507, 2531, 2555, 2579, 2603, 2627, 2651, 2676,\n", + "2700, 2725, 2749, 2774, 2799, 2824, 2849, 2874, 2900, 2925, 2951, 2977, 3003,\n", + "3029, 3055, 3081, 3107, 3133, 3160, 3186, 3213, 3239, 3266, 3293, 3321, 3348,\n", + "3375, 3403, 3430, 3458, 3486, 3514, 3542, 3570, 3598, 3627, 3655, 3684, 3712,\n", + "3741, 3770, 3799, 3828, 3857, 3887, 3916, 3946, 3976, 4005, 4035, 4065, 4095]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "4c26e15b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1048\n" + ] + } + ], + "source": [ + "sampling = (pvl_label['IMAGE']['SAMPLE_BITS']//8)\n", + "line_length = (pvl_label['IMAGE']['LINE_SAMPLES'] * sampling) + pvl_label['IMAGE']['LINE_PREFIX_BYTES']\n", + "print(line_length)\n", + "n_lines = 10\n", + "\n", + "\n", + "image_data = []\n", + "for j in range(n_lines):\n", + " start, stop = j*(line_length), (j+1)*(line_length)\n", + " if (sampling == 2):\n", + " dtype = np.int16\n", + " elif (sampling == 4):\n", + " dtype = np.float32\n", + " else:\n", + " dtype=np.int8\n", + " image_sample = np.frombuffer(b_image_data[start:stop], dtype=dtype, count=int((line_length)/sampling))\n", + " image_data.append(image_sample)\n", + "image_data = np.array(image_data)\n", + "\n", + "# Uncomment the following to compare with an ISIS cube\n", + "# if pvl_label[\"^IMAGE\"][0][0] == 'W':\n", + "# image_data = image_data.byteswap()\n", + "# else:\n", + "# image_data = np.reshape(np.array(list(map(lambda x: lut_list[x], image_data.flatten()))), (n_lines, line_length))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "3815ac94", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4620" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "path_cropped_label = os.path.splitext(label_file)[0] + \".cropped.lbl\"\n", + "path_cropped_file = os.path.splitext(img_file)[0] + \".cropped.img\"\n", + "cropped_file = os.path.split(path_cropped_file)[1]\n", + "\n", + "new_label = pvl_label.copy()\n", + "new_label[\"IMAGE\"][\"LINES\"] = n_lines\n", + "new_label[\"^IMAGE\"][0] = cropped_file\n", + "new_label[\"^IMAGE_HEADER\"][0] = cropped_file\n", + "new_label[\"^TELEMETRY_TABLE\"][0] = cropped_file\n", + "new_label[\"^LINE_PREFIX_TABLE\"][0] = cropped_file\n", + "\n", + "with open(path_cropped_file, 'wb') as fp:\n", + " fp.write(b_original_header)\n", + " fp.write(image_data)\n", + " \n", + "pvl.dump(new_label, path_cropped_label)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28a7937d", + "metadata": {}, + "outputs": [], + "source": [ + "# Below lines used to compare the ISIS cube to the original label since qt 15.0 doesn't work on big sur\n", + "# as of 01/28/2022" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31bd22d4", + "metadata": {}, + "outputs": [], + "source": [ + "cube_file = \"/Path/to/test/output.cub\"\n", + "cube_label = pvl.load(cube_file)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3ea86dc", + "metadata": {}, + "outputs": [], + "source": [ + "with open(cube_file, 'rb') as fp:\n", + " fp.seek(cube_label[\"IsisCube\"][\"Core\"][\"StartByte\"] - 1)\n", + " b_cube_data = fp.read()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b175905", + "metadata": {}, + "outputs": [], + "source": [ + "line_length = cube_label[\"IsisCube\"][\"Core\"][\"Dimensions\"][\"Samples\"] * 2\n", + "n_lines = cube_label[\"IsisCube\"][\"Core\"][\"Dimensions\"][\"Lines\"]\n", + "\n", + "cube_data = []\n", + "for j in range(n_lines):\n", + " start, stop = j*(line_length), (j+1)*(line_length)\n", + " \n", + " cube_sample = np.frombuffer(b_cube_data[start:stop], dtype=np.int16, count=int(line_length/2))\n", + " cube_data.append(cube_sample)\n", + " \n", + "cube_data = np.array(cube_data)\n", + "# cube_data = np.where(cube_data == -32764, 4095, cube_data)\n", + "cube_data = np.where(cube_data < -32764, 1, cube_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8d8c29c", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "plt.figure(0, figsize=(20, 20))\n", + "plt.imshow(cube_data)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6db691e3", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/isis/src/cassini/apps/ciss2isis/ciss2isis.cpp b/isis/src/cassini/apps/ciss2isis/ciss2isis.cpp new file mode 100644 index 0000000000..61c4a8d75e --- /dev/null +++ b/isis/src/cassini/apps/ciss2isis/ciss2isis.cpp @@ -0,0 +1,449 @@ +#include "ciss2isis.h" +#include + +#include + +#include "CisscalFile.h" +#include "FileName.h" +#include "IException.h" +#include "IString.h" +#include "Preference.h" +#include "ProcessImportPds.h" +#include "ProcessByLine.h" +#include "Pvl.h" +#include "SpecialPixel.h" +#include "Stretch.h" +#include "Table.h" +#include "TextFile.h" + +using namespace std; + +namespace Isis{ + // Function prototypes + static double ComputeOverclockAvg(vector pixel); + static vector ConvertLinePrefixPixels(unsigned char *data); + static Table CreateLinePrefixTable(vector prefixData); + static void CreateStretchPairs(); + static void FixDns(Buffer &buf); + static void TranslateCassIssLabels(FileName &labelFile, Cube *ocube, Pvl *log); + //Global variables + static QString compressionType; + static QString dataConversionType; + static double flightSoftware; + static Stretch stretch; + static int sumMode; + static int validMax; + + void ciss2isis(UserInterface &ui, Pvl *log) { + //PROCESS 1: saves off label, header, and line prefix data ==========================================// + ProcessImportPds p; + Pvl label; + FileName in = ui.GetFileName("FROM"); + + try { + p.SetPdsFile(in.expanded(), "", label); + } + catch (IException &e) { + throw IException(e, IException::User, + QObject::tr("Error reading input file. Make sure it contains a PDS label."), + _FILEINFO_); + } + + //Checks if in file is rdr + if(label.hasObject("IMAGE_MAP_PROJECTION")) { + QString msg = "[" + in.name() + "] appears to be an rdr file."; + msg += " Use pds2isis."; + throw IException(IException::User, msg, _FILEINFO_); + } + + // Set the output bit type to SignedWord + CubeAttributeOutput &outAtt = ui.GetOutputAttribute("TO"); + outAtt.setPixelType(SignedWord); + outAtt.setMinimum((double)VALID_MIN2); + outAtt.setMaximum((double)VALID_MAX2); + Cube *ocube = p.SetOutputCube(ui.GetFileName("TO"), outAtt); + + TranslateCassIssLabels(in, ocube, log); + + //Save off header (includes vicar labels and binary telemetry header) + // No need to SetFileHeaderBytes() this is already done by ProcessImportPds automatically + int vicarLabelBytes = label.findObject("IMAGE_HEADER").findKeyword("BYTES"); + p.SaveFileHeader(); + + //Save off line prefix data, always 24 bytes of binary prefix per line,see SIS version 1.1 pg 103 + int linePrefixBytes = label.findObject("IMAGE").findKeyword("LINE_PREFIX_BYTES"); + p.SetDataPrefixBytes(linePrefixBytes); + p.SaveDataPrefix(); + + //SET PROGRESS TEXT, VALID MAXIMUM PIXEL VALUE, AND CREATE STRETCH IF NEEDED + if(dataConversionType != "Table") { //Conversion Type is 12Bit or 8LSB, only save off overclocked pixels + if(dataConversionType == "12Bit") { + p.Progress()->SetText("Image was 12 bit. No conversion needed. \nSaving line prefix data..."); + } + else { //if (dataConversionType == "8LSB") { + p.Progress()->SetText("Image was truncated to 8 least significant bits. No conversion needed. \nSaving line prefix data..."); + } + } + else { //if ConversionType == Table, Use LUT to create stretch pairs for conversion + CreateStretchPairs(); + // Pvl outputLabels; + Pvl *outputLabel = ocube->label(); + //Adjust Table-encoded values from 8 bit back to 12 bit. + PvlGroup &inst = outputLabel->findGroup("Instrument", Pvl::Traverse); + double biasStripMean = inst.findKeyword("BiasStripMean"); + inst.findKeyword("BiasStripMean").setValue(toString(stretch.Map(biasStripMean))); + inst.findKeyword("BiasStripMean").addComment("BiasStripMean value converted back to 12 bit."); + p.Progress()->SetText("Image was converted using 12-to-8 bit table. \nConverting prefix pixels back to 12 bit and saving line prefix data..."); + } + + p.StartProcess(); + + // Write line prefix data to table in output cube + vector > dataPrefix = p.DataPrefix(); + vector prefixBand0 = dataPrefix.at(0); //There is only one band so the outside vector only contains + // one entry and the inside vector only contains nl entries + Table linePrefixTable = CreateLinePrefixTable(prefixBand0); + ocube->write(linePrefixTable); + // Compute readout order (roo) and save to output cube's instrument group + unsigned char *header = (unsigned char *) p.FileHeader(); + int roo = *(header + 50 + vicarLabelBytes) / 32 % 2; //**** THIS MAY NEED TO BE CHANGED, + // SEE BOTTOM OF THIS FILE FOR IN DEPTH COMMENTS ON READOUTORDER + PvlGroup &inst = ocube->label()->findGroup("Instrument", Pvl::Traverse); + inst.addKeyword(PvlKeyword("ReadoutOrder", toString(roo))); + p.EndProcess(); + + // PROCESS 2 : Do 8 bit to 12 bit conversion for image ==============================================// + ProcessByLine p2; + QString ioFile = ui.GetFileName("TO"); + CubeAttributeInput att; + p2.SetInputCube(ioFile, att, ReadWrite); + //if ConversionType == 12Bit or 8LSB, only save off overclocked pixels + if(dataConversionType == "12Bit") { + p2.Progress()->SetText("Setting special pixels and saving as 16bit..."); + } + else if(dataConversionType == "8LSB") { + p2.Progress()->SetText("Setting special pixels and saving as 16bit..."); + } + //if ConversionType == Table, Use LUT to create stretch pairs for conversion + else { + p2.Progress()->SetText("Converting image pixels back to 12-bit and saving as 16bit..."); + } + p2.StartProcess(FixDns); + p2.EndProcess(); + } + + //call this method after stretch is closed in IsisMain() + // write data into table to save in output cube + // author Jeannie Walldren 2008-08-21 + Table CreateLinePrefixTable(vector prefixData) { + TableField overclockPixels("OverclockPixels", TableField::Double, 3); + //3 columns, first two are overclocked pixels and the third is their average + TableRecord linePrefixRecord; + linePrefixRecord += overclockPixels; + Table linePrefixTable("ISS Prefix Pixels", linePrefixRecord); + linePrefixTable.SetAssociation(Table::Lines); + for(int l = 0; l < (int)prefixData.size(); l++) { + unsigned char *linePrefix = (unsigned char *)(prefixData[l]); + linePrefixRecord[0] = ConvertLinePrefixPixels(linePrefix); + linePrefixTable += linePrefixRecord; + } + return linePrefixTable; + } + + //used by CreateLinePrefixTable() to convert prefix data + // author Jeannie Walldren 2008-08-21 + vector ConvertLinePrefixPixels(unsigned char *data) { + Buffer pixelBuf(1, 1, 1, SignedWord); + + vector calibrationPixels; + //Pixel data is MSB, see SIS version 1.1 page 17 + EndianSwapper swapper("MSB"); + + vector pixel; + //12 is start byte for First Overclocked Pixel Sum in Binary Line Prefix, SIS version 1.1 page 94 + pixel.push_back(swapper.ShortInt(& (data[12]))); + //22 is start byte for Last Overclocked Pixel Sum in Binary Line Prefix, see SIS version 1.1 page 94 + pixel.push_back(swapper.ShortInt(& (data[22]))); + pixel.push_back(ComputeOverclockAvg(pixel)); + for(int i = 0; i < (int)pixel.size(); i++) { + pixelBuf[0] = pixel[i]; + // Do 8 bit to 12 bit conversion for prefix data + FixDns(pixelBuf); + double pix = pixelBuf[0]; + if(pix == NULL8) { + calibrationPixels.push_back(NULL2); + } + else if(pix == LOW_REPR_SAT8) { + calibrationPixels.push_back(LOW_REPR_SAT2); + } + else if(pix == LOW_INSTR_SAT8) { + calibrationPixels.push_back(LOW_INSTR_SAT2); + } + else if(pix == HIGH_INSTR_SAT8) { + calibrationPixels.push_back(HIGH_INSTR_SAT2); + } + else if(pix == HIGH_REPR_SAT8) { + calibrationPixels.push_back(HIGH_REPR_SAT2); + } + else { + calibrationPixels.push_back(pix); + } + } + return calibrationPixels; + } + + // Called in IsisMain() if DataConversionType == Table + // Creates stretch pairs for mapping in Fix Dns + // author Jeannie Walldren 2008-08-21 + void CreateStretchPairs() { + // Set up the strech for the 8 to 12 bit conversion from file + PvlGroup &dataDir = Preference::Preferences().findGroup("DataDirectory"); + QString missionDir = (QString) dataDir["Cassini"]; + FileName *lutFile = new FileName(missionDir + "/calibration/lut/lut.tab"); + CisscalFile *stretchPairs = new CisscalFile(lutFile->expanded()); + // Create the stretch pairs + double temp1 = 0; + stretch.ClearPairs(); + for(int i = 0; i < stretchPairs->LineCount(); i++) { + QString line; + stretchPairs->GetLine(line); //assigns value to line + line = line.simplified(); + + foreach (QString value, line.split(QRegExp("[\\s,]"), QString::SkipEmptyParts)) { + stretch.AddPair(temp1, toDouble(value)); + temp1++; + } + } + stretchPairs->Close(); + return; + } + + /** + * The input buffer has a raw 16 bit buffer but the values are still 0 to 255. + * We know that 255 (stretched to 4095 if Table converted) is saturated. + * Sky pixels could have valid DN of 0, but missing pixels are also saved as 0, + * so it is impossible to distinguish between them. + * This method is used by ConvertLinePrefixPixels() and IsisMain() for ProcessByLine p2. + * author Jeannie Walldren 2008-08-21 + * + */ + void FixDns(Buffer &buf) { + for(int i = 0; i < buf.size(); i++) { + // zeros and negatives are valid DN values, according to scientists, + // but likelyhood of a zero in 16 bit is rare, + // so assume these are missing pixels and set them to null + if(buf[i] == 0) { + buf[i] = Null; + } + else if(dataConversionType == "Table") { + buf[i] = stretch.Map((int)buf[i]); + } + // save max values (4095 for table-converted images and 255 for others) as HRS + if(buf[i] >= validMax) { + buf[i] = Hrs; + } + } + } + + + /** + * This method uses the translation table to read labels and adds any + * other needed keywords to Instrument, BandBin, and Kernels groups + * Called in IsisMain() + * + * @param labelFile + * @param ocube + * + * @history 2008-08-21 Jeannie Walldren + * @history 2010-12-08 Sharmila Prasad - Removed traling 'Z' for Start, + * Stop and Image Time labels + * + */ + void TranslateCassIssLabels(FileName &labelFile, Cube *ocube, Pvl *log) { + // Get the directory where the CISS translation tables are. + QString dir = "$ISISROOT/appdata/translations"; + FileName transFile(dir + "/CassiniIss.trn"); + + // Get the translation manager ready + Pvl inputLabel(labelFile.expanded()); + PvlToPvlTranslationManager labelXlater(inputLabel, transFile.expanded()); + + // Pvl outputLabels; + Pvl *outputLabel = ocube->label(); + labelXlater.Auto(*(outputLabel)); + + //Add needed keywords that are not in translation table to cube's instrument group + PvlGroup &inst = outputLabel->findGroup("Instrument", Pvl::Traverse); + QString scc = inputLabel.findKeyword("SPACECRAFT_CLOCK_CNT_PARTITION"); + scc += "/" + (QString) inputLabel.findKeyword("SPACECRAFT_CLOCK_START_COUNT"); + inst.addKeyword(PvlKeyword("SpacecraftClockCount", scc)); + + //Add units of measurement to keywords from translation table + double exposureDuration = inst.findKeyword("ExposureDuration"); + inst.findKeyword("ExposureDuration").setValue(toString(exposureDuration), "Milliseconds"); + + int gainModeId = inst.findKeyword("GainModeId"); + inst.findKeyword("GainModeId").setValue(toString(gainModeId), "ElectronsPerDN"); + + PvlKeyword opticsTemp = inst.findKeyword("OpticsTemperature"); + inst.findKeyword("OpticsTemperature").setValue(opticsTemp[0]); + inst.findKeyword("OpticsTemperature").addValue(opticsTemp[1], "DegreesCelcius"); + + double instDataRate = inst.findKeyword("InstrumentDataRate"); + inst.findKeyword("InstrumentDataRate").setValue(toString(instDataRate), "KilobitsPerSecond"); + + // initialize global variables + dataConversionType = (QString) inst.findKeyword("DataConversionType"); + validMax = inputLabel.findKeyword("ValidMaximum")[1].toInt(); + sumMode = inst.findKeyword("SummingMode"); + compressionType = (QString) inst.findKeyword("CompressionType"); + IString fsw((QString) inst.findKeyword("FlightSoftwareVersionId")); + if(fsw == "Unknown") { + flightSoftware = 0.0; + } + else { + flightSoftware = fsw.ToDouble(); + } + + // Remove the trailing 'Z' in some pds labels + QString sUpdateTime = inst.findKeyword("StartTime")[0]; + sUpdateTime.remove(QRegExp("[Zz]")); + inst.findKeyword("StartTime").setValue(sUpdateTime); + + sUpdateTime = inst.findKeyword("StopTime")[0]; + sUpdateTime.remove(QRegExp("[Zz]")); + inst.findKeyword("StopTime").setValue(sUpdateTime); + + sUpdateTime = inst.findKeyword("ImageTime")[0]; + sUpdateTime.remove(QRegExp("[Zz]")); + inst.findKeyword("ImageTime").setValue(sUpdateTime); + + + // create BandBin group + QString filter = inputLabel.findKeyword("FilterName")[0] + "/" + + inputLabel.findKeyword("FilterName")[1]; + + QString instrumentID = inst.findKeyword("InstrumentId"); + QString cameraAngleDefs; + if(instrumentID.at(3) == 'N') { + cameraAngleDefs = dir + "/CassiniIssNarrowAngle.def"; + } + else if(instrumentID.at(3) == 'W') { + cameraAngleDefs = dir + "/CassiniIssWideAngle.def"; + } + + double center = 0; + double width = 0; + + TextFile cameraAngle(cameraAngleDefs); + int numLines = cameraAngle.LineCount(); + bool foundfilter = false; + for(int i = 0; i < numLines; i++) { + QString line; + cameraAngle.GetLine(line, true); + + QStringList tokens = line.simplified().split(" "); + if(tokens.count() > 2 && tokens.first() == filter) { + center = toDouble(tokens[1]); + width = toDouble(tokens[2]); + foundfilter = true; + break; + } + } + PvlGroup bandBin("BandBin"); + bandBin += PvlKeyword("FilterName", filter); + bandBin += PvlKeyword("OriginalBand", "1"); + + if(foundfilter) { + bandBin += PvlKeyword("Center", toString(center)); + bandBin += PvlKeyword("Width", toString(width)); + } + else { + PvlGroup msgGrp("Warnings"); + msgGrp += PvlKeyword("CameraAngleLookup", "Failed! No Camera information for filter combination: " + filter); + if (log) { + log->addGroup(msgGrp); + } + bandBin += PvlKeyword("Center", "None found for filter combination."); + bandBin += PvlKeyword("Width", "None found for filter combination."); + } + ocube->putGroup(bandBin); + + PvlGroup kerns("Kernels"); + + if(instrumentID == "ISSNA") { + kerns += PvlKeyword("NaifFrameCode", "-82360"); + } + else if(instrumentID == "ISSWA") { + kerns += PvlKeyword("NaifFrameCode", "-82361"); + } + else { + QString msg = "CISS2ISIS only imports Cassini ISS narrow "; + msg += "angle or wide angle images"; + throw IException(IException::User, msg, _FILEINFO_); + } + ocube->putGroup(kerns); + + return; + } + + + + // This method is called in ConvertLinePrefixPixels() and is + // modelled after IDL CISSCAL's OverclockAvg() in cassimg_define.pro + // author Jeannie Walldren 2008-08-21 + double ComputeOverclockAvg(vector pixel) { + // overclocks array is corrupt for lossy images (see cassimg_readvic.pro) + + if(compressionType != "Lossy" && flightSoftware < 1.3) { //numberOfOverclocks == 1 + // if Bltype CASSINI-ISS or CAS-ISS2, i.e. flight software version < 1.3 + // then there is only one column of valid overclocks in prefix pixels table, + // the first column contains nulls, so use column 2 as average + return pixel[1]; + } + else { //numberOfOverclocks == 2 + // number of columns of valid overclocks in prefix pixels table is 2 + // for CAS-ISS3 or CAS-ISS4, i.e. flight software version 1.3 or 1.4 + // calculate appropriate average (as in cassimg_define.pro, CassImg::OverclockAvg()) + if(sumMode == 1) { + return ((((double) pixel[0]) / 2 + ((double) pixel[1]) / 6) / 2); + } + if(sumMode == 2) { + return ((((double)pixel[0]) + ((double) pixel[1]) / 3) / 2); + } + if(sumMode == 4) { + return ((((double) pixel[0]) + ((double) pixel[1])) / 2); + } + else return 0; + } + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // File header and readout order comments... + // OUR FILE HEADER INCLUDES TWO SECTIONS: + // -The first is the VICAR label (SIS page 52). The number of bytes included here is calculated in the IsisMain() + // -The second is the Binary Label Header, or Binary Telemetry Header(SIS page 52). This contains 60 bytes (SIS page 84) of significant data. + // The READOUT ORDER of an image is the order in which the cameras were read. This is needed for radiometric calibration (CISSCAL). + // The possible values are : + // 0 : Narrow-angle camera was read out first + // 1 : Wide-angle camera was read out first + // IDL CISSCAL FILE CASSIMG_SUBTRACTDARK.PRO LINE 333: + // roo = bh[50]/32 MOD 2 ;Readout order is the 2nd bit of the 51st byte + // According to SIS page 92 (Field=Software, Valid Values), the readout order is index 2 (the THIRD bit) of the byte. + // Normally, we would assume that this was the third bit from the right, but there is some confusion on this matter. + // SIS page 17 says bits and bytes are both "big endian" for pixel data, but doesn't mention whether this includes the binary telemetry table data, + // Reading the first 3 bytes of the binary header and comparing with bit values described in SIS Table 7.3.2, + // if the bytes are read as most significant bit first (left-to-right), each value matches up except summation mode. + // In this case, SIS says they shoud be sum1:01, sum2:10, sum4:11. Actual values are sum1:00, sum2:01, sum4:10. + // The IDL code also appears to be written as though bits are read in this manner, accessing the third bit from the left (32 ~ 00100000). + // Since we haven't found a difinitive answer to this, we are mimicking the IDL code to determine the read out order. + // We have not found an image with roo = 1 as of yet to test this. + // If it is found to be the case that bits are read from left to right in this header, it may be more clear in the + // future to rewrite the line using a logical bitwise &-operator: roo = *(header+50+vicarLabelBytes) & (00100000); + // SOURCES : + // Cassini ISS Tour VICAR Image Data File and Detatched PDS Label SIS, Tour Version 1.1 December 1, 2004 + // IDL cisscal application files: cassimg_subtractdark.pro and linetime.pro + // -Jeannie Walldren 08/06/2008 + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} diff --git a/isis/src/cassini/apps/ciss2isis/ciss2isis.h b/isis/src/cassini/apps/ciss2isis/ciss2isis.h new file mode 100644 index 0000000000..49f5238643 --- /dev/null +++ b/isis/src/cassini/apps/ciss2isis/ciss2isis.h @@ -0,0 +1,19 @@ +#ifndef ciss2isis_h +#define ciss2isis_h + +/** This is free and unencumbered software released into the public domain. + +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ + +#include "Cube.h" +#include "UserInterface.h" + +namespace Isis { + extern void ciss2isis(UserInterface &ui, Pvl *log=nullptr); +} + +#endif diff --git a/isis/src/cassini/apps/ciss2isis/main.cpp b/isis/src/cassini/apps/ciss2isis/main.cpp index 6f427715e0..142b609ee8 100644 --- a/isis/src/cassini/apps/ciss2isis/main.cpp +++ b/isis/src/cassini/apps/ciss2isis/main.cpp @@ -6,456 +6,28 @@ find files of those names at the top level of this repository. **/ /* SPDX-License-Identifier: CC0-1.0 */ -//Source: Cassini ISS Tour VICAR Image Data File and Detatched PDS Label SIS, Tour Version 1.1 December 1, 2004 - #include "Isis.h" -#include -#include - -#include - -#include "CisscalFile.h" -#include "Cube.h" -#include "FileName.h" -#include "IException.h" -#include "IString.h" -#include "Preference.h" -#include "ProcessImportPds.h" -#include "ProcessByLine.h" +#include "Application.h" #include "Pvl.h" -#include "SpecialPixel.h" -#include "Stretch.h" -#include "Table.h" -#include "TextFile.h" -#include "UserInterface.h" +#include "ciss2isis.h" - -using namespace std; using namespace Isis; -// Function prototypes -double ComputeOverclockAvg(vector pixel); -vector ConvertLinePrefixPixels(unsigned char *data); -Table CreateLinePrefixTable(vector prefixData); -void CreateStretchPairs(); -void FixDns(Buffer &buf); -void TranslateCassIssLabels(FileName &labelFile, Cube *ocube); -//Global variables -QString compressionType; -QString dataConversionType; -double flightSoftware; -Stretch stretch; -int sumMode; -int validMax; - void IsisMain() { - //PROCESS 1: saves off label, header, and line prefix data ==========================================// - ProcessImportPds p; - Pvl label; UserInterface &ui = Application::GetUserInterface(); - FileName in = ui.GetFileName("FROM"); - + Pvl appLog; try { - p.SetPdsFile(in.expanded(), "", label); - } - catch (IException &e) { - throw IException(e, IException::User, - QObject::tr("Error reading input file. Make sure it contains a PDS label."), - _FILEINFO_); - } - - //Checks if in file is rdr - if(label.hasObject("IMAGE_MAP_PROJECTION")) { - QString msg = "[" + in.name() + "] appears to be an rdr file."; - msg += " Use pds2isis."; - throw IException(IException::User, msg, _FILEINFO_); - } - - // Set the output bit type to SignedWord - CubeAttributeOutput &outAtt = ui.GetOutputAttribute("TO"); - outAtt.setPixelType(SignedWord); - outAtt.setMinimum((double)VALID_MIN2); - outAtt.setMaximum((double)VALID_MAX2); - Cube *ocube = p.SetOutputCube(ui.GetFileName("TO"), outAtt); - - TranslateCassIssLabels(in, ocube); - - //Save off header (includes vicar labels and binary telemetry header) - // No need to SetFileHeaderBytes() this is already done by ProcessImportPds automatically - int vicarLabelBytes = label.findObject("IMAGE_HEADER").findKeyword("BYTES"); - p.SaveFileHeader(); - - //Save off line prefix data, always 24 bytes of binary prefix per line,see SIS version 1.1 pg 103 - int linePrefixBytes = label.findObject("IMAGE").findKeyword("LINE_PREFIX_BYTES"); - p.SetDataPrefixBytes(linePrefixBytes); - p.SaveDataPrefix(); - - //SET PROGRESS TEXT, VALID MAXIMUM PIXEL VALUE, AND CREATE STRETCH IF NEEDED - if(dataConversionType != "Table") { //Conversion Type is 12Bit or 8LSB, only save off overclocked pixels - validMax = 255; - if(dataConversionType == "12Bit") { - p.Progress()->SetText("Image was 12 bit. No conversion needed. \nSaving line prefix data..."); - } - else { //if (dataConversionType == "8LSB") { - p.Progress()->SetText("Image was truncated to 8 least significant bits. No conversion needed. \nSaving line prefix data..."); - } - } - else { //if ConversionType == Table, Use LUT to create stretch pairs for conversion - validMax = 4095; - CreateStretchPairs(); - // Pvl outputLabels; - Pvl *outputLabel = ocube->label(); - //Adjust Table-encoded values from 8 bit back to 12 bit. - PvlGroup &inst = outputLabel->findGroup("Instrument", Pvl::Traverse); - double biasStripMean = inst.findKeyword("BiasStripMean"); - inst.findKeyword("BiasStripMean").setValue(toString(stretch.Map(biasStripMean))); - inst.findKeyword("BiasStripMean").addComment("BiasStripMean value converted back to 12 bit."); - p.Progress()->SetText("Image was converted using 12-to-8 bit table. \nConverting prefix pixels back to 12 bit and saving line prefix data..."); - } - - p.StartProcess(); - - // Write line prefix data to table in output cube - vector > dataPrefix = p.DataPrefix(); - vector prefixBand0 = dataPrefix.at(0); //There is only one band so the outside vector only contains - // one entry and the inside vector only contains nl entries - Table linePrefixTable = CreateLinePrefixTable(prefixBand0); - ocube->write(linePrefixTable); - // Compute readout order (roo) and save to output cube's instrument group - unsigned char *header = (unsigned char *) p.FileHeader(); - int roo = *(header + 50 + vicarLabelBytes) / 32 % 2; //**** THIS MAY NEED TO BE CHANGED, - // SEE BOTTOM OF THIS FILE FOR IN DEPTH COMMENTS ON READOUTORDER - PvlGroup &inst = ocube->label()->findGroup("Instrument", Pvl::Traverse); - inst.addKeyword(PvlKeyword("ReadoutOrder", toString(roo))); - p.EndProcess(); - - // PROCESS 2 : Do 8 bit to 12 bit conversion for image ==============================================// - ProcessByLine p2; - QString ioFile = ui.GetFileName("TO"); - CubeAttributeInput att; - p2.SetInputCube(ioFile, att, ReadWrite); - //if ConversionType == 12Bit or 8LSB, only save off overclocked pixels - if(dataConversionType == "12Bit" || dataConversionType == "8LSB") { - p2.Progress()->SetText("Setting special pixels and saving as 16bit..."); - } - //if ConversionType == Table, Use LUT to create stretch pairs for conversion - else { - p2.Progress()->SetText("Converting image pixels back to 12-bit and saving as 16bit..."); + ciss2isis(ui, &appLog); } - p2.StartProcess(FixDns); - p2.EndProcess(); - return; -} - -//call this method after stretch is closed in IsisMain() -// write data into table to save in output cube -// author Jeannie Walldren 2008-08-21 -Table CreateLinePrefixTable(vector prefixData) { - TableField overclockPixels("OverclockPixels", TableField::Double, 3); - //3 columns, first two are overclocked pixels and the third is their average - TableRecord linePrefixRecord; - linePrefixRecord += overclockPixels; - Table linePrefixTable("ISS Prefix Pixels", linePrefixRecord); - linePrefixTable.SetAssociation(Table::Lines); - for(int l = 0; l < (int)prefixData.size(); l++) { - unsigned char *linePrefix = (unsigned char *)(prefixData[l]); - linePrefixRecord[0] = ConvertLinePrefixPixels(linePrefix); - linePrefixTable += linePrefixRecord; - } - return linePrefixTable; -} - -//used by CreateLinePrefixTable() to convert prefix data -// author Jeannie Walldren 2008-08-21 -vector ConvertLinePrefixPixels(unsigned char *data) { - Buffer pixelBuf(1, 1, 1, SignedWord); - - vector calibrationPixels; - //Pixel data is MSB, see SIS version 1.1 page 17 - EndianSwapper swapper("MSB"); - - vector pixel; - //12 is start byte for First Overclocked Pixel Sum in Binary Line Prefix, SIS version 1.1 page 94 - pixel.push_back(swapper.ShortInt(& (data[12]))); - //22 is start byte for Last Overclocked Pixel Sum in Binary Line Prefix, see SIS version 1.1 page 94 - pixel.push_back(swapper.ShortInt(& (data[22]))); - pixel.push_back(ComputeOverclockAvg(pixel)); - for(int i = 0; i < (int)pixel.size(); i++) { - pixelBuf[0] = pixel[i]; - // Do 8 bit to 12 bit conversion for prefix data - FixDns(pixelBuf); - double pix = pixelBuf[0]; - if(pix == NULL8) { - calibrationPixels.push_back(NULL2); - } - else if(pix == LOW_REPR_SAT8) { - calibrationPixels.push_back(LOW_REPR_SAT2); - } - else if(pix == LOW_INSTR_SAT8) { - calibrationPixels.push_back(LOW_INSTR_SAT2); - } - else if(pix == HIGH_INSTR_SAT8) { - calibrationPixels.push_back(HIGH_INSTR_SAT2); - } - else if(pix == HIGH_REPR_SAT8) { - calibrationPixels.push_back(HIGH_REPR_SAT2); - } - else { - calibrationPixels.push_back(pix); + catch (...) { + for (auto grpIt = appLog.beginGroup(); grpIt!= appLog.endGroup(); grpIt++) { + Application::Log(*grpIt); } + throw; } - return calibrationPixels; -} - -// Called in IsisMain() if DataConversionType == Table -// Creates stretch pairs for mapping in Fix Dns -// author Jeannie Walldren 2008-08-21 -void CreateStretchPairs() { - // Set up the strech for the 8 to 12 bit conversion from file - PvlGroup &dataDir = Preference::Preferences().findGroup("DataDirectory"); - QString missionDir = (QString) dataDir["Cassini"]; - FileName *lutFile = new FileName(missionDir + "/calibration/lut/lut.tab"); - CisscalFile *stretchPairs = new CisscalFile(lutFile->expanded()); - // Create the stretch pairs - double temp1 = 0; - stretch.ClearPairs(); - for(int i = 0; i < stretchPairs->LineCount(); i++) { - QString line; - stretchPairs->GetLine(line); //assigns value to line - line = line.simplified(); - foreach (QString value, line.split(QRegExp("[\\s,]"), QString::SkipEmptyParts)) { - stretch.AddPair(temp1, toDouble(value)); - temp1++; - } + for (auto grpIt = appLog.beginGroup(); grpIt!= appLog.endGroup(); grpIt++) { + Application::Log(*grpIt); } - stretchPairs->Close(); - return; } - -/** -* The input buffer has a raw 16 bit buffer but the values are still 0 to 255. -* We know that 255 (stretched to 4095 if Table converted) is saturated. -* Sky pixels could have valid DN of 0, but missing pixels are also saved as 0, -* so it is impossible to distinguish between them. -* This method is used by ConvertLinePrefixPixels() and IsisMain() for ProcessByLine p2. -* author Jeannie Walldren 2008-08-21 -* -*/ -void FixDns(Buffer &buf) { - for(int i = 0; i < buf.size(); i++) { - // zeros and negatives are valid DN values, according to scientists, - // but likelyhood of a zero in 16 bit is rare, - // so assume these are missing pixels and set them to null - if(buf[i] == 0) { - buf[i] = Null; - } - else if(dataConversionType == "Table") { - buf[i] = stretch.Map((int)buf[i]); - } - // save max values (4095 for table-converted images and 255 for others) as HRS - if(buf[i] == validMax) { - buf[i] = Hrs; - } - } -} - - -/** - * This method uses the translation table to read labels and adds any - * other needed keywords to Instrument, BandBin, and Kernels groups - * Called in IsisMain() - * - * @param labelFile - * @param ocube - * - * @history 2008-08-21 Jeannie Walldren - * @history 2010-12-08 Sharmila Prasad - Removed traling 'Z' for Start, - * Stop and Image Time labels - * - */ -void TranslateCassIssLabels(FileName &labelFile, Cube *ocube) { - // Get the directory where the CISS translation tables are. - QString dir = "$ISISROOT/appdata/translations"; - FileName transFile(dir + "/CassiniIss.trn"); - - // Get the translation manager ready - Pvl inputLabel(labelFile.expanded()); - PvlToPvlTranslationManager labelXlater(inputLabel, transFile.expanded()); - - // Pvl outputLabels; - Pvl *outputLabel = ocube->label(); - labelXlater.Auto(*(outputLabel)); - - //Add needed keywords that are not in translation table to cube's instrument group - PvlGroup &inst = outputLabel->findGroup("Instrument", Pvl::Traverse); - QString scc = inputLabel.findKeyword("SPACECRAFT_CLOCK_CNT_PARTITION"); - scc += "/" + (QString) inputLabel.findKeyword("SPACECRAFT_CLOCK_START_COUNT"); - inst.addKeyword(PvlKeyword("SpacecraftClockCount", scc)); - - //Add units of measurement to keywords from translation table - double exposureDuration = inst.findKeyword("ExposureDuration"); - inst.findKeyword("ExposureDuration").setValue(toString(exposureDuration), "Milliseconds"); - - int gainModeId = inst.findKeyword("GainModeId"); - inst.findKeyword("GainModeId").setValue(toString(gainModeId), "ElectronsPerDN"); - - PvlKeyword opticsTemp = inst.findKeyword("OpticsTemperature"); - inst.findKeyword("OpticsTemperature").setValue(opticsTemp[0]); - inst.findKeyword("OpticsTemperature").addValue(opticsTemp[1], "DegreesCelcius"); - - double instDataRate = inst.findKeyword("InstrumentDataRate"); - inst.findKeyword("InstrumentDataRate").setValue(toString(instDataRate), "KilobitsPerSecond"); - - // initialize global variables - dataConversionType = (QString) inst.findKeyword("DataConversionType"); - sumMode = inst.findKeyword("SummingMode"); - compressionType = (QString) inst.findKeyword("CompressionType"); - IString fsw((QString) inst.findKeyword("FlightSoftwareVersionId")); - if(fsw == "Unknown") { - flightSoftware = 0.0; - } - else { - flightSoftware = fsw.ToDouble(); - } - - // Remove the trailing 'Z' in some pds labels - QString sUpdateTime = inst.findKeyword("StartTime")[0]; - sUpdateTime.remove(QRegExp("[Zz]")); - inst.findKeyword("StartTime").setValue(sUpdateTime); - - sUpdateTime = inst.findKeyword("StopTime")[0]; - sUpdateTime.remove(QRegExp("[Zz]")); - inst.findKeyword("StopTime").setValue(sUpdateTime); - - sUpdateTime = inst.findKeyword("ImageTime")[0]; - sUpdateTime.remove(QRegExp("[Zz]")); - inst.findKeyword("ImageTime").setValue(sUpdateTime); - - - // create BandBin group - QString filter = inputLabel.findKeyword("FilterName")[0] + "/" + - inputLabel.findKeyword("FilterName")[1]; - - QString instrumentID = inst.findKeyword("InstrumentId"); - QString cameraAngleDefs; - if(instrumentID.at(3) == 'N') { - cameraAngleDefs = dir + "/CassiniIssNarrowAngle.def"; - } - else if(instrumentID.at(3) == 'W') { - cameraAngleDefs = dir + "/CassiniIssWideAngle.def"; - } - - double center = 0; - double width = 0; - - TextFile cameraAngle(cameraAngleDefs); - int numLines = cameraAngle.LineCount(); - bool foundfilter = false; - for(int i = 0; i < numLines; i++) { - QString line; - cameraAngle.GetLine(line, true); - - QStringList tokens = line.simplified().split(" "); - if(tokens.count() > 2 && tokens.first() == filter) { - center = toDouble(tokens[1]); - width = toDouble(tokens[2]); - foundfilter = true; - break; - } - } - PvlGroup bandBin("BandBin"); - bandBin += PvlKeyword("FilterName", filter); - bandBin += PvlKeyword("OriginalBand", "1"); - - if(foundfilter) { - bandBin += PvlKeyword("Center", toString(center)); - bandBin += PvlKeyword("Width", toString(width)); - } - else { - PvlGroup msgGrp("Warnings"); - msgGrp += PvlKeyword("CameraAngleLookup", "Failed! No Camera information for filter combination: " + filter); - Application::Log(msgGrp); - bandBin += PvlKeyword("Center", "None found for filter combination."); - bandBin += PvlKeyword("Width", "None found for filter combination."); - } - ocube->putGroup(bandBin); - - PvlGroup kerns("Kernels"); - - if(instrumentID == "ISSNA") { - kerns += PvlKeyword("NaifFrameCode", "-82360"); - } - else if(instrumentID == "ISSWA") { - kerns += PvlKeyword("NaifFrameCode", "-82361"); - } - else { - QString msg = "CISS2ISIS only imports Cassini ISS narrow "; - msg += "angle or wide angle images"; - throw IException(IException::User, msg, _FILEINFO_); - } - ocube->putGroup(kerns); - - return; -} - - - -// This method is called in ConvertLinePrefixPixels() and is -// modelled after IDL CISSCAL's OverclockAvg() in cassimg_define.pro -// author Jeannie Walldren 2008-08-21 -double ComputeOverclockAvg(vector pixel) { - // overclocks array is corrupt for lossy images (see cassimg_readvic.pro) - - if(compressionType != "Lossy" && flightSoftware < 1.3) { //numberOfOverclocks == 1 - // if Bltype CASSINI-ISS or CAS-ISS2, i.e. flight software version < 1.3 - // then there is only one column of valid overclocks in prefix pixels table, - // the first column contains nulls, so use column 2 as average - return pixel[1]; - } - else { //numberOfOverclocks == 2 - // number of columns of valid overclocks in prefix pixels table is 2 - // for CAS-ISS3 or CAS-ISS4, i.e. flight software version 1.3 or 1.4 - // calculate appropriate average (as in cassimg_define.pro, CassImg::OverclockAvg()) - if(sumMode == 1) { - return ((((double) pixel[0]) / 2 + ((double) pixel[1]) / 6) / 2); - } - if(sumMode == 2) { - return ((((double)pixel[0]) + ((double) pixel[1]) / 3) / 2); - } - if(sumMode == 4) { - return ((((double) pixel[0]) + ((double) pixel[1])) / 2); - } - else return 0; - } -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// File header and readout order comments... -// OUR FILE HEADER INCLUDES TWO SECTIONS: -// -The first is the VICAR label (SIS page 52). The number of bytes included here is calculated in the IsisMain() -// -The second is the Binary Label Header, or Binary Telemetry Header(SIS page 52). This contains 60 bytes (SIS page 84) of significant data. -// The READOUT ORDER of an image is the order in which the cameras were read. This is needed for radiometric calibration (CISSCAL). -// The possible values are : -// 0 : Narrow-angle camera was read out first -// 1 : Wide-angle camera was read out first -// IDL CISSCAL FILE CASSIMG_SUBTRACTDARK.PRO LINE 333: -// roo = bh[50]/32 MOD 2 ;Readout order is the 2nd bit of the 51st byte -// According to SIS page 92 (Field=Software, Valid Values), the readout order is index 2 (the THIRD bit) of the byte. -// Normally, we would assume that this was the third bit from the right, but there is some confusion on this matter. -// SIS page 17 says bits and bytes are both "big endian" for pixel data, but doesn't mention whether this includes the binary telemetry table data, -// Reading the first 3 bytes of the binary header and comparing with bit values described in SIS Table 7.3.2, -// if the bytes are read as most significant bit first (left-to-right), each value matches up except summation mode. -// In this case, SIS says they shoud be sum1:01, sum2:10, sum4:11. Actual values are sum1:00, sum2:01, sum4:10. -// The IDL code also appears to be written as though bits are read in this manner, accessing the third bit from the left (32 ~ 00100000). -// Since we haven't found a difinitive answer to this, we are mimicking the IDL code to determine the read out order. -// We have not found an image with roo = 1 as of yet to test this. -// If it is found to be the case that bits are read from left to right in this header, it may be more clear in the -// future to rewrite the line using a logical bitwise &-operator: roo = *(header+50+vicarLabelBytes) & (00100000); -// SOURCES : -// Cassini ISS Tour VICAR Image Data File and Detatched PDS Label SIS, Tour Version 1.1 December 1, 2004 -// IDL cisscal application files: cassimg_subtractdark.pro and linetime.pro -// -Jeannie Walldren 08/06/2008 -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/isis/src/cassini/apps/ciss2isis/tsts/narrowAngle/Makefile b/isis/src/cassini/apps/ciss2isis/tsts/narrowAngle/Makefile deleted file mode 100644 index f467f5cbb7..0000000000 --- a/isis/src/cassini/apps/ciss2isis/tsts/narrowAngle/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -APPNAME = ciss2isis - -labels.txt.IGNORELINES = Bytes StartByte TileSamples TileLines - -include $(ISISROOT)/make/isismake.tsts - -commands: - $(APPNAME) from=$(INPUT)/N1472853667_1.LBL \ - to=$(OUTPUT)/N1472853667_1.truth.cub > /dev/null; - catlab from=$(OUTPUT)/N1472853667_1.truth.cub to=$(OUTPUT)/labels.txt > /dev/null; diff --git a/isis/src/cassini/apps/ciss2isis/tsts/wideAngle/Makefile b/isis/src/cassini/apps/ciss2isis/tsts/wideAngle/Makefile deleted file mode 100644 index af390ad05c..0000000000 --- a/isis/src/cassini/apps/ciss2isis/tsts/wideAngle/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -APPNAME =ciss2isis - -labels.txt.IGNORELINES = Bytes StartByte ByteOrder TileSamples TileLines - -include $(ISISROOT)/make/isismake.tsts - -commands: - $(APPNAME) FROM=$(INPUT)/W1472855646_5.LBL \ - TO=$(OUTPUT)/W1472855646_5.truth.cub > /dev/null; - catlab from=$(OUTPUT)/W1472855646_5.truth.cub to=$(OUTPUT)/labels.txt > /dev/null; diff --git a/isis/tests/FunctionalTestsCiss2isis.cpp b/isis/tests/FunctionalTestsCiss2isis.cpp new file mode 100644 index 0000000000..4d6f3e4073 --- /dev/null +++ b/isis/tests/FunctionalTestsCiss2isis.cpp @@ -0,0 +1,272 @@ +#include + +#include "ciss2isis.h" +#include "Fixtures.h" +#include "Pvl.h" +#include "PvlGroup.h" +#include "TestUtilities.h" +#include "Histogram.h" + +#include "gtest/gtest.h" + +using namespace Isis; + +static QString APP_XML = FileName("$ISISROOT/bin/xml/ciss2isis.xml").expanded(); + +TEST(Ciss2Isis, Ciss2isisTestNac) { + Pvl appLog; + QTemporaryDir prefix; + QString cubeFileName = prefix.path() + "/ciss2isis_out.cub"; + QVector args = { "from=data/ciss2isis/N1472853667_1.cropped.lbl", + "to=" + cubeFileName }; + UserInterface options(APP_XML, args); + try { + ciss2isis(options, &appLog); + } + catch (IException &e) { + FAIL() << "Unable to ingest file: " << e.toString().toStdString().c_str() << std::endl; + } + + Cube outCube(cubeFileName); + Pvl *outLabel = outCube.label(); + + PvlGroup dimensions = outLabel->findGroup("Dimensions", Pvl::Traverse); + ASSERT_EQ((int)dimensions["Samples"], 1024); + ASSERT_EQ((int)dimensions["Lines"], 10); + ASSERT_EQ((int)dimensions["Bands"], 1); + + PvlGroup pixels = outLabel->findGroup("Pixels", Pvl::Traverse); + ASSERT_EQ(pixels["Type"][0].toStdString(), "SignedWord"); + ASSERT_EQ(pixels["ByteOrder"][0].toStdString(), "Lsb"); + ASSERT_EQ((double)pixels["Base"], 0.0); + ASSERT_EQ((double)pixels["Multiplier"], 1.0); + + std::istringstream iss(R"( + Group = Instrument + SpacecraftName = Cassini-Huygens + InstrumentId = ISSNA + TargetName = Saturn + StartTime = 2004-09-02T21:32:36.410 + StopTime = 2004-09-02T21:36:16.410 + ExposureDuration = 220000.0 + AntibloomingStateFlag = On + + # BiasStripMean value converted back to 12 bit. + BiasStripMean = 50.00196 + CompressionRatio = 1.845952 + CompressionType = Lossless + DataConversionType = Table + DelayedReadoutFlag = No + FlightSoftwareVersionId = 1.3 + GainModeId = 12 + GainState = 3 + ImageTime = 2004-09-02T21:36:16.410 + InstrumentDataRate = 182.783997 + OpticsTemperature = (0.712693, 1.905708 ) + ReadoutCycleIndex = 10 + ShutterModeId = NacOnly + ShutterStateId = Enabled + SummingMode = 1 + InstrumentModeId = Full + SpacecraftClockCount = 1/1472853447.118 + ReadoutOrder = 0 + End_Group + )"); + + PvlGroup truthInstGroup; + iss >> truthInstGroup; + PvlGroup &instGroup = outLabel->findGroup("Instrument", Pvl::Traverse); + + EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, instGroup, truthInstGroup); + + std::istringstream arss(R"( + Group = Archive + DataSetId = CO-S-ISSNA/ISSWA-2-EDR-V1.0 + ImageNumber = 1472853667 + ObservationId = ISS_00ARI_DIFFUSRNG003_PRIME + ProductId = 1_N1472853667.118 + End_Group + )"); + + PvlGroup truthArchiveGroup; + arss >> truthArchiveGroup; + + PvlGroup &archiveGroup = outLabel->findGroup("Archive", Pvl::Traverse); + + EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, archiveGroup, truthArchiveGroup); + + std::istringstream bbss(R"( + Group = BandBin + FilterName = CL1/CL2 + OriginalBand = 1 + Center = 651.065 + Width = 340.923 + End_Group + )"); + + PvlGroup truthBandBinGroup; + bbss >> truthBandBinGroup; + + PvlGroup &bandBinGroup = outLabel->findGroup("BandBin", Pvl::Traverse); + + EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, bandBinGroup, truthBandBinGroup); + + // Check for the ISS prefix pixel table + ASSERT_TRUE(outLabel->hasObject("Table")); + EXPECT_EQ(outLabel->findObject("Table", Pvl::Traverse)["Name"][0], "ISS Prefix Pixels"); + + std::unique_ptr hist (outCube.histogram()); + EXPECT_NEAR(hist->Average(), 247.45226885705699, .00001); + EXPECT_EQ(hist->Sum(), 2470316); + EXPECT_EQ(hist->ValidPixels(), 9983); + EXPECT_NEAR(hist->StandardDeviation(), 27.779542219945746, .0001); +} + +TEST(Ciss2Isis, Ciss2isisTestWac) { + Pvl appLog; + QTemporaryDir prefix; + QString cubeFileName = prefix.path() + "/ciss2isis_out.cub"; + QVector args = { "from=data/ciss2isis/W1472855646_5.cropped.lbl", + "to=" + cubeFileName }; + UserInterface options(APP_XML, args); + try { + ciss2isis(options, &appLog); + } + catch (IException &e) { + FAIL() << "Unable to ingest file: " << e.toString().toStdString().c_str() << std::endl; + } + + Cube outCube(cubeFileName); + Pvl *outLabel = outCube.label(); + + PvlGroup dimensions = outLabel->findGroup("Dimensions", Pvl::Traverse); + ASSERT_EQ((int)dimensions["Samples"], 1024); + ASSERT_EQ((int)dimensions["Lines"], 10); + ASSERT_EQ((int)dimensions["Bands"], 1); + + PvlGroup pixels = outLabel->findGroup("Pixels", Pvl::Traverse); + ASSERT_EQ(pixels["Type"][0].toStdString(), "SignedWord"); + ASSERT_EQ(pixels["ByteOrder"][0].toStdString(), "Lsb"); + ASSERT_EQ((double)pixels["Base"], 0.0); + ASSERT_EQ((double)pixels["Multiplier"], 1.0); + + std::istringstream iss(R"( + Group = Instrument + SpacecraftName = Cassini-Huygens + InstrumentId = ISSWA + TargetName = Saturn + StartTime = 2004-09-02T22:09:15.409 + StopTime = 2004-09-02T22:09:15.409 + ExposureDuration = 5.0 + AntibloomingStateFlag = On + BiasStripMean = 72.644554 + CompressionRatio = NotCompressed + CompressionType = NotCompressed + DataConversionType = 12Bit + DelayedReadoutFlag = Yes + FlightSoftwareVersionId = 1.3 + GainModeId = 29 + GainState = 2 + ImageTime = 2004-09-02T22:09:15.409 + InstrumentDataRate = 182.783997 + OpticsTemperature = (7.024934, -999.0 ) + ReadoutCycleIndex = 0 + ShutterModeId = BothSim + ShutterStateId = Disabled + SummingMode = 1 + InstrumentModeId = Full + SpacecraftClockCount = 1/1472855646.121 + ReadoutOrder = 0 + End_Group + )"); + + PvlGroup truthInstGroup; + iss >> truthInstGroup; + PvlGroup &instGroup = outLabel->findGroup("Instrument", Pvl::Traverse); + + EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, instGroup, truthInstGroup); + + std::istringstream arss(R"( + Group = Archive + DataSetId = CO-S-ISSNA/ISSWA-2-EDR-V1.0 + ImageNumber = 1472855646 + ObservationId = ISS_00ASA_MOS0ASWE001_UVIS + ProductId = 1_W1472855646.121 + End_Group + )"); + + PvlGroup truthArchiveGroup; + arss >> truthArchiveGroup; + + PvlGroup &archiveGroup = outLabel->findGroup("Archive", Pvl::Traverse); + + EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, archiveGroup, truthArchiveGroup); + + std::istringstream bbss(R"( + Group = BandBin + FilterName = CL1/CL2 + OriginalBand = 1 + Center = 633.837 + Width = 285.938 + End_Group + )"); + + PvlGroup truthBandBinGroup; + bbss >> truthBandBinGroup; + + PvlGroup &bandBinGroup = outLabel->findGroup("BandBin", Pvl::Traverse); + + EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, bandBinGroup, truthBandBinGroup); + + // Check for the ISS prefix pixel table + ASSERT_TRUE(outLabel->hasObject("Table")); + EXPECT_EQ(outLabel->findObject("Table", Pvl::Traverse)["Name"][0], "ISS Prefix Pixels"); + + std::unique_ptr hist (outCube.histogram()); + EXPECT_NEAR(hist->Average(), 70.914941406249994, .00001); + EXPECT_EQ(hist->Sum(), 726169); + EXPECT_EQ(hist->ValidPixels(), 10240); + EXPECT_NEAR(hist->StandardDeviation(), 0.84419124016427105, .0001); +} + +TEST(Ciss2Isis, Ciss2isisCustomMax) { + Pvl appLog; + QTemporaryDir prefix; + QString cubeFileName = prefix.path() + "/ciss2isis_out.cub"; + + QString inputLabel = "data/ciss2isis/W1472855646_5.cropped.lbl"; + QString updatedPvlLabel = prefix.path() + "/W1472855646_5.cropped.lbl"; + Pvl inputPvl(inputLabel); + inputPvl["VALID_MAXIMUM"][1] = "70"; + inputPvl.write(updatedPvlLabel); + QFile::copy("data/ciss2isis/W1472855646_5.cropped.img", prefix.path() + "/W1472855646_5.cropped.img"); + + QVector args = { "from=" + updatedPvlLabel, + "to=" + cubeFileName }; + UserInterface options(APP_XML, args); + try { + ciss2isis(options, &appLog); + } + catch (IException &e) { + FAIL() << "Unable to ingest file: " << e.toString().toStdString().c_str() << std::endl; + } + + Cube outCube(cubeFileName); + Pvl *outLabel = outCube.label(); + + PvlGroup dimensions = outLabel->findGroup("Dimensions", Pvl::Traverse); + ASSERT_EQ((int)dimensions["Samples"], 1024); + ASSERT_EQ((int)dimensions["Lines"], 10); + ASSERT_EQ((int)dimensions["Bands"], 1); + + PvlGroup pixels = outLabel->findGroup("Pixels", Pvl::Traverse); + ASSERT_EQ(pixels["Type"][0].toStdString(), "SignedWord"); + ASSERT_EQ(pixels["ByteOrder"][0].toStdString(), "Lsb"); + ASSERT_EQ((double)pixels["Base"], 0.0); + ASSERT_EQ((double)pixels["Multiplier"], 1.0); + + std::unique_ptr hist (outCube.histogram()); + EXPECT_EQ(hist->Maximum(), 69); + EXPECT_EQ(hist->ValidPixels(), 728); + EXPECT_EQ(hist->HrsPixels(), (1024 * 10) - hist->ValidPixels()); +} diff --git a/isis/tests/data/ciss2isis/N1472853667_1.cropped.img b/isis/tests/data/ciss2isis/N1472853667_1.cropped.img new file mode 100644 index 0000000000000000000000000000000000000000..90e70f04dcf6466efd0fb38e190086632cec22f1 GIT binary patch literal 14672 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 +EARTH_RECEIVED_START_TIME = 2004-09-03T09:54:37.232Z +EARTH_RECEIVED_STOP_TIME = 2004-09-03T09:57:12.966Z +ELECTRONICS_BIAS = 112 +EXPECTED_MAXIMUM = (0.756496, 1.9487) +EXPECTED_PACKETS = 519 +EXPOSURE_DURATION = 220000.0 +FILTER_NAME = (CL1, CL2) +FILTER_TEMPERATURE = -0.468354 +FLIGHT_SOFTWARE_VERSION_ID = '1.3' +GAIN_MODE_ID = '12 ELECTRONS PER DN' +IMAGE_MID_TIME = 2004-09-02T21:34:26.410Z +IMAGE_NUMBER = '1472853667' +IMAGE_OBSERVATION_TYPE = {SCIENCE} +IMAGE_TIME = 2004-09-02T21:36:16.410Z +INSTRUMENT_DATA_RATE = 182.783997 +INSTRUMENT_HOST_NAME = 'CASSINI ORBITER' +INSTRUMENT_ID = ISSNA +INSTRUMENT_MODE_ID = FULL +INSTRUMENT_NAME = 'IMAGING SCIENCE SUBSYSTEM NARROW ANGLE' +INST_CMPRS_PARAM = ('N/A', 'N/A', 'N/A', 'N/A') +INST_CMPRS_RATE = (3.6, 4.333807) +INST_CMPRS_RATIO = 1.845952 +INST_CMPRS_TYPE = LOSSLESS +LIGHT_FLOOD_STATE_FLAG = ON +METHOD_DESC = "ISSPT2.5.3;Saturn-Ering;ISS_00ARI_DIFFUSRNG003_PRIME_4" +MISSING_LINES = 511 +MISSING_PACKET_FLAG = NO +MISSION_NAME = 'CASSINI-HUYGENS' +MISSION_PHASE_NAME = 'TOUR PRE-HUYGENS' +OBSERVATION_ID = ISS_00ARI_DIFFUSRNG003_PRIME +OPTICS_TEMPERATURE = (0.712693, 1.905708) +ORDER_NUMBER = 2 +PARALLEL_CLOCK_VOLTAGE_INDEX = 9 +PREPARE_CYCLE_INDEX = 12 +PRODUCT_CREATION_TIME = 2004-09-03T10:39:14Z +PRODUCT_ID = '1_N1472853667.118' +PRODUCT_VERSION_TYPE = FINAL +READOUT_CYCLE_INDEX = 10 +RECEIVED_PACKETS = 576 +SENSOR_HEAD_ELEC_TEMPERATURE = 1.633024 +SEQUENCE_ID = S03 +SEQUENCE_NUMBER = 2 +SEQUENCE_TITLE = ISS_00ARI_DIFFUSRNG003_PRIME +SHUTTER_MODE_ID = NACONLY +SHUTTER_STATE_ID = ENABLED +SOFTWARE_VERSION_ID = 'ISS 9.00 02-05-2004' +SPACECRAFT_CLOCK_CNT_PARTITION = 1 +SPACECRAFT_CLOCK_START_COUNT = '1472853447.118' +SPACECRAFT_CLOCK_STOP_COUNT = '1472853667.118' +START_TIME = 2004-09-02T21:32:36.410Z +STOP_TIME = 2004-09-02T21:36:16.410Z +TARGET_DESC = 'Saturn-Ering' +TARGET_LIST = 'N/A' +TARGET_NAME = SATURN +TELEMETRY_FORMAT_ID = UNK +VALID_MAXIMUM = (9896, 4095) +OBJECT = IMAGE_HEADER + INTERCHANGE_FORMAT = ASCII + HEADER_TYPE = VICAR2 + BYTES = 3144 + RECORDS = 1 + ^DESCRIPTION = 'VICAR2.TXT' +END_OBJECT = IMAGE_HEADER +OBJECT = TELEMETRY_TABLE + INTERCHANGE_FORMAT = BINARY + ROWS = 1 + COLUMNS = 2 + ROW_BYTES = 1048 + ^STRUCTURE = 'TLMTAB.FMT' + OBJECT = COLUMN + NAME = NULL_PADDING + DATA_TYPE = MSB_UNSIGNED_INTEGER + START_BYTE = 61 + BYTES = 987 + END_OBJECT = COLUMN +END_OBJECT = TELEMETRY_TABLE +OBJECT = LINE_PREFIX_TABLE + INTERCHANGE_FORMAT = BINARY + ROWS = 1024 + COLUMNS = 7 + ROW_BYTES = 24 + ROW_SUFFIX_BYTES = 1024 + ^LINE_PREFIX_STRUCTURE = 'PREFIX2.FMT' +END_OBJECT = LINE_PREFIX_TABLE +OBJECT = IMAGE + LINES = 10 + LINE_SAMPLES = 1024 + SAMPLE_BITS = 8 + SAMPLE_TYPE = SUN_INTEGER + LINE_PREFIX_BYTES = 24 +END_OBJECT = IMAGE +END diff --git a/isis/tests/data/ciss2isis/W1472855646_5.cropped.img b/isis/tests/data/ciss2isis/W1472855646_5.cropped.img new file mode 100644 index 0000000000000000000000000000000000000000..4f3206098988cb579956091557412827916dc4bd GIT binary patch literal 26936 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 +EARTH_RECEIVED_START_TIME = 2004-09-03T10:20:50.946Z +EARTH_RECEIVED_STOP_TIME = 2004-09-03T10:28:31.463Z +ELECTRONICS_BIAS = 112 +EXPECTED_MAXIMUM = (0.0, 0.0) +EXPECTED_PACKETS = 2277 +EXPOSURE_DURATION = 5.0 +FILTER_NAME = (CL1, CL2) +FILTER_TEMPERATURE = 3.192976 +FLIGHT_SOFTWARE_VERSION_ID = '1.3' +GAIN_MODE_ID = '29 ELECTRONS PER DN' +IMAGE_MID_TIME = 2004-09-02T22:09:15.409Z +IMAGE_NUMBER = '1472855646' +IMAGE_OBSERVATION_TYPE = {CALIBRATION} +IMAGE_TIME = 2004-09-02T22:09:15.409Z +INSTRUMENT_DATA_RATE = 182.783997 +INSTRUMENT_HOST_NAME = 'CASSINI ORBITER' +INSTRUMENT_ID = ISSWA +INSTRUMENT_MODE_ID = FULL +INSTRUMENT_NAME = 'IMAGING SCIENCE SUBSYSTEM WIDE ANGLE' +INST_CMPRS_PARAM = ('N/A', 'N/A', 'N/A', 'N/A') +INST_CMPRS_RATE = (16.0, 16.0) +INST_CMPRS_RATIO = 'N/A' +INST_CMPRS_TYPE = NOTCOMP +LIGHT_FLOOD_STATE_FLAG = ON +METHOD_DESC = "ISSPT2.5.2;Saturn;ISS_00ASA_MOS0ASWC001_UVIS_4" +MISSING_LINES = 449 +MISSING_PACKET_FLAG = YES +MISSION_NAME = 'CASSINI-HUYGENS' +MISSION_PHASE_NAME = 'TOUR PRE-HUYGENS' +OBSERVATION_ID = ISS_00ASA_MOS0ASWE001_UVIS +OPTICS_TEMPERATURE = (7.024934, -999.0) +ORDER_NUMBER = 2 +PARALLEL_CLOCK_VOLTAGE_INDEX = 9 +PREPARE_CYCLE_INDEX = 3 +PRODUCT_CREATION_TIME = 2004-09-06T10:31:30Z +PRODUCT_ID = '1_W1472855646.121' +PRODUCT_VERSION_TYPE = FINAL +READOUT_CYCLE_INDEX = 0 +RECEIVED_PACKETS = 1297 +SENSOR_HEAD_ELEC_TEMPERATURE = 2.270205 +SEQUENCE_ID = S03 +SEQUENCE_NUMBER = 2 +SEQUENCE_TITLE = '--' +SHUTTER_MODE_ID = BOTSIM +SHUTTER_STATE_ID = DISABLED +SOFTWARE_VERSION_ID = 'ISS 9.00 02-05-2004' +SPACECRAFT_CLOCK_CNT_PARTITION = 1 +SPACECRAFT_CLOCK_START_COUNT = '1472855646.121' +SPACECRAFT_CLOCK_STOP_COUNT = '1472855646.121' +START_TIME = 2004-09-02T22:09:15.409Z +STOP_TIME = 2004-09-02T22:09:15.409Z +TARGET_DESC = Saturn +TARGET_LIST = 'N/A' +TARGET_NAME = SATURN +TELEMETRY_FORMAT_ID = UNK +VALID_MAXIMUM = (4095, 4095) +OBJECT = IMAGE_HEADER + INTERCHANGE_FORMAT = ASCII + HEADER_TYPE = VICAR2 + BYTES = 4144 + RECORDS = 1 + ^DESCRIPTION = 'VICAR2.TXT' +END_OBJECT = IMAGE_HEADER +OBJECT = TELEMETRY_TABLE + INTERCHANGE_FORMAT = BINARY + ROWS = 1 + COLUMNS = 2 + ROW_BYTES = 2072 + ^STRUCTURE = 'TLMTAB.FMT' + OBJECT = COLUMN + NAME = NULL_PADDING + DATA_TYPE = MSB_UNSIGNED_INTEGER + START_BYTE = 61 + BYTES = 2011 + END_OBJECT = COLUMN +END_OBJECT = TELEMETRY_TABLE +OBJECT = LINE_PREFIX_TABLE + INTERCHANGE_FORMAT = BINARY + ROWS = 1024 + COLUMNS = 7 + ROW_BYTES = 24 + ROW_SUFFIX_BYTES = 2048 + ^LINE_PREFIX_STRUCTURE = 'PREFIX2.FMT' +END_OBJECT = LINE_PREFIX_TABLE +OBJECT = IMAGE + LINES = 10 + LINE_SAMPLES = 1024 + SAMPLE_BITS = 16 + SAMPLE_TYPE = SUN_INTEGER + LINE_PREFIX_BYTES = 24 +END_OBJECT = IMAGE +END