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
MocLabels.cpp
1
6
7/* SPDX-License-Identifier: CC0-1.0 */
8
9#include "MocLabels.h"
10
11#include <cmath>
12#include <cfloat>
13#include <iostream>
14#include <vector>
15#include <fstream>
16#include <boost/core/ignore_unused.hpp>
17
18#include "Cube.h"
19#include "IException.h"
20#include "IString.h"
21#include "iTime.h"
22#include "mocxtrack.h"
23#include "spiceql.h"
24#include "TextFile.h"
25#include "AlphaCube.h"
26#include "Preference.h"
27
28using namespace std;
29namespace Isis {
33 MocLabels::MocLabels(const QString &file) {
34 Cube cube(file, "r");
35 Init(cube);
36 }
37
42 Init(cube);
43 }
44
49 void MocLabels::Init(Cube &cube) {
50 // Initialize gain tables
52
53 try {
54 ReadLabels(cube);
56 Compute();
57 }
58 catch(IException &e) {
59 string msg = "Labels do not appear contain a valid MOC instrument";
60 throw IException(IException::Unknown, msg, _FILEINFO_);
61 }
62 }
63
68 // Get stuff out of the instrument group
69 Pvl &lab = *cube.label();
70 PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse);
71 p_instrumentId = (QString) inst["InstrumentId"];
72 p_startingSample = inst["FirstLineSample"];
73 p_crosstrackSumming = inst["CrosstrackSumming"];
74 p_downtrackSumming = inst["DowntrackSumming"];
75 p_exposureDuration = inst["LineExposureDuration"];
76 p_focalPlaneTemp = inst["FocalPlaneTemperature"];
77 p_clockCount = (QString) inst["SpacecraftClockCount"];
78 p_orbitNumber = 0;
79 if(inst.hasKeyword("OrbitNumber")) {
80 p_orbitNumber = inst["OrbitNumber"];
81 }
82 p_gainModeId = (QString) inst["GainModeId"];
83 p_offsetModeId = inst["OffsetModeId"];
84 p_startTime = (QString) inst["StartTime"];
85
86 // Get stuff out of the archive group
87 p_dataQuality = "Unknown";
88 PvlGroup &arch = lab.findGroup("Archive", Pvl::Traverse);
89 if(arch.hasKeyword("DataQualityDesc")) {
90 p_dataQuality = (QString) arch["DataQualityDesc"];
91 }
92
93 // Get Stuff out of the band bind group
94 PvlGroup &bandBin = lab.findGroup("BandBin", Pvl::Traverse);
95 p_filter = (QString) bandBin["FilterName"];
96
97 // Get the number of samples in the initial cube as it may have been
98 // cropped or projected
99 AlphaCube a(cube);
100 p_ns = a.AlphaSamples();
101 p_nl = a.AlphaLines();
102
103 // Get the two kernels for time computations
104 PvlGroup &kerns = lab.findGroup("Kernels", Pvl::Traverse);
105 p_lsk = FileName(kerns["LeapSecond"][0]);
106 p_sclk = FileName(kerns["SpacecraftClock"][0]);
107 }
108
113 // Validate the camera type
114 p_mocNA = false;
115 p_mocRedWA = false;
116 p_mocBlueWA = false;
117
118 if(p_instrumentId == "MOC-NA") p_mocNA = true;
119 if(p_instrumentId == "MOC-WA") {
120 if(p_filter == "RED") p_mocRedWA = true;
121 if(p_filter == "BLUE") p_mocBlueWA = true;
122 }
123
124 if(!p_mocNA && !p_mocRedWA && !p_mocBlueWA) {
125 QString msg = "InstrumentID [" + p_instrumentId + "] and/or FilterName ["
126 + p_filter + "] are inappropriate for the MOC camera";
127 throw IException(IException::Unknown, msg, _FILEINFO_);
128 }
129
130 // Validate summing modes for narrow angle camera
131 if(p_mocNA) {
132 if((p_crosstrackSumming < 1) || (p_crosstrackSumming > 8)) {
133 string msg = "MOC-NA keyword [CrosstrackSumming] must be between ";
134 msg += "1 and 8, but is [" + IString(p_crosstrackSumming) + "]";
135 throw IException(IException::Unknown, msg, _FILEINFO_);
136 }
137
138 if((p_downtrackSumming < 1) || (p_downtrackSumming > 8)) {
139 string msg = "MOC-NA keyword [DowntrackSumming] must be between ";
140 msg += "1 and 8, but is [" + IString(p_downtrackSumming) + "]";
141 throw IException(IException::Unknown, msg, _FILEINFO_);
142 }
143 }
144
145 // Validate summing modes for the wide angle camera
146 if((p_mocRedWA) || (p_mocBlueWA)) {
147 if((p_crosstrackSumming < 1) || (p_crosstrackSumming > 127)) {
148 string msg = "MOC-WA keyword [CrosstrackSumming] must be between ";
149 msg += "1 and 127, but is [" + IString(p_crosstrackSumming) + "]";
150 throw IException(IException::Unknown, msg, _FILEINFO_);
151 }
152
153 if((p_downtrackSumming < 1) || (p_downtrackSumming > 127)) {
154 string msg = "MOC-WA keyword [DowntrackSumming] must be between ";
155 msg += "1 and 127, but is [" + IString(p_downtrackSumming) + "]";
156 throw IException(IException::Unknown, msg, _FILEINFO_);
157 }
158 }
159 }
160
164 // Compute line rate in seconds
165 p_trueLineRate = p_exposureDuration * (double) p_downtrackSumming;
166 p_trueLineRate /= 1000.0;
167
168 // Fix the exposure duration for NA images
169 if(NarrowAngle() && (p_downtrackSumming != 1)) {
170 p_exposureDuration *= p_downtrackSumming;
171 }
172
173 // Lookup the gain using the gain mode in the gain maps
174 map<QString, double>::iterator p;
175 if(NarrowAngle()) {
176 p = p_gainMapNA.find(p_gainModeId);
177 if(p == p_gainMapNA.end()) {
178 QString msg = "Invalid value for PVL keyword GainModeId [" +
179 p_gainModeId + "]";
180 throw IException(IException::Unknown, msg, _FILEINFO_);
181 }
182 }
183 else {
184 p = p_gainMapWA.find(p_gainModeId);
185 if(p == p_gainMapWA.end()) {
186 QString msg = "Invalid value for PVL keyword GainModeId [" +
187 p_gainModeId + "]";
188 throw IException(IException::Unknown, msg, _FILEINFO_);
189 }
190 }
191 p_gain = p->second;
192
193 // Compute the offset using the offset mode id
194 p_offset = p_offsetModeId * 5.0;
195
196 // Ok the gain computation for narrow angle changed from
197 // pre-mapping to mapping phase. Fix it up if necessary
198 // (when the Downtrack summing is not 1)
199 if(NarrowAngle() && (p_downtrackSumming != 1)) {
200 iTime currentTime(p_startTime);
201 iTime mappingPhaseBeginTime("1999-04-03T01:00:40.441");
202 if(currentTime < mappingPhaseBeginTime) {
203 double newGain = p_gain / (double) p_downtrackSumming;
204 double mindiff = DBL_MAX;
205 map<QString, double>::iterator p;
206 QString index = "";
207 p = p_gainMapNA.begin();
208 while(p != p_gainMapNA.end()) {
209 double diff = abs(newGain - p->second);
210 if(diff < mindiff) {
211 index = p->first;
212 mindiff = diff;
213 }
214
215 p ++;
216 }
217
218 p = p_gainMapNA.find(index);
219 if(p == p_gainMapNA.end()) {
220 string msg = "Could not find new gain for pre-mapping narrow angle image";
221 throw IException(IException::Unknown, msg, _FILEINFO_);
222 }
223 p_gain = p->second;
224 }
225 }
226
227 // Initialize the maps from sample coordinate to detector coordinates
229
230 bool useWeb = QString(Preference::Preferences().findGroup("SpiceQL")["UseSpiceQL"]).toUpper() == "TRUE";
231
232 auto [output, kernels] = SpiceQL::strSclkToEt(-94, p_clockCount.toLatin1().data(), "mgs", useWeb);
233 p_etStart = output;
234 p_etEnd = EphemerisTime((double)p_nl);
235
236 }
237
243 p_gainMapNA["F2"] = 1.0;
244 p_gainMapNA["D2"] = 1.456;
245 p_gainMapNA["B2"] = 2.076;
246 p_gainMapNA["92"] = 2.935;
247 p_gainMapNA["72"] = 4.150;
248 p_gainMapNA["52"] = 5.866;
249 p_gainMapNA["32"] = 8.292;
250 p_gainMapNA["12"] = 11.73;
251 p_gainMapNA["EA"] = 7.968;
252 p_gainMapNA["CA"] = 11.673;
253 p_gainMapNA["AA"] = 16.542;
254 p_gainMapNA["8A"] = 23.386;
255 p_gainMapNA["6A"] = 33.067;
256 p_gainMapNA["4A"] = 46.740;
257 p_gainMapNA["2A"] = 66.071;
258 p_gainMapNA["0A"] = 93.465;
259
260 p_gainMapWA["9A"] = 1.0;
261 p_gainMapWA["8A"] = 1.412;
262 p_gainMapWA["7A"] = 2.002;
263 p_gainMapWA["6A"] = 2.832;
264 p_gainMapWA["5A"] = 4.006;
265 p_gainMapWA["4A"] = 5.666;
266 p_gainMapWA["3A"] = 8.014;
267 p_gainMapWA["2A"] = 11.34;
268 p_gainMapWA["1A"] = 16.03;
269 p_gainMapWA["0A"] = 22.67;
270 p_gainMapWA["96"] = 16.030;
271 p_gainMapWA["86"] = 22.634;
272 p_gainMapWA["76"] = 32.092;
273 p_gainMapWA["66"] = 45.397;
274 p_gainMapWA["56"] = 64.216;
275 p_gainMapWA["46"] = 90.826;
276 p_gainMapWA["36"] = 128.464;
277 p_gainMapWA["26"] = 181.780;
278 p_gainMapWA["16"] = 256.961;
279 p_gainMapWA["06"] = 363.400;
280 };
281
287 int MocLabels::StartDetector(int sample) const {
288 if((sample < 1) || (sample > p_ns)) {
289 string msg = "Out of array bounds in MocLabels::StartDetector";
290 throw IException(IException::Unknown, msg, _FILEINFO_);
291 }
292 return p_startDetector[sample-1];
293 }
294
299 int MocLabels::EndDetector(int sample) const {
300 if((sample < 1) || (sample > p_ns)) {
301 string msg = "Out of array bounds in MocLabels::EndDetector";
302 throw IException(IException::Unknown, msg, _FILEINFO_);
303 }
304 return p_endDetector[sample-1];
305 }
306
311 double MocLabels::Sample(int detector) const {
312 if((detector < 0) || (detector >= Detectors())) {
313 string msg = "Out of array bounds in MocLabels::Sample";
314 throw IException(IException::Unknown, msg, _FILEINFO_);
315 }
316 return p_sample[detector];
317 }
318
322 // Create sample to detector maps
323 if(p_crosstrackSumming == 13) {
324 for(int i = 0; i < p_ns; i++) {
325 p_startDetector[i] = mode13_table[i].starting_pixel +
326 p_startingSample - 1;
327 p_endDetector[i] = mode13_table[i].ending_pixel +
328 p_startingSample - 1;
329 }
330 }
331 else if(p_crosstrackSumming == 27) {
332 for(int i = 0; i < p_ns; i++) {
333 p_startDetector[i] = mode27_table[i].starting_pixel +
334 p_startingSample - 1;
335 p_endDetector[i] = mode27_table[i].ending_pixel +
336 p_startingSample - 1;
337 }
338 }
339 else {
340 int detector = (p_startingSample - 1);
341 for(int i = 0; i < p_ns; i++) {
342 p_startDetector[i] = detector;
343 detector += p_crosstrackSumming - 1;
344 p_endDetector[i] = detector;
345 detector++;
346 }
347 }
348
349 // Now create a detector to sample map
350 // Start by setting each position to a invalid sample
351 for(int det = 0; det < Detectors(); det++) {
352 p_sample[det] = -1.0;
353 }
354
355 for(int samp = 1; samp <= p_ns; samp++) {
356 int sd = p_startDetector[samp-1];
357 int ed = p_endDetector[samp-1];
358
359 double m = ((samp + 0.5) - (samp - 0.5)) / ((ed + 0.5) - (sd - 0.5));
360 for(int det = sd; det <= ed; det++) {
361 p_sample[det] = m * (det - (sd - 0.5)) + (samp - 0.5);
362 }
363 }
364 }
365
370 double MocLabels::EphemerisTime(double line) const {
371 return p_etStart + (line - 0.5) * p_trueLineRate;
372 }
373
378 // return gain at a line
379 double MocLabels::Gain(int line) {
380 if(NarrowAngle()) return p_gain;
381
382 InitWago();
383
384 double etLine = EphemerisTime((double)line);
385 for(int i = (int)p_wagos.size() - 1; i >= 0; i--) {
386 if(etLine >= p_wagos[i].et) {
387 return p_wagos[i].gain;
388 }
389 }
390
391 return p_gain;
392 }
393
398 double MocLabels::Offset(int line) {
399 if(NarrowAngle()) return p_offset;
400 InitWago();
401
402 double etLine = EphemerisTime((double)line);
403 for(int i = (int)p_wagos.size() - 1; i >= 0; i--) {
404 if(etLine >= p_wagos[i].et) {
405 return p_wagos[i].offset;
406 }
407 }
408 return p_offset;
409 }
410
420 // Only do this once
421 static bool firstTime = true;
422 if(!firstTime) return;
423 firstTime = false;
424
425 //Set up file for reading
426 FileName wagoFile("$mgs/calibration/MGSC_????_wago.tab");
427 wagoFile = wagoFile.highestVersion();
428 QString nameOfFile = wagoFile.expanded();
429 ifstream temp(nameOfFile.toLatin1().data());
430 vector<int> wholeFile;
431
432 // Read file into a vector of bytes, ignoring EOL chars
433 while(temp.good()) {
434 int nextByte = temp.get();
435 if(nextByte != 10 && nextByte != 13) {
436 wholeFile.push_back(nextByte);
437 }
438 }
439 temp.close();
440
441 //Set up to binary search for the desired time
442 int low = 1;
443 int high = wholeFile.size() / 35;
444 int middle;
445 IString line, filter, sclk, offsetId;
446 QString gainId;
447 WAGO wago;
448
449 //Binary search. This determines the middle of the current range and
450 //moves through the file until it reaches that line, at which point it
451 //analyzes to see if the time is within the set limits.
452 while(low <= high) {
453 middle = (low + high) / 2;
454 int SclkStart = middle * 35 + 8;
455 int SclkEnd = SclkStart + 15;
456 string currentSclk;
457
458 //Build sclk string and convert to an actual time
459 for(int i = SclkStart; i < SclkEnd; i++) {
460 currentSclk += (char)wholeFile[i];
461 }
462 sclk = currentSclk;
463 sclk.Remove("\"");
464 sclk.Trim(" ");
465
466 bool useWeb = QString(Preference::Preferences().findGroup("SpiceQL")["UseSpiceQL"]).toUpper() == "TRUE";
467 auto [et, kernels] = SpiceQL::strSclkToEt(-94, currentSclk, "mgs", useWeb);
468
469 //Compare time against given parameters, if it fits, process
470 if(et < p_etEnd && et > p_etStart) {
471 int linenum = middle;
472 int top = middle;
473 int bottom = middle;
474
475 //First, find the highest line that will meet requirements
476 while(et >= p_etStart) {
477 linenum--;
478 int lineStart = (linenum * 35);
479 int lineEnd = lineStart + 35;
480
481 string currentLine = "";
482 for(int i = lineStart; i < lineEnd; i++) {
483 currentLine += (char)wholeFile[i];
484 }
485 line = currentLine;
486
487 currentSclk = "";
488 for(int i = 8; i < 23; ++i) {
489 currentSclk += currentLine[i];
490 }
491 sclk = currentSclk;
492 sclk.Trim(" ");
493
494 tie(et, kernels) = SpiceQL::strSclkToEt(-94, currentSclk, "mgs", useWeb);
495 scs2e_c(-94, currentSclk.c_str(), &et);
496
497 bottom = linenum;
498 }
499 //Next, find the lowest line to meet requirements
500 while(et <= p_etEnd) {
501 linenum++;
502 int lineStart = (linenum * 35);
503 int lineEnd = lineStart + 35;
504
505 string currentLine = "";
506 for(int i = lineStart; i < lineEnd; i++) {
507 currentLine += (char)wholeFile[i];
508 }
509 line = currentLine;
510
511 currentSclk = "";
512 for(int i = 8; i < 23; ++i) {
513 currentSclk += currentLine[i];
514 }
515 sclk = currentSclk;
516 sclk.Trim(" ");
517 tie(et, kernels) = SpiceQL::strSclkToEt(-94, currentSclk, "mgs", useWeb);
518 top = linenum;
519 }
520 //Now, go from the upper limit to the lower limit, and grab all lines
521 //that meet requirements
522 for(int i = bottom; i <= top; ++i) {
523 int lineStart = (i * 35);
524 int lineEnd = lineStart + 35;
525 string currentLine = "";
526 for(int j = lineStart; j < lineEnd; j++) {
527 currentLine += (char)wholeFile[j];
528 }
529 line = currentLine;
530
531 // Get the filter color (red or blue)
532 filter = line.Token(",");
533 filter.Remove("\"");
534 filter.Trim(" ");
535
536 // If it's not the filter we want then skip to loop end
537 if((filter == "RED") && (WideAngleBlue())) continue;
538 if((filter == "BLUE") && (WideAngleRed())) continue;
539
540 // Get the sclk and convert to et
541 sclk = line.Token(",");
542 sclk.Remove("\"");
543 sclk.Trim(" ");
544
545 tie(et, kernels) = SpiceQL::strSclkToEt(-94, currentSclk, "mgs", useWeb);
546
547 // Get the gain mode id
548 gainId = line.Token(",").ToQt().remove("\"").trimmed();
549
550 // Get the offset mode id
551 offsetId = line;
552 offsetId.Remove("\"");
553 offsetId.ConvertWhiteSpace();
554 offsetId.Trim(" ");
555
556 // Compute the gain
557 map<QString, double>::iterator p;
558 p = p_gainMapWA.find(gainId);
559 if(p == p_gainMapWA.end()) {
560 // Unload the naif kernels
561
562 QString msg = "Invalid GainModeId [" + gainId + "] in wago table";
563 throw IException(IException::Unknown, msg, _FILEINFO_);
564 }
565 double gain = p->second;
566
567 // Compute the offset
568 double offset = offsetId.ToDouble() * 5.0;
569
570 // Push everything onto a stack
571 wago.et = et;
572 wago.gain = gain;
573 wago.offset = offset;
574 p_wagos.push_back(wago);
575
576 }
577
578 low = high++;
579 }
580 //If we're too high, search beginning of array
581 else if(et < p_etStart) {
582 low = middle + 1;
583 }
584 //If we're too low, search end of array
585 else {
586 high = middle - 1;
587 }
588 }
589
590 // Ok sort and unique the wago list by time
591 sort(p_wagos.begin(), p_wagos.end());
592 boost::ignore_unused((unique(p_wagos.begin(), p_wagos.end())));
593
594 }
595}
This class is used to rewrite the "alpha" keywords out of the AlphaCube group or Instrument group.
Definition AlphaCube.h:46
int AlphaSamples() const
Returns the number of samples in the alpha cube.
Definition AlphaCube.h:77
int AlphaLines() const
Returns the number of lines in the alpha cube.
Definition AlphaCube.h:67
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
void ValidateLabels()
Verifies that the labels are valid.
double Offset(int line=1)
Returns the offset at the given line.
bool WideAngleBlue() const
Indicates whether the camera was blue wide angle.
Definition MocLabels.h:84
int StartDetector(int sample) const
Converts from sample to starting detector.
void ReadLabels(Cube &cube)
Reads required keywords from the labels.
Definition MocLabels.cpp:67
double EphemerisTime(double line) const
Returns the ephemeris time at the given line.
bool NarrowAngle() const
Indicates whether the camera was narrow angle.
Definition MocLabels.h:61
MocLabels(Cube &cube)
Construct MocLabels object using a Pvl object.
Definition MocLabels.cpp:41
int Detectors() const
Returns 2048 if narrow angle and 3456 if wide angle.
Definition MocLabels.h:145
double Sample(int detector) const
Converts from detector to sample.
void InitGainMaps()
Creates a lookup of gain modes to gain values.
int EndDetector(int sample) const
Converts from sample to ending detector.
void InitDetectorMaps()
Creates lookup table from sample to detectors and vice versa.
double Gain(int line=1)
Returns the true gain at a given line.
void Compute()
Computes some constants.
void InitWago()
Reads the wide-angle gain/offset table and internalizes.
void Init(Cube &cube)
General initializer.
Definition MocLabels.cpp:49
bool WideAngleRed() const
Indicates whether the camera was red wide angle.
Definition MocLabels.h:76
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
Namespace for the standard library.