/*****************************************************************************
 * Copyright (c) 2002-2020 MBARI
 * Monterey Bay Aquarium Research Institute, all rights reserved.
 *****************************************************************************
 * @file    DeltaT.cc
 * @authors r. henthorn
 * @date    07/23/2020
 * @brief   DeltaT client interface module
 *
 * Project: LRAUV Obstacle Avoidance (oahu)
 * Summary: Modeled after the Dorado DeltaT interface. Read Imagenex Delta T
 *         (idt) packets from UDP socket. Use Idt83pParser to create Idt83pData
 *         objects for application use.
 *****************************************************************************/
/*****************************************************************************
  * Copyright Information:
  * Copyright 2002-2020 MBARI
  * Monterey Bay Aquarium Research Institute, all rights reserved.
  *
  * Terms of Use:
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
  * Software Foundation; either version 3 of the License, or (at your option)
  * any later version. You can access the GPLv3 license at
  * http://www.gnu.org/licenses/gpl-3.0.html
  *
  * This program is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  * more details (http://www.gnu.org/licenses/gpl-3.0.html).
  *
  * MBARI provides the documentation and software code "as is", with no
  * warranty, express or implied, as to the software, title, non-infringement
  * of third party rights, merchantability, or fitness for any particular
  * purpose, the accuracy of the code, or the performance or results which you
  * may obtain from its use. You assume the entire risk associated with use of
  * the code, and you agree to be responsible for the entire cost of repair or
  * servicing of the program with which you are using the code.
  *
  * In no event shall MBARI be liable for any damages,whether general, special,
  * incidental or consequential damages, arising out of your use of the
  * software, including, but not limited to,the loss or corruption of your data
  * or damages of any kind resulting from use of the software, any prohibited
  * use, or your inability to use the software. You agree to defend, indemnify
  * and hold harmless MBARI and its officers, directors, and employees against
  * any claim,loss,liability or expense,including attorneys' fees,resulting from
  * loss of or damage to property or the injury to or death of any person
  * arising out of the use of the software.
  *
  * The MBARI software is provided without obligation on the part of the
  * Monterey Bay Aquarium Research Institute to assist in its use, correction,
  * modification, or enhancement.
  *
  * MBARI assumes no responsibility or liability for any third party and/or
  * commercial software required for the database or applications. Licensee
  * agrees to obtain and maintain valid licenses for any additional third party
  * software required.
  *****************************************************************************/

/******************************
 * Headers
 ******************************/

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <string>

#include "DeltaT.h"
#include "macrologger.h"
#include "iniparser.h"

/******************************
 * Macros
 ******************************/

// Indices into the External Control packet
#define RANGE_LOC      7
#define GAIN_LOC       8
#define DGAIN_LOC      9
#define GEQUAL_LOC    10
#define SECTOR_LOC    11
#define WIDTH_LOC     12
#define NBEAM_LOC     13
#define AVG_LOC       14
#define SOS_LOC       17
#define MODE_LOC      19
#define ENABLE_LOC    20
#define PPD_LOC       21
#define MRANGE_LOC    22
#define LEVEL_LOC     23
#define DIR_LOC       24
#define ANGLE_LOC     25
#define RECORD837_LOC 28
#define RECORD83P_LOC 29
#define RECORD83B_LOC 30
#define ETC_LOC       31
#define DELAY_LOC     32
#define PPF_LOC       34


/******************************
 * Code
 ******************************/

DeltaT::DeltaT()
   : _port(0), _timeout(DELTAT_DEFAULT_TIMEOUT), _name(NULL),
     _dataSourceFile(NULL), _sockfd(-1), _raw(-1), _log(NULL), _send()
{
   _name = strdup(DELTAT_DEFAULT_NAME);

   // initialize EC switches
   setRange(DELTAT_DEFAULT_RANGE);
   setGain(DELTAT_DEFAULT_GAIN);
   setDisplayGain(DELTAT_DEFAULT_DGAIN);
   setGainEqualization(DELTAT_DEFAULT_GAINEQUAL);
   setSectorSize(DELTAT_DEFAULT_SECTOR);
   setBeamWidth(DELTAT_DEFAULT_WIDTH);
   setNumBeams(DELTAT_DEFAULT_BEAMS);
   setAvgFilter(DELTAT_DEFAULT_AVERAGE);
   setSOS(DELTAT_DEFAULT_SOS);
   setRunMode(DELTAT_DEFAULT_MODE);
   setProfileMinRange(DELTAT_DEFAULT_MINRANGE);
   setProfileMinLevel(DELTAT_DEFAULT_MINLEVEL);
   setTransducerDirection(DELTAT_DEFAULT_XD_DIR);
   setProfileTiltAngle(DELTAT_DEFAULT_TILT);
   setProfilePointDetection(DELTAT_DEFAULT_POINT_DETECT);
   setProfilePointFilter(DELTAT_DEFAULT_POINT_FILTER);
   setRecord837(DELTAT_DEFAULT_RECORD837);
   setRecord83P(DELTAT_DEFAULT_RECORD83P);
   setRecord83B(DELTAT_DEFAULT_RECORD83B);
   setExternalTriggerControl(DELTAT_DEFAULT_TRIGGER);
   setExternalTriggerXmitDelay(DELTAT_DEFAULT_XMIT_DELAY);
   setOutputEnable(DELTAT_DEFAULT_ENABLE);
}

