|
Isis 3.0 Application Source Code Reference |
Home |
00001 #include "Isis.h" 00002 #include "Chip.h" 00003 #include "UserInterface.h" 00004 #include "FileName.h" 00005 #include "Pvl.h" 00006 #include "IString.h" 00007 #include "IException.h" 00008 #include "iTime.h" 00009 #include "Apollo.h" 00010 #include "PvlTranslationTable.h" 00011 #include "Statistics.h" 00012 #include "ProcessImportPds.h" 00013 00014 #include <cstdio> 00015 00016 using namespace std; 00017 using namespace Isis; 00018 00019 bool FindCode(); 00020 void CalculateTransform(); 00021 void RefineCodeLocation(); 00022 void TranslateCode(); 00023 void TranslateApolloLabels (IString filename, Cube *pack); 00024 bool IsValidCode(); 00025 QString FrameTime(); 00026 int Altitude(); 00027 double ShutterInterval(); 00028 QString FMC(); 00029 00030 int codeSample, codeLine; 00031 bool code[4][32]; 00032 bool codeFound = true; 00033 00034 Cube cube; 00035 Apollo *apollo; 00036 QString utcTime; 00037 00038 int RADIUS = 46; 00039 double rotation, sampleTranslation, lineTranslation; 00040 00041 // Main program 00042 void IsisMain() { 00043 ProcessImportPds p; 00044 Pvl pdsLabel; 00045 UserInterface &ui = Application::GetUserInterface(); 00046 FileName inFile = ui.GetFileName("FROM"); 00047 00048 p.SetPdsFile(inFile.expanded(), "", pdsLabel); 00049 00050 QString filename = FileName(ui.GetFileName("FROM")).baseName(); 00051 FileName toFile = ui.GetFileName("TO"); 00052 00053 apollo = new Apollo(filename); 00054 00055 utcTime = (QString)pdsLabel["START_TIME"]; 00056 00057 // Setup the output cube attributes for a 16-bit unsigned tiff 00058 Isis::CubeAttributeOutput cao; 00059 cao.setPixelType(Isis::Real); 00060 p.SetOutputCube(toFile.expanded(), cao); 00061 00062 // Import image 00063 p.StartProcess(); 00064 p.EndProcess(); 00065 00066 cube.open(toFile.expanded(), "rw"); 00067 00068 // Once the image is imported, we need to find and decrypt the code 00069 if (apollo->IsMetric() && FindCode()) 00070 TranslateCode(); 00071 00072 CalculateTransform(); 00073 // Once we have decrypted the code, we need to populate the image labels 00074 TranslateApolloLabels(filename, &cube); 00075 cube.close(); 00076 } 00077 00078 // Find the location of the code 00079 bool FindCode() { 00080 // start by looking for a high value pixel 00081 int CENTER_SAMPLE = 1030, 00082 CENTER_LINE = 21350, 00083 DELTAX = 1000, 00084 DELTAY = 250; 00085 Chip chip(2*DELTAX+2*RADIUS, 2*DELTAY+4*RADIUS); 00086 chip.TackCube(CENTER_SAMPLE+RADIUS, CENTER_LINE+2*RADIUS); 00087 chip.Load(cube); 00088 for (int i=1; i<chip.Samples()-6*RADIUS-1; i+=RADIUS/8) { 00089 for (int j=1; j<=chip.Lines()-4*RADIUS-1; j+=RADIUS/8) { 00090 if ( chip.GetValue(i,j) > 60000 && chip.GetValue(i+2*RADIUS, j) > 60000 && 00091 chip.GetValue(i+2*RADIUS, j+2*RADIUS) < 60000 && 00092 chip.GetValue(i+2*RADIUS, j+4*RADIUS) > 60000) { 00093 // Now that we've found one, let's refine the location 00094 codeFound = true; 00095 codeSample = CENTER_SAMPLE - DELTAX + i; 00096 codeLine = CENTER_LINE - DELTAY + j; 00097 RefineCodeLocation(); 00098 return true; 00099 } 00100 } 00101 } 00102 00103 // If the code was not found, we will use the default location 00104 codeFound = false; 00105 codeSample = CENTER_SAMPLE; 00106 codeLine = CENTER_LINE; 00107 return false; 00108 } 00109 00110 // Calculate the translation and rotation of the scan 00111 void CalculateTransform() { 00112 if (!apollo->IsMetric() || !codeFound) { 00113 sampleTranslation = 0.0; 00114 lineTranslation = 0.0; 00115 rotation = 0.0; 00116 return; 00117 } 00118 double sampleUL, lineUL, 00119 sampleUR, lineUR, 00120 sampleLL, lineLL, 00121 sampleLR, lineLR; 00122 00123 RefineCodeLocation(); 00124 sampleUL = codeSample; 00125 lineUL = codeLine; 00126 codeSample += 6*RADIUS; 00127 RefineCodeLocation(); 00128 sampleUR = codeSample; 00129 lineUR = codeLine; 00130 codeLine += 62*RADIUS; 00131 RefineCodeLocation(); 00132 sampleLR = codeSample; 00133 lineLR = codeLine; 00134 codeSample -= 6*RADIUS; 00135 RefineCodeLocation(); 00136 sampleLL = codeSample; 00137 lineLL = codeLine; 00138 00139 if (code[0][0] == 1 && code[0][31] ==1 && code[3][0] == 1 && code[3][31] == 1) { 00140 rotation = -1*atan((sampleLR-sampleUR+sampleLL-sampleUL)/(lineLR-lineUR+lineLL-lineUL)) - 0.00281891751001 - 0.000648055741779; 00141 sampleTranslation = (sampleUL+sampleUR+sampleLL+sampleLR)/4; 00142 lineTranslation = (lineUL+lineUR+lineLL+lineLR)/4; 00143 } 00144 else if (code[0][0] == 1 && code[0][31] == 1) { 00145 rotation = -1*atan((sampleLL-sampleUL)/(lineLL-lineUL)) - 0.00281891751001 - 0.000648055741779; 00146 sampleTranslation = (sampleUL + sampleLL)/2 + 3*RADIUS; 00147 lineTranslation = (lineUL + lineLL)/2; 00148 } 00149 else { 00150 rotation = -0.00281891751001 - 0.000648055741779; 00151 sampleTranslation = 600; 00152 lineTranslation = 22700; 00153 } 00154 00155 if (apollo->IsApollo17()) 00156 rotation += 0.0111002410269388; 00157 } 00158 00159 void RefineCodeLocation() { 00160 Chip chip(2*RADIUS, 2*RADIUS); 00161 chip.TackCube(codeSample, codeLine); 00162 chip.Load(cube); 00163 int bestSample = 0, 00164 bestLine = 0; 00165 double max = 0.0; 00166 for (int s = RADIUS/2+1; s < 3*RADIUS/2; s++) { 00167 for (int l = RADIUS/2+1; l < 3*RADIUS/2; l++) { 00168 double value = 0.0; 00169 // Run a quick gaussian-esch filter 00170 for (int x=RADIUS/-2; x<=RADIUS/2; x++) { 00171 for (int y=RADIUS/-2; y<=RADIUS/2; y++) { 00172 //value += chip(s+x,l+y)/Exp(x*x + y*y); 00173 if (sqrt(x*x + y*y) < RADIUS/2) 00174 value += chip.GetValue(s+x,l+y); 00175 } 00176 } 00177 if (value > max) { 00178 bestSample = s; 00179 bestLine = l; 00180 max = value; 00181 } 00182 } 00183 } 00184 00185 codeSample = codeSample - (chip.Samples()+1)/2 + bestSample; 00186 codeLine = codeLine - (chip.Lines()+1)/2 + bestLine; 00187 } 00188 00189 // translate the code once it is found 00190 void TranslateCode() { 00191 // read the code from the image 00192 Chip chip(8*RADIUS, 64*RADIUS); 00193 chip.TackCube(codeSample+3*RADIUS, codeLine+31*RADIUS); 00194 chip.Load(cube); 00195 for (int j=0; j<32; j++) { 00196 for (int i=0; i<4; i++) { 00197 Statistics stats; 00198 // Get the average of the subchip 00199 for (int x=1; x<=2*RADIUS; x++) { 00200 for (int y=1; y<=2*RADIUS; y++) { 00201 stats.AddData(chip.GetValue(i*2*RADIUS + x,j*2*RADIUS + y)); 00202 } 00203 } 00204 // see if it is on or off 00205 if (stats.Average() > 20000) 00206 code[i][31-j] = true; 00207 else code[i][31-j] = false; 00208 } 00209 } 00210 00211 for (int j=0; j<32; j++) { 00212 for (int i=0; i<4; i++) { 00213 } 00214 } 00215 } 00216 00217 // Populate cube label using filname and film code 00218 // Code decrypted as specified in film decoder document (July 23, 1971 Revision) 00219 // available at ASU Apollo Resources archive 00220 void TranslateApolloLabels (IString filename, Cube *opack) { 00221 //Instrument group 00222 PvlGroup inst("Instrument"); 00223 PvlGroup kern("Kernels"); 00224 PvlGroup codeGroup("Code"); 00225 00226 inst += PvlKeyword("SpacecraftName", apollo->SpacecraftName()); 00227 inst += PvlKeyword("InstrumentId", apollo->InstrumentId()); 00228 inst += PvlKeyword("TargetName", apollo->TargetName()); 00229 00230 if ( !IsValidCode() ){ 00231 PvlGroup error("ERROR"); 00232 error.AddComment("The decrypted code is invalid."); 00233 for (int i=0; i<4; i++) { 00234 PvlKeyword keyword("Column"+toString(i+1)); 00235 for (int j=0; j<32; j++) { 00236 keyword += toString((int)code[i][j]); 00237 } 00238 error.AddKeyword(keyword); 00239 codeGroup += keyword; 00240 } 00241 Application::Log(error); 00242 } 00243 else { 00244 codeGroup += PvlKeyword("StartTime", FrameTime()); 00245 codeGroup += PvlKeyword("SpacecraftAltitude", toString(Altitude()),"meters"); 00246 00247 if (apollo->IsMetric()){ 00248 codeGroup += PvlKeyword("ExposureDuration", toString(ShutterInterval()), "milliseconds"); 00249 codeGroup += PvlKeyword("ForwardMotionCompensation", FMC()); 00250 } 00251 00252 for (int i=0; i<4; i++) { 00253 PvlKeyword keyword("Column"+toString(i+1)); 00254 for (int j=0; j<32; j++) { 00255 keyword += toString((int)code[i][j]); 00256 } 00257 codeGroup += keyword; 00258 } 00259 } 00260 00261 PvlGroup bandBin("BandBin"); 00262 // There are no filters on the camera, so the default is clear with id # of 1 00263 // the BandBin group is only included to work with the spiceinit application 00264 bandBin += PvlKeyword("FilterName", "CLEAR"); 00265 bandBin += PvlKeyword("FilterId", "1"); 00266 00267 kern += PvlKeyword("NaifFrameCode", apollo->NaifFrameCode()); 00268 00269 // Set up the nominal reseaus group 00270 Isis::PvlGroup &dataDir = Isis::Preference::Preferences().FindGroup("DataDirectory"); 00271 Process p; 00272 PvlTranslationTable tTable( 00273 (QString)p.MissionData("base", "translations/MissionName2DataDir.trn")); 00274 QString missionDir = dataDir[tTable.Translate("MissionName", apollo->SpacecraftName())][0]; 00275 Pvl resTemplate(missionDir + "/reseaus/" + apollo->InstrumentId() + "_NOMINAL.pvl"); 00276 PvlGroup *reseaus = &resTemplate.FindGroup("Reseaus"); 00277 00278 // Update reseau locations based on refined code location 00279 for (int i=0; i<(reseaus->FindKeyword("Type")).Size(); i++) { 00280 double x = toDouble(reseaus->FindKeyword("Sample")[i]) + sampleTranslation + 2278, 00281 y = toDouble(reseaus->FindKeyword("Line")[i]) + lineTranslation - 20231; 00282 00283 if (apollo->IsApollo17()) { 00284 x += 50; 00285 y += 20; 00286 } 00287 00288 reseaus->FindKeyword("Sample")[i] = toString( 00289 cos(rotation)*(x-sampleTranslation) - sin(rotation)*(y-lineTranslation) + sampleTranslation); 00290 reseaus->FindKeyword("Line")[i] = toString( 00291 sin(rotation)*(x-sampleTranslation) + cos(rotation)*(y-lineTranslation) + lineTranslation); 00292 } 00293 inst += PvlKeyword("StartTime", utcTime); 00294 00295 opack->putGroup(inst); 00296 opack->putGroup(bandBin); 00297 opack->putGroup(kern); 00298 opack->putGroup(*reseaus); 00299 opack->putGroup(codeGroup); 00300 } 00301 00302 // Validates the code based on February 1971 Revision 00303 bool IsValidCode() { 00304 return codeFound && ( code[0][0] || !code[0][3] || !code[0][5] || code[0][10] || !code[0][11] || !code[0][12] || code[0][15] || !code[0][16] || code[0][21] || !code[0][22] || !code[0][26] || code[0][31] || 00305 code[1][0] || !code[1][6] || !code[1][11] || code[1][12] || !code[1][13] || !code[1][14] || code[1][15] || !code[1][28] || code[1][29] || !code[1][30] || code[1][31] || 00306 code[2][0] || code[2][15] || !code[2][21] || code[2][22] || !code[2][23] || !code[2][24] || !code[2][25] || !code[2][26] || !code[2][27] || !code[2][28] || !code[2][29] || !code[2][30] || code[2][31] ) 00307 && (!(apollo->IsMetric()) || (code[3][0] || !code[3][13] || !code[3][14] || code[3][15] || !code[3][16] || !code[3][17] || !code[3][18] || !code[3][19] || !code[3][20] || !code[3][21] || 00308 !code[3][22] || !code[3][23] || !code[3][24] || !code[3][25] || !code[3][26] || !code[3][27] || !code[3][28] || !code[3][30] || code[3][31])); 00309 } 00310 00311 QString FrameTime() { 00312 iTime launch = apollo->LaunchDate(); 00313 int year = launch.Year(), 00314 month = launch.Month(), 00315 days = launch.Day(), 00316 hours = launch.Hour(), 00317 minutes = launch.Minute(); 00318 double seconds = launch.Second(); 00319 for (int i=0; i<5; i++) { 00320 if (i<4) days += (int)pow(2.0, i)*code[0][9-i]; 00321 else days += 10*(int)pow(2.0, i-4)*code[0][8-i]; 00322 } 00323 00324 for (int i=0; i<6; i++) { 00325 if (i<4) hours += (int)pow(2.0, i)*code[0][20-i]; 00326 else hours += 10*(int)pow(2.0, i-4)*code[0][18-i]; 00327 } 00328 00329 for (int i=0; i<7; i++) { 00330 if (i<4) minutes += (int)pow(2.0, i)*code[0][30-i]; 00331 else minutes += 10*(int)pow(2.0, i-4)*code[0][29-i]; 00332 } 00333 00334 for (int i=0; i<7; i++) { 00335 if (i<4) seconds += (int)pow(2.0, i)*code[1][10-i]; 00336 else seconds += 10*(int)pow(2.0, i-4)*code[1][9-i]; 00337 } 00338 00339 // And the milliseconds 00340 for (int i=0; i<12; i++) { 00341 if (i<4) seconds += 0.001* pow(2.0, i)*code[1][27-i]; 00342 else if (i<8) seconds += 0.01*pow(2.0, i-4)*code[1][27-i]; 00343 else seconds += 0.1*pow(2.0, i-8)*code[1][27-i]; 00344 } 00345 00346 // Perform checks to make sure the time is correct 00347 // (i.e. convert 60 seconds to a minute, etc.) 00348 if (seconds > 60) { 00349 seconds -= 60; 00350 minutes += 1; 00351 } 00352 if (minutes >= 60) { 00353 minutes -= 60; 00354 hours += 1; 00355 } 00356 if ( hours >= 24) { 00357 hours -= 24; 00358 days +=1; 00359 } 00360 // This last check will only affect Apollo 15 which launched in July (31 days) and landed in August 00361 // Both Apollo 16 and Apollo 17 launched and landed in the same month 00362 if (days > 31){ 00363 days -= 31; 00364 month += 1; 00365 } 00366 00367 QString sTime = toString(year) + "-"; 00368 if (month < 10) sTime += "0"; 00369 sTime += toString(month)+ "-"; 00370 if (days <10) sTime += "0"; 00371 sTime += toString(days) + "T"; 00372 if (hours <10) sTime += "0"; 00373 sTime += toString(hours) + ":"; 00374 if (minutes <10) sTime += "0"; 00375 sTime += toString(minutes) + ":"; 00376 if (seconds <10) sTime += "0"; 00377 sTime += toString(seconds); 00378 00379 return sTime; 00380 } 00381 00382 int Altitude() { 00383 int altitude = 0; 00384 for (int i=0; i<18; i++) { 00385 if (i<5) altitude += (int)pow(2.0, i)*code[2][20-i]; 00386 else altitude += (int)pow(2.0, i)*code[2][19-i]; 00387 } 00388 return altitude; 00389 } 00390 00391 double ShutterInterval() { 00392 int shutterInterval = 0; 00393 for (int i=0; i<10; i++) { 00394 shutterInterval += (int)pow(2.0, i)*code[3][12-i]; 00395 } 00396 return 0.1*shutterInterval; 00397 } 00398 00399 QString FMC() { 00400 if (code[3][29]) return "True"; 00401 return "False"; 00402 }