Isis 3 Programmer Reference
RosettaVirtisCamera.cpp
1
7/* SPDX-License-Identifier: CC0-1.0 */
8
9#include "RosettaVirtisCamera.h"
10
11#include <cctype>
12#include <iostream>
13#include <iomanip>
14#include <sstream>
15#include <algorithm>
16
17#include <QRegExp>
18#include <QString>
19
20#include <tnt/tnt_array2d_utils.h>
21
22#include "Camera.h"
23#include "CameraFocalPlaneMap.h"
24#include "IException.h"
25#include "IString.h"
26#include "iTime.h"
27#include "Kernels.h"
28#include "LineScanCameraDetectorMap.h"
29#include "LineScanCameraGroundMap.h"
30#include "LineScanCameraSkyMap.h"
31#include "NaifStatus.h"
32#include "NumericalApproximation.h"
33
34// #define DUMP_INFO 1
35
36using namespace std;
37namespace Isis {
38 // constructors
46
47 m_instrumentNameLong = "Visual and Infrared Thermal Imaging Spectrometer";
48 m_instrumentNameShort = "VIRTIS";
49 m_spacecraftNameLong = "Rosetta";
50 m_spacecraftNameShort = "Rosetta";
51
52 // cout << "Testing RosettaVirtisCamera...\n";
53
54 Pvl &lab = *cube.label();
55// PvlGroup &archive = lab.findGroup("Archive", Isis::Pvl::Traverse);
56 PvlGroup &inst = lab.findGroup("Instrument", Isis::Pvl::Traverse);
57
58 QString instrumentId = inst["InstrumentId"];
59 if ( "virtis" != instrumentId.toLower()) {
60 QString mess = "This data is apparently not from the VIRTIS instrument but "
62 throw IException(IException::User, mess, _FILEINFO_);
63 }
64
65
66 int procLevel = inst["ProcessingLevelId"];
67 m_is1BCalibrated = (procLevel > 2) ? true : false;
68
69 // Get the start time from labels
70 QString channelId = inst["ChannelId"];
71
72 QString instMode = inst["InstrumentModeId"];
73 m_slitMode = instMode[14].toLatin1(); // "F" for full slit, Q for quarter slit
74
75
76 // Check for presence of articulation kernel
77 bool hasArtCK = hasArticulationKernel(lab);
78
79 // Set proper end frame
80 int virFrame(0);
81 QString frameId ("");
82 if (channelId == "VIRTIS_M_VIS") {
83 // Frame ROS_VIRTIS-M_VIS : ROS_VIRTIS-M_VIS_ZERO
84 virFrame = (hasArtCK) ? -226211 : -226112;
85 frameId = "ROS_VIRTIS-M_VIS";
86 }
87 else if (channelId == "VIRTIS_M_IR") {
88 // Frame ROS_VIRTIS-M_IR : ROS_VIRTIS-M_IR_ZERO
89 virFrame = (hasArtCK) ? -226213 : -226214;
90 frameId = "ROS_VIRTIS-M_IR";
91 }
92
93 instrumentRotation()->SetFrame(virFrame);
94
95 // We do not want to downsize the cache
96 instrumentRotation()->MinimizeCache(SpiceRotation::No);
97
98 // Set up the camera info from ik/iak kernels
101
102 // Get other info from labels
103 PvlKeyword &frameParam = inst["FrameParameter"];
104
105 // convert milliseconds to seconds
106
107 m_exposureTime = toDouble(frameParam[0]) * 0.001;
108 m_summing = toDouble(frameParam[1]);
109 m_scanRate = toDouble(frameParam[2]);
110
111 // Setup detector map
112 // Get the line scan rates/times
113
114 if (!m_is1BCalibrated) {
115 readHouseKeeping(lab.fileName(), m_scanRate);
116 }
117 else {
118 readSCET(lab.fileName());
119 }
120
123
124 // Setup focal plane map
125 new CameraFocalPlaneMap(this, naifIkCode());
126 // Retrieve boresight location from instrument kernel (IK) (addendum?)
127 QString ikernKey = "INS" + toString(naifIkCode()) + "_BORESIGHT_SAMPLE";
128 double sampleBoreSight = getDouble(ikernKey);
129
130 ikernKey = "INS" + toString(naifIkCode()) + "_BORESIGHT_LINE";
131 double lineBoreSight = getDouble(ikernKey);
132 FocalPlaneMap()->SetDetectorOrigin(sampleBoreSight, lineBoreSight);
133
134 // Setup distortion map
135 new CameraDistortionMap(this);
136 // Setup the ground and sky map
137 new LineScanCameraGroundMap(this);
138 new LineScanCameraSkyMap(this);
139 // Set initial start time always (label start time is inaccurate)
140
141 if (!m_is1BCalibrated){
143 }
144
145 // Now check to determine if we have a cache already. If we have a cache
146 // table, we are beyond spiceinit and have already computed the proper
147 // point table from the housekeeping data or articulation kernel.
148 if (!instrumentRotation()->IsCached() && !hasArtCK && !m_is1BCalibrated) {
149 // Create new table here prior to creating normal caches
150 Table quats = getPointingTable(frameId, virFrame);
151
152 // Create all system tables - all kernels closed after this
153 LoadCache();
154 instrumentRotation()->LoadCache(quats);
155 }
156 else {
157 LoadCache();
158 }
159
160#if defined(DUMP_INFO)
161 Table cache = instrumentRotation()->Cache("Loaded");
162 cout << "Total Records: " << cache.Records() << "\n";
163
164 for (int i = 0 ; i < cache.Records() ; i++) {
165 TableRecord rec = cache[i];
166 string separator("");
167 for (int f = 0 ; f < rec.Fields() ; f++) {
168 cout << separator << (double) rec[f];
169 separator = ", ";
170 }
171 cout << "\n";
172 }
173#endif
174 }
175
181
188 return (-226000);
189 }
190
191
198 return (1);
199 }
200
201
208 return (1);
209 }
210
211
218 return (m_summing);
219 }
220
221
228 return (m_exposureTime);
229 }
230
231
238 return (m_scanRate);
239 }
240
241
247 double RosettaVirtisCamera::lineStartTime(const double midExpTime) const {
248 return (midExpTime - (exposureTime() / 2.0));
249 }
250
251
257 double RosettaVirtisCamera::lineEndTime(const double midExpTime) const {
258 return (midExpTime+(exposureTime()/2.0));
259 }
260
261
268 return (lineStartTime(m_mirrorData[0].m_scanLineEt));
269 }
270
271
278 return (lineEndTime(m_mirrorData[hkLineCount()-1].m_scanLineEt));
279 }
280
281
288 return (m_mirrorData.size());
289 }
290
291
301 void RosettaVirtisCamera::readSCET(const QString &filename) {
302 // Open the ISIS table object
303 std::vector<double> cacheTime;
304 Table hktable("VIRTISHouseKeeping", filename);
305 m_lineRates.clear();
306 int lineno(1);
307 double lineEndTime = 0;
308 for (int i = 0; i < hktable.Records(); i++) {
309 TableRecord &trec = hktable[i];
310 QString scetString = trec["dataSCET"];
311 lineEndTime = getClockTime(scetString, naifSpkCode()).Et();
312 m_lineRates.push_back(LineRateChange(lineno,
314 exposureTime()));
315 cacheTime.push_back(lineEndTime-exposureTime());
316 lineno++;
317 }
318 cacheTime.push_back(lineEndTime);
319
320 // Adjust the last time
321 LineRateChange lastR = m_lineRates.back();
322
323 // Normally the line rate changes would store the line scan rate instead of exposure time.
324 // Storing the exposure time instead allows for better time calculations within a line.
325 // In order for the VariableLineScanCameraDetectorMap to work correctly with this change,
326 // every line in the cube must have a LineRateChange object. This is because determining
327 // the start time for one line based on another line requires the line scan rate. Having
328 // a LineRateChange for every line means never needing to calculate the start time for a line
329 // because the start time is stored in that line's LineRateChange. So, the detector map only
330 // calculates times within a given line.
331 // See VariableLineScanCameraDetectorMap::exposureDuration() for a description of the
332 // difference between exposure time and line scan rate.
333
334 m_lineRates.back() = LineRateChange(lastR.GetStartLine(),
335 lastR.GetStartEt(),
336 exposureTime());
337
338 instrumentRotation()->SetCacheTime(cacheTime);
339 }
340
341
356 void RosettaVirtisCamera::readHouseKeeping(const QString &filename,
357 double lineRate) {
358 // Open the ISIS table object
359 Table hktable("VIRTISHouseKeeping", filename);
360
361 m_lineRates.clear();
362 int lineno(1);
364 for (int i = 0; i < hktable.Records(); i++) {
365 TableRecord &trec = hktable[i];
366 double scet = (double) trec["dataSCET"];
367 int shutterMode = (int) trec["Data Type__Shutter state"];
368
369 // Compute the optical mirror angle
370 double mirrorSin = trec["M_MIRROR_SIN_HK"];
371 double mirrorCos = trec["M_MIRROR_COS_HK"];
372 double scanElecDeg = atan(mirrorSin/mirrorCos) * dpr_c();
373 double optAng = ((scanElecDeg - 3.7996979) * 0.25/0.257812);
374 optAng /= 1000.0;
375
376
377 ScanMirrorInfo smInfo;
378 double lineMidTime;
379 // scs2e_c(naifSpkCode(), scet.c_str(), &lineMidTime);
380 lineMidTime = getClockTime(toString(scet), naifSpkCode()).Et();
381 bool isDark = (shutterMode == 1);
382
383 // Add fit data for all open angles
384 if ( ! isDark ) { angFit.AddData(lineno, optAng); }
385
386#if defined(DUMP_INFO)
387 cout << "Line(" << ((isDark) ? "C): " : "O): ") << i
388 << ", OptAng(D): " << setprecision(12) << optAng * dpr_c()
389 << ", MidExpTime(ET): " << lineMidTime
390 << "\n";
391#endif
392
393 // Store line,
394 smInfo.m_lineNum = lineno;
395 smInfo.m_scanLineEt = lineMidTime;
396 smInfo.m_mirrorSin = mirrorSin;
397 smInfo.m_mirrorCos = mirrorCos;
398 smInfo.m_opticalAngle = optAng;
399 smInfo.m_isDarkCurrent = isDark;
400
401 if ((!m_is1BCalibrated) || (!(m_is1BCalibrated && isDark))) {
402 m_lineRates.push_back(LineRateChange(lineno,
403 lineStartTime(lineMidTime),
404 exposureTime()));
405 m_mirrorData.push_back(smInfo);
406 lineno++;
407 }
408 }
409
410 // Adjust the last time
411 LineRateChange lastR = m_lineRates.back();
412
413 // Normally the line rate changes would store the line scan rate instead of exposure time.
414 // Storing the exposure time instead allows for better time calculations within a line.
415 // In order for the VariableLineScanCameraDetectorMap to work correctly with this change,
416 // every line in the cube must have a LineRateChange object. This is because determining
417 // the start time for one line based on another line requires the line scan rate. Having
418 // a LineRateChange for every line means never needing to calculate the start time for a line
419 // because the start time is stored in that line's LineRateChange. So, the detector map only
420 // calculates times within a given line.
421 // See VariableLineScanCameraDetectorMap::exposureDuration() for a description of the
422 // difference between exposure time and line scan rate.
423
424 m_lineRates.back() = LineRateChange(lastR.GetStartLine(),
425 lastR.GetStartEt(),
426 exposureTime());
427
428 // Run through replacing all closed optical angles with fitted data.
429 // These are mostly first/last lines so must set proper extrapolation
430 // option.
431 for (unsigned int a = 0 ; a < m_mirrorData.size() ; a++) {
432 if (m_mirrorData[a].m_isDarkCurrent) {
433 m_mirrorData[a].m_opticalAngle = angFit.Evaluate(a+1,
435 }
436 }
437
438 // Gut check on housekeeping contents and cube lines
439 if ((int) m_lineRates.size() != Lines()) {
440 ostringstream mess;
441 mess << "Number housekeeping lines determined (" << m_lineRates.size()
442 << ") is not equal to image lines(" << Lines() << ")";
443 throw IException(IException::Programmer, mess.str(), _FILEINFO_);
444 }
445 }
446
447
459 const int zeroFrame) {
460
461 // Create Spice Pointing table
462 TableField q0("J2000Q0", TableField::Double);
463 TableField q1("J2000Q1", TableField::Double);
464 TableField q2("J2000Q2", TableField::Double);
465 TableField q3("J2000Q3", TableField::Double);
466 TableField av1("AV1", TableField::Double);
467 TableField av2("AV2", TableField::Double);
468 TableField av3("AV3", TableField::Double);
470
471 TableRecord record;
472 record += q0;
473 record += q1;
474 record += q2;
475 record += q3;
476 record += av1;
477 record += av2;
478 record += av3;
479 record += t;
480
481 // Get pointing table
482 Table quats("SpiceRotation", record);
483 int nfields = record.Fields();
484
485 QString virZero = virChannel + "_ZERO";
486
487 // Allocate output arrays
488 int nvals = nfields - 1;
489 int nlines = m_lineRates.size();
490
491 SpiceDouble eulang[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
492 SpiceDouble xform[6][6], xform2[6][6];
493 SpiceDouble m[3][3];
494 SpiceDouble q_av[7], *av(&q_av[4]);
495
496 for (int i = 0 ; i < nlines ; i++) {
497 int index = min(i, nlines-1);
498 double etTime = m_mirrorData[index].m_scanLineEt; // mid exposure ET
499 double optAng = m_mirrorData[index].m_opticalAngle;
500 try {
501 // J2000 -> ROS_VIRTIS-M_{channel}_ZERO
502 SMatrix state = getStateRotation("J2000", virZero, etTime);
503
504 // Set rotation of optical scan mirror (in radians)
505 eulang[1] = -optAng;
506 eul2xf_c(eulang, 1, 2, 3, xform);
507 mxmg_c(xform, &state[0][0], 6, 6, 6, xform2);
508
509 // Transform to output format
510 xf2rav_c(xform2, m, av); // Transfers AV to output q_av via pointer
511 m2q_c(m, q_av); // Transfers quaternion
512
513 // Now populate the table record with the line pointing
514 for (int k = 0 ; k < nvals ; k++) {
515 record[k] = q_av[k];
516 }
517
518 // Add time to record; record to table
519 record[nvals] = etTime;
520 quats += record;
521 }
522 catch (IException &ie) {
523 ostringstream mess;
524 mess << "Failed to get point state for line " << i+1;
525 throw IException(ie, IException::User, mess.str(), _FILEINFO_);
526 }
527 }
528
529 // Add some necessary keywords
530 quats.Label() += PvlKeyword("CkTableStartTime", toString(startTime()));
531 quats.Label() += PvlKeyword("CkTableEndTime", toString(endTime()));
532 quats.Label() += PvlKeyword("CkTableOriginalSize", toString(quats.Records()));
533
534 // Create the time dependant frames keyword
535 int virZeroId = getInteger("FRAME_" + virZero);
536 PvlKeyword tdf("TimeDependentFrames", toString(virZeroId)); // ROS_VIRTIS_M_{ID}_ZERO
537 tdf.addValue("-226200"); // ROS_VIRTIS
538 tdf.addValue("-226000"); // ROSETTA_SPACECRAFT
539 tdf.addValue("1"); // J2000
540 quats.Label() += tdf;
541
542 // Create constant rotation frames
543 PvlKeyword cf("ConstantFrames", toString(virZeroId));
544 cf.addValue(toString(virZeroId));
545 quats.Label() += cf;
546
547 SpiceDouble identity[3][3];
548 ident_c(identity);
549
550 // Store DAWN_VIR_{ID}_ZERO -> DAWN_VIR_{ID}_ZERO identity rotation
551 PvlKeyword crot("ConstantRotation");
552 for (int i = 0 ; i < 3 ; i++) {
553 for (int j = 0 ; j < 3 ; j++) {
554 crot.addValue(toString(identity[i][j]));
555 }
556 }
557
558 quats.Label() += crot;
559
560 return (quats);
561 }
562
563
581 const QString &frame2,
582 const double &etTime)
583 const {
584 SMatrix state(6,6);
586 try {
587 // Get pointing w/AVs
588 sxform_c(frame1.toLatin1().data(), frame2.toLatin1().data(), etTime,
589 (SpiceDouble (*)[6]) state[0]);
591 }
592 catch (IException &) {
593 try {
594 SMatrix rot(3,3);
595 pxform_c(frame1.toLatin1().data(), frame2.toLatin1().data(), etTime,
596 (SpiceDouble (*)[3]) rot[0]);
598 SpiceDouble av[3] = {0.0, 0.0, 0.0 };
599 rav2xf_c((SpiceDouble (*)[3]) rot[0], av,
600 (SpiceDouble (*)[6]) state[0]);
601 }
602 catch (IException &ie2) {
603 ostringstream mess;
604 mess << "Could not get state rotation for Frame1 (" << frame1
605 << ") to Frame2 (" << frame2 << ") at time " << etTime;
606 throw IException(ie2, IException::User, mess.str(), _FILEINFO_);
607 }
608 }
609 return (state);
610 }
611
612
628 Kernels kerns(label);
629 QStringList cks = kerns.getKernelList("CK");
630 QRegExp virCk("*ROS_VIRTIS_M_????_????_V?.BC");
631 virCk.setPatternSyntax(QRegExp::Wildcard);
632 for (int i = 0 ; i < cks.size() ; i++) {
633 if ( virCk.exactMatch(cks[i]) ) return (true);
634 }
635 return (false);
636 }
637
638}
639
643extern "C" Isis::Camera *RosettaVirtisCameraPlugin(Isis::Cube &cube) {
644 return new Isis::RosettaVirtisCamera(cube);
645}
void SetDetectorSampleSumming(const double summing)
Set sample summing mode.
Distort/undistort focal plane coordinates.
Convert between distorted focal plane and detector coordinates.
void SetDetectorOrigin(const double sample, const double line)
Set the detector origin.
QString m_spacecraftNameLong
Full spacecraft name.
Definition Camera.h:499
int Lines() const
Returns the number of lines in the image.
Definition Camera.cpp:2816
void SetFocalLength()
Reads the focal length from the instrument kernel.
Definition Camera.cpp:1422
void SetPixelPitch()
Reads the Pixel Pitch from the instrument kernel.
Definition Camera.cpp:1429
void LoadCache()
This loads the spice cache big enough for this image.
Definition Camera.cpp:2450
QString m_instrumentNameShort
Shortened instrument name.
Definition Camera.h:498
QString instrumentId()
This method returns the InstrumentId as it appears in the cube.
Definition Camera.cpp:2906
QString m_spacecraftNameShort
Shortened spacecraft name.
Definition Camera.h:500
CameraFocalPlaneMap * FocalPlaneMap()
Returns a pointer to the CameraFocalPlaneMap object.
Definition Camera.cpp:2866
QString m_instrumentNameLong
Full instrument name.
Definition Camera.h:497
IO Handler for Isis Cubes.
Definition Cube.h:168
Pvl * label() const
Returns a pointer to the IsisLabel object associated with the cube.
Definition Cube.cpp:1707
Isis exception class.
Definition IException.h:91
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
Determine SPICE kernels defined in an ISIS file.
Definition Kernels.h:94
Container class for storing timing information for a section of an image.
Convert between undistorted focal plane and ground coordinates.
Generic class for Line Scan Cameras.
LineScanCameraDetectorMap * DetectorMap()
Returns a pointer to the LineScanCameraDetectorMap object.
Convert between undistorted focal plane and ra/dec coordinates.
static void CheckErrors(bool resetNaif=true)
This method looks for any naif errors that might have occurred.
NumericalApproximation provides various numerical analysis methods of interpolation,...
@ NearestEndpoint
Evaluate() returns the y-value of the nearest endpoint if a is outside of the domain.
Contains multiple PvlContainers.
Definition PvlGroup.h:41
Container for cube-like labels.
Definition Pvl.h:119
A single keyword-value pair.
Definition PvlKeyword.h:87
@ Traverse
Search child objects.
Definition PvlObject.h:158
Camera model for both Rosetta VIRTIS-M instruments.
double startTime() const
Return start time for the entire cube.
double m_scanRate
Line scan rate.
double lineStartTime(const double midExpTime) const
Return the start time for a given line exposure time.
std::vector< ScanMirrorInfo > m_mirrorData
vector of mirror info for each line
double exposureTime() const
Return the exposure time.
RosettaVirtisCamera(Cube &cube)
Creates a camera for a Rosetta VIRTIS-M cube.
std::vector< LineRateChange > m_lineRates
vector of timing info for each line
char m_slitMode
Slit mode of the instrument.
double endTime() const
Return end time for the entire cube.
virtual int CkReferenceId() const
CK Reference ID - J2000.
int pixelSumming() const
Return the pixel summing rate.
void readHouseKeeping(const QString &filename, double lineRate)
Read the VIRTIS houskeeping table from cube.
int m_summing
Summing/binnning mode.
int hkLineCount() const
Returns number of housekeeping records found in the cube Table.
Table getPointingTable(const QString &channelId, const int zeroFrame)
Compute the pointing table for each line.
TNT::Array2D< SpiceDouble > SMatrix
2-D buffer
bool m_is1BCalibrated
is determined by Archive/ProcessingLevelId
virtual int CkFrameId() const
CK Frame ID - Instrument Code from spacit run on CK.
double scanLineTime() const
Return the line scan rate.
virtual int SpkReferenceId() const
SPK Reference ID - J2000.
SMatrix getStateRotation(const QString &frame1, const QString &frame2, const double &et) const
Compute the state rotation at a given time for given frames.
bool hasArticulationKernel(Pvl &label) const
determine if the CK articulation kernels are present/given
double m_exposureTime
Line exposure time.
double lineEndTime(const double midExpTime) const
Return the end time for a given line exposure time.
void readSCET(const QString &filename)
For calibrated VIRTIS-M images, read the SCET values from the cube.
void setTime(const iTime &time)
By setting the time you essential set the position of the spacecraft and body as indicated in the cla...
Definition Sensor.cpp:99
virtual iTime getClockTime(QString clockValue, int sclkCode=-1, bool clockTicks=false)
This converts the spacecraft clock ticks value (clockValue) to an iTime.
Definition Spice.cpp:1060
SpiceInt naifSpkCode() const
This returns the NAIF SPK code to use when reading from SPK kernels.
Definition Spice.cpp:957
virtual SpiceRotation * instrumentRotation() const
Accessor method for the instrument rotation.
Definition Spice.cpp:1634
SpiceInt naifIkCode() const
This returns the NAIF IK code to use when reading from instrument kernels.
Definition Spice.cpp:975
SpiceDouble getDouble(const QString &key, int index=0)
This returns a value from the NAIF text pool.
Definition Spice.cpp:1046
SpiceInt getInteger(const QString &key, int index=0)
This returns a value from the NAIF text pool.
Definition Spice.cpp:1032
@ No
Do not downsize the cache.
Class for storing an Isis::Table's field information.
Definition TableField.h:47
@ Double
The values in the field are 8 byte doubles.
Definition TableField.h:54
Class for storing Table blobs information.
Definition Table.h:61
int Fields() const
Returns the number of fields that are currently in the record.
Convert between parent image coordinates and detector coordinates.
Parse and return pieces of a time string.
Definition iTime.h:65
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition IString.cpp:211
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition IString.cpp:149
Namespace for the standard library.