Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

Isis 3 Programmer Reference
RosettaVirtisCamera.cpp
1
6
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 "
61 + instrumentId;
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 // Check for presence of articulation kernel
73 bool hasArtCK = hasArticulationKernel(lab);
74
75 // Set proper end frame
76 int virFrame(0);
77 QString frameId ("");
78 if (channelId == "VIRTIS_M_VIS") {
79 // Frame ROS_VIRTIS-M_VIS : ROS_VIRTIS-M_VIS_ZERO
80 virFrame = (hasArtCK) ? -226211 : -226112;
81 frameId = "ROS_VIRTIS-M_VIS";
82 }
83 else if (channelId == "VIRTIS_M_IR") {
84 // Frame ROS_VIRTIS-M_IR : ROS_VIRTIS-M_IR_ZERO
85 virFrame = (hasArtCK) ? -226213 : -226214;
86 frameId = "ROS_VIRTIS-M_IR";
87 }
88
89 instrumentRotation()->SetFrame(virFrame);
90
91 // We do not want to downsize the cache
93
94 // Set up the camera info from ik/iak kernels
97
98 // Get other info from labels
99 PvlKeyword &frameParam = inst["FrameParameter"];
100
101 // convert milliseconds to seconds
102
103 m_exposureTime = toDouble(frameParam[0]) * 0.001;
104 m_summing = toDouble(frameParam[1]);
105 m_scanRate = toDouble(frameParam[2]);
106
107 // Setup detector map
108 // Get the line scan rates/times
109
110 if (!m_is1BCalibrated) {
111 readHouseKeeping(lab.fileName(), m_scanRate);
112 }
113 else {
114 readSCET(lab.fileName());
115 }
116
119
120 // Setup focal plane map
121 new CameraFocalPlaneMap(this, naifIkCode());
122 // Retrieve boresight location from instrument kernel (IK) (addendum?)
123 QString ikernKey = "INS" + toString(naifIkCode()) + "_BORESIGHT_SAMPLE";
124 double sampleBoreSight = getDouble(ikernKey);
125
126 ikernKey = "INS" + toString(naifIkCode()) + "_BORESIGHT_LINE";
127 double lineBoreSight = getDouble(ikernKey);
128 FocalPlaneMap()->SetDetectorOrigin(sampleBoreSight, lineBoreSight);
129
130 // Setup distortion map
131 new CameraDistortionMap(this);
132 // Setup the ground and sky map
133 new LineScanCameraGroundMap(this);
134 new LineScanCameraSkyMap(this);
135 // Set initial start time always (label start time is inaccurate)
136
137 if (!m_is1BCalibrated){
139 }
140
141 // Now check to determine if we have a cache already. If we have a cache
142 // table, we are beyond spiceinit and have already computed the proper
143 // point table from the housekeeping data or articulation kernel.
144 if (!instrumentRotation()->IsCached() && !hasArtCK && !m_is1BCalibrated) {
145 // Create new table here prior to creating normal caches
146 Table quats = getPointingTable(frameId, virFrame);
147
148 // Create all system tables - all kernels closed after this
149 LoadCache();
151 }
152 else {
153 LoadCache();
154 }
155
156#if defined(DUMP_INFO)
157 Table cache = instrumentRotation()->Cache("Loaded");
158 cout << "Total Records: " << cache.Records() << "\n";
159
160 for (int i = 0 ; i < cache.Records() ; i++) {
161 TableRecord rec = cache[i];
162 string separator("");
163 for (int f = 0 ; f < rec.Fields() ; f++) {
164 cout << separator << (double) rec[f];
165 separator = ", ";
166 }
167 cout << "\n";
168 }
169#endif
170 }
171
177
184 return (-226000);
185 }
186
187
194 return (1);
195 }
196
197
204 return (1);
205 }
206
207
214 return (m_summing);
215 }
216
217
224 return (m_exposureTime);
225 }
226
227
234 return (m_scanRate);
235 }
236
237
243 double RosettaVirtisCamera::lineStartTime(const double midExpTime) const {
244 return (midExpTime - (exposureTime() / 2.0));
245 }
246
247
253 double RosettaVirtisCamera::lineEndTime(const double midExpTime) const {
254 return (midExpTime+(exposureTime()/2.0));
255 }
256
257
264 return (lineStartTime(m_mirrorData[0].m_scanLineEt));
265 }
266
267
274 return (lineEndTime(m_mirrorData[hkLineCount()-1].m_scanLineEt));
275 }
276
277
284 return (m_mirrorData.size());
285 }
286
287
297 void RosettaVirtisCamera::readSCET(const QString &filename) {
298 // Open the ISIS table object
299 std::vector<double> cacheTime;
300 Table hktable("VIRTISHouseKeeping", filename);
301 m_lineRates.clear();
302 int lineno(1);
303 double lineEndTime = 0;
304 for (int i = 0; i < hktable.Records(); i++) {
305 TableRecord &trec = hktable[i];
306 QString scetString = trec["dataSCET"];
307 lineEndTime = getClockTime(scetString, naifSpkCode()).Et();
308 m_lineRates.push_back(LineRateChange(lineno,
310 exposureTime()));
311 cacheTime.push_back(lineEndTime-exposureTime());
312 lineno++;
313 }
314 cacheTime.push_back(lineEndTime);
315
316 // Adjust the last time
317 LineRateChange lastR = m_lineRates.back();
318
319 // Normally the line rate changes would store the line scan rate instead of exposure time.
320 // Storing the exposure time instead allows for better time calculations within a line.
321 // In order for the VariableLineScanCameraDetectorMap to work correctly with this change,
322 // every line in the cube must have a LineRateChange object. This is because determining
323 // the start time for one line based on another line requires the line scan rate. Having
324 // a LineRateChange for every line means never needing to calculate the start time for a line
325 // because the start time is stored in that line's LineRateChange. So, the detector map only
326 // calculates times within a given line.
327 // See VariableLineScanCameraDetectorMap::exposureDuration() for a description of the
328 // difference between exposure time and line scan rate.
329
330 m_lineRates.back() = LineRateChange(lastR.GetStartLine(),
331 lastR.GetStartEt(),
332 exposureTime());
333
334 instrumentRotation()->SetCacheTime(cacheTime);
335 }
336
337
352 void RosettaVirtisCamera::readHouseKeeping(const QString &filename,
353 double lineRate) {
354 // Open the ISIS table object
355 Table hktable("VIRTISHouseKeeping", filename);
356
357 m_lineRates.clear();
358 int lineno(1);
360 for (int i = 0; i < hktable.Records(); i++) {
361 TableRecord &trec = hktable[i];
362 double scet = (double) trec["dataSCET"];
363 int shutterMode = (int) trec["Data Type__Shutter state"];
364
365 // Compute the optical mirror angle
366 double mirrorSin = trec["M_MIRROR_SIN_HK"];
367 double mirrorCos = trec["M_MIRROR_COS_HK"];
368 double scanElecDeg = atan(mirrorSin/mirrorCos) * dpr_c();
369 double optAng = ((scanElecDeg - 3.7996979) * 0.25/0.257812);
370 optAng /= 1000.0;
371
372
373 ScanMirrorInfo smInfo;
374 double lineMidTime;
375 // scs2e_c(naifSpkCode(), scet.c_str(), &lineMidTime);
376 lineMidTime = getClockTime(toString(scet), naifSpkCode()).Et();
377 bool isDark = (shutterMode == 1);
378
379 // Add fit data for all open angles
380 if ( ! isDark ) { angFit.AddData(lineno, optAng); }
381
382#if defined(DUMP_INFO)
383 cout << "Line(" << ((isDark) ? "C): " : "O): ") << i
384 << ", OptAng(D): " << setprecision(12) << optAng * dpr_c()
385 << ", MidExpTime(ET): " << lineMidTime
386 << "\n";
387#endif
388
389 // Store line,
390 smInfo.m_lineNum = lineno;
391 smInfo.m_scanLineEt = lineMidTime;
392 smInfo.m_mirrorSin = mirrorSin;
393 smInfo.m_mirrorCos = mirrorCos;
394 smInfo.m_opticalAngle = optAng;
395 smInfo.m_isDarkCurrent = isDark;
396
397 if ((!m_is1BCalibrated) || (!(m_is1BCalibrated && isDark))) {
398 m_lineRates.push_back(LineRateChange(lineno,
399 lineStartTime(lineMidTime),
400 exposureTime()));
401 m_mirrorData.push_back(smInfo);
402 lineno++;
403 }
404 }
405
406 // Adjust the last time
407 LineRateChange lastR = m_lineRates.back();
408
409 // Normally the line rate changes would store the line scan rate instead of exposure time.
410 // Storing the exposure time instead allows for better time calculations within a line.
411 // In order for the VariableLineScanCameraDetectorMap to work correctly with this change,
412 // every line in the cube must have a LineRateChange object. This is because determining
413 // the start time for one line based on another line requires the line scan rate. Having
414 // a LineRateChange for every line means never needing to calculate the start time for a line
415 // because the start time is stored in that line's LineRateChange. So, the detector map only
416 // calculates times within a given line.
417 // See VariableLineScanCameraDetectorMap::exposureDuration() for a description of the
418 // difference between exposure time and line scan rate.
419
420 m_lineRates.back() = LineRateChange(lastR.GetStartLine(),
421 lastR.GetStartEt(),
422 exposureTime());
423
424 // Run through replacing all closed optical angles with fitted data.
425 // These are mostly first/last lines so must set proper extrapolation
426 // option.
427 for (unsigned int a = 0 ; a < m_mirrorData.size() ; a++) {
428 if (m_mirrorData[a].m_isDarkCurrent) {
429 m_mirrorData[a].m_opticalAngle = angFit.Evaluate(a+1,
431 }
432 }
433
434 // Gut check on housekeeping contents and cube lines
435 if ((int) m_lineRates.size() != Lines()) {
436 ostringstream mess;
437 mess << "Number housekeeping lines determined (" << m_lineRates.size()
438 << ") is not equal to image lines(" << Lines() << ")";
439 throw IException(IException::Programmer, mess.str(), _FILEINFO_);
440 }
441 }
442
443
454 Table RosettaVirtisCamera::getPointingTable(const QString &virChannel,
455 const int zeroFrame) {
456
457 // Create Spice Pointing table
458 TableField q0("J2000Q0", TableField::Double);
459 TableField q1("J2000Q1", TableField::Double);
460 TableField q2("J2000Q2", TableField::Double);
461 TableField q3("J2000Q3", TableField::Double);
462 TableField av1("AV1", TableField::Double);
463 TableField av2("AV2", TableField::Double);
464 TableField av3("AV3", TableField::Double);
465 TableField t("ET", TableField::Double);
466
467 TableRecord record;
468 record += q0;
469 record += q1;
470 record += q2;
471 record += q3;
472 record += av1;
473 record += av2;
474 record += av3;
475 record += t;
476
477 // Get pointing table
478 Table quats("SpiceRotation", record);
479 int nfields = record.Fields();
480
481 QString virZero = virChannel + "_ZERO";
482
483 // Allocate output arrays
484 int nvals = nfields - 1;
485 int nlines = m_lineRates.size();
486
487 SpiceDouble eulang[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
488 SpiceDouble xform[6][6], xform2[6][6];
489 SpiceDouble m[3][3];
490 SpiceDouble q_av[7], *av(&q_av[4]);
491
492 for (int i = 0 ; i < nlines ; i++) {
493 int index = min(i, nlines-1);
494 double etTime = m_mirrorData[index].m_scanLineEt; // mid exposure ET
495 double optAng = m_mirrorData[index].m_opticalAngle;
496 try {
497 // J2000 -> ROS_VIRTIS-M_{channel}_ZERO
498 SMatrix state = getStateRotation("J2000", virZero, etTime);
499
500 // Set rotation of optical scan mirror (in radians)
501 eulang[1] = -optAng;
502 eul2xf_c(eulang, 1, 2, 3, xform);
503 mxmg_c(xform, &state[0][0], 6, 6, 6, xform2);
504
505 // Transform to output format
506 xf2rav_c(xform2, m, av); // Transfers AV to output q_av via pointer
507 m2q_c(m, q_av); // Transfers quaternion
508
509 // Now populate the table record with the line pointing
510 for (int k = 0 ; k < nvals ; k++) {
511 record[k] = q_av[k];
512 }
513
514 // Add time to record; record to table
515 record[nvals] = etTime;
516 quats += record;
517 }
518 catch (IException &ie) {
519 ostringstream mess;
520 mess << "Failed to get point state for line " << i+1;
521 throw IException(ie, IException::User, mess.str(), _FILEINFO_);
522 }
523 }
524
525 // Add some necessary keywords
526 quats.Label() += PvlKeyword("CkTableStartTime", toString(startTime()));
527 quats.Label() += PvlKeyword("CkTableEndTime", toString(endTime()));
528 quats.Label() += PvlKeyword("CkTableOriginalSize", toString(quats.Records()));
529
530 // Create the time dependant frames keyword
531 int virZeroId = getInteger("FRAME_" + virZero);
532 PvlKeyword tdf("TimeDependentFrames", toString(virZeroId)); // ROS_VIRTIS_M_{ID}_ZERO
533 tdf.addValue("-226200"); // ROS_VIRTIS
534 tdf.addValue("-226000"); // ROSETTA_SPACECRAFT
535 tdf.addValue("1"); // J2000
536 quats.Label() += tdf;
537
538 // Create constant rotation frames
539 PvlKeyword cf("ConstantFrames", toString(virZeroId));
540 cf.addValue(toString(virZeroId));
541 quats.Label() += cf;
542
543 SpiceDouble identity[3][3];
544 ident_c(identity);
545
546 // Store DAWN_VIR_{ID}_ZERO -> DAWN_VIR_{ID}_ZERO identity rotation
547 PvlKeyword crot("ConstantRotation");
548 for (int i = 0 ; i < 3 ; i++) {
549 for (int j = 0 ; j < 3 ; j++) {
550 crot.addValue(toString(identity[i][j]));
551 }
552 }
553
554 quats.Label() += crot;
555
556 return (quats);
557 }
558
559
575
577 const QString &frame2,
578 const double &etTime)
579 const {
580 SMatrix state(6,6);
582 try {
583 // Get pointing w/AVs
584 sxform_c(frame1.toLatin1().data(), frame2.toLatin1().data(), etTime,
585 (SpiceDouble (*)[6]) state[0]);
587 }
588 catch (IException &) {
589 try {
590 SMatrix rot(3,3);
591 pxform_c(frame1.toLatin1().data(), frame2.toLatin1().data(), etTime,
592 (SpiceDouble (*)[3]) rot[0]);
594 SpiceDouble av[3] = {0.0, 0.0, 0.0 };
595 rav2xf_c((SpiceDouble (*)[3]) rot[0], av,
596 (SpiceDouble (*)[6]) state[0]);
597 }
598 catch (IException &ie2) {
599 ostringstream mess;
600 mess << "Could not get state rotation for Frame1 (" << frame1
601 << ") to Frame2 (" << frame2 << ") at time " << etTime;
602 throw IException(ie2, IException::User, mess.str(), _FILEINFO_);
603 }
604 }
605 return (state);
606 }
607
608
624 Kernels kerns(label);
625 QStringList cks = kerns.getKernelList("CK");
626 QRegExp virCk("*ROS_VIRTIS_M_????_????_V?.BC");
627 virCk.setPatternSyntax(QRegExp::Wildcard);
628 for (int i = 0 ; i < cks.size() ; i++) {
629 if ( virCk.exactMatch(cks[i]) ) return (true);
630 }
631 return (false);
632 }
633
634}
635
639extern "C" Isis::Camera *RosettaVirtisCameraPlugin(Isis::Cube &cube) {
640 return new Isis::RosettaVirtisCamera(cube);
641}
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:1975
Determine SPICE kernels defined in an ISIS file.
Definition Kernels.h:94
QStringList getKernelList(const QString &ktype="") const
Provide a list of all the kernels found.
Definition Kernels.cpp:673
Container class for storing timing information for a section of an image.
Convert between undistorted focal plane and ground coordinates.
LineScanCamera(Isis::Cube &cube)
Constructs the LineScanCamera object.
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,...
double Evaluate(const double a, const ExtrapType &etype=ThrowError)
Calculates interpolated or extrapolated value of tabulated data set for given domain value.
@ NearestEndpoint
Evaluate() returns the y-value of the nearest endpoint if a is outside of the domain.
void AddData(const double x, const double y)
Add a datapoint to the set.
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
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:97
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:1270
SpiceInt naifSpkCode() const
This returns the NAIF SPK code to use when reading from SPK kernels.
Definition Spice.cpp:1167
virtual SpiceRotation * instrumentRotation() const
Accessor method for the instrument rotation.
Definition Spice.cpp:1844
SpiceInt naifIkCode() const
This returns the NAIF IK code to use when reading from instrument kernels.
Definition Spice.cpp:1185
SpiceDouble getDouble(const QString &key, int index=0)
This returns a value from the NAIF text pool.
Definition Spice.cpp:1256
SpiceInt getInteger(const QString &key, int index=0)
This returns a value from the NAIF text pool.
Definition Spice.cpp:1242
Table Cache(const QString &tableName)
Return a table with J2000 to reference rotations.
void LoadCache(double startTime, double endTime, int size)
Cache J2000 rotation quaternion over a time range.
void SetFrame(int frameCode)
Change the frame to the given frame code.
@ No
Do not downsize the cache.
void MinimizeCache(DownsizeStatus status)
Set the downsize status to minimize cache.
Convert between parent image coordinates and detector coordinates.
Parse and return pieces of a time string.
Definition iTime.h:65
double Et() const
Returns the ephemeris time (TDB) representation of the time as a double.
Definition iTime.h:126
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(const LinearAlgebra::Vector &vector, int precision)
A global function to format LinearAlgebra::Vector as a QString with the given precision.
Namespace for the standard library.
bool m_isDarkCurrent
If the line is dark current data.
double m_opticalAngle
Optical angle in degrees.
double m_mirrorCos
Raw mirror cosine value.
double m_scanLineEt
Center of line time in ET.