//***************************************************************** // File name: B3Spectrum.cpp // // Classes defined: // SpeHeader -- Member class to read spectral header // MFMData -- Member class to read MFM data // FSMStepHeader -- Member class to read an FSM step header // FSMStepData -- Member class to read one step of FSM data // // Classes referenced: // none // // Purpose: Base class to read a Pioneer 10/11 spectral file in // ASCII format. A spectral file consists of four header lines // followed by a succesion of spectra. There are two types of // spectra: maximum flux mode (MFM), and full scan mode (FSM). // In MFM mode, the instrument returns one measurement for each // energy step in the direction in which flux was highest. In // FSM mode, the intrument returns a succession of measurments // over a complete spacecraft rotation for each energy step. // Each spectrum consists of two or more logical records. // There are four types of logical records: spectral headers, // MFM data records, FSM step headers, and FSM data records. // An MFM spectrum consists of a spectral header followed by // a single MFM data record that contains all of the data for // every energy step in the spectrum. An FSM spectrum consists // of a spectral header followed by successive pairs of FSM step // header and FSM data records that correspond to successive // energy steps of the spectrum. // Each logical record consists of a succesion of lines of // text terminated by a carriage returns. The longest lines // contain 28 5-digit integer words (140 characters). For // reasons of clarity, and to allow space for end-of-line // characters, most ASCII text is read and stored as // 150-element character arrays. // // General design philosophy: // 1) Code is designed to resemble JAVA. // 2) Most constructors call other functions to open, read, // and close files and perform calculations. // 3) Text files are read line by line, converted to string // streams and tokenized using the '<<' opperator. // 4) The STL String class is used to represent character // strings. Strings are passed by value to take advantage of // the translation capability of the string copy constructor. // 5) Classes, structures and arrays are passed by reference // whenever possible. // 6) Variables of member classes are left public. For // other classes, variables are protected and accessed by // access functions. // // Notes and warnings: This code compiles and runs correctly // under Borland C++ version 5.1 on a Windows 95 system with 32 // MB of memory. It should run on most other systems and // compilers, but it may be necessary to change the names of // the files on some systems. There may also be // problems with use of STRING variables under non-standard // implementations of the Standard Template Library. // To reduce memory requirements, FSM step header information // and data are stored in SHORT arrays in the B3Spectra class // rather than as arrays of fsmStepHeader and fsmStepData // objects. These arrays may be sufficiently large to prevent // this code from running properly on certain systems. See // the relevant notes in the comments for the B3Spectra class. // // Author: P. R. Gazis 17-MAY-1999 // Modified: 18-JUN-1999 //***************************************************************** // Include functions from the ordinary C/C++ libraries #include #include #include #include #include #include #include #include // Include classes from the Standard Template Library #include using namespace std; //***************************************************************** // class: SpeHeader // // Purpose: Member class to read, verify, translate, and store // one logical record that contains a header for an MFM or FSM // spectrum. This class can accept and diagnose a wide range // of syntax errors. // Each spectral header consists of a two-line record // separator, followed by a header line, followed by two lines // of spectral header information. Because the two-line record // separator is unique to spectral headers and spectral headers // only occur at the beginning of a spectrum, this two-line // record separator can also be used to identify the beginning // of a spectrum. // The first line of the record separator is a dummy line // that consists of a comment character, '%', followed by a // space and a string of '*'s. // The second line of the record separator consists of a // comment character, followed by the words 'Record Separator', // followed by a description of the type of spectrum and either // the number of lines in the spectrum if this is an MFM spectrum // or the number of energy steps in the if this is an FSM // spectrum. // The header line consists of a comment character, followed // by the spacecraft name, followed by a description of the type // of spctrum and instrument operating mode, followed by the // Earth received time (ERT) at the beginning of the spectrum. // The first line of header data consists of 12 6-digit // integer words. These contain the first 12 words (words 0-11) // of the spectral header. // The second line of header data consists of either 10 // 6-digit integer words for an MFM spectrum or 9 6-digit // integer words for an FSM spectrum. These contain the final // 9 or 10 (words 12-21 or 12-21) of the spectral header. // // Functions: // read( speFile) -- Read header record // write( speFile) -- Write header record // // Author: P. R. Gazis 17-MAY-1999 // Modified: 18-JUN-1999 //***************************************************************** class SpeHeader { public: unsigned read( ifstream &speFile); // Read header record unsigned write( ofstream &outFile); // Write output record // Because this is a member class, variables are left public int nErrors; unsigned isMFM, isDetectorB; unsigned isHighEnergyMode, isIonMode; unsigned isSuppressed; int nLines, isc; int iyr, idy, ihr, imin, isec, msec; int sMode; int iTarget, nTargets; int iStep, nSteps; int nValues; short value[ 22]; }; //***************************************************************** // SpeHeader::read( speFile) -- Searches speFile for the record // separator that indicates the beginning of a spectral header // record. Then reads the record, verifies its format, and // translates and stores the spectral header information. This // function tests for a wide range of possible syntax errors. // Succesive lines are read into character arrays using // getline so that this function can tolerate and identify syntax // errors. This function records the number of errors it finds. // It returns '1' if it reads a header successfully or '0' if it // encounters a blank line, fatal format error, or end of file. unsigned SpeHeader::read( ifstream &speFile) { // Initialize mode flags and accounting information isMFM = 1; isDetectorB = 1; isHighEnergyMode = 1; isIonMode = 1; isSuppressed = 0; nLines = 0; nErrors = 0; // Test for EOF if( speFile.eof() != 0) return 0; // Define buffers to read and translate lines of ASCII text char textLine[ 150]; string token0, token1, token2; // While loop: Read file to locate the next record separator, // verify it is in a valid format, then extract the type of // spectrum and number of lines (MFM) or energy steps (FSM) int nBefore = 0; while( speFile.eof() == 0) { if( speFile.eof() != 0) return 0; // This test is redundant strcpy( textLine, ""); speFile.getline( textLine, 150); if( strlen( textLine) <= 0) return 0; // Search for the first line of a record separator if( textLine[ 0] != '%' || textLine[ 2] != '*') { nBefore++; continue; } // Verify the existance of a second separator line if( speFile.eof() != 0) return 0; speFile.getline( textLine, 150); istrstream reply( textLine, 0); reply >> token0 >> token1 >> token2;; if( token0 != "%" || token1 != "Record" || token2 != "Separator:") { cout << "SpeHeader::read: ERROR, " << " bad second separator line\n"; nErrors++; return 0; } // Extract the type of spectrum, extract the number of lines // or energy steps, and exit the loop reply >> token0 >> token1 >> nLines; if( token0 != "MFM") isMFM = 0; break; } // Report if lines were encountered before the record separator if( nBefore > 0) { cout << "SpeHeader::read: possible error, " << nBefore << " lines before record separator\n"; nErrors++; } // Read the header line and verify the presence of a comment // character if( speFile.eof() != 0) return 0; speFile.getline( textLine, 150); if( textLine[ 0] != '%') { cout << "SpeHeader::read: ERROR, " << " the header line appears to be missing\n"; nErrors++; return 0; } // Verify that the spacecraft name is correct istrstream reply( textLine, 0); reply >> token0 >> token1 >> token2; if( token1 != "Pioneer") { cout << "SpeHeader::read: ERROR, " << "incorrect spacecraft name <" << token1 << ">\n"; nErrors++; } // Extract the spacecraft number from the header line int length = token2.find_first_not_of( "0123456789"); if( length <= 0) isc = 0; else isc = atoi( (token2.substr( 0, length)).c_str()); // Get detector A or B reply >> token0 >> token1; if( token1 != "B,") isDetectorB = 0; // Verify that the MFM or FSM word in the header line is // consistent with the MFM or FSM word in the record separator reply >> token0; if( (unsigned) ( token0 == "MFM") != isMFM) { cout << "SpeHeader::read: ERROR, " << "inconsistent MFM/FSM modes in separator and header\n"; nErrors++; } // Extract high energy and ion modes from the header line reply >> token0 >> token1 >> token2; if( token1 != "HE") isHighEnergyMode = 0; if( token2 != "ion,") isIonMode = 0; // Extract date and time from the header line reply >> iyr >> idy >> token0; ihr = 0; imin = 0; isec = 0; msec = 0; if( token0.length() < 12) { cout << "SpeHeader::read: ERROR, " << "bad time token " << token0 << "\n"; nErrors++; } else { ihr = atoi( (token0.substr( 0, 2)).c_str()); imin = atoi( (token0.substr( 3, 2)).c_str()); isec = atoi( (token0.substr( 6, 2)).c_str()); msec = atoi( (token0.substr( 9, 3)).c_str()); } // Get the number of values in the spectral header if( isMFM != 0) nValues = 22; else nValues = 21; // Read the first line of spectral header information if( speFile.eof() != 0) return 0; speFile.getline( textLine, 150); if( textLine[ 0] == '%') { cout << "SpeHeader::Read: ERROR, " << " first line of header information is missing\n"; return 0; } istrstream firstLine( textLine, 0); for( int i = 0; i < 12; i++) firstLine >> value[ i]; // Read the second line of spectral header information if( speFile.eof() != 0) return 0; if( textLine[ 0] == '%') { cout << "SpeHeader::Read: ERROR, " << " second line of header information is missing\n"; return 0; } speFile.getline( textLine, 150); istrstream secondLine( textLine, 0); for( int i = 12; i < nValues; i++) secondLine >> value[ i]; // Extract the mode information word, SMODE, word from the // header information, check it for consistency with the MFM // or FSM word in the header line, then use SMODE to set the // number of targets sMode = (int) value[ 1]; if( isDetectorB != (unsigned) ( (sMode == 1) || ( sMode == -5))) { cout << "SpeHeader::read: ERROR, " << "inconsistent detector in header\n"; nErrors++; } if( sMode == 1 || sMode == -5) { iTarget = 1; nTargets = 5; } else if( sMode == 2 || sMode == -6) { iTarget = 1; nTargets = 13; } else if( sMode == 3 || sMode == -7) { iTarget = 14; nTargets = 13; } else if( sMode == 4) { iTarget = 1; nTargets = 26; } else { cout << "MSpeHeader::read: ERROR, " << "unrecognized sMode " << sMode << "\n"; nErrors++; iTarget = 1; nTargets = 5; } // Extract suppression mode from the header information if( value[ 5] == 0) isSuppressed = 0; else isSuppressed = 1; // Extract electron/ion mode from the header information // and check it for consistency with the header line if( ( (unsigned) value[ 6]) != isIonMode) { cout << "SpeHeader::read: ERROR, " << "inconsistent electron/ion modes in header\n"; nErrors++; } // Extract energy step information from the header information // and check it for consistency with number of lines or steps // listed in the record separator nSteps = (int) value[ 9]; iStep = (int) value[ 12]; if( isMFM) { if( nLines != nSteps + 5 && !(nSteps == 0 && nLines == 4)) { cout << "SpeHeader::read: ERROR, " << "inconsistent number of lines in header\n"; nErrors++; } } // Extract the spacecraft number from the header information // and check it for consistency with the header line if( value[ 13] != isc) { cout << "SpeHeader::read: ERROR, " << "inconsistent spacecraft number in header\n"; nErrors++; } // Extract the date and time from the header information and // check it for consistency with the header line if( 1900 + value[ 14] != iyr || value[ 15] != idy || value[ 16] != ihr || value[ 17] != imin || value[ 18] != isec || value[ 19] != msec) { cout << "SpeHeader::read: ERROR, " << "inconsistent date and time in header\n"; nErrors++; } // Report success return 1; } //***************************************************************** // SpeHeader::write( outFile) -- Write a two-line record separator, // a header line, and the associated header information to an // to an output file unsigned SpeHeader::write( ofstream &outFile) { // Write the first line of the record separator outFile << "% ************************************************" << "**********************\n"; // Generate and write the second line of the record separator string modeToken = "FSM"; if( isMFM) modeToken = "MFM"; outFile << "% Record Separator: " << modeToken << " mode," << setw( 5) << nLines << " steps *******************************\n"; // Write the header line string detectorToken = "A"; if( isDetectorB != 0) detectorToken = "B"; string energyModeToken = "LE"; if( isHighEnergyMode != 0) energyModeToken = "HE"; string ionModeToken = " e-"; if( isIonMode != 0) ionModeToken = "ion"; outFile << "% Pioneer " << setw( 2) << isc << ", Detector " << detectorToken << ", " << modeToken << " mode, " << energyModeToken << " " << ionModeToken << ", " << setw( 4) << iyr << " " << setw( 3) << idy << " " << setfill( '0') << setw( 2) << ihr << ":" << setfill( '0') << setw( 2) << imin << ":" << setfill( '0') << setw( 2) << isec << "." << setfill( '0') << setw( 3) << msec << " ******\n"; outFile << setfill( ' '); // Get the number of words of header information if( isMFM != 0) nValues = 22; else nValues = 21; // Write the first line of header information for( int i = 0; i < 12; i++) outFile << setw( 6) << value[ i]; outFile << "\n"; // Write the second line of header information for( int i = 12; i < nValues; i++) outFile << setw( 6) << value[ i]; outFile << "\n"; return 1; } //***************************************************************** // class: MFMData // // Purpose: Member class to read and store one logical record // that contains the data for an MFM spectrum. This class // performs some error checking. // Each MFM record consists of one header line followed by // several lines of data in multi-column format. The header // line consists of a comment character, '%' followed by words // that describe the contents of each column of data. Each line // of data consists of a succesion of 5-digit integer words that // contain the energy step number, followed by a sector number, // followed by the counts or currents measured by the relevant // detector (Detector A or B). // // Functions: // read( speFile, speHeader) -- Read MFM data record // write( outFile, speHeader) -- Write MFM data record // // Author: P. R. Gazis 17-MAY-1999 // Modified: 18-JUN-1999 //***************************************************************** class MFMData { public: unsigned read( ifstream& speFile, // Read an MFM data record SpeHeader& speHeader); unsigned write( ofstream& outFile, // Write an MFM data record SpeHeader& speHeader); // Because this is a member class, variables are left public int nErrors; unsigned hasBadData; int iStep, nSteps; int iTarget, nTargets; short eStep[ 64], sector[ 64], counts[ 64][ 26]; }; //***************************************************************** // MFMData::read( speFile, speHeader) -- Reads a logical record // containing MFM data that corresponds to 'speHeader'. This // function does not search for the beginning of the record, but // it does perform some tests to make sure that the file is // correctly positioned and the record is in the correct format. // This function records the number of errors it finds in the // record. It returns '1' if it reads the record successfully or // '0' if it encounters a blank line, fatal format error, or end // of file. unsigned MFMData::read( ifstream& speFile, SpeHeader& speHeader) { // Extract step and target information from speHeader iStep = speHeader.iStep; nSteps = speHeader.nSteps; iTarget = speHeader.iTarget; nTargets = speHeader.nTargets; nErrors = 0; hasBadData = 0; // Test for EOF if( speFile.eof() != 0) return 0; // Define buffers to lines of ASCII text char textLine[ 150]; string token0, token1; // Read and verify the header line of an MFM data record speFile.getline( textLine, 150); if( strlen( textLine) <= 0) return 0; istrstream reply( textLine, 0); reply >> token0 >> token1; if( token1 != "EN") { cout << "MFMData::read: ERROR, " << "bad header line -- token1 '" << token1 << "' != 'EN'\n"; nErrors++; return 0; } // Loop: read successive lines of an MFM data record for( int i = 0; i < nSteps; i++) { speFile.getline( textLine, 150); if( textLine[ 0] == '%') { cout << "MFMData::read: ERROR, " << "data record terminated early\n"; nErrors++; return 0; } istrstream dataList( textLine, 0); dataList >> eStep[ i] >> sector[ i]; for( int j = 0; j < nTargets; j++) { dataList >> counts[ i][ j]; if( counts[ i][ j] >= 512) hasBadData = 1; } } // Report success return 1; } //***************************************************************** // MFMData::write( speFile, speHeader) -- Writes an MFM data record // that corresponds to 'speHeader'. unsigned MFMData::write( ofstream& outFile, SpeHeader& speHeader) { // Write the separator for an MFM data record if( speHeader.nTargets == 5) { outFile << "% EN SN" << " t1 t2 t3 t4 t5 *********" << "***************************\n"; } else if( speHeader.nTargets == 13) { if( speHeader.iTarget == 1) { outFile << "% EN SN" << " c1 c2 c3 c4 c5 c6 c7" << " c8 c9 c10 c11 c12 c13\n"; } else{ outFile << "% EN SN" << " c14 c15 c16 c17 c18 c19 c20" << " c21 c22 c23 c24 c25 c26\n"; } } else{ outFile << "% EN SN" << " c1 c2 c3 c4 c5 c6 c7" << " c8 c9 c10 c11 c12 c13" << " c14 c15 c16 c17 c18 c19 c20" << " c21 c22 c23 c24 c25 c26\n"; } // Loop: Write the body of an MFM data record for( int i = 0; i < speHeader.nSteps; i++) { outFile << setw( 5) << eStep[ i] << setw( 5) << sector[ i]; for( int j = 0; j < speHeader.nTargets; j++) outFile << setw( 5) << counts[ i][ j]; outFile << "\n"; } return 1; } //***************************************************************** // class: FSMStepHeader // // Purpose: Member class to read, verify, translate, and store a // logical record that contains the header for one energy step // of an FSM spectrum. This class can accept and diagnose a // wide range of syntax errors. // Each FSM step header consists of one header line followed // by two lines of header information. The header line consists // of a comment character, '%', followed a three word description // unique to FSM step headers, followed by the total number of // lines in this FSM step header and the associated FSM data // record that follows it. The two lines of header information // contain 12 and 10 6-digit integer words respectively which // contain the 22 word FSM step header. // // Functions: // read( speFile, speHeader) -- Read FSM step header record // write( outFile) -- Write FSM step header record // // Author: P. R. Gazis 17-MAY-1999 // Modified: 18-JUN-1999 //***************************************************************** class FSMStepHeader { public: unsigned read( ifstream& speFile, // Read FSM step header SpeHeader& speHeader); unsigned write( ofstream& outFile); // Write FSM step header // Because this is a member class, variables are left public int nErrors; unsigned isMFM, isDetectorB; unsigned isHighEnergyMode, isIonMode; int nLines; int iyr, idy, ihr, imin, isec, msec; int sMode; int iTarget, nTargets; int iSlice, nSlices; short value[ 22]; }; //***************************************************************** // FSMStepHeader::read( speFile, speHeader) -- Reads one FSM step // header associated with the spectral header 'speHeader'. This // function searches speFile for the next header line of an FSM // step header and performs extensive tests to make sure this FSM // step header is in the correct format and consistent with the // spectral header. // Successive lines are read into character arrays using getline // so that this function can tolerate and identify syntax errors. // This function records the number of errors it finds in the FSM // step header. It returns '1' if it reads a header successfully // or '0' if it encounters a blank line, fatal format error, or // end of file. unsigned FSMStepHeader::read( ifstream& speFile, SpeHeader &speHeader) { // Initialize mode flags and accounting information isMFM = 1; isDetectorB = speHeader.isDetectorB; isHighEnergyMode = speHeader.isHighEnergyMode; isIonMode = speHeader.isIonMode; nLines = 0; nErrors = 0; // Test for EOF if( speFile.eof() != 0) return 0; // Define buffers to read lines of ASCII text char textLine[ 150]; string token0, token1, token2; // Location for (optional) code to read file to locate the // next header line of an FSM step header // Read file to locate the next header line for an FSM step // header and verify that it is in a valid format // Read file to locate the next FSM step header, verify that // it is in a valid format, then the number of lines int nBefore = 0; while( speFile.eof() == 0) { if( speFile.eof() != 0) return 0; // This test is redundant speFile.getline( textLine, 150); if( strlen( textLine) <= 0) return 0; istrstream reply( textLine, 0); // Search for the header line of an FSM step header reply >> token0 >> token1 >> token2;; if( token0 != "%" || token1 != "FSM" || token2 != "step") { nBefore++; continue; } // Extract the number of lines and exit the loop reply >> token0 >> nLines; break; } // Report if lines were encountered before the FSM step header if( nBefore > 0) { cout << "FSMStepHeader::read: ERROR, " << nBefore << " lines before FSM step header\n"; nErrors++; } // Read the first line of FSM step header information if( speFile.eof() != 0) return 0; speFile.getline( textLine, 150); if( textLine[ 0] == '%') { cout << "FSMStepHeader::read: ERROR, " << "first line of FSM step header is missing\n"; return 0; } istrstream firstLine( textLine, 0); for( int i = 0; i < 12; i++) firstLine >> value[ i]; // Read the second line of FSM step header information if( speFile.eof() != 0) return 0; speFile.getline( textLine, 150); if( textLine[ 0] == '%') { cout << "FSMStepHeader::read: ERROR, " << "second line of FSM step header is missing\n"; return 0; } istrstream secondLine( textLine, 0); for( int i = 12; i < 22; i++) secondLine >> value[ i]; // Extract SMODE from the FSM step header information and // check for consistency with speHeader sMode = (int) value[ 1]; if( value[ 1] != -speHeader.value[ 1]) { cout << "FSMStepHeader::read: ERROR, sMode " << sMode << "inconsistent with speHeader\n"; nErrors++; } // Evaluate SMODE to determine detector A or B, check for // consistency with speHeader, and set the number of targets if( isDetectorB != (unsigned) (sMode == 5)) { cout << "FSMStepHeader::read: ERROR, " << "inconsistent detector in header\n"; nErrors++; } if( sMode == 5) { iTarget = 1; nTargets = 5; } else if( sMode == 6) { iTarget = 1; nTargets = 13; } else if( sMode == 7) { iTarget = 14; nTargets = 13; } else { cout << "FSMStepHeader::read, ERROR, " << "unrecognized sMode " << sMode << "\n"; iTarget = 1; nTargets = 5; } // Extract number of 'slices' (sectors) from the FSM header // information and verify consistency with the number of // lines listed in the header line nSlices = (int) value[ 9]; iSlice = (int) value[ 12]; if( isMFM) { if( nLines != nSlices + 4) { cout << "FSMStepHeader::read: ERROR, " << "nSlices inconsistent with nLines in header\n"; nErrors++; } } // Extract date and time information from the FSM step // header information iyr = 1900 + (int) value[ 14]; idy = (int) value[ 15]; ihr = (int) value[ 16]; imin = (int) value[ 17]; isec = (int) value[ 18]; msec = (int) value[ 19]; // Verify that the relevant mode and engineering information // in the FSM step header is consistent with speHeader unsigned isConsistent = 1; for( int i = 3; i < 14; i++) { if( i == 9 || i == 12) continue; if( value[ i] != speHeader.value[ i]) { cout << "FSMStepHeader::read: ERROR, value[ " << i << "] inconsistent with speHeader " << value[ i] << " " << speHeader.value[ i] << "\n"; isConsistent = 0; } } if( isConsistent == 0) nErrors++; // Report success return 1; } //***************************************************************** // FSMStepHeader::write( speFile, speHeader) -- Write an FSM step // header unsigned FSMStepHeader::write( ofstream& outFile) { // Generate and write the header line for this FSM energy step int nLines = 4 + nSlices; outFile << "% FSM step header:" << setw( 5) << nLines << " lines ******************************" << "************\n"; // Write the first line of FSM step header information for( int i = 0; i < 12; i++) outFile << setw( 6) << value[ i]; outFile << "\n"; // Write the second line of FSM step header information for( int i = 12; i < 22; i++) outFile << setw( 6) << value[ i]; outFile << "\n"; return 1; } //***************************************************************** // class: FSMStepData // // Purpose: Member class to read and store one logical record // that contains the data for one FSM energy step. This class // performs some error checking. // Each FSM step record consists of one header line followed // followed by several lines of data in multi-column format. // The header line separator consists of a comment character, // '%' followed by words that describe the contents of each // column of data. Each line of data consists of an energy step // number (note that because this record only contains data from // a single FSM energy step, all of the step numbers will be the // same!) and a sector number followed by the counts or currents // measured by the relevant detector (A or B). // // Functions: // read( speFile, fsmStepHeader) -- Read FSM data record // write( outFile, fsmStepHeader) -- Write FSM data record // // Author: P. R. Gazis 17-MAY-1999 // Modified: 18-JUN-1999 //***************************************************************** class FSMStepData { public: unsigned read( ifstream& speFile, // Read FSM data record FSMStepHeader& fsmStepHeader); unsigned write( ofstream& outFile, // Write FSM data record FSMStepHeader& fsmStepHeader); // Because this is a member class, variables are left public int nErrors; unsigned hasBadData; int iSlice, nSlices; int iTarget, nTargets; short eStep[ 512], sector[ 512], counts[ 512][ 13]; }; //***************************************************************** // FSMStepData::read( speFile, fsmStepHeader) -- Reads a logical // record containing one FSM energy step that corresponds to // 'fsmStepHeader'. This function does not search for the // beginning of the record, but it does perform some tests to // verify that the file is correctly positioned and the record is // in the correct format. // This function records the number of errors it finds in the // record. It returns '1' if it reads the record successfully or // '0' if it encounters a blank line, fatal format error, or end // of file. unsigned FSMStepData::read( ifstream& speFile, FSMStepHeader& fsmStepHeader) { // Get slice and target information from fsmStepHeader iSlice = fsmStepHeader.iSlice; nSlices = fsmStepHeader.nSlices; iTarget = fsmStepHeader.iTarget; nTargets = fsmStepHeader.nTargets; nErrors = 0; hasBadData = 0; // Test for EOF if( speFile.eof() != 0) return 0; // Define buffers to read lines of ASCII text char textLine[ 150]; string token0, token1; // Read and verify the header line of an FSM data record speFile.getline( textLine, 120); if( strlen( textLine) <= 0) return 0; istrstream reply( textLine, 0); reply >> token0 >> token1; if( token1 != "EN") { cout << "FSMStepData::read: ERROR, " << token1 << " is not 'EN'\n"; nErrors++; return 0; } // Loop: read successive lines of an FSM data record for( int i = 0; i < nSlices; i++) { speFile.getline( textLine, 120); if( textLine[ 0] == '%') { cout << "FSMData::read: ERROR, " << "data record terminated early\n"; nErrors++; return 0; } istrstream dataList( textLine, 0); dataList >> eStep[ i] >> sector[ i]; for( int j = 0; j < nTargets; j++) { dataList >> counts[ i][ j]; if( counts[ i][ j] >= 512) hasBadData = 1; } } // Report success return 1; } //***************************************************************** // MFMData::write( speFile, speHeader) -- Writes an FSM data record // that corresponds to 'fsmStepHeader' unsigned FSMStepData::write( ofstream& outFile, FSMStepHeader& fsmStepHeader) { // Write the header line of an FSM data record if( fsmStepHeader.nTargets == 5) { outFile << "% EN SN" << " t1 t2 t3 t4 t5 *********" << "***************************\n"; } else { if( fsmStepHeader.iTarget == 1) { outFile << "% EN SN" << " c1 c2 c3 c4 c5 c6 c7" << " c8 c9 c10 c11 c12 c13\n"; } else{ outFile << "% EN SN" << " c14 c15 c16 c17 c18 c19 c20" << " c21 c22 c23 c24 c25 c26\n"; } } // Loop: Write the body of an FSM data record for( int i = 0; i < fsmStepHeader.nSlices; i++) { outFile << setw( 5) << eStep[ i] << setw( 5) << sector[ i]; for( int j = 0; j < fsmStepHeader.nTargets; j++) outFile << setw( 5) << counts[ i][ j]; outFile << "\n"; } return 1; } //***************************************************************** // class: B3Spectrum // // Purpose: Base class to read and hold one spectrum from a // Pioneer 10/11 spectral file in ASCII format. Each spectrum // consists of a spectral header followed by either an MFM data // record or a succesion of FSM step header and data records // as described above. // This base class instantiates member classes for the four // relevant types of logical record (spectral header, MFM data // record, FSM step header, and FSM step data record). Spectral // headers and MFM data are read and stored as member classes. // FSM step header information and data are handled differently, // as decsribed below. // WARNING! The memory requirements associated with FSM data // can be extremely large (an FSM spectrum can contain up to // 64 * 512 * 13 values). To reduce these memory requirements // to a minimum, the FSM step header information and data are // read using the relevant member classes and stored in several // SHORT integer arrays. If this code does not run properly and // the problem appears to be related to memory requirements, it // is probably safe to reduce the largest array dimensions from // 512 to 384 as long as provisions are made to ensure that the // relevant indices remain within bounds. // // Functions: // B3Spectrum( speFileSpec) -- constructor // ~B3Spectrum() -- destructor // B3Spectrum( B3Spectrum&) -- protected copy constructor // // openSpeFile() -- Open and rewind input file // isSpeFileOpen() -- Is the spectral file open? // loadFSMStepHeader( jStep) -- Load FSM step header // loadFSMStepData( jStep) -- Load FSM step data // read() -- Read next spectrum // eof() -- Report end of file? // // openOutFile( string outFileSpec) -- Open output file // isOutFileOpen() -- Is output file open? // closeOutFile() -- Close output file // // xxxxx() -- Various access functions // // Author: P. R. Gazis 17-MAY-1999 // Modified: 18-JUN-1999 //***************************************************************** class B3Spectrum { public: B3Spectrum( string speFileSpec); // Constructor ~B3Spectrum() // Destructor { speFile.close(); closeOutFile(); } void openSpeFile(); // Open the input file unsigned isSpeFileOpen() // Is speFile open? { return speFileOpenFlag;} void loadFSMStepHeader( int jStep); // Load step header void loadFSMStepData( int jStep); // Load step data void read(); // Read the next spectrum int eof(); // Report end of file void openOutFile( string outFileSpec); // Open output file unsigned isOutFileOpen() // Is output file open? { return outFileOpenFlag;} void closeOutFile() // Close output file { if( outFileOpenFlag != 0) outFile.close();} // Access functions for mode and header information unsigned isMFM() { return speHeader.isMFM;} unsigned isHighEnergyMode() { return speHeader.isHighEnergyMode;} unsigned isIonMode() { return speHeader.isIonMode;} unsigned isSuppressed() { return speHeader.isSuppressed;} int iyrHeader() { return speHeader.iyr;} int idyHeader() { return speHeader.idy;} int ihrHeader() { return speHeader.ihr;} int iminHeader() { return speHeader.imin;} int isecHeader() { return speHeader.isec;} int msecHeader() { return speHeader.msec;} int todHeader() { return ( (float) ( speHeader.msec + 1000 * ( speHeader.isec + 60 * ( speHeader.imin + 60 * speHeader.ihr)))) / 3.6e+06; } int iStep() { return speHeader.iStep;} int nSteps() { return speHeader.nSteps;} int iTarget() { return speHeader.iTarget;} int nTargets() { return speHeader.nTargets;} // Access functions for MFM spectra int mfmStep( int jStep); int mfmSector( int jStep); int mfmCounts( int jStep, int jTarget); // Access functions for FSM spectra int fsmStep( int jStep); int iSlice( int jStep); int nSlices( int jStep); int fsmSector( int jStep, int jSlice); int fsmCounts( int jStep, int jSlice, int jTarget); // Access functions for accounting information int nRecs() { return iRec;} int nSpectra() { return iSpectrum;} int nSpeHeaders() { return iSpeHeader;} int nBadSpeHeaders() { return iBadSpeHeader;} int nMFMRecords() { return iMFMRecord;} int nBadMFMRecords() { return iBadMFMRecord;} int nBadMFMValuesA() { return iBadMFMValuesA;} int nBadMFMValuesB() { return iBadMFMValuesB;} int nBadMFMValues() { return iBadMFMValuesA + iBadMFMValuesB; } int nFSMStepHeaders() { return iFSMStepHeader;} int nBadFSMStepHeaders() { return iBadFSMStepHeader;} int nFSMStepRecords() { return iFSMStepRecord;} int nBadFSMStepRecords() { return iBadFSMStepRecord;} int nBadFSMStepValuesA() { return iBadFSMStepValuesA;} int nBadFSMStepValuesB() { return iBadFSMStepValuesB;} int nBadFSMStepValues() { return iBadFSMStepValuesA + iBadFSMStepValuesB; } protected: B3Spectrum( B3Spectrum&); // Protect the copy constructor // I/O files and status flags ifstream speFile; ofstream outFile; int speFileOpenFlag, eofFlag; int outFileOpenFlag; // Member classes to access spectrum SpeHeader speHeader; MFMData mfmData; FSMStepHeader fsmStepHeader; FSMStepData fsmStepData; // Arrays to hold FSM step headers and data short fsmStepHeaderValue[ 64][ 22]; short eStep[ 64]; short sector[ 64][ 512]; short counts[ 64][ 512][ 13]; // Strings to hold file header information string headerLine0, headerLine1, headerLine2; // Accounting information int iRec, iSpectrum; int iSpeHeader, iBadSpeHeader; int iMFMRecord, iBadMFMRecord; int iBadMFMValuesA, iBadMFMValuesB; int iFSMStepHeader, iBadFSMStepHeader; int iFSMStepRecord, iBadFSMStepRecord; int iBadFSMStepValuesA, iBadFSMStepValuesB; }; //***************************************************************** // B3Spectrum::B3Spectrum( speFileSpec) -- Constructor. Initialize // member classes, open the spectral file, initialize accounting // information, rewind the spectral file, athen read and discard // four header lines. B3Spectrum::B3Spectrum( string speFileSpec) : speFileOpenFlag( 0), eofFlag( 0), outFileOpenFlag( 0), iRec( 0), iSpectrum( 0), iSpeHeader( 0), iBadSpeHeader( 0), iMFMRecord( 0), iBadMFMRecord( 0), iBadMFMValuesA( 0), iBadMFMValuesB( 0), iFSMStepHeader( 0), iBadFSMStepHeader( 0), iFSMStepRecord( 0), iBadFSMStepRecord( 0), iBadFSMStepValuesA( 0), iBadFSMStepValuesB( 0) { // Open speFile speFile.open( speFileSpec.c_str(), ios::nocreate|ios::in); if( speFile.bad()) { cout << "B3Spectrum::B3Spectrum: couldn't open " << speFileSpec << "\n"; speFileOpenFlag = 0; } else { cout << "B3Spectrum::B3Spectrum: opening " << speFileSpec << "\n"; speFileOpenFlag = 1; } // Rewind speFile (this is an unnecessary step that was // included for development purposes), speFile.seekg( 0, ios::beg); // Save the first 3 header lines of the file and discard // the 4th header line char textLine[ 150]; speFile.getline( textLine, 150); headerLine0 = textLine; speFile.getline( textLine, 150); headerLine1 = textLine; speFile.getline( textLine, 150); headerLine2 = textLine; speFile.getline( textLine, 150); // Test for end of file if( speFile.eof() != 0) { eofFlag = 1; cout << "B3Spectrum::B3Spectrum: possible error, " << "this seems to be an awfully short file!\n"; } } //***************************************************************** // B3Spectrum::loadFSMStepHeader( jStep) -- Load values for this // FSM step header into the fsmStepHeaderValue array. void B3Spectrum::loadFSMStepHeader( int jStep) { if( jStep < 0) jStep = 0; if( jStep >= 64) jStep = 63; for( int i = 0; i < 22; i++) fsmStepHeaderValue[ jStep][ i] = fsmStepHeader.value[ i]; } //***************************************************************** // B3Spectrum::loadFSMStepData( jStep) -- Load values for this // FSM step data record into the eStep, sector, and counts arrays. void B3Spectrum::loadFSMStepData( int jStep) { if( jStep < 0) jStep = 0; if( jStep >= 64) jStep = 63; eStep[ jStep] = fsmStepData.eStep[ 0]; int nSlices = fsmStepHeader.nSlices; if( nSlices <= 0) nSlices = 1; if( nSlices >= 512) nSlices = 512; for( int i = 0; i < nSlices; i++) { sector[ jStep][ i] = fsmStepData.sector[ i]; int nTargets = fsmStepHeader.nTargets; if( nTargets <= 0) nTargets = 1; if( nTargets >= 13) nTargets = 13; for( int j = 0; j < nTargets; j++) { counts[ jStep][ i][ j] = fsmStepData.counts[ i][ j]; } } } //***************************************************************** // B3Spectrum::read() -- Read the next spectrum and increment // the accounting information. Invoke speHeader.read() to read the // spectral header and identify the mode of this spectrum. Then // invoke the read functions of the relevant member classes to // read the body of the spectrum. void B3Spectrum::read() { // Don't try to read file if it isn't open if( speFileOpenFlag == 0) return; // Read the spectral header if( speHeader.read( speFile) == 0) return; iRec++; iSpectrum++; iSpeHeader++; if( speHeader.nErrors > 0) { cout << "B3Spectrum::read: Error in spectral header at " << speHeader.iyr << " " << speHeader.idy << " " << setw( 2) << setfill( '0') << speHeader.ihr << ":" << setw( 2) << setfill( '0') << speHeader.imin << ":" << setw( 2) << setfill( '0') << speHeader.isec << "." << setw( 3) << setfill( '0') << speHeader.msec << "\n"; cout << setfill ( ' '); iBadSpeHeader++; } // Read MFM or FSM spectrum if( speHeader.isMFM != 0) { if( speHeader.nSteps > 0) { mfmData.read( speFile, speHeader); iRec++; iMFMRecord++; if( mfmData.nErrors > 0) { cout << "B3Spectrum::read: Error in MFM record at " << speHeader.iyr << " " << speHeader.idy << " " << setw( 2) << setfill( '0') << speHeader.ihr << ":" << setw( 2) << setfill( '0') << speHeader.imin << ":" << setw( 2) << setfill( '0') << speHeader.isec << "." << setw( 3) << setfill( '0') << speHeader.msec << "\n"; cout << setfill ( ' '); iBadMFMRecord++; } if( mfmData.hasBadData != 0) { if( speHeader.isDetectorB == 0) iBadMFMValuesA++; else iBadMFMValuesB++; } } } else { int nSteps = speHeader.nSteps; // Loop: Read successive FSM energy steps for( int i = 0; i < nSteps; i++) { fsmStepHeader.read( speFile, speHeader); loadFSMStepHeader( i); iRec++; iFSMStepHeader++; if( fsmStepHeader.nErrors > 0) { cout << "B3Spectrum::read: Error in FSM step header at " << fsmStepHeader.iyr << " " << fsmStepHeader.idy << " " << setw( 2) << setfill( '0') << fsmStepHeader.ihr << ":" << setw( 2) << setfill( '0') << fsmStepHeader.imin << ":" << setw( 2) << setfill( '0') << fsmStepHeader.isec << "." << setw( 3) << setfill( '0') << fsmStepHeader.msec << "\n"; cout << setfill ( ' '); iBadFSMStepHeader++; } fsmStepData.read( speFile, fsmStepHeader); loadFSMStepData( i); iRec++; iFSMStepRecord++; if( fsmStepData.nErrors > 0) { cout << "B3Spectrum::read: Error in FSM step record at " << fsmStepHeader.iyr << " " << fsmStepHeader.idy << " " << setw( 2) << setfill( '0') << fsmStepHeader.ihr << ":" << setw( 2) << setfill( '0') << fsmStepHeader.imin << ":" << setw( 2) << setfill( '0') << fsmStepHeader.isec << "." << setw( 3) << setfill( '0') << fsmStepHeader.msec << "\n"; cout << setfill ( ' '); iBadFSMStepRecord++; if( fsmStepData.hasBadData != 0) { if( speHeader.isDetectorB == 0) iBadFSMStepValuesA++; else iBadFSMStepValuesB++; } } } } // Check for end of file and set end of file flag if( speFile.eof() != 0) eofFlag = 1; } //***************************************************************** // B3Spectrum::eof() -- Check for end of the input file int B3Spectrum::eof() { if( eofFlag != 0) return 1; if( speFile.eof() != 0) return 1; return 0; } //***************************************************************** // B3Spectrum::openOutFile( outFileSpec) -- Open output file and // write the file header void B3Spectrum::openOutFile( string outFileSpec) { // Open outFileSpec or report error outFile.open( outFileSpec.c_str(), ios::out); if( outFile.bad()) { cout << "B3Spectrum::openOutFile(): couldn't open " << outFileSpec << "\n"; outFileOpenFlag = 0; return; } // Set open flag and write header outFileOpenFlag = 1; outFile << "% File name:\n"; outFile << "% Version:\n"; outFile << "% Documentation in:\n"; outFile << "%\n"; } //***************************************************************** // B3Spectrum::mfmStep( jStep) -- Get the energy step number for // step 'jStep' of an MFM spectrum int B3Spectrum::mfmStep( int jStep) { if( speHeader.isMFM == 0) return 0; if( jStep < 0) jStep = 0; if( jStep >= speHeader.nSteps) jStep = speHeader.nSteps - 1; return mfmData.eStep[ jStep]; } //***************************************************************** // B3Spectrum::mfmSector( jStep) -- Get the sector for step 'jStep' // of an MFM spectrum int B3Spectrum::mfmSector( int jStep) { if( speHeader.isMFM == 0) return 0; if( jStep < 0) jStep = 0; if( jStep >= speHeader.nSteps) jStep = speHeader.nSteps - 1; return mfmData.sector[ jStep]; } //***************************************************************** // B3Spectrum::mfmCounts( jStep, jTarget) -- Get the counts or // current for step 'jStep' and target or channeltron 'jTarget' of // an MFM spectrum int B3Spectrum::mfmCounts( int jStep, int jTarget) { if( speHeader.isMFM == 0) return 0; if( jStep < 0) jStep = 0; if( jStep >= speHeader.nSteps) jStep = speHeader.nSteps - 1; if( jTarget < 0) jTarget = 0; if( jTarget >= speHeader.nTargets) jTarget = speHeader.nTargets - 1; return mfmData.counts[ jStep][ jTarget]; } //***************************************************************** // B3Spectrum::fsmStep( jStep) -- Get the energy step number for // step 'jStep' of an FSM spectrum int B3Spectrum::fsmStep( int jStep) { if( speHeader.isMFM == 1) return 0; if( jStep < 0) jStep = 0; if( jStep >= speHeader.nSteps) jStep = speHeader.nSteps - 1; return (int) eStep[ jStep]; } //***************************************************************** // B3Spectrum::iSlice( jStep) -- Get the number of the first slice // for step 'jStep' of an FSM spectrum from value[ 12] of the // FSM step header. int B3Spectrum::iSlice( int jStep) { if( speHeader.isMFM == 1) return 0; if( jStep < 0) jStep = 0; if( jStep >= speHeader.nSteps) jStep = speHeader.nSteps - 1; return (int) fsmStepHeaderValue[ jStep][ 12]; } //***************************************************************** // B3Spectrum::nSlices( jStep) -- Get the number of slices for // step 'jStep' of an FSM spectrum from value[ 9] of the FSM // step header. int B3Spectrum::nSlices( int jStep) { if( speHeader.isMFM == 1) return 0; if( jStep < 0) jStep = 0; if( jStep >= speHeader.nSteps) jStep = speHeader.nSteps - 1; return (int) fsmStepHeaderValue[ jStep][ 9]; } //***************************************************************** // B3Spectrum::fsmSector( jStep, jSlice) -- Get the sector for // step 'jStep' and slice 'jSlice' of an FSM spectrum int B3Spectrum::fsmSector( int jStep, int jSlice) { if( speHeader.isMFM == 1) return 0; if( jStep < 0) jStep = 0; if( jStep >= speHeader.nSteps) jStep = speHeader.nSteps - 1; if( jSlice < 0) jSlice = 0; if( jSlice >= 512) jSlice = 511; return (int) sector[ jSlice]; } //***************************************************************** // B3Spectrum::fsmCounts( jStep, jSlice, jTarget) -- Get the // counts or current for step 'jStep', slice 'jSlice', and target // or channeltron 'jTarget' of an FSM spectrum int B3Spectrum::fsmCounts( int jStep, int jSlice, int jTarget) { if( speHeader.isMFM == 1) return 0; if( jStep < 0) jStep = 0; if( jStep >= speHeader.nSteps) jStep = speHeader.nSteps - 1; if( jSlice < 0) jSlice = 0; if( jSlice >= 512) jSlice = 511; if( jTarget < 0) jTarget = 0; if( jTarget >= 13) jTarget = 12; return (int) counts[ jSlice][ jTarget]; }