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