Isis 3 Programmer Reference
MocLabels.cpp
1
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 "TextFile.h"
24#include "AlphaCube.h"
25
26using namespace std;
27namespace Isis {
31 MocLabels::MocLabels(const QString &file) {
32 Cube cube(file, "r");
33 Init(cube);
34 }
35
40 Init(cube);
41 }
42
47 void MocLabels::Init(Cube &cube) {
48 // Initialize gain tables
50
51 try {
52 ReadLabels(cube);
54 Compute();
55 }
56 catch(IException &e) {
57 string msg = "Labels do not appear contain a valid MOC instrument";
58 throw IException(IException::Unknown, msg, _FILEINFO_);
59 }
60 }
66 // Get stuff out of the instrument group
67 Pvl &lab = *cube.label();
68 PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse);
69 p_instrumentId = (QString) inst["InstrumentId"];
70 p_startingSample = inst["FirstLineSample"];
71 p_crosstrackSumming = inst["CrosstrackSumming"];
72 p_downtrackSumming = inst["DowntrackSumming"];
73 p_exposureDuration = inst["LineExposureDuration"];
74 p_focalPlaneTemp = inst["FocalPlaneTemperature"];
75 p_clockCount = (QString) inst["SpacecraftClockCount"];
76 p_orbitNumber = 0;
77 if(inst.hasKeyword("OrbitNumber")) {
78 p_orbitNumber = inst["OrbitNumber"];
79 }
80 p_gainModeId = (QString) inst["GainModeId"];
81 p_offsetModeId = inst["OffsetModeId"];
82 p_startTime = (QString) inst["StartTime"];
83
84 // Get stuff out of the archive group
85 p_dataQuality = "Unknown";
86 PvlGroup &arch = lab.findGroup("Archive", Pvl::Traverse);
87 if(arch.hasKeyword("DataQualityDesc")) {
88 p_dataQuality = (QString) arch["DataQualityDesc"];
89 }
90
91 // Get Stuff out of the band bind group
92 PvlGroup &bandBin = lab.findGroup("BandBin", Pvl::Traverse);
93 p_filter = (QString) bandBin["FilterName"];
94
95 // Get the number of samples in the initial cube as it may have been
96 // cropped or projected
97 AlphaCube a(cube);
98 p_ns = a.AlphaSamples();
99 p_nl = a.AlphaLines();
100
101 // Get the two kernels for time computations
102 PvlGroup &kerns = lab.findGroup("Kernels", Pvl::Traverse);
103 p_lsk = FileName(kerns["LeapSecond"][0]);
104 p_sclk = FileName(kerns["SpacecraftClock"][0]);
105 }
106
111 // Validate the camera type
112 p_mocNA = false;
113 p_mocRedWA = false;
114 p_mocBlueWA = false;
115
116 if(p_instrumentId == "MOC-NA") p_mocNA = true;
117 if(p_instrumentId == "MOC-WA") {
118 if(p_filter == "RED") p_mocRedWA = true;
119 if(p_filter == "BLUE") p_mocBlueWA = true;
120 }
121
122 if(!p_mocNA && !p_mocRedWA && !p_mocBlueWA) {
123 QString msg = "InstrumentID [" + p_instrumentId + "] and/or FilterName ["
124 + p_filter + "] are inappropriate for the MOC camera";
125 throw IException(IException::Unknown, msg, _FILEINFO_);
126 }
127
128 // Validate summing modes for narrow angle camera
129 if(p_mocNA) {
130 if((p_crosstrackSumming < 1) || (p_crosstrackSumming > 8)) {
131 string msg = "MOC-NA keyword [CrosstrackSumming] must be between ";
132 msg += "1 and 8, but is [" + IString(p_crosstrackSumming) + "]";
133 throw IException(IException::Unknown, msg, _FILEINFO_);
134 }
135
136 if((p_downtrackSumming < 1) || (p_downtrackSumming > 8)) {
137 string msg = "MOC-NA keyword [DowntrackSumming] must be between ";
138 msg += "1 and 8, but is [" + IString(p_downtrackSumming) + "]";
139 throw IException(IException::Unknown, msg, _FILEINFO_);
140 }
141 }
142
143 // Validate summing modes for the wide angle camera
144 if((p_mocRedWA) || (p_mocBlueWA)) {
145 if((p_crosstrackSumming < 1) || (p_crosstrackSumming > 127)) {
146 string msg = "MOC-WA keyword [CrosstrackSumming] must be between ";
147 msg += "1 and 127, but is [" + IString(p_crosstrackSumming) + "]";
148 throw IException(IException::Unknown, msg, _FILEINFO_);
149 }
150
151 if((p_downtrackSumming < 1) || (p_downtrackSumming > 127)) {
152 string msg = "MOC-WA keyword [DowntrackSumming] must be between ";
153 msg += "1 and 127, but is [" + IString(p_downtrackSumming) + "]";
154 throw IException(IException::Unknown, msg, _FILEINFO_);
155 }
156 }
157 }
162 // Compute line rate in seconds
163 p_trueLineRate = p_exposureDuration * (double) p_downtrackSumming;
164 p_trueLineRate /= 1000.0;
165
166 // Fix the exposure duration for NA images
167 if(NarrowAngle() && (p_downtrackSumming != 1)) {
168 p_exposureDuration *= p_downtrackSumming;
169 }
170
171 // Lookup the gain using the gain mode in the gain maps
172 map<QString, double>::iterator p;
173 if(NarrowAngle()) {
174 p = p_gainMapNA.find(p_gainModeId);
175 if(p == p_gainMapNA.end()) {
176 QString msg = "Invalid value for PVL keyword GainModeId [" +
177 p_gainModeId + "]";
178 throw IException(IException::Unknown, msg, _FILEINFO_);
179 }
180 }
181 else {
182 p = p_gainMapWA.find(p_gainModeId);
183 if(p == p_gainMapWA.end()) {
184 QString msg = "Invalid value for PVL keyword GainModeId [" +
185 p_gainModeId + "]";
186 throw IException(IException::Unknown, msg, _FILEINFO_);
187 }
188 }
189 p_gain = p->second;
190
191 // Compute the offset using the offset mode id
192 p_offset = p_offsetModeId * 5.0;
193
194 // Ok the gain computation for narrow angle changed from
195 // pre-mapping to mapping phase. Fix it up if necessary
196 // (when the Downtrack summing is not 1)
197 if(NarrowAngle() && (p_downtrackSumming != 1)) {
198 iTime currentTime(p_startTime);
199 iTime mappingPhaseBeginTime("1999-04-03T01:00:40.441");
200 if(currentTime < mappingPhaseBeginTime) {
201 double newGain = p_gain / (double) p_downtrackSumming;
202 double mindiff = DBL_MAX;
203 map<QString, double>::iterator p;
204 QString index = "";
205 p = p_gainMapNA.begin();
206 while(p != p_gainMapNA.end()) {
207 double diff = abs(newGain - p->second);
208 if(diff < mindiff) {
209 index = p->first;
210 mindiff = diff;
211 }
212
213 p ++;
214 }
215
216 p = p_gainMapNA.find(index);
217 if(p == p_gainMapNA.end()) {
218 string msg = "Could not find new gain for pre-mapping narrow angle image";
219 throw IException(IException::Unknown, msg, _FILEINFO_);
220 }
221 p_gain = p->second;
222 }
223 }
224
225 // Initialize the maps from sample coordinate to detector coordinates
227
228 // Temporarily load some naif kernels
229 QString lsk = p_lsk.expanded();
230 QString sclk = p_sclk.expanded();
231 furnsh_c(lsk.toLatin1().data());
232 furnsh_c(sclk.toLatin1().data());
233
234 // Compute the starting ephemeris time
235 scs2e_c(-94, p_clockCount.toLatin1().data(), &p_etStart);
236 p_etEnd = EphemerisTime((double)p_nl);
237
238 // Unload the naif kernels
239 unload_c(lsk.toLatin1().data());
240 unload_c(sclk.toLatin1().data());
241 }
242
248 p_gainMapNA["F2"] = 1.0;
249 p_gainMapNA["D2"] = 1.456;
250 p_gainMapNA["B2"] = 2.076;
251 p_gainMapNA["92"] = 2.935;
252 p_gainMapNA["72"] = 4.150;
253 p_gainMapNA["52"] = 5.866;
254 p_gainMapNA["32"] = 8.292;
255 p_gainMapNA["12"] = 11.73;
256 p_gainMapNA["EA"] = 7.968;
257 p_gainMapNA["CA"] = 11.673;
258 p_gainMapNA["AA"] = 16.542;
259 p_gainMapNA["8A"] = 23.386;
260 p_gainMapNA["6A"] = 33.067;
261 p_gainMapNA["4A"] = 46.740;
262 p_gainMapNA["2A"] = 66.071;
263 p_gainMapNA["0A"] = 93.465;
264
265 p_gainMapWA["9A"] = 1.0;
266 p_gainMapWA["8A"] = 1.412;
267 p_gainMapWA["7A"] = 2.002;
268 p_gainMapWA["6A"] = 2.832;
269 p_gainMapWA["5A"] = 4.006;
270 p_gainMapWA["4A"] = 5.666;
271 p_gainMapWA["3A"] = 8.014;
272 p_gainMapWA["2A"] = 11.34;
273 p_gainMapWA["1A"] = 16.03;
274 p_gainMapWA["0A"] = 22.67;
275 p_gainMapWA["96"] = 16.030;
276 p_gainMapWA["86"] = 22.634;
277 p_gainMapWA["76"] = 32.092;
278 p_gainMapWA["66"] = 45.397;
279 p_gainMapWA["56"] = 64.216;
280 p_gainMapWA["46"] = 90.826;
281 p_gainMapWA["36"] = 128.464;
282 p_gainMapWA["26"] = 181.780;
283 p_gainMapWA["16"] = 256.961;
284 p_gainMapWA["06"] = 363.400;
285 };
286
292 int MocLabels::StartDetector(int sample) const {
293 if((sample < 1) || (sample > p_ns)) {
294 string msg = "Out of array bounds in MocLabels::StartDetector";
295 throw IException(IException::Unknown, msg, _FILEINFO_);
296 }
297 return p_startDetector[sample-1];
298 }
304 int MocLabels::EndDetector(int sample) const {
305 if((sample < 1) || (sample > p_ns)) {
306 string msg = "Out of array bounds in MocLabels::EndDetector";
307 throw IException(IException::Unknown, msg, _FILEINFO_);
308 }
309 return p_endDetector[sample-1];
310 }
316 double MocLabels::Sample(int detector) const {
317 if((detector < 0) || (detector >= Detectors())) {
318 string msg = "Out of array bounds in MocLabels::Sample";
319 throw IException(IException::Unknown, msg, _FILEINFO_);
320 }
321 return p_sample[detector];
322 }
327 // Create sample to detector maps
328 if(p_crosstrackSumming == 13) {
329 for(int i = 0; i < p_ns; i++) {
330 p_startDetector[i] = mode13_table[i].starting_pixel +
331 p_startingSample - 1;
332 p_endDetector[i] = mode13_table[i].ending_pixel +
333 p_startingSample - 1;
334 }
335 }
336 else if(p_crosstrackSumming == 27) {
337 for(int i = 0; i < p_ns; i++) {
338 p_startDetector[i] = mode27_table[i].starting_pixel +
339 p_startingSample - 1;
340 p_endDetector[i] = mode27_table[i].ending_pixel +
341 p_startingSample - 1;
342 }
343 }
344 else {
345 int detector = (p_startingSample - 1);
346 for(int i = 0; i < p_ns; i++) {
347 p_startDetector[i] = detector;
348 detector += p_crosstrackSumming - 1;
349 p_endDetector[i] = detector;
350 detector++;
351 }
352 }
353
354 // Now create a detector to sample map
355 // Start by setting each position to a invalid sample
356 for(int det = 0; det < Detectors(); det++) {
357 p_sample[det] = -1.0;
358 }
359
360 for(int samp = 1; samp <= p_ns; samp++) {
361 int sd = p_startDetector[samp-1];
362 int ed = p_endDetector[samp-1];
363
364 double m = ((samp + 0.5) - (samp - 0.5)) / ((ed + 0.5) - (sd - 0.5));
365 for(int det = sd; det <= ed; det++) {
366 p_sample[det] = m * (det - (sd - 0.5)) + (samp - 0.5);
367 }
368 }
369 }
375 double MocLabels::EphemerisTime(double line) const {
376 return p_etStart + (line - 0.5) * p_trueLineRate;
377 }
383 // return gain at a line
384 double MocLabels::Gain(int line) {
385 if(NarrowAngle()) return p_gain;
386
387 InitWago();
388
389 double etLine = EphemerisTime((double)line);
390 for(int i = (int)p_wagos.size() - 1; i >= 0; i--) {
391 if(etLine >= p_wagos[i].et) {
392 return p_wagos[i].gain;
393 }
394 }
395
396 return p_gain;
397 }
403 double MocLabels::Offset(int line) {
404 if(NarrowAngle()) return p_offset;
405 InitWago();
406
407 double etLine = EphemerisTime((double)line);
408 for(int i = (int)p_wagos.size() - 1; i >= 0; i--) {
409 if(etLine >= p_wagos[i].et) {
410 return p_wagos[i].offset;
411 }
412 }
413 return p_offset;
414 }
425 // Only do this once
426 static bool firstTime = true;
427 if(!firstTime) return;
428 firstTime = false;
429
430 // Load naif kernels
431 QString lskKern = p_lsk.expanded();
432 QString sclkKern = p_sclk.expanded();
433 furnsh_c(lskKern.toLatin1().data());
434 furnsh_c(sclkKern.toLatin1().data());
435
436 //Set up file for reading
437 FileName wagoFile("$mgs/calibration/MGSC_????_wago.tab");
438 wagoFile = wagoFile.highestVersion();
439 QString nameOfFile = wagoFile.expanded();
440 ifstream temp(nameOfFile.toLatin1().data());
441 vector<int> wholeFile;
442
443 // Read file into a vector of bytes, ignoring EOL chars
444 while(temp.good()) {
445 int nextByte = temp.get();
446 if(nextByte != 10 && nextByte != 13) {
447 wholeFile.push_back(nextByte);
448 }
449 }
450 temp.close();
451
452 //Set up to binary search for the desired time
453 int low = 1;
454 int high = wholeFile.size() / 35;
455 int middle;
456 IString line, filter, sclk, offsetId;
457 QString gainId;
458 WAGO wago;
459
460 //Binary search. This determines the middle of the current range and
461 //moves through the file until it reaches that line, at which point it
462 //analyzes to see if the time is within the set limits.
463 while(low <= high) {
464 middle = (low + high) / 2;
465 int SclkStart = middle * 35 + 8;
466 int SclkEnd = SclkStart + 15;
467 string currentSclk;
468
469 //Build sclk string and convert to an actual time
470 for(int i = SclkStart; i < SclkEnd; i++) {
471 currentSclk += (char)wholeFile[i];
472 }
473 sclk = currentSclk;
474 sclk.Remove("\"");
475 sclk.Trim(" ");
476 double et;
477 scs2e_c(-94, currentSclk.c_str(), &et);
478
479 //Compare time against given parameters, if it fits, process
480 if(et < p_etEnd && et > p_etStart) {
481 int linenum = middle;
482 int top = middle;
483 int bottom = middle;
484
485 //First, find the highest line that will meet requirements
486 while(et >= p_etStart) {
487 linenum--;
488 int lineStart = (linenum * 35);
489 int lineEnd = lineStart + 35;
490
491 string currentLine = "";
492 for(int i = lineStart; i < lineEnd; i++) {
493 currentLine += (char)wholeFile[i];
494 }
495 line = currentLine;
496
497 currentSclk = "";
498 for(int i = 8; i < 23; ++i) {
499 currentSclk += currentLine[i];
500 }
501 sclk = currentSclk;
502 sclk.Trim(" ");
503 scs2e_c(-94, currentSclk.c_str(), &et);
504
505 bottom = linenum;
506 }
507 //Next, find the lowest line to meet requirements
508 while(et <= p_etEnd) {
509 linenum++;
510 int lineStart = (linenum * 35);
511 int lineEnd = lineStart + 35;
512
513 string currentLine = "";
514 for(int i = lineStart; i < lineEnd; i++) {
515 currentLine += (char)wholeFile[i];
516 }
517 line = currentLine;
518
519 currentSclk = "";
520 for(int i = 8; i < 23; ++i) {
521 currentSclk += currentLine[i];
522 }
523 sclk = currentSclk;
524 sclk.Trim(" ");
525 scs2e_c(-94, currentSclk.c_str(), &et);
526 top = linenum;
527 }
528 //Now, go from the upper limit to the lower limit, and grab all lines
529 //that meet requirements
530 for(int i = bottom; i <= top; ++i) {
531 int lineStart = (i * 35);
532 int lineEnd = lineStart + 35;
533 string currentLine = "";
534 for(int j = lineStart; j < lineEnd; j++) {
535 currentLine += (char)wholeFile[j];
536 }
537 line = currentLine;
538
539 // Get the filter color (red or blue)
540 filter = line.Token(",");
541 filter.Remove("\"");
542 filter.Trim(" ");
543
544 // If it's not the filter we want then skip to loop end
545 if((filter == "RED") && (WideAngleBlue())) continue;
546 if((filter == "BLUE") && (WideAngleRed())) continue;
547
548 // Get the sclk and convert to et
549 sclk = line.Token(",");
550 sclk.Remove("\"");
551 sclk.Trim(" ");
552
553 scs2e_c(-94, sclk.c_str(), &et);
554
555 // Get the gain mode id
556 gainId = line.Token(",").ToQt().remove("\"").trimmed();
557
558 // Get the offset mode id
559 offsetId = line;
560 offsetId.Remove("\"");
561 offsetId.ConvertWhiteSpace();
562 offsetId.Trim(" ");
563
564 // Compute the gain
565 map<QString, double>::iterator p;
566 p = p_gainMapWA.find(gainId);
567 if(p == p_gainMapWA.end()) {
568 // Unload the naif kernels
569 unload_c(lskKern.toLatin1().data());
570 unload_c(sclkKern.toLatin1().data());
571
572 QString msg = "Invalid GainModeId [" + gainId + "] in wago table";
573 throw IException(IException::Unknown, msg, _FILEINFO_);
574 }
575 double gain = p->second;
576
577 // Compute the offset
578 double offset = offsetId.ToDouble() * 5.0;
579
580 // Push everything onto a stack
581 wago.et = et;
582 wago.gain = gain;
583 wago.offset = offset;
584 p_wagos.push_back(wago);
585
586 }
587
588 low = high++;
589 }
590 //If we're too high, search beginning of array
591 else if(et < p_etStart) {
592 low = middle + 1;
593 }
594 //If we're too low, search end of array
595 else {
596 high = middle - 1;
597 }
598 }
599
600 // Ok sort and unique the wago list by time
601 sort(p_wagos.begin(), p_wagos.end());
602 boost::ignore_unused((unique(p_wagos.begin(), p_wagos.end())));
603
604 // Unload the naif kernels
605 unload_c(lskKern.toLatin1().data());
606 unload_c(sclkKern.toLatin1().data());
607 }
608}
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:1707
File name manipulation and expansion.
Definition FileName.h:100
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition FileName.cpp:196
Isis exception class.
Definition IException.h:91
@ Unknown
A type of error that cannot be classified as any of the other error types.
Definition IException.h:118
Adds specific functionality to C++ strings.
Definition IString.h:165
IString Remove(const std::string &del)
Remove all instances of any character in the string from the IString.
Definition IString.cpp:1266
IString Trim(const std::string &chars)
Removes characters from the beginning and end of the IString.
Definition IString.cpp:525
IString Token(const IString &separator)
Returns the first token in the IString.
Definition IString.cpp:897
QString ToQt() const
Retuns the object string as a QString.
Definition IString.cpp:869
IString ConvertWhiteSpace()
Returns the string with all "new lines", "carriage returns", "tabs", "form feeds",...
Definition IString.cpp:1238
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:65
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:39
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:47
bool WideAngleRed() const
Indicates whether the camera was red wide angle.
Definition MocLabels.h:76
Contains multiple PvlContainers.
Definition PvlGroup.h:41
Container for cube-like labels.
Definition Pvl.h:119
@ Traverse
Search child objects.
Definition PvlObject.h:158
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.