// 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 <bdr.h>
#include <edr.h>
#include <gmdr.h>

#include <iostream.h>
#include <strstream.h>

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;
  }
}
