/****************************************************************************/
/* Copyright (c) 2012 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : DeltaT.cc                                                     */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 09/17/2012                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <sys/select.h>
#include <unistd.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <errno.h>
#include <unix.h>
#include <math.h>

#include "TimeP.h"
#include "Syslog.h"
#include "DeltaT.h"

#define TO_UINT(upper,lower) ((upper)<<8 | (lower))

//////////////////////////////////////////////////////////////////////////////
// Ctor
DeltaT::DeltaT(char *name, unsigned int port)
{
  _name = strdup(name);
  _sos = 14985;
  _fd83p = 0;
  _auto_sos = False;
  _ctd = 0;
  _last_ping_number = 0L;


  Syslog::write("%s DeltaT: constructing...\n", _name);

  // RGH 11-May-2014
  // Simulated sector size
  // The beamformer will always send 120 degrees, 120 beams.
  // Using this variable, this driver will edit the beamformer
  // data and output and log the desired sector size.
  //
  // When the server sends this driver a message to change the
  // sector size, only this variable is changed.
  // The editing of the beamformer data is done in function
  // 
  _sector_size = 120;

  char compound_name[256];
  _input = new DeltaTInput(DeltaTInput::makeName(compound_name, (char*)name,
						 sizeof(compound_name)));
  _output = new DeltaTOutput(DeltaTOutput::makeName(compound_name, (char*)name,
						    sizeof(compound_name)));
  _msgQ = new DeltaTMessage(MessageQueue::Read,
			    DeltaTMessage::makeName(compound_name,
						    (char*)name,
						    sizeof(compound_name)));
    
  DeltaTMessage::makeName(compound_name,
		    (char*)name,
		    sizeof(compound_name));
  Syslog::write("%s DeltaT: msgQ %s constructed...\n", name, compound_name);

  try {
    _log = new DeltaTLog( this, DataLog::BinaryFormat, _name );
  }
  catch ( ... ) {
    throw Exception("DeltaT::DeltaT() - DeltaTLog contructor failed\n");
  }
  _port = port;
  _sockfd = -1;

  // Attempt to connect to the FastCat server
  //
  Syslog::write("DeltaT::DeltaT - Attaching to FastCat");
  attach_ctd_server(15);

  Syslog::write("DeltaT::DeltaT - Setting external control");
  initExternalControlSwitches();
  start();
}


//////////////////////////////////////////////////////////////////////////////
// Dtor
DeltaT::~DeltaT()
{
  if (_fd83p > 0) close(_fd83p);
  delete _input;
  delete _output;
}

