// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I // The various magic numbers used to extract portions of the record // header are documented in publication 618-306. In this particular // case they have been kept as magic numbers instead of declared as // constants, since they have no obvious meaningful names. #include #include #include #include #include int edr_file::record_size[N_SC_MODES]; // ************************ edr_record ***************************** // constructor edr_record::edr_record(void) { } // copy constructor edr_record::edr_record(const edr_record& e) : data_record() { _record_length = e._record_length; heap_check(_record = new byte [_record_length]); for (int n = 0; n < _record_length; n++) _record[n] = e._record[n]; } // constructor with bdr_record (see note in edr.h) edr_record::edr_record(const bdr_record& bdr) : data_record() { *this = bdr; } void edr_record::operator=(const bdr_record& bdr) { int old_record_length = _record_length; // first, we make it the standard length _record_length = 1872; if (old_record_length != _record_length) { destroy_array(_record); heap_check(_record = new byte [_record_length]); } // put the right stuff in the parts of the header that we use (this is // the really messy part) _record[3] = 0x70; // mark as science _record[3] = _record[3] | (byte)(2 - bdr.sc()); _record[30] = (byte)bdr.year(); // what is the hour of year? int16 hr = 24 * bdr.day() + bdr.hour(); // we have to be careful placing values > 8 bits because of the different // architectures on which MIDAS runs char lb, ub; lb = hr & 0xff; ub = (hr >> 8) & 0xff; _record[24] = ub; _record[25] = lb; // what is second of hour? int16 sec = 60 * bdr.minute() + bdr.second(); lb = sec & 0xff; ub = (sec >> 8) & 0xff; _record[26] = ub; _record[27] = lb; // we invent a new "mode", called `browse'. _record[6] = browse; // now do the data. const int bias = 272; // clear the record for (int n = bias; n < 1872; n++) _record[n] = 0; // place the data in the record for (int s = 1; s <= 2; s++) { for (n = 1; n < 200; n++) { if (n >= bdr.min_valid_channel_nr() && n <= bdr.max_valid_channel_nr()) _record[n + (bias - 1) + 200 * (s - 1)] = (byte)(bdr.datum(s, n) * 255.0 / 8000); } } // build a reasonable status int16 status = 0; // pra submode == pollo status = status | (0x0 << 11); // write status into the edr lb = status & 0xff; ub = (status >> 8) & 0xff; _record[bias] = ub; _record[bias + 1] = lb; // build a non-zero mode command int16 mode_command = 1; // write mode command into the edr lb = mode_command & 0xff; ub = (mode_command >> 8) & 0xff; _record[240] = ub; _record[241] = lb; // build a non-zero configuration command int16 configuration_command = 1; // write mode command into the edr lb = configuration_command & 0xff; ub = (configuration_command >> 8) & 0xff; _record[242] = ub; _record[243] = lb; } void edr_record::operator=(const edr_record& edr) { destroy_array(_record); _record_length = edr._record_length; heap_check(_record = new byte [_record_length]); for (int n = 0; n < _record_length; n++) _record[n] = edr._record[n]; } enum edr_record_type edr_record::record_type(void) const { switch (_record[3] >> 4) { case 0x0b : return engineering; case 0x0f : return decom; case 0x07 : return science; default : return unknown; } } char* edr_record::record_type_name(void) const { switch (record_type()) { case engineering : return "ENGINEERING"; case decom : return "DECOM"; case science : return "SCIENCE"; case unknown : default : return "UNKNOWN"; } } // edr_record = edr_file void edr_record::operator=(edr_file& datafile) { datafile.read(*this); } // write(edr_file) int edr_record::write(edr_file& outfile) { return outfile._write(_record); } // through which physical channel did this signal pass? (messy...) // this is a codified form of the notes of R G Peltzer dated merely // "post launch". int science_edr_record::channel(const int sweep, const int channel) const { // do VIM modes first if (vim()) then return (sweep - 1); // do it all with a big, nasty switch switch (pra_submode(sweep)) { case pollo : case harad : { if (ad_from_lower(sweep)) then return lower; else return upper; } case pollo1 : case harad1 : { if (!ad_from_lower(sweep)) then return upper; if (high_band(channel)) then return lower; else return upper; } case level : case level1 : case level2 : case level3 : case fixlol : case fixloh : { int index = ad_from_lower(sweep) * 2 + configuration_channel_toggle_disable(); switch (index) { case 0 : { int temp = channel % 4; if ((temp == 0) || (temp == 3)) then return upper; else return lower; } case 1 : { return upper; } case 2 : { int temp = channel % 4; if ((temp == 0) || (temp == 3)) then return lower; else return upper; } case 3 : { return lower; } } break; } case vlobrl : case vlobrh : case polhil : case polhih : { int index = ad_from_lower(sweep) * 2 + configuration_channel_toggle_disable(); switch (index) { case 0 : { if (ODD(channel)) then return lower; else return upper; } case 1 : { return upper; } case 2 : { if (ODD(channel)) then return upper; else return lower; } case 3 : { return lower; } } break; } case xxxxxl : case xxxxxh : { warning("Call to edr_science_record::channel() with illegal mode"); return -1; // signal illegal call } } // cannot reach here return -2; } // what is the polarisation of this channel? (messy...) // this is a codified form of the notes of R G Peltzer dated merely // "post launch". Perhaps it would be better to use a clever class // which acts as a lookup table, but I'll do it the ugly way until I // get the chance to think about it (if ever...) int science_edr_record::polarisation(const int sweep, const int channel) const { // it's easy if we are in browse mode if (sc_mode() == browse) then return ((sweep == 1) ? left : right); // it's also easy in vim if (vim()) then return (ODD(sweep) ? left : right); // do it all with a big, nasty switch switch (pra_submode(sweep)) { case pollo : case harad : { int index = ad_from_lower(sweep) * 2 + rhcupper(sweep); switch (index) { case 0 : case 3 : { if ((ODD(channel) && high_band(channel)) || (EVEN(channel) && low_band(channel))) then return left; else return right; } case 1 : case 2 : { if ((ODD(channel) && high_band(channel)) || (EVEN(channel) && low_band(channel))) then return right; else return left; } } break; } case pollo1 : case harad1 : { int index = ad_from_lower(sweep) * 2 + rhcupper(sweep); switch (index) { case 0 : case 3 : { if (high_band(channel)) then return left; else return right; } case 1 : case 2 : { if (high_band(channel)) then return right; else return left; } } break; } case level : case level1 : { int index = ad_from_lower(sweep) * 2 + configuration_channel_toggle_disable(); switch (index) { case 0 : case level1 : { int temp = channel % 4; if (high_band(channel)) then { if (temp < 2) then return right; else return left; } else // low band { if (temp < 2) then return left; else return right; } } case 1 : { if (((ODD(channel)) && (high_band(channel))) || ((EVEN(channel)) && (low_band(channel)))) then return left; else return right; } case 2 : { int temp = channel % 4; if (high_band(channel)) then { if (temp < 2) then return left; else return right; } else // low band { if (temp < 2) then return right; else return left; } } case 3 : { if (((ODD(channel)) && (high_band(channel))) || ((EVEN(channel)) && (low_band(channel)))) then return right; else return left; } } break; } case level2 : { int index = ad_from_lower(sweep) * 2 + configuration_channel_toggle_disable(); switch (index) { case 0 : { int temp = channel % 4; if (high_band(channel)) then { if ((temp == 0) || (temp == 3)) then return left; else return right; } else // low band { if ((temp == 0) || (temp == 3)) then return right; else return left; } } case 1 : { if (high_band(channel)) then return left; else return right; } case 2 : { int temp = channel % 4; if (high_band(channel)) then { if ((temp == 0) || (temp == 3)) then return right; else return left; } else // low band { if ((temp == 0) || (temp == 2)) then return left; else return right; } } case 3 : { if (high_band(channel)) then return right; else return left; } } break; } case fixlol : { int index = ad_from_lower(sweep) * 2 + configuration_channel_toggle_disable(); switch (index) { case 0 : { if ((channel % 4) < 2) then return left; else return right; } case 1 : { if (ODD(channel)) then return right; else return left; } case 2 : { if ((channel % 4) < 2) then return right; else return left; } case 3 : { if (ODD(channel)) then return left; else return right; } } break; } case fixloh : { int index = ad_from_lower(sweep) * 2 + configuration_channel_toggle_disable(); switch (index) { case 0 : { if ((channel % 4) < 2) then return right; else return left; } case 1 : { if (ODD(channel)) then return left; else return right; } case 2 : { if ((channel % 4) < 2) then return left; else return right; } case 3 : { if (ODD(channel)) then return right; else return left; } } break; } case vlobrl : case polhil : { int index = ad_from_lower(sweep) * 2 + configuration_channel_toggle_disable(); switch (index) { case 0 : { if (ODD(channel)) then return left; else return right; } case 1 : { return right; } case 2 : { if (ODD(channel)) then return right; else return left; } case 3 : { return left; } } break; } case vlobrh : case polhih : { int index = ad_from_lower(sweep) * 2 + configuration_channel_toggle_disable(); switch (index) { case 0 : { if (ODD(channel)) then return right; else return left; } case 1 : { return left; } case 2 : { if (ODD(channel)) then return left; else return right; } case 3 : { return right; } } break; } case xxxxxl : case xxxxxh : { warning("Call to science_edr_record::polarisation() with illegal mode"); return -1; // signal illegal call } } // cannot reach here return -2; } // frequency of a given GS-8 scan uint32 science_edr_record::fixlo_frequency(const int n) const { if (sc_mode() != gs8) then fatal_error("Science_edr_record::fixlo_frequency(const int) called when not in mode GS-8"); static lookup_table[64] = { 51, 43, 35, 90, 82, 74, 66, 58, 50, 42, 34, 89, 81, 73, 65, 57, 49, 41, 33, 96, 88, 80, 72, 64, 56, 48, 40, 95, 87, 79, 71, 63, 55, 47, 39, 94, 86, 78, 70, 62, 54, 46, 38, 93, 85, 77, 69, 61, 53, 45, 37, 92, 84, 76, 68, 60, 52, 44, 36, 91, 83, 75, 67, 59 }; // this is all a bit of undocumented magic; there is no guarantee // that it will work for all time... int apparent_frequency_of_first_scan = frequency(1); // this is really the frequency of the FIFTH scan!! for (int index = 0; lookup_table[index] != apparent_frequency_of_first_scan; index++); int frequency_command = lookup_table[index - 4 + n - 1]; return (int32)(10 * ((frequency_command - 33) * 19.2 + 1.2)); } // Miscellaneous routines int n_statuses_per_edr(const int mode) { switch (mode) { case cr5 : return 4; // break; case engr : return 0; // break; default : return 8; // break; } } // how long (in seconds) is a sweep? int science_edr_record::sweep_duration(void) const { switch (sc_mode()) { case cr5 : return 60; case cr5a : case uv5a : // VIM is 'orrible, mostly because the sweeps are acquired // simultaneously in pairs. We "solve" this dilemma by pretending // that the sweeps are actually acquired consecutively and that // each takes only half the length that it actually takes to acquire. // This fudge is no worse than the fact that the frequency data are // acquired in a "comb" fashion rather than by a simple stepping // of frequency between adjacent channels. // On Voyager 1, the new table came into effect at 92:114:8:11:6; // until we find the corresponding time for V2 (which, knowing // Poynter, will be never), use the same time. if (time() < timeclass(92, 114, 8, 11, 6)) then return 384; // I believe this should be halved return 48; case browse : return 48; default : return 6; } } int science_edr_record::min_valid_channel_nr(void) const { switch (sc_mode()) { case cr5a : case uv5a : // if (sc() == 2) then // return 137; // On Voyager 1, the new table came into effect at 92:114:8:11:6 if (time() < timeclass(92, 114, 8, 11, 6)) then return 137; return 185; case engr : return 0; // should never happen default : return 3; } } int science_edr_record::max_valid_channel_nr(void) const { switch (sc_mode()) { case engr : return -1; // smaller than min_ for loops default : return 200; } } // ************************ edr_file **************************** // constructor with previously-open file edr_file::edr_file(FILE* file_pointer) : record_posn(0) { fp = file_pointer; previously_open = true; recent_write = true; _initialise_static(); } // constructor with unopened file edr_file::edr_file(DREstring file_name, const char* mode) : record_posn(0) { fp = fopen(file_name, mode); previously_open = false; recent_write = true; _initialise_static(); } // destructor edr_file::~edr_file(void) { if (!previously_open) then fclose(fp); // destroy_array(record_posn); } // private function int32 edr_file::_get_file_size(void) { recent_write = false; if (fseek(fp, 3, 0)) then return 0; int n_records = 0; BIG_ARRAY(uint32) temp_ptr(20000); // big, quasi-arbitrary number temp_ptr.auto_resize(); // heap_check(temp_ptr = new uint32 [1000]); // big number == max nr of records byte this_sc_mode, record_type; boolean no_error = true; while ((read_binary(fp, record_type)) && no_error) { record_type = (record_type >> 4); switch (record_type) { case 0x0b : // engineering temp_ptr[n_records] = record_size[engr]; break; case 0x0f : // decom temp_ptr[n_records] = decom_map_length; break; case 0x07 : // PRA science fseek(fp, 2, 1); read_binary(fp, this_sc_mode); fseek(fp, -3, 1); if (this_sc_mode >= N_SC_MODES) then { warning((DREstring)"Error in record format, record number " + DREstring10(n_records)); n_records += 2; // make final read record the one with the error no_error = false; } temp_ptr[n_records] = record_size[this_sc_mode]; break; default : // error warning((DREstring)"Invalid record type tag, record number " + DREstring10(n_records) + (DREstring)" in edr_file::_get_file_size(void)"); n_records += 2; no_error = false; } fseek(fp, temp_ptr[n_records++] - 1, 1); } n_records--; // if ((long)n_records * sizeof(uint32) > 64000L) then // fatal_error("File too large for this version of MIDAS"); // tidy up record_posn.resize(temp_ptr.size()); // heap_check(record_posn = new uint32 [n_records]); record_posn[0] = 0; for (int n = 1; n < n_records; n++) record_posn[n] = record_posn[n - 1] + temp_ptr[n - 1]; // destroy_array(temp_ptr); return n_records; } // private function void edr_file::_initialise_static(void) { for (int n = 0; n < N_SC_MODES; n++) record_size[n] = 1872; record_size[cr5a] = record_size[uv5a] = 528; record_size[engr] = 3860; } // Does the file contain the correct magic number? boolean edr_file::magic_number_OK(void) { START(fp); // go to beginning of file uint16 this_file_magic_number; read_binary(fp, this_file_magic_number); return ((this_file_magic_number == 0xd1d4) || (this_file_magic_number == 0xd4d1)); } // seek a particular record number (wrt 0) int32 edr_file::seek(const int rec_nr) { if (!record_posn.size()) then file_size = _get_file_size(); return fseek(fp, record_posn[rec_nr], 0); } // read a record into a buffer int edr_file::_read(byte* buffer) { fread((char*)buffer, 7, 1, fp); int remaining_bytes = record_size[buffer[6]] - 7; // check that it isn't a decom map if (buffer[6] == engr) then { if ((buffer[3] >> 4) == 0x0f) then remaining_bytes = decom_map_length - 7; } char* adr = (char*)&(buffer[7]); return fread(adr, remaining_bytes, 1, fp); } // write from a buffer int edr_file::_write(const byte* buffer) { int buffer_size = record_size[buffer[6]]; recent_write = true; return fwrite(buffer, buffer_size, 1, fp); } uint32 edr_file::size(void) { if (recent_write) then file_size = _get_file_size(); return file_size; } // read(edr_record, rec_nr (wrt 0)) if rec_nr == -1 then read at current // position in the file. void edr_file::read(edr_record& edr, const int rec_nr) { destroy_array(edr._record); edr._record_length = LONGEST_RECORD_LENGTH; heap_check(edr._record = new byte [edr.record_length()]); if (rec_nr >= 0) then seek(rec_nr); // remember seek and _read are virtual _read(edr._record); // switch by byte pairs if necessary if (edr._record[0] == 0xd1) then for (int n = 0; n < record_size[edr.sc_mode()]; n += 2) { byte temp = edr._record[n]; edr._record[n] = edr._record[n + 1]; edr._record[n + 1] = temp; } // calculate the actual record length int predicted_record_length = edr.record_length(); if (edr.record_type() == decom) then predicted_record_length = 716; if (edr.record_type() == science) then predicted_record_length = record_size[edr.sc_mode()]; // did we take up too much space? if (predicted_record_length != edr.record_length()) then { byte* temp; heap_check(temp = new byte [predicted_record_length]); for (int n = 0; n < record_size[edr.sc_mode()]; n++) temp[n] = edr._record[n]; destroy_array(edr._record); edr._record = temp; edr._record_length = predicted_record_length; } // reset the file pointer to be ready to read the next record seek(rec_nr); // back to the current record // const long v1 = ftell(fp); fseek(fp, predicted_record_length, 1); // next record (or past end) // const long int v2 = ftell(fp); } // read a GMDR void edr_file::read(GMDR& gmdr, const int rec_nr) { edr_record edr; read(edr, rec_nr); gmdr = edr; } // edr file with constant-length records // constructor for previously-open file const_edr_file::const_edr_file(FILE* file_pointer, const int rec_size) : edr_file(file_pointer) { record_length = rec_size; } // constructor for unopened file const_edr_file::const_edr_file(DREstring& filename, const char* mode, const int rec_size) : edr_file(filename, mode) { record_length = rec_size; } uint32 const_edr_file::size(void) { END(fp); // go to end int32 file_bytes = ftell(fp); return file_bytes / record_length; } // seek a particular record number (wrt 0) int32 const_edr_file::seek(const int rec_nr) { return fseek(fp, (int32)rec_nr * record_length, 0); } // private function int const_edr_file::_read(byte* buffer) { if (!buffer) then fatal_error("Null buffer passed to const_edr_file::_read(byte*)"); return fread((char*)buffer, record_length, 1, fp); } // read(const_edr_record, rec_nr (wrt 0)) if rec_nr == -1 then read at current // position in the file. void const_edr_file::read(edr_record& edr, const int rec_nr) { destroy_array(edr._record); edr._record_length = record_length; heap_check(edr._record = new byte [edr.record_length()]); if (rec_nr >= 0) then seek(rec_nr); // remember seek and _read are virtual _read(edr._record); // switch by byte pairs if necessary if (edr._record[0] == 0xd1) then for (int n = 0; n < record_length; n += 2) { byte temp = edr._record[n]; edr._record[n] = edr._record[n + 1]; edr._record[n + 1] = temp; } } // read a GMDR void const_edr_file::read(GMDR& gmdr, const int rec_nr) { edr_record edr; read(edr, rec_nr); gmdr = edr; } // ************************ science_edr_record ********************** // constructor science_edr_record::science_edr_record(const edr_record& e) : edr_record(e) { } // copy constructor science_edr_record::science_edr_record(const science_edr_record& s) : edr_record((edr_record)s) { } // science_edr_record == edr_record void science_edr_record::operator=(const edr_record& e) { destroy_array(_record); if (e.record_type() != science) then fatal_error("science_edr_record::operator=(const edr_record&)"); _record_length = e._record_length; heap_check(_record = new byte [_record_length]); for (int n = 0; n < _record_length; n++) _record[n] = e._record[n]; } // return number of sweeps -- in VIM we have two logical sweeps, one // LHC and one RHC, albeit // with 8 statuses and POR counters. // The frequency lookup table changes. We look at only one quarter of // the frequencies we used to examine. Now we have 8 logical sweeps, // four LHC and four RHC. int science_edr_record::n_sweeps(void) const { switch (sc_mode()) { case cr5 : return 4; case cr5a : case uv5a : // things are complicated. If we ever change the frequency table to // something more complex, I may need to revisit the whole idea of // "sweeps" in VIM. // if (sc() == 2) then // return 2; // On Voyager 1, the new table came into effect at 92:114:8:11:6. // Poynter has never provided an exact time for V2, so we will use // the boundary as V1. if (time() < timeclass(92, 114, 8, 11, 6)) then return 2; return 8; case engr : return 1; case browse : return 1; default : return 8; } } // return rhcupper of a sweep [1--8] int science_edr_record::rhcupper(const int s) const { return ((status(s) >> 9) & 1); } // return ad from lower of a sweep [1--8] int science_edr_record::ad_from_lower(const int s) const { return ((status(s) >> 10) & 1); } // return pra_submode of a sweep [1--8] int science_edr_record::pra_submode(const int s) const { return (vim() ? fixlol : ((status(s) >> 11) & 15)); } // return name of pra submode in a sweep const char* science_edr_record::pra_submode_name(const int s) const { return _pra_submode_name[pra_submode(s)]; } // return pra_mode of a sweep [1--8] int science_edr_record::pra_mode(const int s) const { switch (pra_submode(s)) { case pollo : case pollo1 : return prapollo; case harad : case harad1 : return praharad; case level : case level1 : case level2 : case level3 : return pralevel; case fixlol : case fixloh : return prafixlo; case vlobrl : case vlobrh : return pravlobr; case xxxxxl : case xxxxxh : return praxxxxx; case polhil : case polhih : return prapolhi; } // cannot get here return -1; } // datum(sweep [1--8], channel [1--200]) int science_edr_record::datum(const int s, const int c) const { switch (sc_mode()) { case cr5a : case uv5a : if (/* (sc() == 2) || */ (time() < timeclass(92, 114, 8, 11, 6))) then // in VIM, we have two logical scans, the first is LHC, the second RHC // we have to do a lot of fudging because of the idiotic VIM format { // the next horrendous line maps from regular channel numbers to VIM // channel numbers in range 1, 64 const int vim_channel_nr = ((c - 137) * 8 + 19 + (c - 137) / 8) % 64 + 1; const int n_extra_bytes = ((vim_channel_nr - 1) / 8 + 1) * 3; const int start_this_channel = ((vim_channel_nr - 1) * 4 + n_extra_bytes + 4) % 280; const int index = start_this_channel + ((s == 1) ? 0 : 2) + 248; return (((int)_record[index] << 8) + _record[index + 1]); } // V2, post change in lookup table { // *** const int vim_channel_nr = (8 + (c - 185) * 2 + (c - 185) / 8) % 16 + 1; const int n_extra_bytes = 3 + ((s - 1) / 2) * 6 + ((vim_channel_nr - 1) / 8) * 3; const int start_this_channel = ((vim_channel_nr - 1) * 4 + n_extra_bytes + (((s + 1) / 2) - 1) * 64 + 4) % 280; const int index = start_this_channel + (( s % 2) ? 0 : 2) + 248; return (((int)_record[index] << 8) + _record[index + 1]); } default : return _record[271 + (s - 1) * 200 + c]; // break; } } // return status of a sweep [1--8] int science_edr_record::status(const int s) const { switch (sc_mode()) { case cr5a : case uv5a : return ((_record[252 + (s - 1) * 35] << 8) + _record[253 + (s - 1) * 35]); default : return ((_record[272 + (s - 1) * 200] << 8) + _record[273 + (s - 1) * 200]); } } // return por counter for a sweep [1--8] int science_edr_record::por_counter(const int s) const { switch (sc_mode()) { case cr5a : case uv5a : return _record[254 + (s - 1) * 35]; default : return ((status(s) & 64) >> 5) + ((status(s) & 16) >> 4); } } // return total attenuation [1--8] (in dB) int science_edr_record::attenuation(const int s) const { const int temp = (status(s) & 7); int rv = 0; rv += (15 * (temp & 1)); rv += (30 * ((temp & 2) >> 1)); rv += (45 * ((temp & 4) >> 2)); return rv; } // return the eight bits of subheader frequency information [1--8] int science_edr_record::frequency(const int s) const { return (((_record[239 + (s - 1) * 4 + 1] & 15) << 4) | (_record[239 + (s - 1) * 4 + 2] >> 4)); } // return a complete scan -- passed address must be able to hold it! void science_edr_record::scan(int* d, const int s) const { switch (sc_mode()) { case cr5a : case uv5a : { for (int n = 0; n < (248 + 280); *(d + n++) = _record[n]) ; break; } default : { for (int n = 0; n < 200; *(d + n++) = _record[272 + (s - 1) * 200 + n]) ; break; } } } // how long is a scan (in bytes) int science_edr_record::scan_octets(void) const { switch (sc_mode()) { case cr5a : case uv5a : return (248 + 280); default : return 200; } } const int edr_duration(const int sc_mode) { switch (sc_mode) { case oc1 : case gs3 : return 48; case cr5 : return 480; default : fatal_error((DREstring)"Attempt to return duration of unknown spacecraft mode " + DREstring10(sc_mode) + (DREstring)" in edr_duration(const int)"); // unreachable return 0; } }