DeltaT::DeltaT(const char* name, const uint32_t port, const bool logData)
   : DeltaT()
{
   _port = port;
   initializeName(name);
   initializeSource();
   initializeLogs(logData);
}

DeltaT::DeltaT(const DeltaTConfig &config)
  : DeltaT()
{
   initializeName(config.name);
   _port = config.port;

   if (config.dataSourceFile)
   {
      if (_dataSourceFile) delete _dataSourceFile;
      _dataSourceFile = strdup(config.dataSourceFile);
   }

   if (config.readTimeout != 0) _timeout = config.readTimeout;

   initializeSource();
   initializeLogs(config.logData);
}

DeltaT::~DeltaT()
{
   LOG_DEBUG("Deleting DeltaT object: \'%s\' on port %d", _name, _port);
   closeSock();
   if (_raw > 0) close(_raw);
   if (_name) delete _name;
}

void DeltaT::initializeName(const char* name)
{
   if (name)
   {
      if (_name) delete _name;
      _name = strdup(name);
   }
}

void DeltaT::initializeLogs(bool logData)
{
   if (!logData) return;

   // Use EC setting to determine number of beams in the log
   // Important that the setting values have been assigned
   uint16_t nb = 0;
   if (_nbeams == DELTAT_CONFIG_120BEAMS)
      nb = 120;
   else
      nb = 240;

   _log = new DeltaTLog(nb);
   createRawFile();

   return;
}

int DeltaT::initializeSource()
{
   memset((void*)&_client_addr, 0, sizeof(_client_addr));
   memset((void*)&_server_addr, 0, sizeof(_server_addr));

   if (_dataSourceFile)
   {
      _sockfd = open(_dataSourceFile, O_RDONLY);
      if (_sockfd < 0)
      {
         LOG_ERROR("Could not open data source file %s", _dataSourceFile);
         perror("DeltaT::initializeSource");
      }
      return _sockfd;
   }

   // create and bind to a socket
   if ( (_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) {
      LOG_ERROR("DeltaT::initializeSocket() %s can't open socket: %d",
          _name, errno);
      return _sockfd;
   }

   // 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 use _timeout ms
   struct timeval tv;
   tv.tv_sec  = (_timeout/1000);        // TODO config or adjustable
   tv.tv_usec = (_timeout%1000) * 1000;

   // Bind client socket
   int b = 0;
   if ((b = bind(_sockfd, (struct sockaddr *)&_client_addr,
                                                sizeof(_client_addr))) < 0)
   {
      LOG_ERROR("DeltaT::initializeSocket() %s can't bind dgram socket:%d",
         _name, errno);
      closeSock();
      return b;
   }

   // set options timeout and reuse
   if ((b = setsockopt(_sockfd, SOL_SOCKET, SO_RCVTIMEO,
                             (const void **)&tv, sizeof(struct timeval))) < 0)
   {
      LOG_ERROR("DeltaT::initializeSocket() %s setsockopt failed: %d",
                _name, errno);
      closeSock();
      return b;
   }

   int reuse = 1;
   if ((b = setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse,
                                                            sizeof(reuse))) < 0)
   {
      LOG_ERROR("DeltaT::initializeSocket(): setsockopt(SO_REUSEADDR) failed");
      perror("DeltaT::initializeSocket(): setsockopt(SO_REUSEADDR) failed");
      closeSock();
      return b;
   }

   if ((b = setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse,
                                                            sizeof(reuse))) < 0)
   {
      LOG_ERROR("DeltaT::initializeSocket(): setsockopt(SO_REUSEPORT) failed");
      perror("DeltaT::initializeSocket(): setsockopt(SO_REUSEPORT) failed");
      closeSock();
      return b;
   }

   LOG_INFO("DeltaT::initializeSocket() %s socket ready on port %d",
              _name, _port);

   return _sockfd;
}