///////////////////////////////////////////////////////////////////////////
// Initialize the external control switch packet.
// Settings are persisted, all settings are sent in each
// control packet. Code originated from Imagenex sample file.
//
void DeltaT::initExternalControlSwitches()
{
  for(int i=0;i<256;i++) _send_buf[i] = 0;

  _send_buf[0] = 'E';
  _send_buf[1] = 'C';
  _send_buf[2] = 0;    //ID

  _send_buf[3] = 1;   //Control Byte 1
  //Bit0: 0 = LocalControl, 1 = ExternalControl
  //Bit0 must be set to ExternalControl (1) for Switch
  //settings to take effect

  _send_buf[4] = 0;		//Control Byte 2
  //Bit0: 0 = Transmit & Receive, 1 = Receive Only (disable transmitter)

  _send_buf[5] = 0;		//Control Byte 3
  _send_buf[6] = 0;		//Control Byte 4

  _send_buf[7] = 10;		//Range
  //must be in units of Meters
  //0 = n/a 1 = n/a
  //2 = 5M  3 = 10M  4 = 20M  5 = 30M  6 = 40M  7 = 50M  8 = 60M  9 = 80M  10 = 100M 11 = 150

  _send_buf[8] = 12;		//Gain, 0 to 20dB
  _send_buf[9] = 50;		//Display Gain, 1 to 100 percent
  _send_buf[10] = 0;		//Gain Equalization, 0=Off, 1=On
  _send_buf[11] = 3;		//Sector Size, 0=30, 1=60, 2=90, 3=120deg
  _send_buf[12] = 1;		//Beamwidth, 0=Wide, 1=Normal, 2=Narrow, 3=Narrow Mixed
  _send_buf[13] = 2;		//Number of Beams, 0=480, 1=240, 2=120
  _send_buf[14] = 0;		//Averaging, 0,1=Off, 2,3,4,...10 = #shots to average

  _send_buf[15] = (0&0xFF00)>>8;			//Persistence (Hi Byte), 0 to 600sec
  _send_buf[16] = (0&0x00FF);				//Persistence (Lo Byte)

  _send_buf[17] = (15000&0xFF00)>>8;; //Sound Velocity*10 (Hi Byte), 1400.0 to 1600.0m/s
  _send_buf[18] = (15000&0x00FF);     //Sound Velocity*10 (Lo Byte) in units of Meters

  _send_buf[19] = 3; //Mode, 0=Sector, 1=Linear, 2=Perspective, 3=Profile, 4=Beamtest
  _send_buf[20] = 0; //83P/83B Output Enable, 0=83P, 1=83B
                     //For 83P Output:
                     //Enable Profile Point Detection (set _send_buf[21]=1)
                     //For 83B Output:
                     //Sector Size must be 120 Deg (set _send_buf[11]=3)
                     //Number of Beams must be 120 (set _send_buf[13]=2)

   _send_buf[21] = 1;		//Profile Point Detection, 0=Disable, 1=Enable
   _send_buf[22] = 1;		//Profile Minimum Range, 0 to 100M
   							//must be in units of Meters
   _send_buf[23] = 25;		//Profile Minimum Level, 10 to 90 percent
   _send_buf[24] = 0;		//Transducer Up/Down, 0=Down, 1=Up
   _send_buf[25] = 0+180;	//Profile Tilt Angle + 180, -30 to +30deg
   _send_buf[26] = 0;		//Roll Correction, 0=Off, 1=On
   _send_buf[27] = 0;		//Measurement Units, 0=Meters, 1=Feet, 2=Yards
   _send_buf[28] = 1;		//Record Start/Start (.837)

   _send_buf[29] = 0;		//Record Start/Start (.83P)
   //Not implemented
   _send_buf[30] = 0;		//Record Start/Start (.83B)
   //Not implemented

   //The following External Trigger Control Bytes (31-33) are valid only for
   //DeltaT Sonar Heads supplied with the External Trigger Hardware Option
   _send_buf[31] = 0x03;					//External Trigger Control
   //Bit0, Edge: 	 0=NEG,     1=POS
   //Bit1, Enable: 0=Disable, 1=Enable

   _send_buf[32] = (0&0xFF00)>>8;		//External Trigger Transmit Delay (Hi Byte)
   _send_buf[33] = (0&0x00FF);		//External Trigger Transmit Delay (Lo Byte)
   //0 to 10000 in 100 microsecond increments

   _send_buf[34] = 2;		//Profile Point Filter
   //0=First Return, 1=Maximum Return, 2=Bottom Following                                    
}

//////////////////////////////////////////////////////////////////////////////
// Initialize comms to DeltaT beam former
DeviceIF::Status DeltaT::initialize(void)
{
  Syslog::write("%s DeltaT: initializing...\n", _name);

  _sockfd = initialize_socket(_port);

  if (_output83p)
    create83P();

  if (_sockfd >= 0 && _fd83p >= 0)
    return DeviceIF::Ok;
  else
    return DeviceIF::Error;
}

