Isis 3 Programmer Reference
DawnVirCamera.cpp
1
7/* SPDX-License-Identifier: CC0-1.0 */
8
9#include "DawnVirCamera.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 Spectrometer";
49 m_spacecraftNameLong = "Dawn";
50 m_spacecraftNameShort = "Dawn";
51
52 // cout << "Testing DawnVirCamera...\n";
53
54 Pvl &lab = *cube.label();
55 PvlGroup &archive = lab.findGroup("Archive", Isis::Pvl::Traverse);
56 int procLevel = archive["ProcessingLevelId"];
57 m_is1BCalibrated = (procLevel > 2) ? true : false;
58
59 // Get the start time from labels
60 PvlGroup &inst = lab.findGroup("Instrument", Isis::Pvl::Traverse);
61 QString channelId = inst["ChannelId"];
62
63 QString instMode = inst["InstrumentModeId"];
64 m_slitMode = instMode[14].toLatin1(); // "F" for full slit, Q for quarter slit
65
66 // Check for presence of articulation kernel
67 bool hasArtCK = hasArticulationKernel(lab);
68
69 // Set proper end frame
70 int virFrame(0);
71 if (channelId == "VIS") {
72 // Frame DAWN_VIR_VIS : DAWN_VIR_VIS_ZERO
73 virFrame = (hasArtCK) ? -203211 : -203221;
74 }
75 else { // (channelId == "IR)
76 // Frame DAWN_VIR_IR : DAWN_VIR_IR_ZERO
77 virFrame = (hasArtCK) ? -203213 : -203223;
78 }
79
80 instrumentRotation()->SetFrame(virFrame);
81
82 // We do not want to downsize the cache
83 instrumentRotation()->MinimizeCache(SpiceRotation::No);
84
85 // Set up the camera info from ik/iak kernels
88
89 // Get other info from labels
90 PvlKeyword &frameParam = inst["FrameParameter"];
91 m_exposureTime = toDouble(frameParam[0]);
92 m_summing = toDouble(frameParam[1]);
93 m_scanRate = toDouble(frameParam[2]);
94
95 // Setup detector map
96 // Get the line scan rates/times
97 readHouseKeeping(lab.fileName(), m_scanRate);
100
101 // Setup focal plane map
102 new CameraFocalPlaneMap(this, naifIkCode());
103
104 // Retrieve boresight location from instrument kernel (IK) (addendum?)
105 QString ikernKey = "INS" + toString(naifIkCode()) + "_BORESIGHT_SAMPLE";
106 double sampleBoreSight = getDouble(ikernKey);
107
108 ikernKey = "INS" + toString(naifIkCode()) + "_BORESIGHT_LINE";
109 double lineBoreSight = getDouble(ikernKey);
110
111 FocalPlaneMap()->SetDetectorOrigin(sampleBoreSight, lineBoreSight);
112
113 // Setup distortion map
114 new CameraDistortionMap(this);
115
116 // Setup the ground and sky map
117 new LineScanCameraGroundMap(this);
118 new LineScanCameraSkyMap(this);
119
120 // Set initial start time always (label start time is inaccurate)
121 setTime(iTime(startTime())); // Isis3nightly
122// SetEphemerisTime(startTime()); // Isis3.2.1
123
124 // Now check to determine if we have a cache already. If we have a cache
125 // table, we are beyond spiceinit and have already computed the proper
126 // point table from the housekeeping data or articulation kernel.
127 if (!instrumentRotation()->IsCached() && !hasArtCK) {
128
129 // Create new table here prior to creating normal caches
130 Table quats = getPointingTable(channelId, virFrame);
131
132 // Create all system tables - all kernels closed after this
133 LoadCache();
134 instrumentRotation()->LoadCache(quats);
135 }
136 else {
137 LoadCache();
138 }
139
140#if defined(DUMP_INFO)
141 Table cache = instrumentRotation()->Cache("Loaded");
142 cout << "Total Records: " << cache.Records() << "\n";
143
144 for (int i = 0 ; i < cache.Records() ; i++) {
145 TableRecord rec = cache[i];
146 string separator("");
147 for (int f = 0 ; f < rec.Fields() ; f++) {
148 cout << separator << (double) rec[f];
149 separator = ", ";
150 }
151 cout << "\n";
152 }
153#endif
154 }
155
161
168 return (-203000);
169 }
170
171
178 return (1);
179 }
180
181
188 return (1);
189 }
190
191
205 QString DawnVirCamera::scrub(const QString &text) const {
206 QString ostr;
207 for (int i = 0 ; i < text.size() ; i++) {
208 if ((text[i] > ' ') && (text[i] <= 'z')) ostr += text[i];
209 }
210 return (ostr);
211 }
212
213
220 return (m_summing);
221 }
222
223
230 return (m_exposureTime);
231 }
232
233
240 return (m_scanRate);
241 }
242
243
249 double DawnVirCamera::lineStartTime(const double midExpTime) const {
250 return (midExpTime-(exposureTime()/2.0));
251 }
252
253
259 double DawnVirCamera::lineEndTime(const double midExpTime) const {
260 return (midExpTime+(exposureTime()/2.0));
261 }
262
263
270 return (lineStartTime(m_mirrorData[0].m_scanLineEt));
271 }
272
273
279 double DawnVirCamera::endTime() const {
280 return (lineEndTime(m_mirrorData[hkLineCount()-1].m_scanLineEt));
281 }
282
283
290 return (m_mirrorData.size());
291 }
292
293
308 void DawnVirCamera::readHouseKeeping(const QString &filename,
309 double lineRate) {
310 // Open the ISIS table object
311 Table hktable("VIRHouseKeeping", filename);
312
313 m_lineRates.clear();
314 int lineno(1);
316 for (int i = 0; i < hktable.Records(); i++) {
317 TableRecord &trec = hktable[i];
318 QString scet = scrub(trec["ScetTimeClock"]);
319 QString shutterMode = scrub(trec["ShutterStatus"]);
320
321 // Compute the optical mirror angle
322 double mirrorSin = trec["MirrorSin"];
323 double mirrorCos = trec["MirrorCos"];
324 double scanElecDeg = atan(mirrorSin/mirrorCos) * dpr_c();
325 double optAng = ((scanElecDeg - 3.7996979) * 0.25/0.257812);
326 optAng /= 1000.0;
327
328
329 ScanMirrorInfo smInfo;
330 double lineMidTime;
331 // scs2e_c(naifSpkCode(), scet.c_str(), &lineMidTime);
332 lineMidTime = getClockTime(scet, naifSpkCode()).Et();
333 bool isDark = shutterMode.toLower() == "closed";
334
335 // Add fit data for all open angles
336 if ( ! isDark ) { angFit.AddData(lineno, optAng); }
337
338#if defined(DUMP_INFO)
339 cout << "Line(" << ((isDark) ? "C): " : "O): ") << i
340 << ", OptAng(D): " << setprecision(12) << optAng * dpr_c()
341 << ", MidExpTime(ET): " << lineMidTime
342 << "\n";
343#endif
344
345 // Store line,
346 smInfo.m_lineNum = lineno;
347 smInfo.m_scanLineEt = lineMidTime;
348 smInfo.m_mirrorSin = mirrorSin;
349 smInfo.m_mirrorCos = mirrorCos;
350 smInfo.m_opticalAngle = optAng;
351 smInfo.m_isDarkCurrent = isDark;
352
353 if ((!m_is1BCalibrated) || (!(m_is1BCalibrated && isDark))) {
354 m_lineRates.push_back(LineRateChange(lineno,
355 lineStartTime(lineMidTime),
356 exposureTime()));
357 m_mirrorData.push_back(smInfo);
358 lineno++;
359 }
360 }
361
362 // Adjust the last time
363 LineRateChange lastR = m_lineRates.back();
364
365 // Normally the line rate changes would store the line scan rate instead of exposure time.
366 // Storing the exposure time instead allows for better time calculations within a line.
367 // In order for the VariableLineScanCameraDetectorMap to work correctly with this change,
368 // every line in the cube must have a LineRateChange object. This is because determining
369 // the start time for one line based on another line requires the line scan rate. Having
370 // a LineRateChange for every line means never needing to calculate the start time for a line
371 // because the start time is stored in that line's LineRateChange. So, the detector map only
372 // calculates times within a given line.
373 // See VariableLineScanCameraDetectorMap::exposureDuration() for a description of the
374 // difference between exposure time and line scan rate.
375
376 m_lineRates.back() = LineRateChange(lastR.GetStartLine(),
377 lastR.GetStartEt(),
378 exposureTime());
379
380 // Run through replacing all closed optical angles with fitted data.
381 // These are mostly first/last lines so must set proper extrapolation
382 // option.
383 for (unsigned int a = 0 ; a < m_mirrorData.size() ; a++) {
384 if (m_mirrorData[a].m_isDarkCurrent) {
385 m_mirrorData[a].m_opticalAngle = angFit.Evaluate(a+1,
387 }
388 }
389
390 // Gut check on housekeeping contents and cube lines
391 if ((int) m_lineRates.size() != Lines()) {
392 ostringstream mess;
393 mess << "Number housekeeping lines determined (" << m_lineRates.size()
394 << ") is not equal to image lines(" << Lines() << ")";
395 throw IException(IException::Programmer, mess.str(), _FILEINFO_);
396 }
397 }
398
399
410 Table DawnVirCamera::getPointingTable(const QString &virChannel,
411 const int zeroFrame) {
412
413 // Create Spice Pointing table
414 TableField q0("J2000Q0", TableField::Double);
415 TableField q1("J2000Q1", TableField::Double);
416 TableField q2("J2000Q2", TableField::Double);
417 TableField q3("J2000Q3", TableField::Double);
418 TableField av1("AV1", TableField::Double);
419 TableField av2("AV2", TableField::Double);
420 TableField av3("AV3", TableField::Double);
422
423 TableRecord record;
424 record += q0;
425 record += q1;
426 record += q2;
427 record += q3;
428 record += av1;
429 record += av2;
430 record += av3;
431 record += t;
432
433 // Get pointing table
434 Table quats("SpiceRotation", record);
435 int nfields = record.Fields();
436
437 QString virId = "DAWN_VIR_" + virChannel;
438 QString virZero = virId + "_ZERO";
439
440 // Allocate output arrays
441 int nvals = nfields - 1;
442 int nlines = m_lineRates.size();
443
444 SpiceDouble eulang[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
445 SpiceDouble xform[6][6], xform2[6][6];
446 SpiceDouble m[3][3];
447 SpiceDouble q_av[7], *av(&q_av[4]);
448
449 for (int i = 0 ; i < nlines ; i++) {
450 int index = min(i, nlines-1);
451 double etTime = m_mirrorData[index].m_scanLineEt; // mid exposure ET
452 double optAng = m_mirrorData[index].m_opticalAngle;
453 try {
454 // J2000 -> DAWN_VIR_{channel}_ZERO
455 SMatrix state = getStateRotation("J2000", virZero, etTime);
456
457 // Set rotation of optical scan mirror (in radians)
458 eulang[1] = -optAng;
459 eul2xf_c(eulang, 1, 2, 3, xform);
460 mxmg_c(xform, &state[0][0], 6, 6, 6, xform2);
461
462 // Transform to output format
463 xf2rav_c(xform2, m, av); // Transfers AV to output q_av via pointer
464 m2q_c(m, q_av); // Transfers quaternion
465
466 // Now populate the table record with the line pointing
467 for (int k = 0 ; k < nvals ; k++) {
468 record[k] = q_av[k];
469 }
470
471 // Add time to record; record to table
472 record[nvals] = etTime;
473 quats += record;
474 }
475 catch (IException &ie) {
476 ostringstream mess;
477 mess << "Failed to get point state for line " << i+1;
478 throw IException(ie, IException::User, mess.str(), _FILEINFO_);
479 }
480 }
481
482 // Add some necessary keywords
483 quats.Label() += PvlKeyword("CkTableStartTime", toString(startTime()));
484 quats.Label() += PvlKeyword("CkTableEndTime", toString(endTime()));
485 quats.Label() += PvlKeyword("CkTableOriginalSize", toString(quats.Records()));
486
487 // Create the time dependant frames keyword
488 int virZeroId = getInteger("FRAME_" + virZero);
489 PvlKeyword tdf("TimeDependentFrames", toString(virZeroId)); // DAWN_VIR_{ID}_ZERO
490 tdf.addValue("-203200"); // DAWN_VIR
491 tdf.addValue("-203000"); // DAWN_SPACECRAFT
492 tdf.addValue("1"); // J2000
493 quats.Label() += tdf;
494
495 // Create constant rotation frames
496 PvlKeyword cf("ConstantFrames", toString(virZeroId));
497 cf.addValue(toString(virZeroId));
498 quats.Label() += cf;
499
500 SpiceDouble identity[3][3];
501 ident_c(identity);
502
503 // Store DAWN_VIR_{ID}_ZERO -> DAWN_VIR_{ID}_ZERO identity rotation
504 PvlKeyword crot("ConstantRotation");
505 for (int i = 0 ; i < 3 ; i++) {
506 for (int j = 0 ; j < 3 ; j++) {
507 crot.addValue(toString(identity[i][j]));
508 }
509 }
510
511 quats.Label() += crot;
512
513 return (quats);
514 }
515
516
534 const QString &frame2,
535 const double &etTime)
536 const {
537 SMatrix state(6,6);
539 try {
540 // Get pointing w/AVs
541 sxform_c(frame1.toLatin1().data(), frame2.toLatin1().data(), etTime,
542 (SpiceDouble (*)[6]) state[0]);
544 }
545 catch (IException &) {
546 try {
547 SMatrix rot(3,3);
548 pxform_c(frame1.toLatin1().data(), frame2.toLatin1().data(), etTime,
549 (SpiceDouble (*)[3]) rot[0]);
551 SpiceDouble av[3] = {0.0, 0.0, 0.0 };
552 rav2xf_c((SpiceDouble (*)[3]) rot[0], av,
553 (SpiceDouble (*)[6]) state[0]);
554 }
555 catch (IException &ie2) {
556 ostringstream mess;
557 mess << "Could not get state rotation for Frame1 (" << frame1
558 << ") to Frame2 (" << frame2 << ") at time " << etTime;
559 throw IException(ie2, IException::User, mess.str(), _FILEINFO_);
560 }
561 }
562 return (state);
563 }
564
565
581 Kernels kerns(label);
582 QStringList cks = kerns.getKernelList("CK");
583 QRegExp virCk("*dawn_vir_?????????_?.bc");
584 virCk.setPatternSyntax(QRegExp::Wildcard);
585 for (int i = 0 ; i < cks.size() ; i++) {
586 if ( virCk.exactMatch(cks[i]) ) return (true);
587 }
588 return (false);
589 }
590
591}
592
596extern "C" Isis::Camera *DawnVirCameraPlugin(Isis::Cube &cube) {
597 return new Isis::DawnVirCamera(cube);
598}
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 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
Camera model for both Danw VIR VIS and IR instruments.
QString scrub(const QString &text) const
Scrubs a string coming out of the housekeeping table.
int hkLineCount() const
Returns number of housekeeping records found in the cube Table.
char m_slitMode
Slit mode of the instrument.
double endTime() const
Return end time for the entire cube.
virtual int CkFrameId() const
CK Frame ID - Instrument Code from spacit run on CK.
double startTime() const
Return start time for the entire cube.
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 m_scanRate
Line scan rate.
double scanLineTime() const
Return the line scan rate.
int m_summing
Summing/binnning mode.
double m_exposureTime
Line exposure time.
TNT::Array2D< SpiceDouble > SMatrix
2-D buffer
~DawnVirCamera()
Destructor.
void readHouseKeeping(const QString &filename, double lineRate)
Read the VIR houskeeping table from cube.
bool hasArticulationKernel(Pvl &label) const
determine if the CK articulation kernels are present/given
Table getPointingTable(const QString &channelId, const int zeroFrame)
Compute the pointing table for each line.
virtual int SpkReferenceId() const
SPK Reference ID - J2000.
bool m_is1BCalibrated
is determined by Archive/ProcessingLevelId
double lineEndTime(const double midExpTime) const
Return the end time for a given line exposure time.
int pixelSumming() const
Return the pixel summing rate.
double exposureTime() const
Return the exposure time.
DawnVirCamera(Cube &cube)
Creates a camera for a Dawn VIR cube.
std::vector< LineRateChange > m_lineRates
vector of timing info for each line
virtual int CkReferenceId() const
CK 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.
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
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.