// attempt to read from either a socket or raw DeltaT data file.
// return parsed info in the Idt83pData parameter object.
// returns the number of bytes read if successful.
// returns 0 if unsuccessful.
// otherwise, returns pointer to the parsed data in the
// Idt83pData parameter object passed in.
// TODO: perhaps create separate functions for file and socket reads.
int DeltaT::readData(Idt83pData *data)
{
   uint32_t nbytes = 0;
   if (_dataSourceFile)
   {
      if (sizeof(nbytes) != read(_sockfd, &nbytes, sizeof(nbytes)))
      {
         LOG_DEBUG("DeltaT::readData() read only %i bytes (eof?)", nbytes);
         nbytes = 0;
      }
      if (nbytes != read(_sockfd, &_receive_buf, nbytes))
      {
         LOG_DEBUG("DeltaT::readData() read less than %i bytes (eof?)", nbytes);
         nbytes = 0;
      }
      usleep(667*1000);
      if (nbytes == 0) return 0;
   }
   else
   {
      // read data from the socket
      socklen_t serversize = sizeof(_server_addr);
      if ((nbytes = recvfrom(_sockfd, _receive_buf, DELTAT_BUF_SIZE, 0,
                             (struct sockaddr *)&_server_addr, &serversize)) < 0)
      {
         perror("DeltaT::readData() ");
         if (errno == EAGAIN)
         {
            LOG_INFO("DeltaT::readData() %s recvfrom timeout", _name);
            setSend(); // Trigger an external control message
         }
         else
         {
            LOG_ERROR("DeltaT::readData() %s ERROR: recvfrom error %d",
                      _name, errno);
         }
         return nbytes;
      }
      if (_send)
      {
         sendEC();
      }
   }

   LOG_DEBUG("DeltaT::readData() read %d bytes", nbytes);
   // process the packet and log data if successful
   if (parseIdt83p(data, _receive_buf, nbytes) > 0)
      return nbytes;
   else
      return 0;
}

// attempt to read a packet from the DeltaT source.
// if successful log data and return the populated 83P structure.
// returns NULL if unsuccessful.
// return parsed info in the Idt83pData parameter object.
// otherwise, returns pointer to the parsed data in the
// Idt83pData parameter object passed in.
Idt83pData* DeltaT::getData(Idt83pData *data)
{
   // TODO: assert instead?
   if (_sockfd < 0 || NULL == data)
   {
      LOG_ERROR("Bad file descriptor or 83P data ptr: fd = %i, ptr = %p",
        _sockfd, data);
      return NULL;
   }

   // log data if successful
   uint32_t nbytes = readData(data);
   if (nbytes > 0)
   {
      if (_log)
      {
         LOG_DEBUG("Logging data");
         _log->setFields(data);
         _log->write();

         // log the raw data as well if the raw data file is open
         if (_raw > 0)
         {
            // write the number of bytes in the packet, then the packet
            ssize_t total = write(_raw, &nbytes, sizeof(nbytes)) +
                            write(_raw, _receive_buf, nbytes);
            LOG_DEBUG("Logging raw data: %ld bytes", total);
         }
      }
      return data;
   }
   else
   {
      return NULL;
   }
}

void DeltaT::createRawFile()
{
   if (NULL == _log)
      return;

   std::string s(_log->fileName());
   size_t lp = s.find(".log");
   if (lp != std::string::npos)
   {
      s.replace(lp, 4, ".raw");
      LOG_INFO("Raw file name is %s", s.c_str());
      _raw = open(s.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP);
      if (_raw < 0)
      {
         LOG_ERROR("Could not open file %s", s.c_str());
      }
   }

}

void DeltaT::closeSock()
{
   if (_sockfd > 0) close(_sockfd);
   _sockfd = -1;
}