int DeltaT::initialize_socket(int port)
{

  Syslog::write("%s DeltaT: initializing port %d...\n", _name, port);

  // Setup socket to receive the deltaT packets
  if ( (_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) {
    Syslog::write("%s can't open dgram socket\n", _name, errno);
    exit(1);
  }

  memset((void*)&_client_addr, 0, sizeof(_client_addr));

  // Set up client info
  // Beam former sends data to us
  //
  _client_addr.sin_family      = AF_INET;
  _client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  _client_addr.sin_port        = htons(port);

  // For socket option SO_RCVTIMEO
  _server_tv.tv_sec = 1;
  _server_tv.tv_usec = 500000L;

  // Bind client socket
  if ( bind(_sockfd, (struct sockaddr *)&_client_addr, sizeof(_client_addr)) < 0) {
    Syslog::write("%s can't bind dgram socket: %d", _name, errno);
    exit(1);
  }
  else {
    if (0 > setsockopt(_sockfd, SOL_SOCKET, SO_RCVTIMEO,
		       (const void **)&_server_tv, sizeof(struct timeval))) {
      Syslog::write("%s DeltaT::initialize() setsockopt failed: %d", _name, errno);
      exit(1);
    }
    unsigned int minb = 32;
    if (0 > setsockopt(_sockfd, SOL_SOCKET, SO_RCVLOWAT,
		       (const void *)&minb, sizeof(unsigned int))) {
      Syslog::write("%s DeltaT::initialize() setsockopt failed: %d", _name, errno);
      exit(1);
    }
  }

  // Used to index into the receive buffer when more/less than a
  // single, complete DeltaT message is received.
  // In nominal case, value will always = 0.
  //
  _offset = 0;

  Syslog::write("%s We are ready to receive datagrams from any interface on port %d sockfd %d...\n", _name,
	 port, _sockfd);

  return _sockfd;
} 

//////////////////////////////////////////////////////////////////////////////
// Continuously listen for data packets from the beam former. Process data
// when packets are received. Also handle any messages from the DeltaT
// server process.
//
void DeltaT::run()
{

  Boolean debug = False;

  dprintf("Running DeltaT driver %s on %d...", _name, _port);

  // Act on any messages from DeltaTServer
  //
  messageHandler();

  // Read and process data from DeltaT beamformer
  // One ping. One ping only Vasili.
  //
  int serveraddrsize = sizeof(_server_addr);

  int nbytes = 0;
  dprintf("See if IDT packet available, read and process...");
  if ((nbytes = recvfrom(_sockfd, _receive_buf+ _offset,
			 sizeof(_receive_buf) - _offset,
			 0, (struct sockaddr *)&_server_addr,
			 &serveraddrsize )) < 0) {
    if (errno == EAGAIN)
      Syslog::write("%s recvfrom timeout", _name);
    else
      Syslog::write("%s ERROR: recvfrom failed with error %d\n", _name, errno);

    nbytes = 0;
  }

  int msg_type = 0;
  nbytes += _offset;
  if (nbytes > 0) {
    msg_type = processPacket(nbytes);

    if (DT_83P == msg_type) process83PData();
  }

  // Update the speed of sound if configured
  //
  if (_auto_sos)
  {
    if (False) //(!_ctd)
    {
      // Attempt to connect to the FastCat server
      //
      attach_ctd_server(0);
    }

    if (_ctd)
    {
      // Get the SOS every 15 seconds
      //
      if (0 == (time(NULL) % 15))
      {
        float sos = 0.;
        TimeIF::TimeSpec sampleTime;
        _ctd->soundVelocity(&sos, &sampleTime);
        long lsos = (long)(sos+0.5);    // Round the sos value
        Syslog::write("%s DeltaT: Auto-updating SOS to %ld", _name, lsos*10);
        set_sos(lsos*10);
      }
    }
  }

  // According to Jeff Patterson at Imagenex, the beam forming
  // application is designed so that the optimum time to use
  // external control messages is immediately after the
  // app transmits beam data packets.
  //
  sendExternalControlSwitches();


  return;
}

//////////////////////////////////////////////////////////////////////////////
// Attempt to connect to the FastCat server. Non-fatal if none available.
void DeltaT::attach_ctd_server(int wait_time)
{
  try {
    _ctd = new FastCatIF("fastcat", "FastCat", wait_time);
  }
  catch( Exception e ) {
    Syslog::write("%s DeltaT -- Caught exception: %s", _name, e.msg );
  }
  catch( ... ) {
    Syslog::write("%s DeltaT -- Caught exception: %s", _name );
  }

  if (!_ctd) {
    Syslog::write("%s DeltaT - No FastCat server available yet", _name);
  }
}


int DeltaT::processPacket(int nbytes)
{
  Boolean debug = False;

  int msg_type = 0;
  int serveraddrsize = sizeof(_server_addr);

  dprintf("DeltaT::processData- %d bytes in packet: buf[0:2]=>%c %c %c",
	    nbytes, _receive_buf[0], _receive_buf[1], _receive_buf[2]);

  unsigned int expected_length = 0;
  if (0 == strncmp((char const*)_receive_buf, "83P", 3)) {
    if (nbytes >= 256) {
      // Examine the rest of the 256-byte header
    
      unsigned int nbeams = TO_UINT(_receive_buf[70], _receive_buf[71]);
      dprintf("DeltaT::processData - got header? %d bytes, %d beams", nbytes, nbeams);
      unsigned int intensities = _receive_buf[117];
      expected_length = 256 + 2*nbeams + 2*nbeams*intensities;
      dprintf("DeltaT::processData - packet size should be = %d", expected_length);

      // Is this a complete and valid 83P message?
      if (nbytes >= expected_length) {
	msg_type = DT_83P;
      }
    }
  }
  else if (0 == strncmp((char const*)_receive_buf, "83B", 3)) {
    expected_length = 256 + TO_UINT(_receive_buf[70], _receive_buf[71]);
    msg_type = DT_83B;
  }
  else if (0 == strncmp((char const*)_receive_buf, "83Z", 3)) {
    expected_length = 32;
    msg_type = DT_83Z;
  }

  if (nbytes = expected_length) {
    _offset = 0;                      // Clean, nominal case
  }
  else if (nbytes < expected_length) {
    Syslog::write("%s DeltaT::processPacket() - recvd %d bytes, needed at least %d", _name,
		  nbytes, expected_length);
		  
    _offset = nbytes;   // Need more data, do another recvfrom
  }
  else if (nbytes > expected_length) {
    Syslog::write("%s DeltaT::processPacket() - recvd %d bytes, expected only %d", _name,
		  nbytes, expected_length);
    // Move the "next" message to the head of the buffer
    _offset = nbytes - expected_length;
    memcpy(_receive_buf, _receive_buf+expected_length, _offset);
  }

  
  return msg_type;
}

// Ascii string to month
// Returns integer value for month between 0 and 11
//
int DeltaT::a2month(const char* month)
{
  char* months[] = {"jan", "feb", "mar", "apr", "may", "jun", 
                    "jul", "aug", "sep", "oct", "nov", "dec"};
  int tm_mon;
  for (tm_mon = 0; tm_mon < 12; tm_mon++)
    if ( !strcasecmp(month, months[tm_mon]) )
      break;
  return tm_mon;
}

//////////////////////////////////////////////////////////////////////////////
// Process 83P data packets from the beam former
//
void DeltaT::process83PData()
{
  Boolean debug = False;

  // Process 83P data packet
  //
  unsigned int nsamples = TO_UINT(_receive_buf[72], _receive_buf[73]);
  unsigned int resolution = TO_UINT(_receive_buf[85], _receive_buf[86]);

  unsigned int nbeams = TO_UINT(_receive_buf[70], _receive_buf[71]);

  unsigned int interval = TO_UINT(_receive_buf[91], _receive_buf[92]);
  _output->data.dt_interval = interval;

  // Extract the ping number and store
  //
  unsigned int ping = TO_UINT(_receive_buf[95], _receive_buf[96]);
  if (ping != _last_ping_number+1)
  {
    Syslog::write("DeltaT::process83PData() - skipped ping number: %ul -> %u",
                   _last_ping_number, ping);
  }

  _output->data.ping_number = ping;
  _last_ping_number = ping;

  dprintf("DeltaT interogation timestamp: %s %s%s\n", _receive_buf+8, _receive_buf+20, _receive_buf+112);
  // Extract ping interrogation time from data packet, convert it to
  // seconds.milliseconds.
  //
  struct tm i_tm;
  i_tm.tm_isdst = -1;   // Let mktime() use system settings to determine DST
  char month[4];
  float msec;
  sscanf((const char*)_receive_buf+8  , "%2d-%3s-%4d", &i_tm.tm_mday, month, &i_tm.tm_year);
  sscanf((const char*)_receive_buf+20 , "%2d:%2d:%2d", &i_tm.tm_hour, &i_tm.tm_min, &i_tm.tm_sec);
  sscanf((const char*)_receive_buf+112, "%4f", &msec);
  i_tm.tm_year -= 1900;
  i_tm.tm_mon = a2month(month);
  time_t idt_sec = mktime(&i_tm);
  double idt_msec = (float)(idt_sec) + msec;
  _output->data.interrogation_time = idt_msec;
  dprintf("DeltaT converted timestamp: %.3f\n", idt_msec);

  // This is the time at which the values in the output object was updated.
  // In other words, the system time right now.
  //
  TimeIF::TimeSpec ts;
  Time::gettime(&ts);
  _output->data.update_time.seconds = ts.seconds;

  // DeltaT bottom pick
  memcpy((void*)&_output->data.dt_range, (const void*)&_receive_buf[133], sizeof(float));
  unsigned char *ranges = _receive_buf+256;
  unsigned char *intens = _receive_buf+256+(2*nbeams);

  // RGH 10-MAY-2014 Sector size dependent output:
  //  * Keep the number of baseline beams constant (e.g. 120) regardless of sector size
  //  * We'll simulate the sector size by writing only the desired beams to the output
  // Example:
  //  * 120 beams (default) baseline sector
  //  * 30 degree desired sector size from the middle of the baseline swath
  //  * Output a swath that is 15 degrees on either side of nadir
  //  * So we skip the first X beams of the input data
  //  * X = [ (nbeams-from-beamformer) - (desired-sector-size) ] / 2
  //  * In our example, X = (120 - 30) / 2 = 45
  //  * Skip the first and last 45 degrees of data from the beamformer
  //
  // This only works if the sector size is not more than the number of beams
  // read from the beamformer
  //
  _output->data.nbeams = nbeams;
  int start_beam = 0;
  if (_sector_size < nbeams) {
    start_beam = (nbeams - _sector_size) / 2; // Just output the middle sector_size swath
    _output->data.nbeams = _sector_size;      // Use sector size for the number of beams
  } else {
    dprintf("Sector size outside limit of %d\n", nbeams);
  }

  for (int i = 0, b = start_beam; i < _sector_size; i++, b++) {
    _output->data.beam_ranges[i] = TO_UINT(ranges[b*2],ranges[b*2+1]) * resolution/1000.;
    _output->data.intensities[i] = TO_UINT(intens[b*2],intens[b*2+1]);

    // For testing purposes only, insert recognizable data
    //
    //if (_sector_size != nbeams) {
    //  _output->data.beam_ranges[i] = b/1.0;
    //}
    //
    //

    if (_output->data.beam_ranges[i] > 0)
      dprintf("beam[%d]: range= %f, intensity= %d", i, _output->data.beam_ranges[i],
	      _output->data.intensities[i]);
  }

  if (debug)
    Syslog::write("%s %d Beams, %d Sector Size, Interval: %dms, Range: %f", _name, nbeams, _sector_size,
		  interval, _output->data.dt_range);

  _output->write();
  if (_enabled) _log->write();
  if (_output83p) write83P(256+(4*nbeams));

  return;
}

////////////////////////////////////////////////////////////////////////////
// Handle message from DeltaTServer
//
int DeltaT::messageHandler()
{
  int nmsgs = 0;
  long sos;


  // Extract messages from the server and handle one by one
  //
  DeltaTMessage::Message msg;
  while (_msgQ->read(&msg) > 0) {

    Syslog::write("DeltaT::messageHandler");
    Syslog::write("%s DeltaT::messageHandler: Got msg: %d", _name, msg._msg);
    nmsgs++;
    _xmit = True;
    switch (msg._msg) {

    case DeltaTMessage::StartMVCLogging:
      start();
      break;

    case DeltaTMessage::StopMVCLogging:
      stop();
      break;

    case DeltaTMessage::SetSOS:
      sos = msg._float * 10.;    // Change to decimeters for beamformer
      set_sos(sos);
      break;

    case DeltaTMessage::SetAutoSOS:
      _auto_sos = (msg._int > 0)? True: False;
      break;

    case DeltaTMessage::SetRange:
      set_range(msg._int);
      break;

    case DeltaTMessage::SetGain:
      set_gain(msg._int);
      break;

    case DeltaTMessage::SetNBeams:
      set_nbeams(msg._int);
      break;

    case DeltaTMessage::SetSectorSize:
      set_sector_size(msg._int);
      break;

    case DeltaTMessage::SetBeamWidth:
      set_beam_width(msg._int);
      break;

    case DeltaTMessage::SetAveraging:
      set_averaging(msg._int);
      break;

    case DeltaTMessage::SetGainEq:
      Boolean on;
      on = (msg._int > 0)? True: False;
      set_gain_eq(on);
      break;

    case DeltaTMessage::SetProfileMinRange:
      set_profile_min_range(msg._int);
      break;

    case DeltaTMessage::SetTiltAngle:
      set_tilt_angle(msg._int);
      break;

    case DeltaTMessage::SetTriggerEnable:
      Boolean enable;
      enable = (msg._int > 0)? True: False;
      set_trigger_enable(enable);
      break;

    case DeltaTMessage::SetTriggerEdge:
      Boolean positive;
      positive = (msg._int > 0)? True: False;
      set_trigger_edge(positive);
      break;

    case DeltaTMessage::SetTriggerDelay:
      set_trigger_delay((long)msg._float);
      break;

    case DeltaTMessage::SetXducerOrientation:
      Boolean up;
      up = (msg._int > 0)? True: False;
      set_xducer_orientation(up);
      break;

    case DeltaTMessage::SetProfilePointFilter:
      set_profile_point_filter(msg._int);
      break;

    case DeltaTMessage::SetLogging:
      on = (msg._int > 0)? True: False;
      set_idt_logging(on);
      break;

    default:
      nmsgs--;
      Syslog::write("%s DeltaT::messageHandler() - Invalid DeltaTMessage: %d", _name, msg._msg);
      break;
    }
  }

  if (nmsgs > 0)
    Syslog::write("%s DeltaT::Sending to %ld:%d... 837:%d Range:%d Gain:%d Gain_eq:%d Nbeams:%d Sector:%d Width:%d Avg:%d SOS:%ld Trigger:%d", _name,
		  _server_addr.sin_addr.s_addr, _server_addr.sin_port,
		  _send_buf[28], _send_buf[7], _send_buf[8], _send_buf[10], _send_buf[13],
		  _send_buf[11],_send_buf[12],_send_buf[14], _sos, _send_buf[31]);

  return nmsgs;
}


void DeltaT::start()
{
  Syslog::write("%s DeltaT - start mvc logging", _name);
  set_mvc_logging(True);
}

void DeltaT::stop()
{
  Syslog::write("%s DeltaT - stop mvc logging", _name);
  set_mvc_logging(False);
}

void DeltaT::set_mvc_logging(Boolean on_off_switch)
{
  _enabled = on_off_switch;
  _output->data.enabled = on_off_switch;
  _output->write();
}

void DeltaT::set_idt_logging(Boolean on)
{
  Syslog::write("%s DeltaT - set 837 logging to %d", _name, on);
  if (on)
    _send_buf[28] = 1;		//837 data logging, 0=Off, 1=On
  else
    _send_buf[28] = 1;		//Do not turn logging off
    //_send_buf[28] = 0;
}

void DeltaT::set_range(long range)
{
  Syslog::write("%s DeltaT - set range to %d", _name, range);
  //must be in units of Meters
  //0 = n/a
  //1 = n/a
  //2 = 5M
  //3 = 10M
  //4 = 20M
  //5 = 30M
  //6 = 40M
  //7 = 50M
  //8 = 60M
  //9 = 80M
  //10 = 100M

  if (range <= 5) {
    _send_buf[7] = 2;		//Range
  }
  else if (range <= 10) {
    _send_buf[7] = 3;
  }
  else if (range <= 20) {
    _send_buf[7] = 4;
  }
  else if (range <= 30) {
    _send_buf[7] = 5;
  }
  else if (range <= 40) {
    _send_buf[7] = 6;
  }
  else if (range <= 50) {
    _send_buf[7] = 7;
  }
  else if (range <= 60) {
    _send_buf[7] = 8;
  }
  else if (range <= 80) {
    _send_buf[7] = 9;
  }
  else if (range <= 100) {
    _send_buf[7] = 10;
  }
  else if (range > 100) {
    _send_buf[7] = 11;
  }
}

void DeltaT::set_averaging(long avg)
{
  Syslog::write("%s DeltaT - setting averaging to %d", _name, avg);
  if (avg == 0 || avg == 1  //Averaging, 0,1=Off, 3, 5, 7 = #shots to average
      || avg == 3 || avg == 5 || avg == 7)
    _send_buf[14] = avg;
  else {
    Syslog::write("%s DeltaT - Error! invalid setting for averaging: %d", _name, avg);
    return;
  }
}

void DeltaT::set_beam_width(long width)
{
  Syslog::write("%s DeltaT - setting beam width to %d", _name, width);
  if (width >= 0 && width <=3)
    _send_buf[12] = width; //Beamwidth, 0=Wide, 1=Normal, 2=Narrow, 3=Narrow Mixed
  else {
    Syslog::write("%s DeltaT - Error! invalid setting for beam width: %d", _name, width);
    return;
  }
}

// RGH 10-May-2014  Sector size dependent output:
//  * Keep the baseline sector size constant at 120
//  * Simulate the desired sector size by only writing the
//    beams in the desired sector to the output
//
void DeltaT::set_sector_size(long size)
{
  _sector_size = size;
  int skip = (120 - _sector_size) / 2;
  Syslog::write("%s DeltaT - using beams %d to %d", _name, skip, skip+size-1);

#if 0
  if (size == 120)
    _send_buf[11] = 3; //Sector Size, 0=30, 1=60, 2=90, 3=120deg
  else if (size == 90)
    _send_buf[11] = 2;
  else if (size == 60)
    _send_buf[11] = 1;
  else if (size == 30)
    _send_buf[11] = 0;
  else {
    Syslog::write("%s DeltaT - Error! invalid setting for sector size: %d", _name, size);
    return;
  }
#endif
}

void DeltaT::set_nbeams(long nb)
{
  Syslog::write("%s DeltaT - setting nbeams to %d", _name, nb);
  if (nb == 120)
    _send_buf[13] = 2;		//Number of Beams, 0=480, 1=240, 2=120
  else if (nb == 240)
    _send_buf[13] = 1;
  else if (nb == 480)
    _send_buf[13] = 0;
  else {
    Syslog::write("%s DeltaT - Error! invalid setting for nbeams: %d", _name, nb);
    return;
  }
}

// Beamformer wants sos in units of decimeters
//
void DeltaT::set_sos(long sos)
{
  _auto_sos = False;
  if (sos >= 14000 && sos <= 16000) {
    _sos = sos;
    _send_buf[17] = (sos&0xFF00)>>8;;	//Sound Velocity*10 (Hi Byte), 14000.0 to 16000.0 decimeters/s
    _send_buf[18] = (sos&0x00FF);	//Sound Velocity*10 (Lo Byte)
  }
}

void DeltaT::set_gain(long gain)
{
  Syslog::write("%s DeltaT - set gain to %d", _name, gain);
  if (gain > 0 && gain < 21) {
    _send_buf[8] = gain;		//Gain, 0 to 20dB
  }
}

void DeltaT::set_gain_eq(Boolean on)
{
  Syslog::write("%s DeltaT - set gain-eq to %d", _name, on);
  if (on)
    _send_buf[10] = 1;		//Gain Equalization, 0=Off, 1=On
  else
    _send_buf[10] = 0;
}

void DeltaT::set_profile_min_range(long range)
{
  if (range < 0 || range > 100) {
    Syslog::write("%s DeltaT - Invalid input to set_profile_min_range: %ld", _name, range);
    return;
  }
  Syslog::write("%s DeltaT - set_profile_min_range to %ld", _name, range);
  _send_buf[22] = range;
}

void DeltaT::set_xducer_orientation(Boolean up)
{
  if (up)
    _send_buf[24] = 1;
  else
    _send_buf[24] = 0;

  Syslog::write("%s DeltaT - set_xducer_orientation to %d", _name, _send_buf[24]);
}

void DeltaT::set_tilt_angle(long degrees)
{
  if (degrees < -45 || degrees > 45) {
    Syslog::write("%s DeltaT - Invalid input to set_tilt_angle: %ld", _name, degrees);
    return;
  }
  Syslog::write("%s DeltaT - set_tilt_angle to %ld", _name, degrees);
  _send_buf[25] = degrees+180;
}

void DeltaT::set_trigger_enable(Boolean enable)
{
  // Set or clear the second bit
  //
  if (enable)
    _send_buf[31] |= 0x02;
  else
    _send_buf[31] &= 0x01;

  Syslog::write("%s DeltaT - set_trigger_enable to %d, value = %d", _name, enable, _send_buf[31]);
}

void DeltaT::set_trigger_edge(Boolean positive)
{
  // Set or clear the first bit
  //
  if (positive)
    _send_buf[31] |= 0x01;
  else
    _send_buf[31] &= 0x02;

  Syslog::write("%s DeltaT - set_trigger_edge to %d, value = %d", _name, positive, _send_buf[31]);
}

void DeltaT::set_trigger_delay(long usec)
{
  if (usec < 0 || usec > 10000000L) {
    Syslog::write("%s DeltaT - Invalid input to set_trigger_delay: %ld", _name, usec);
    return;
  }
  Syslog::write("%s DeltaT - set_trigger_delay to %ld", _name, usec);
  long t = usec/100;
  _send_buf[32] = (t&0xFF00)>>8;
  _send_buf[33] = (t&0xFF00);
}

void DeltaT::set_profile_point_filter(int ppf)
{
  Syslog::write("%s DeltaT - set profile point filter to %d", _name, ppf);
  if (ppf >= 0 && ppf <=2)
    _send_buf[34] = ppf;
  else
    Syslog::write("%s DeltaT - Invalid input to set_profile_point_filter: %d", _name, ppf);
}

void DeltaT::sendExternalControlSwitches()
{
  Boolean debug = False;
  int stat;

  if (debug) 
    Syslog::write("%s DeltaT::Sending to %ld:%d... 837:%d Range:%d Gain:%d Gain_eq:%d Nbeams:%d Sector:%d Width:%d Avg:%d SOS:%ld Trigger:%d", _name,
		  _server_addr.sin_addr.s_addr, _server_addr.sin_port,
		  _send_buf[28], _send_buf[7], _send_buf[8], _send_buf[10], _send_buf[13],
		  _send_buf[11],_send_buf[12],_send_buf[14], _sos, _send_buf[31]);

  if ((stat = sendto(_sockfd, &_send_buf[0], sizeof(_send_buf), 0,
		     (struct sockaddr *)&_server_addr, sizeof(_server_addr))) < 0) {
    Syslog::write("%s External command comms failure: %d", _name, errno);
  }
  return;
}

int DeltaT::create83P()
{
  // Don't create the file if it already exists and is open
  //
  if (_fd83p != 0)
    return _fd83p;

  char fname[180];
  sprintf(fname, "%s/latest/%s.83P", getenv(AuvLogDirName), _name);

  _fd83p = open(fname, O_CREAT | O_APPEND | O_RDWR, (mode_t)644); //S_IWUSR + S_IRUSR + S_IRGRP + S_IROTH);

  if (_fd83p < 0)
    perror("DeltaT failed to create 83P file!");
  else
    Syslog::write("%s Opened 83P file on fd: %d", _name, _fd83p);


  return _fd83p;
}

int DeltaT::write83P(int nbytes)
{
  if (_fd83p == 0)
    create83P();

  if (_fd83p != 0) {
    nbytes = write(_fd83p, (void*)_receive_buf, nbytes);
    //fsync(_fd83p);
  }

  return 0;
}

Boolean DeltaT::output83p()
{
  return _output83p;
}

Boolean DeltaT::output83p(Boolean on_off)
{
  int old_setting = _output83p;
  _output83p = on_off;

  if (on_off)
    Syslog::write("%s 83P output on", _name);
  else
    Syslog::write("%s 83P output on", _name);

  return old_setting;
}