// populate the EC buffer with the current state of the EC switches
int DeltaT::prepECMsg()
{
   memset(_send_buf, 0, DELTAT_EC_BUF_SIZE);

   // First seven bytes 'fixed'
   _send_buf[0] = 'E';
   _send_buf[1] = 'C';
   _send_buf[2] = 0;
   _send_buf[3] = 1;
   _send_buf[4] = 0;
   _send_buf[5] = 0;
   _send_buf[6] = 0;

   // variables
   _send_buf[RANGE_LOC]  = _range;
   _send_buf[GAIN_LOC]   = _gain;
   _send_buf[DGAIN_LOC]  = _dgain;
   _send_buf[GEQUAL_LOC] = _equal;

   _send_buf[SECTOR_LOC] = _sector;
   _send_buf[WIDTH_LOC]  = _width;
   _send_buf[NBEAM_LOC]  = _nbeams;
   _send_buf[AVG_LOC]    = _avg;

   _send_buf[SOS_LOC]   = (_sos&0xFF00)>>8;
   _send_buf[SOS_LOC+1] = (_sos&0x00FF);

   _send_buf[MODE_LOC]   = _mode;
   _send_buf[ENABLE_LOC] = _enable;
   _send_buf[PPD_LOC]    = _ppd;

   _send_buf[MRANGE_LOC] = _mrange;
   _send_buf[LEVEL_LOC]  = _level;
   _send_buf[DIR_LOC]    = _dir;
   _send_buf[ANGLE_LOC]  = _angle;

   _send_buf[RECORD837_LOC] = _r837;
   _send_buf[RECORD83P_LOC] = _r83P;
   _send_buf[RECORD83B_LOC] = _r83B;

   _send_buf[ETC_LOC]     = _etc;
   _send_buf[DELAY_LOC]   = (_delay&0xFF00)>>8;
   _send_buf[DELAY_LOC+1] = (_delay&0x00FF);

   _send_buf[PPF_LOC]  = _ppf;

   return 0;
}

// External Control value setting functions
int DeltaT::sendEC()
{
   // send only if we have recieved data from the beamformer
   if (_server_addr.sin_port == 0)
      return -1;

   // do not send if we're playing back a data file
   if (_dataSourceFile)
      return -1;

   // otherwise prep EC buffer
   prepECMsg();

   // then send EC buffer
   int stat = sendto(_sockfd, &_send_buf[0], sizeof(_send_buf), 0,
                    (struct sockaddr *)&_server_addr, sizeof(_server_addr));

   if (stat < 0)
   {
      LOG_ERROR("%s failed to send EC to beamformer", _name);
   }
   else
   {
      LOG_DEBUG("%s sent EC to beamformer", _name);
   }

   resetSend();
   return 0;
}

int DeltaT::setOutputEnable(int enable)
{
   if (enable >= 0 && enable <= 1)
   {
      _enable = enable;
      LOG_DEBUG("Enable setting: %d", _enable);

      if (_enable == 0)    // 83P
      {
         setProfilePointDetection(1);
      }
      else                 // 83B
      {
         setNumBeams(2);
         setSectorSize(3);
      }
      return setSend();
   }
   LOG_ERROR("Enable setting ignored: %d", enable);
   return -1;
}

int DeltaT::setRange(int range)
{
   if (range >= 2 && range <= 14)
   {
      _range = range;
      LOG_DEBUG("Range setting: %d", _range);
      return setSend();
   }
   LOG_ERROR("Range setting ignored: %d", range);
   return -1;
}

int DeltaT::setGain(int gain)
{
   if (gain >= 0 && gain <= 20)
   {
      _gain = gain;
      LOG_DEBUG("Gain setting: %d", _gain);
      return setSend();
   }
   LOG_ERROR("Gain setting ignored: %d", gain);
   return -1;
}

int DeltaT::setDisplayGain(int dgain)
{
   if (dgain >= 1 && dgain <= 100)
   {
      _dgain = dgain;
      LOG_DEBUG("DisplayGain setting: %d", _dgain);
      return setSend();
   }
   LOG_ERROR("DisplayGain setting ignored: %d", dgain);
   return -1;
}

int DeltaT::setGainEqualization(int equal)
{
   _equal = (equal != 0);
   LOG_DEBUG("GainEqualization setting: %d", _equal);
   return setSend();
}

int DeltaT::setSectorSize(int sector)
{
   if (sector >= 0 && sector <= 3)
   {
      _sector = sector;
      LOG_DEBUG("SectorSize setting: %d", _sector);
      return setSend();
   }
   LOG_ERROR("SectorSize setting ignored: %d", sector);
   return -1;
}

int DeltaT::setBeamWidth(int width)
{
   if (width >= 0 && width <= 3)
   {
      _width = width;
      LOG_DEBUG("Gain setting: %d", _width);
      return setSend();
   }
   LOG_ERROR("BeamWidth setting ignored: %d", width);
   return -1;
}

int DeltaT::setNumBeams(int nbeams)
{
   if (nbeams >= 0 && nbeams <= 2)
   {
      _nbeams = nbeams;
      LOG_DEBUG("NumBeams setting: %d", _nbeams);
      return setSend();
   }
   LOG_ERROR("NumBeams setting ignored: %d", nbeams);
   return -1;
}

int DeltaT::setAvgFilter(int avg)
{
   if (avg == 3 || avg == 5 || avg == 7 || avg == 9)
   {
      _avg = avg;
      LOG_DEBUG("AvgFilter setting: %d", _avg);
      return setSend();
   }
   LOG_ERROR("AvgFilter setting ignored: %d", avg);
   return -1;
}

int DeltaT::setSOS(int sos)
{
   if (sos >= 1400 && sos <= 1600)
   {
      _sos = sos*10;
      LOG_DEBUG("SoundVelocityMeters setting: %d", _sos);
      return setSend();
   }
   LOG_ERROR("SoundVelocityMeters setting ignored: %d", sos);
   return -1;
}

int DeltaT::setRunMode(int run)
{
   if (run >= 0 && run <= 4)
   {
      _run = run;
      LOG_DEBUG("RunMode setting: %d", _run);
      return setSend();
   }
   LOG_ERROR("RunMode setting ignored: %d", run);
   return -1;
}

int DeltaT::setProfileMinRange(int mrange)
{
   if (mrange >= 0 && mrange <= 100)
   {
      _mrange = mrange;
      LOG_DEBUG("ProfileMinRange setting: %d", _mrange);
      return setSend();
   }
   LOG_ERROR("ProfileMinRange setting ignored: %d", mrange);
   return -1;
}

int DeltaT::setProfileMinLevel(int level)
{
   if (level >= 10 && level <= 90)
   {
      _level = level;
      LOG_DEBUG("ProfileMinLevel setting: %d", _level);
      return setSend();
   }
   LOG_ERROR("ProfileMinLevel setting ignored: %d", level);
   return -1;
}

int DeltaT::setTransducerDirection(int dir)
{
   _dir = (dir != 0);
   LOG_DEBUG("TransducerDirection setting: %d", _dir);
   return setSend();
}

int DeltaT::setProfileTiltAngle(int angle)
{
   if ( (angle >= 135 && angle <= 225) || angle == 50 || angle == 51)
   {
      _angle = angle;
      LOG_DEBUG("ProfileTiltAngle setting: %d", _angle);
      return setSend();
   }
   LOG_ERROR("ProfileTiltAngle setting ignored: %d", angle);
   return -1;
}

int DeltaT::setRecord83P(int r83P)
{
   _r83P = (r83P != 0);
   LOG_DEBUG("Record83P setting: %d", _r83P);
   return setSend();
}

int DeltaT::setRecord837(int r837)
{
   _r837 = (r837 != 0);
   LOG_DEBUG("Record837 setting: %d", _r837);
   return setSend();
}

int DeltaT::setRecord83B(int r83B)
{
   _r83B = (r83B != 0);
   LOG_DEBUG("Record83B setting: %d", _r83B);
   return setSend();
}

int DeltaT::setProfilePointDetection(int ppd)
{
   _ppd = (ppd != 0);
   LOG_DEBUG("ProfilePointDetection setting: %d", _ppd);
   return setSend();
}

int DeltaT::setProfilePointFilter(int ppf)
{
   if (ppf >= 0 && ppf <= 2)
   {
      _ppf = ppf;
      LOG_DEBUG("ProfilePointFilter setting: %d", _ppf);
      return setSend();
   }
   LOG_ERROR("ProfilePointFilter setting ignored: %d", ppf);
   return -1;
}

int DeltaT::setExternalTriggerControl(int etc)
{
   _etc = (etc != 0);
   LOG_DEBUG("ExternalTriggerControl setting: %d", _etc);
   return setSend();
}

int DeltaT::setExternalTriggerXmitDelay(int delay)
{
   if (delay >= 0 && delay <= 10000)
   {
      _delay = delay;
      LOG_DEBUG("ExternalTriggerXmitDelay setting: %d", _delay);
      return setSend();
   }
   LOG_ERROR("ExternalTriggerXmitDelay setting ignored: %d", delay);
   return -1;
}

#include <math.h>
#define NO_VAL (0 - (M_PI - 1.2))

// static function to configure a DeltaT object for external control
// as per a config file
void DeltaT::configDeltaT(DeltaT *idt, const char* cfg)
{
   if (NULL == idt || NULL == cfg)
   {
      LOG_ERROR("NULL passed as a parameter: DeltaT = %s, cfgfile = %s",
        idt?"Valid":"NULL", cfg?"Valid":"NULL");
      return;
   }

   // load the config file using iniparser
   dictionary* ini = iniparser_load(cfg);
   if (NULL != ini)
   {
      // set DeltaT EC items for each item in the cfg file
      int range = iniparser_getint(ini, "Settings:Range", NO_VAL);
      if (range != NO_VAL) idt->setRange(range);

      int gain = iniparser_getint(ini, "Settings:Gain", NO_VAL);
      if (gain != NO_VAL) idt->setGain(gain);

      int equal = iniparser_getint(ini, "Settings:GainEqualization", NO_VAL);
      if (equal != NO_VAL) idt->setGainEqualization(equal);

      int sector = iniparser_getint(ini, "Settings:SectorSize", NO_VAL);
      if (sector != NO_VAL) idt->setSectorSize(sector);

      int width = iniparser_getint(ini, "Settings:BeamWidth", NO_VAL);
      if (width != NO_VAL) idt->setBeamWidth(width);

      int nbeams = iniparser_getint(ini, "Settings:NumBeams", NO_VAL);
      if (nbeams != NO_VAL) idt->setNumBeams(nbeams);

      int avg = iniparser_getint(ini, "Settings:AvgFilter", NO_VAL);
      if (avg != NO_VAL) idt->setAvgFilter(avg);

      int sos = iniparser_getint(ini,"Settings:SoundVelocityMeters",NO_VAL);
      if (sos != NO_VAL) idt->setSOS(sos);

      int mode = iniparser_getint(ini, "Settings:RunMode", NO_VAL);
      if (mode != NO_VAL) idt->setRunMode(mode);

      int minr = iniparser_getint(ini,"Settings:ProfileMinRange",NO_VAL);
      if (minr != NO_VAL) idt->setProfileMinRange(minr);

      int level = iniparser_getint(ini, "Settings:ProfileMinLevel", NO_VAL);
      if (level != NO_VAL) idt->setProfileMinLevel(level);

      int dir = iniparser_getint(ini, "Settings:TransducerDirection", NO_VAL);
      if (dir != NO_VAL) idt->setTransducerDirection(dir);

      double tilt = iniparser_getdouble(ini,"Settings:ProfileTiltAngle",NO_VAL);
      if (tilt != NO_VAL) idt->setProfileTiltAngle(tilt);

      int r83P = iniparser_getint(ini, "Settings:Record83P", NO_VAL);
      if (r83P != NO_VAL) idt->setRecord83P(r83P);

      int r837 = iniparser_getint(ini, "Settings:Record837", NO_VAL);
      if (r837 != NO_VAL) idt->setRecord837(r837);

      int r83B = iniparser_getint(ini, "Settings:Record83B", NO_VAL);
      if (r83B != NO_VAL) idt->setRecord83B(r83B);

      int ppf = iniparser_getint(ini, "Settings:ProfilePointFilter", NO_VAL);
      if (ppf != NO_VAL) idt->setProfilePointFilter(ppf);

      int ppd = iniparser_getint(ini, "Settings:ProfilePointDetection", NO_VAL);
      if (ppd != NO_VAL) idt->setProfilePointDetection(ppd);

      int etc = iniparser_getint(ini, "Settings:ExternalTriggerControl", NO_VAL);
      if (etc != NO_VAL) idt->setExternalTriggerControl(etc);

      int delay = iniparser_getint(ini, "Settings:ExternalTriggerXmitDelay", NO_VAL);
      if (delay != NO_VAL) idt->setExternalTriggerXmitDelay(delay);

      int dgain = iniparser_getint(ini, "Settings:DisplayGain", NO_VAL);
      if (dgain != NO_VAL) idt->setDisplayGain(dgain);

      int enable = iniparser_getint(ini, "Settings:OutputEnable", NO_VAL);
      if (enable != NO_VAL) idt->setOutputEnable(enable);
   }
   else
   {
      LOG_ERROR("Config INI Load failed: %s", cfg);
   }
   iniparser_freedict(ini);

   return;
}
