#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <time.h>
#include "Simulator.h"
#include "System.h"
#include "MathP.h"
#include "VehicleConfigurationIF.h"
#include "FloatAttribute.h"
#include "AngleAttribute.h"
#include "AttributeParser.h"
#include "Syslog.h"
#include "System.h"
#include "WorkSiteIF.h"
#include "NavUtils.h"
#include "matrixMath.h"

// Number of simulation steps per command
#define NSteps 10

/*-----------------------------------------------------------------------*
 | structure of this file:
 |   global variables declarations       (there are a lot of them)
 |   main()                              simple interface to test (for now)
 |   start_sub(),                        calls lhs
 |   lhs()                               precompute lhs of hydro equations
 |   motion()                            main computation
 |   hydro()                             calcs rhs of hydro equations
 |   fins()                              computes forces caused by fins
 |   act_model()                         fin actuator model (just a delay)
 |   thruster_force(),thruster_torque()  thruster model
 |   matinv()
 |
 *-----------------------------------------------------------------------*/



/*------------------- generic constants -------------------------------*/

#define FTOM   0.3048               /* from feet to meters */
#define FTOM2  (0.3048 * 0.3048)
#define FTOM3  (0.3048 * 0.3048 * 0.3048)
#define STOKG  14.59
#define TWOPI  (2*PI)

// Simulation time increment, in seconds
const double Simulator::_timeIncr = (NavigationPeriodMillisec / 1000.) / NSteps;

/* coordinate transformation matrix, we use only indices 1-6 */
double Simulator::ctrn[7][7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
				0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
				0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
				0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
				0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
				0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
				0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};


double Simulator::_position[7]  = {0., 0., 0., 0., 0., 0., 0.};
double Simulator::_position0[7] = {0., 0., 0., 0., 0., 0., 0.};
double Simulator::_rate[7]      = {0., 0., 0., 0., 0., 0., 0.};
//
//global pointer to current simulator object.  This is for the call to bisect.
Simulator *psim;

/*----------------------- vehicle parameters ------------------------------*/

/* Note that some of the parameters are defined in the data structure.
   If READ_HYDRO is 1 (in sim_main.c), most of the following
   parameters get overwritten from get_am.tex and get_drag.tex, which
   are generated by running get_params.m in the matlab environment.
   The default parameters listed here are current 5/25/95, based on
   Draper's Maneuvering Tool.  If there is a chance we'll overwrite the
   value, it is NOT declared a constant.

   x is fwd, y stbd, z down.

   The following values give a cg-cb separation that accounts for zero
   static pitch, 0 degrees static stb roll, and an undamped roll period of
   approximately 3 seconds */

const double Simulator::Ixy = 0.;
const double Simulator::Izx = 0.;
const double Simulator::Iyz = 0.;

const double Simulator::fin_speed = Math::degToRad(35.);
const double Simulator::cdo = 0.0065;       /* Cd from NACA 0015 section */
const double Simulator::ec = 0.90;          /* "Oswald" efficiency factor */
const int Simulator::delay = 30;            /* 10 int. steps per sample prd*/

/* THRUSTER. ------------------------------------------------------------*/

/* torque constant, in Nm/A - inc gearbox */
const double Simulator::Kt = .456; 


Simulator::Simulator(const char *configFileName, const char *outputFileName)
  : SimulatorIF_SK(),
    _attributes("Simulator"),
    _eventLog("eventLog")
{
  Boolean debug = True;

  _log = new SimulationLog(this, DataLog::BinaryFormat);

  _nBatts = 3;
  _battLog = new BluefinBattLog(this, DataLog::BinaryFormat, _nBatts);

  _missionClock = new MissionClock();
  //
  // Get flight-code vehicle parameters from vehicle.cfg
  //
  dprintf("Create VehicleConfigurationIF...\n");
  _configuration = new VehicleConfigurationIF("configuration");


//  dprintf("Create DropWeightIF...\n");
//  _dropWeight = new DropWeightIF("dropWeight");

  dprintf("Okay...\n");
  _input.vehicleMass = _configuration->mass();

  _configuration->centerOfMass(_centerOfMass);
  _configuration->centerOfBuoyancy(_centerOfBuoyancy);

  Pit            = _configuration->propPitch();
  Area           = _configuration->ductArea();
  Eta            = _configuration->propEfficiency();
  Xuabu          = _configuration->xuabu();
  mTs            = _configuration->Ts();
  mTsMsec        = (long) mTs * 1000;

  //
  // On-board hack:  Fixed OdysseyTailcone.cc; but the Pit number
  // doesn't match the sim.  Hardcode a temporary fix 2.403; 
  // FIX THIS LATER rsm 00/1/18
  //
  Pit *= 2.403;

  VehicleConfigurationIF::Vector location;

  _configuration->dropWeight(1, &Wdw1, &Bdw1, location);
  Xdw1 = location[X];
  Ydw1 = location[Y];
  Zdw1 = location[Z];

  _configuration->dropWeight(2, &Wdw2, &Bdw2, location);
  Xdw2 = location[X];
  Ydw2 = location[Y];
  Zdw2 = location[Z];

  // Get fin locations
  VehicleConfigurationIF::Vector lowerRudder, portElevator;
  VehicleConfigurationIF::Vector upperRudder, stbdElevator;

  _configuration->finLocations(lowerRudder, upperRudder, 
			       portElevator, stbdElevator);

  // Copy into an array that starts at one (for example r1[0] is unused).
  for (int i = X; i <= Z; i++) {
    r1[i+1] = lowerRudder[i];
    r2[i+1] = portElevator[i];
    r3[i+1] = upperRudder[i];
    r4[i+1] = stbdElevator[i];
  }

  dprintf("Create attributes...\n");
  // Define attributes and default values.
  // Set some attribute defaults to Odyssey values for now!
  /* translational and rotational body mass */
  _attributes.add(new FloatAttribute("Ixx", "?",  &Ixx));
  _attributes.add(new FloatAttribute("Iyy", "?",  &Iyy));
  _attributes.add(new FloatAttribute("Izz", "?",  &Izz));

  /* hydrodynamic parameters ----------------------------------------------*/
  _attributes.add(new FloatAttribute("Kpabp", "?",  &Kpabp));
  _attributes.add(new FloatAttribute("Mqabq", "?",  &Mqabq));
  _attributes.add(new FloatAttribute("Nrabr", "?",  &Nrabr));
  _attributes.add(new FloatAttribute("Yvabv", "?",  &Yvabv));
  _attributes.add(new FloatAttribute("Zwabw", "?",  &Zwabw));

  /* added mass */
  _attributes.add(new FloatAttribute("Kpdot", "?",  &Kpdot));
  _attributes.add(new FloatAttribute("Mqdot", "?",  &Mqdot));
  _attributes.add(new FloatAttribute("Nrdot", "?",  &Nrdot));
  _attributes.add(new FloatAttribute("Xudot", "?",  &Xudot));
  _attributes.add(new FloatAttribute("Yvdot", "?",  &Yvdot));
  _attributes.add(new FloatAttribute("Zwdot", "?",  &Zwdot));

  /* added mass cross terms */
  _attributes.add(new FloatAttribute("Kvdot", "?",  &Kvdot));
  _attributes.add(new FloatAttribute("Mwdot", "?",  &Mwdot));
  _attributes.add(new FloatAttribute("Nvdot", "?",  &Nvdot));
  _attributes.add(new FloatAttribute("Yrdot", "?",  &Yrdot));
  _attributes.add(new FloatAttribute("Ypdot", "?",  &Ypdot));
  _attributes.add(new FloatAttribute("Zqdot", "?",  &Zqdot));

  /* quadratic drag cross terms */
  _attributes.add(new FloatAttribute("Mwabw", "?",  &Mwabw));
  _attributes.add(new FloatAttribute("Nvabv", "?",  &Nvabv));
  _attributes.add(new FloatAttribute("Yrabr", "?",  &Yrabr));
  _attributes.add(new FloatAttribute("Zqabq", "?",  &Zqabq));

  /* in-line lift and drag */
  _attributes.add(new FloatAttribute("Muq", "?",  &Muq));
  _attributes.add(new FloatAttribute("Nuv", "?",  &Nuv));
  _attributes.add(new FloatAttribute("Muw", "?",  &Muw));
  _attributes.add(new FloatAttribute("Mpr", "?",  &Mpr));
  _attributes.add(new FloatAttribute("Nur", "?",  &Nur));
  _attributes.add(new FloatAttribute("Npq", "?",  &Npq));
  _attributes.add(new FloatAttribute("Xvv", "?",  &Xvv));
  _attributes.add(new FloatAttribute("Xww", "?",  &Xww));
  _attributes.add(new FloatAttribute("Xvr", "?",  &Xvr));
  _attributes.add(new FloatAttribute("Xwq", "?",  &Xwq));
  _attributes.add(new FloatAttribute("Xrr", "?",  &Xrr));
  _attributes.add(new FloatAttribute("Xqq", "?",  &Xqq));
  _attributes.add(new FloatAttribute("Yur", "?",  &Yur));
  _attributes.add(new FloatAttribute("Yuv", "?",  &Yuv));
  _attributes.add(new FloatAttribute("Ywp", "?",  &Ywp));
  _attributes.add(new FloatAttribute("Zuq", "?",  &Zuq));
  _attributes.add(new FloatAttribute("Zuw", "?",  &Zuw));
  _attributes.add(new FloatAttribute("Zvp", "?",  &Zvp));

  _attributes.add(new FloatAttribute("Kvt2", "?",  &Kvt2));
  //
  // Tailcone and fin model
  //
  _attributes.add(new FloatAttribute("aspectRatio", "?",  &aspect_ratio));
  _attributes.add(new AngleAttribute("stallAngle", "?",  &stall_angle));
  _attributes.add(new AngleAttribute("wideHystRud", "?",  &wide_hyst_rud));
  _attributes.add(new AngleAttribute("centerHystRud", "?", 
				     &center_hyst_rud));
  _attributes.add(new AngleAttribute("wideHystElev", "?",  &wide_hyst_elev));
  _attributes.add(new AngleAttribute("centerHystElev", "?",  
				     &center_hyst_elev));
  _attributes.add(new FloatAttribute("finArea", "?",  &S));
  _attributes.add(new FloatAttribute("CDc", "?",  &CDc));
  _attributes.add(new FloatAttribute("dCL", "?",  &dCL));
  //
  // Initial Vehicle State
  //
  _attributes.add(new FloatAttribute("initX", "?",  &_position[1], 0.));
  _attributes.add(new FloatAttribute("initY", "?",  &_position[2], 0.));
  _attributes.add(new FloatAttribute("initZ", "?",  &_position[3], 0.));
  _attributes.add(new AngleAttribute("initPhi", "?",  &_position[4], 0.));
  _attributes.add(new AngleAttribute("initTheta", "?",  &_position[5], 0.));
  _attributes.add(new AngleAttribute("initPsi", "?",  &_position[6], 0.));
  _attributes.add(new FloatAttribute("initU", "?",  &_rate[1], 0.));
  _attributes.add(new FloatAttribute("initV", "?",  &_rate[2], 0.));
  _attributes.add(new FloatAttribute("initW", "?",  &_rate[3], 0.));
  _attributes.add(new AngleAttribute("initP", "?",  &_rate[4], 0.));
  _attributes.add(new AngleAttribute("initQ", "?",  &_rate[5], 0.));
  _attributes.add(new AngleAttribute("initR", "?",  &_rate[6], 0.));
  _attributes.add(new FloatAttribute("zMax" , "?",  &zMax,  210.)); 
  _attributes.add(new FloatAttribute("northCurrent", "?",  
				     &northCurrent, 0.));
  _attributes.add(new FloatAttribute("eastCurrent", "?",  
				     &eastCurrent, 0.));

  char depthName[32], yName[32];
  for( i=0; i< MAXNDEPTHS; i++ )
  {
     sprintf(depthName, "depth%d", i+1);
     _attributes.add(new FloatAttribute(depthName,  depthName,  &_depth[i], -1)); 

     sprintf(yName,     "ydepth%d",i+1);
     _attributes.add(new FloatAttribute(yName, yName,  &_ydepth[i], -1)); 
  }


  _attributes.add(new FloatAttribute("kWhr",    "?",  &_energy0, 2.)); 
  _attributes.add(new FloatAttribute("hotel_W", "?",  &_Ph, 30.)); 


  // Move this to vehicle.cfg
  rho         = 1.025e3;     /* kg/m^3 density of seawater */

  dprintf("Simulator:: initPsi = %f\n", _position[6]*180/PI);
  dprintf("Done creating attributes. Parse 'em...\n");

  AttributeParser::parse(configFileName, &_attributes);

  //
  // Copy the config file (usually simulator.cfg) to the log dir:
  System::copyToLogDir( configFileName );

  dprintf("Simulator:: initPsi = %f\n", _position[6]*180/PI);
  dprintf("Simulator:: zMax = %f\n", zMax);
  // Convert the initial lat/long provided in worksite.cfg
  //
  WorkSiteIF workSite("workSite");

  // Initialize x-y to worksite location
  NavUtils::geoToUtm(workSite.latitude(), workSite.longitude(), 
		     workSite.utmZone(),
		     &_position[1], &_position[2]);
  //
  // Save the initial position
  _position0[1] = _position[1];
  _position0[2] = _position[2];
		      
  // Initialize the state.
  //
  dprintf(" The utm zone is %d\n", workSite.utmZone());
  dprintf(" Simulator: The lat/long in workSite.cfg is %g, %g",
            workSite.latitude(), workSite.longitude());
  dprintf(" Simulator: The lat/long in workSite.cfg, cvt'ed to UTM is %g, %g",
            _position[1], _position[2]);

  for( i=0; i<MAXNDEPTHS; i++ )  
  {
     _normalArray[0][i] = 0.;
     _normalArray[1][i] = 0.;
     _normalArray[2][i] = 1.;
     _yinter[i] = 0.;
     _slope[i] = 0.;
  }

  //
  // These y axis locations are specified as relative distances in meters.
  // Convert them to absolute UTM.

  _nydepths=0;
  for( i=0; i<MAXNDEPTHS; i++ )  
  {
     if( _ydepth[i] == -1 ) break;
     _ydepth[i] = _ydepth[i] + _position0[2];
     _nydepths++;
  }
  //
  // If no depths are specified, assume 1km flat:
  if( !_nydepths ) 
  {
     _depth[0] = 1000.;
     _nydepths++;
  }

  _bottomNormal[0]=0.;
  _bottomNormal[1]=0.;
  _bottomNormal[2]=1.;

  //
  // Check for a legacy simulator.cfg file, where there were only three
  // depths but four y axis points specified. The depths refered to flat
  // sections between even/odd y point pairs.  Now there is no requirement
  // for flat segments, and (y_i, depth_i) refers to a point along the y axis.
  //
  // 
  if( _nydepths == 4 && _depth[3] == -1 )
  {
     Syslog::write("Simulator::Simulator - I'm assuming this is a legacy simulator.cfg.\n "
                   "           Copying depth3 to depth4 and depth2 to depth3.\n");
     _depth[3]=_depth[2];
     _depth[2]=_depth[1];
  }

  dprintf("Simulator:  _nydepths = %d", _nydepths);
  for( i=0; i<MAXNDEPTHS; i++ ) 
     dprintf("Simulator:  %d _depth=%.1f, _ydepth-_position0[2]=%.1f ",
     i, _depth[i], _ydepth[i]-_position0[2] );

  double alpha;
  for( i=0; i<_nydepths-1; i++ )
  {
     if( _ydepth[i+1] - _ydepth[i] < 1.0 ) 
     {
	Syslog::write("Simulator::Simulator - Error: \n"
	              "    You must set ydepth%d >= ydepth%d + 1 meter.  "
	              "Exiting.", i+2, i+1);
	exit(1);
     }
     //
     _slope[i]  = (_depth[i+1] - _depth[i]) / (_ydepth[i+1] - _ydepth[i]);
     _yinter[i] = _depth[i]    - _slope[i]*_ydepth[i];
     //
     // See p. 364 of CRC Standard Mathematical Tables, 18th Ed.
     alpha = atan(_slope[i]);
     //
     // Put the normal vector in Newtonian (N) coordinates, which are NED.
     _normalArray[0][i] =  0.;
     _normalArray[1][i] = -sin(alpha);
     _normalArray[2][i] =  cos(alpha);
  }


  dprintf("Simulator:  _nydepths = %d", _nydepths);
  for( i=0; i<MAXNDEPTHS; i++ ) 
     dprintf("Simulator:  %d _depth=%.1f, _ydepth-_position0[2]=%.1f, "
     "_slope=%.2e, _yinter=%.2e", 
     i, _depth[i], _ydepth[i]-_position0[2], _slope[i], _yinter[i]);


  // ATTACH DEBUGGER
  // sleep(20);

  //
  // I'm approximating the length here as 4.2 meters.
  mgl = _input.vehicleMass * 9.806 * 4.2;

  _layeredControl = new LayeredControlIF("layeredControl");

  // Want to be notified when mission status changes
  subscribe(_layeredControl, LayeredControlIF::MissionStarted,
	    (EventCallback )Simulator::missionStatusCallback);

  _missionStarted = False;

  kk = 0;        //Sample period counter; called "k" in discrete-time analysis
  //
  // Initialize the simulation pointer.  This makes it possible for beamAlt
  // to be passed as a function pointer to bisect().
  psim = this;

  //_energy0 = 2000.*3600;  //kW-hr*3600*1000 = Watt-seconds.  Initial charge
			  //in the battery.
  //_Ph      = 30;          //Watts.  Hotel load.

  _energy  = _energy0*1000.*3600.;         //Convert to Watt-Seconds.
  _lastSimTime = 0.;

//printf("Simulation::Constructor\n");


  init_simulation();

}


Simulator::~Simulator()
{
  delete _configuration;
//  delete _dropWeight;
  delete _layeredControl;
  delete _missionClock;
  delete _log;
  delete _battLog;
}



long Simulator::setCurrent(double north, double east)
{
  _input.northCurrent = north;
  _input.eastCurrent = east;

  return 0;
}


long Simulator::command(double propSpeedCmd, double elevator, double rudder)
{ 
  Boolean debug = False;
  double LHS;
  static Boolean first=True;

  if (!_missionStarted) return 0;
  if ( first )
  {
    _missionClock->reset();
    first = False;
    printf("Simulator: Got here!\n");
  }
  //
  // We've replaced uC with propSpeedCmd as the first argument above.  
  // propSpeedCmd (omega_P) is now computed as a function of uC in 
  // DynamicControlServer.cc
  //
  // Also, thrAmps used to be computed here, and passed to motion(), 
  // and then to thruster_force() and thruster_torque().  These have been
  // changed to take omega_P as the input.
  //
     omega_P = propSpeedCmd;

     dprintf(" Simulator: propSpeedCmd = %.2f\n", propSpeedCmd );

#if 0
  double uC = 1.54;
  //
  // Convert commanded vehicle speed, uC, to commanded 
  // propeller speed, using the Momentum Theory Equations in Franz's 
  // Modeling report, Eqns. 30 - 32.  Also see my notes, book #3, p.5
  // The implementation here is a cleaned up version of that in Odyssey.
  // 
  // Although the model is unrealistic at slow or negative speeds, I've
  // fixed the implementation so that a negative speed command results in
  // the prop reversing direction, and has a negative current command.
  //
  // I've also made the emperical Cdf1 symmetric for +/- speed, because it
  // surely is not valid in the reverse direction, and this way it 
  // at least gives a reasonable answer.  
  // 
  // Beware that the basic force derivative relation is
  //
  // Xuabu = - 1/2 rho Af Cdf0
  //
  // Here, they use a more accurate version of Xuabu;
  //
  // Xuabu1 = Xuabu Cdf1
  //
  // Cdf0 is the coeffecient of drag, and Cdf1 is an emperical modifying 
  // factor that depends on speed.  See Franz's Ody. Sys. Id. report, Eqn. 4.
  //
  // Kthr is not constant here since Cdf = Cdf0*Cdf1 contains uC.
  //
  // The sign on thrAmps must be explicitly set for reverse, since this
  // equation contains only an omega^2 and cannot implicitly know the sign.
  //
  // Beware that this model is NOT used in the actual tailcone algorithm, the
  // cubic fix is.
  //
  Cdf1 = ( 2.455 - 2.175*fabs(uC) + 0.75*pow(uC,2.) );
  Kthr = ( 1. + sqrt( 1. - 4.*Xuabu*Cdf1/rho/Area) )/Eta/Pit/2.;

  omega_P  = Kthr * uC;
  thrAmps  = .5 * rho * Area * Eta * Pit * 
             ( pow( Eta*Pit*omega_P, 2. ) - pow( uC, 2. ) )/Kt;
  //
  // Retain forward motion only until the fins() routine is checked out
  // for reverse.
  //
  //thrAmps *= Math::sgn(uC);
  //
  omega_P = fabs(omega_P);
  thrAmps  = Math::limit( thrAmps, 5.0, -5.0 );

  dprintf(" Simulator: Computed propSpeedCmd = %.2f\n", omega_P );

  //
  //This was the original version of the above:
  //
//   LHS = -Xuabu*pow(uC,2.)*( 2.455 - 2.175*uC +
// 				      0.75*pow(uC,2.) ) ;
//   omega_P = ( ductArea*rho*propEfficiency*propPitch*uC +
// 	        sqrt( pow(ductArea*rho*propEfficiency*propPitch*
// 		      uC,2.) +
// 		     4.*LHS*ductArea*rho*propEfficiency*propEfficiency*
// 		     propPitch*propPitch ) ) /
//               (2.*ductArea*rho*pow(propEfficiency*propPitch,2.));
  //
  // OK, we'll do it the old way.  Compute thruster current here.
  //
//   thrAmps =
//    (.5*rho*ductArea * pow(propEfficiency * propPitch,3.) * pow(omega_P, 2.)-
//     .5*rho*ductArea *     propEfficiency * propPitch     * pow(uC,2.))/Kt;
// 			    /* make sign same as desired speed */
//   thrAmps *= Math::sgn(uC);
// 			    /* limit to +- 5 amps, update */
//   thrAmps  = Math::limit( thrAmps, 5.0, -5.0 );
#endif


  Boolean timeDebug = False;

  //  _simTime = _missionClock->seconds();

  //  dprintf("Simulator::command() - t=%.2f   elevator=%.7f\n", 
  //  _simTime, elevator);

  struct timespec startTime, endTime;

  // Get starting time
  if (timeDebug)
    clock_gettime(CLOCK_REALTIME, &startTime);

  _input.propOmega    = omega_P;
  _input.elevator     = elevator;
  _input.rudder       = rudder;

  long simTimeMsec;
  //
  // This should be simTimeMsec = (long) ( _simTime * 1000.);
  // but this compiles and then omits many "write"s.  There is a
  // rounding problem; so I'll try counting with kk.
  //
  simTimeMsec = _simTime * 1000.;
  //
  // I'd like to use mTsMsec instead of hardcoding 1000 below, but that
  // causes a floating point exception.
  //
  dprintf("DEBUG - simTime = %f, simTimeMsec=%d, modulo 1000=%d\n", 
	  _simTime, simTimeMsec, simTimeMsec % 1000);
  //  if( (simTimeMsec % 1000) == 0 ) 
  //    Syslog::write("\n simTime = %.2f\n", _simTime);
  //
  // Save and log water depth.
  _waterDepth = waterDepth(0., 0.);
  if( (kk % 5) == 0 )
  {
    Syslog::write(" simTime  = %.2f, (simTime - realTime) = %.2f\n"
		  "\tHeading  = %.2f \n\tDepth = %.2f\n\tSpeed = %.2f\n"
		  "\tPitch = %.2f",
		  _simTime, _simTime-_missionClock->seconds(),
                  _position[6]*180/PI, _position[3], _rate[1], 
		  _position[5]*180/PI);
  }
  kk++;

  for (int i = 0; i < NSteps; i++) 
  {
    motion(_rate, _position, omega_P,
	   _input.rudder, _input.elevator, _timeIncr);

    _simTime += _timeIncr;
  }
  //
  // Compute battery energy remaining and voltage:
  battery();
  //
  // Log data at every sampling instant.  Move this into motion() to log
  // at every integration step.
  //
  _log->write();
  //
  // Copy the direction cosine into an array that works with TVMult.
  //
  // Had to move this back into beamAlt and also usbl().  I don't know why it
  // didn't work here.
  // for( i=0; i<3; i++ ) 
  //    for( int j=0; j<3; j++ ) T_N_B[i][j] = psim->ctrn[i+1][j+1];

  // Trigger NewOutput event to subscribers
  triggerEvent(SimulatorIF::NewOutput);

  if (timeDebug) {
    // Get ending time
    clock_gettime(CLOCK_REALTIME, &endTime);

    dprintf("*** startTime: %d sec, %d nsec\nendTime: %d sec, %d nsec\n",
	    startTime.tv_sec, startTime.tv_nsec,
	    endTime.tv_sec, endTime.tv_nsec);

    double deltaT = 
      (endTime.tv_sec * 1000 + endTime.tv_nsec / 1000000) - 
      (startTime.tv_sec * 1000 + startTime.tv_nsec / 1000000);

    dprintf("*** deltaT = %.0f millisec\n", deltaT);
  }

  return 0;
}
void Simulator::battery( void )
{
////////////////////////////////////////////
//
// Purpose:          Simulate decreasing battery energy and voltage.
// Original Author:  R. McEwen, 17 Oct 2007.
// Change log:   (1)
//
////////////////////////////////////////////
//
// Power consumption from mapping vehicle speed step run 2004.120.06.  Propulsion 
// is modeled as P = K1*omega^3 + K0, NOT including hotel.
// [K1 K0]' =
// [(pi/30)^3*[288^3 228^3 188^3 150^3 130^3] ; 1 1 1 1 1]'\...
// [129 75.0 50.5 33.1 28]'

   double K1    = 4.0595e-003;      // 
   double K0    = 1.8406e+001;      // Watts
//
// Page 6 of the BF battery manual, 8/14/2002.  The voltage decreases to 29
// at 90% discharge.
   double yInt  = 32.0;             // Voltage at full charge.
   double y90   = 29.0;             // Voltage at 90% discharge.
   double maxDis= .9;               // Battery dies at 90% discharge.
   double slope = (y90 - yInt)/maxDis;  

   double voltage, power, discharge;
   double omega = fabs(_output.propOmega);
   double Ts    = _simTime - _lastSimTime;
   _lastSimTime = _simTime;

   //
   // Compute instantaneous power consumption:
   power     =   ( K1 * pow(omega,3.) + K0 + _Ph );
   if( omega < .1 ) power = _Ph;

   //
   // Compute energy left.  This is a state.  The equation below is an
   // accumulator.
   //printf("power = %.2f, energy = %.2f, Ts = %.2f\n", power, _energy, Ts);
   _energy   -=  power*Ts; 
   if( _energy <= 0. ) _energy = 0.;

   discharge =   1. - _energy/(_energy0*3600.*1000.);
   voltage = slope*discharge + yInt;

   if( voltage > yInt ) voltage = yInt;
   if( voltage < y90 )  voltage = y90;

//
// Assume we're using a single battery.
   if( discharge <= maxDis )
   {
      _battData.state     = BluefinBatteryIF::Discharging;
      _battData.fault     = BluefinBatteryIF::Clear;
      _battData.voltage   = voltage;
      _battData.current   = power/voltage;
   }
   else
   {
      _battData.state     = BluefinBatteryIF::Shutdown;
      _battData.fault     = BluefinBatteryIF::UnderVolt;
      _battData.voltage   = 28.;
      _battData.current   = 0.;
   }

   //
   // Log every two seconds.  It's pretty slow.
   if( (kk % 10) == 0 ) _battLog->write();
}
void Simulator::getSimBatteryData(BluefinBatteryIF::BfBattData *simBattData)
{
   memcpy( simBattData, &_battData, sizeof(BluefinBatteryIF::BfBattData) );
}

#if 0
void Simulator::hydroscat( void )
{
   _hydroscatData.snorm1 = (short) 2*_position[3];
   _hydroscatData.snorm2 = (short) 5*_position[3];
   _hydroscatData.snorm3 = (short) 10*_position[3];
   _hydroscatData.calculated.fl676_uncorr = 15.*_position[3];

   memcpy( &(_hydroscatOutput->data), &_hydroscatData, 
	   sizeof(HydroscatIF::Data));

   _hydroscatOutput->data.deviceReady = True;
   _hydroscatOutput->write();
}
void Simulator::isus( void )
{
   _isusOutput->data._deviceReady = True;
   _isusOutput->data._nitrate = 5*_position[3];
   _isusOutput->write();
}
#endif

#if 0
//***********DEBUG Remove this
void Simulator::getSimBatteryData(BluefinBatteryIF::BfBattData *simBattData)
{
   return;
}
//***********END DEBUG
#endif

long Simulator::rotationalVelocity(SimulatorIF::Vector velocity, 
				   SimulatorIF::Vector backDiff)
{
  memcpy((void *)velocity, (void *)_output.rotationalVelocity, 
	 sizeof(SimulatorIF::Vector));

  memcpy((void *)backDiff, (void *)_output.rotationalBackDiff,
	 sizeof(SimulatorIF::Vector));

  return 0;
}


long Simulator::translationalVelocity(SimulatorIF::Vector velocity, 
				      SimulatorIF::Vector backDiff)
{
  memcpy((void *)velocity, (void *)_output.translationalVelocity, 
	 sizeof(SimulatorIF::Vector));

  memcpy((void *)backDiff, (void *)_output.translationalBackDiff,
	 sizeof(SimulatorIF::Vector));

  return 0;
}

long Simulator::translationalBottomVelocity(SimulatorIF::Vector vel_Bo_N_B)
{
  memcpy((void *)vel_Bo_N_B, (void *)_output.vel_Bo_N_B,
	 sizeof(SimulatorIF::Vector));
  return 0;
}


long Simulator::state(SimulatorIF::Vector position,
		      SimulatorIF::Vector positionRate,
		      SimulatorIF::Vector eulerAngles,
		      SimulatorIF::Vector rotationRate)
{
  memcpy((void *)position, (void *)_output.position, 
	 sizeof(SimulatorIF::Vector));

  memcpy((void *)positionRate, (void *)_output.translationalVelocity,
	 sizeof(SimulatorIF::Vector));

  memcpy((void *)eulerAngles, (void *)_output.eulerAngles, 
	 sizeof(SimulatorIF::Vector));

  memcpy((void *)rotationRate, (void *)_output.rotationalVelocity,
	 sizeof(SimulatorIF::Vector));

  return 0;
}


long Simulator::controlSurfaces(double *rudder, double *elevator)
{
//   printf("Begin controlSurfaces()\n");
  *rudder = _output.rudder;
  *elevator = _output.elevator;
//   printf("End controlSurfaces()\n");

  return 0;
}


long Simulator::force(SimulatorIF::Vector vector)
{
  memcpy((void *)vector, (void *)_output.force, 
	 sizeof(SimulatorIF::Vector));

  return 0;
}


long Simulator::torque(SimulatorIF::Vector vector)
{
  memcpy((void *)vector, (void *)_output.torque,
	 sizeof(SimulatorIF::Vector));

  return 0;
}


long Simulator::propOmega(double *omega)
{
//   printf("Begin propOmega()\n");
  *omega = _output.propOmega;
//   printf("End propOmega()\n");

  return 0;
}


long Simulator::motorCurrent(double *amps)
{
  *amps = thrAmps;
  return 0;
}


double Simulator::simTime()
{
  //  return _missionClock->seconds();
  return _simTime;
}
//
// Compute depth as a function of _position + (dx,dy).
// Parameters not local here are initialized or computed in the constructor.
double Simulator::waterDepth( double dx, double dy)
{
   double depth, posx, posy;
   int i, j;
   posx = _position[1] + dx;
   posy = _position[2] + dy;


   if( _ydepth[0] == -1 || posy < _ydepth[0] ) 
   {
      depth = _depth[0];
      if( _computeGrazing )
      {
	 _bottomNormal[0]=0.;
	 _bottomNormal[1]=0.;
	 _bottomNormal[2]=1.;
      }
   }
   else if( posy< _ydepth[_nydepths-1] )
   {
      for( i=0; i< _nydepths-1; i++ )
      {
	 if( posy >= _ydepth[i] && posy < _ydepth[i+1] ) 
	 {
	    depth = _slope[i]*posy + _yinter[i];
	    if( _computeGrazing )
	       for( j=0; j<3; j++ ) _bottomNormal[j] = _normalArray[j][i];
	    break;
	 }
	 if( i >= _nydepths-1 )
	 {
	    Syslog::write("waterDepth() ERROR - Shouldn't get here. Depth = %.2f "
	    "posy = %.2f, _ydepth[%d]=%.2f, simTime= %.2f", 
	    depth, posy, i+1, _ydepth[i+1], _simTime);
	    exit(1);
	 }
      }
   }
   else
   {
      depth = _depth[ _nydepths-1 ];
      if( _computeGrazing )
	 for( j=0; j<3; j++ ) _bottomNormal[j] = _normalArray[j][_nydepths-1];
   }

   return depth;
}
//
// PURPOSE: Return the altitude of a point rho along the beam.  This is
// called by beamRange() further below.  The business with the psim pointer
// below allows this function to be passed as a function pointer to the
// bisect() routine in utils/Math.cc, by beamRange().
//
double Simulator::beamAlt( double rho )
{
   //
   // p_N is the vector from inertial origin (No) to a point p along the Dvl
   // line-of-sight (los), coordinatized in the Earth-fixed inertial frame N.
   // The depth is the third component, p_N[Z].  Then, p_N is
   //
   // p_N =  r_No_Bo_N + r_Bo_p_N.                                           (0)
   //
   // r_Bo_p_N is the vector from the body-fixed frame Bo to a point p that lies
   // along the Dvl los, coordinatized in the inertially fixed N frame.
   //
   // p_N =  r_No_Bo_N + T_N_B*( r_Bo_Dvlo_B + los_B*rho ).                  (1)
   //
   // r_No_Bo_N is the vector from No to the origin of the body-fixed frame Bo,
   // coordinatized in the inertially fixed N frame.
   //
   // This reduces to a scalar equation for the third component:
   //
   // p_N[Z] = r_No_Bo_N[Z] + [0 0 1]*( T_N_B*( r_Bo_Dvlo_B + los_B*rho ) ). (2)
   //
   // The altitude of p_N is then 
   //
   // waterDepth( r_Bo_p_N[X], r_Bo_p_N[Y]) - p_N[Z].                        (3)
   //
   // Bo is the origin of the body-fixed reference frame.
   // In Simulator.cc; r_No_Bo_N = _position, and T_N_B = ctrn[1:3][1:3].    (4)
   // Also, r_Bo_Dvlo_B = _offset_B.                                        (4')
   //
   // NOTE: For the simple case of the Dvl los, r_Bo_Dvlo_B = [0 0 0]', and
   // los_B = [0 0 1]'; so Eqns. 2 and 3 reduce to 
   //
   // altitude = waterDepth(p_N[X], p_N[Y]) - r_No_Bo_N[Z] - T_N_B[3][3]*rho.(5)
   //
   // NOTE: The Dvl is used for example here.  This algo works for any sonar.
   //
   // First compute r_Bo_p_N from Eqns. 0 and 1.
   double r_Bo_p_B[3], r_Bo_p_N[3], T_N_B[3][3];
   double altitude, los_N[3];
   //for( int i = 0; i<3; i++ ) r_Bo_p_N[i] = r_Bo_Dvlo_B[i] + los_B[i]*rho )
   for( int i = 0; i<3; i++ ) r_Bo_p_B[i] = psim->_offset_B[i] + 
			      psim->_los_B[i]*rho;
  //
  // Copy the direction cosine into an array that works with TVMult.
  //
   for( i=0; i<3; i++ ) 
      for( int j=0; j<3; j++ ) T_N_B[i][j] = psim->ctrn[i+1][j+1];

   TVMult( r_Bo_p_N, T_N_B, r_Bo_p_B );

   altitude = psim->waterDepth( r_Bo_p_N[0], r_Bo_p_N[1] ) 
              - (psim->_position[3] + r_Bo_p_N[2] );


   if( !psim->_computeGrazing ) return altitude;
   //
   // Grazing angle computation:
   //
   //for( i=0; i<3; i++) los_N[i] = r_Bo_p_N[i];
   TVMult( los_N, T_N_B, psim->_los_B );

   Vnormalize( los_N );
   //
   // Compute the grazing angle
   double losCrossNormal[3], norm;

   Vcross( losCrossNormal, los_N, psim->_bottomNormal );

   norm = Vnorm( losCrossNormal);

   if( norm  > 1. ) norm = 1.;

   psim->_grazing = PI/2. - asin( norm );

#if 0
   Syslog::write("beamAlt - los_B        = [%9.3f %9.3f %9.3f]",
   psim->_los_B[0], psim->_los_B[1], psim->_los_B[2]);
   Syslog::write("beamAlt - los_N        = [%9.3f %9.3f %9.3f]",
                  los_N[0],los_N[1],los_N[2]);
   Syslog::write("beamAlt - bottomNormal = [%9.3f %9.3f %9.3f]",
   psim->_bottomNormal[0],psim->_bottomNormal[1],psim->_bottomNormal[2]);
   Syslog::write("beamAlt - norm = %9.3f", norm);
   Syslog::write("beamAlt - grazing = %9.3f deg.", psim->_grazing*180./PI);
   Syslog::write("_simTime=%.2f", psim->_simTime);
#endif

   return altitude;

}
//
// PURPOSE: Compute range to the bottom along the specified line-of-sight. 
// 
#define MAXINC 16
double Simulator::beamRange( SimulatorIF::Vector offset_B, 
			     SimulatorIF::Vector los_B, 
			     double minRange, double maxRange, double rangeTol,
                             long *simCntr, double *grazing )
{
   double rho  = 0.;
   double drho = maxRange /( (double) MAXINC );

   _computeGrazing = False;

   //
   // Pass los_B to beamAlt() through member variables.  It can't be in
   // beamAlt's argument list because bisect requires only the independent
   // variable.
   for( int i=0; i<3; i++ ) 
   {
      _los_B[i] = los_B[i];
      _offset_B[i] = offset_B[i];
   }
   //
   if( beamAlt( minRange ) < 0. ) 
   {
      //Syslog::write("Simulator::beamRange.  Error.  Negative beam range.\n");
      return 0.;
   }
   //
   // First, ensure that the bottom is within range.  This immediately brings
   // up the issue of multiple zeros, because we can't simply check
   // beamAlt(maxRange).  If the vehicle is pitched up, the beam may pass
   // through a hill and still have beamAlt(maxRange) > 0.  So, a very crude
   // way to avoid this is to walk down the beam in fixed increments and
   // check beamAlt at each increment.
   for( i=1; i<=MAXINC; i++ )
   {
      //rho = ( (double) i)/( (double) MAXINC) * maxRange;
      rho = ( (double) i )*drho;
      if( beamAlt( rho ) < 0. ) 
      {
	 maxRange = rho;
	 minRange = rho - drho;
	 break;
      }
   }

 //if( beamAlt( maxRange ) > 0. ) return 0.;   //This is what RDI does.
   if( beamAlt( maxRange ) > 0. ) 
   {
      *grazing = 0.0;
      return maxRange; 
   }
   //
   // Use bisect to find the intersection of the beam with the bottom:
   _beamRange =  Math::bisect( beamAlt, minRange, maxRange, rangeTol, count );

   _computeGrazing = True;

   double altitude;
   altitude = beamAlt(_beamRange);

   _computeGrazing = False;

   *grazing = _grazing;

   *simCntr = (long) *count + (long) i;
   return _beamRange;
}

double Simulator::usbl(SimulatorIF::Vector usblOffset_B, 
		       SimulatorIF::Vector transponder,
		       double minRange, double maxRange, 
		       SimulatorIF::Vector usblRange )
{
   double r_N[3], T_B_N[3][3], usblOffset_N[3];
   int i;
   //
   // Copy the direction cosine into an array that works with TVMult.
   //
   for( i=0; i<3; i++ ) 
      for( int j=0; j<3; j++ ) T_N_B[i][j] = psim->ctrn[i+1][j+1];
   TTransp(T_B_N,T_N_B);
   TVMult(usblOffset_N, T_N_B, usblOffset_B);
   // 
   // _position[0] is unused, x is in _position[1], etc.
   for( i=0; i<3; i++ ) 
      r_N[i] = transponder[i] - psim->_position[i+1] - usblOffset_N[i];
   TVMult(usblRange, T_B_N, r_N);
   return 0.;
}


long Simulator::nTs()
{
  return kk;
}


int Simulator::spawnAuxTasks()
{
  return 0;
}


Simulator::InputData::InputData()
{
  vehicleMass = propOmega = rudder = elevator = 
    northCurrent = eastCurrent = 0.;
}


Simulator::OutputData::OutputData()
{
  initialize();

}


void Simulator::OutputData::initialize()
{
  for (int i = 0; i < 3; i++) {

    position[i] = eulerAngles[i] = rotationalVelocity[i] = 
      rotationalBackDiff[i] = translationalVelocity[i] = 
      translationalBackDiff[i] = force[i] = torque[i] = 0.;
  }

  rudder = elevator = propOmega = 0.;

  M1_1 = M2_1 = M3_1 = M4_1 = M1_2 = M2_2 = M3_2 = M4_2 = 0.;
}


/*-----------------------------------------------------------------------*
  Copyright (C) 1994-1998, Massachusetts Institute of Technology.
  Proprietary to Sea Grant AUV Laboratory.  All rights reserved.
  $Id: Simulator.cc,v 1.47 2011/02/11 00:23:20 rob Exp $
 *-----------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*
 | sim.c    Odyssey II hydrodynamic simulator with Franz Hover's updates |
 |                                                                       |
 | Last mod:  6/9/95 Franz Hover                                         |
 | History: copied to SGI from Mac by June 1, 1994 by jleonard           |
 |          compilation of vehicle simulation routines written by        |
 |          Jim Bellingham and other Sea Grant staff (198?-1994)         |
 |          90% of the code came from Draper, a student of Abkowitz.     |
 |                                                                       |
 |          From March to June, 1995 this code was significantly         |
 |          improved by Franz Hover.  Errors were removed and the        |
 |          model was updated based on theoretical considerations,       |
 |          Draper's modeling program, and analysis of mission data      |
 |                                                                       |
 | NOTE: this program reads hydro coefficients from two files,           |
 | get_am.tex and get_drag.tex;  The coefficients given in the source    |
 | code here are from Draper and are not as accurate as the coefficients |
 | in the files                                                          |
 *-----------------------------------------------------------------------*/


char *s;


/*----------------------------------------------------------------------*/

void Simulator::init_simulation( void )
{
  _simTime = 0.;

  _output.initialize();

  lhs();

  // Old Odyssey simulation starts by doing this
  motion(_rate, _position, 0., 0., 0., _timeIncr);
  _simTime += _timeIncr;  
  // command(0., 0., 0.);
}

/*----------------------------------------------------------------------*/

void Simulator::lhs()       /* calculates left-hand-side of the eqn */
{
  double a[7][7],work[7][7];
  int i, j, debug=0;

                                /* fsh checked inertia matrix 3/22/94 */

  a[1][1] =  _input.vehicleMass - Xudot;    /* surge force */
  a[1][2] = 0.;
  a[1][3] = 0.;
  a[1][4] = 0.;
  a[1][5] = _input.vehicleMass * _centerOfMass[Z];
  a[1][6] = -_input.vehicleMass * _centerOfMass[Y];

  a[2][1] = 0.;               /* sway force */
  a[2][2] =  _input.vehicleMass - Yvdot ;
  a[2][3] = 0.;
  a[2][4] = -_input.vehicleMass * _centerOfMass[Z] - Ypdot;
  a[2][5] = 0.;
  a[2][6] =  _input.vehicleMass * _centerOfMass[X] - Yrdot;

  a[3][1] = 0.;               /* heave force */
  a[3][2] = 0.;
  a[3][3] =  _input.vehicleMass - Zwdot;
  a[3][4] =  _input.vehicleMass * _centerOfMass[Y];
  a[3][5] = -_input.vehicleMass * _centerOfMass[X] - Zqdot;
  a[3][6] = 0.;

  a[4][1] = 0.;               /* roll moment */
  a[4][2] = -_input.vehicleMass * _centerOfMass[Z] - Kvdot;
  a[4][3] =  _input.vehicleMass * _centerOfMass[Y];
  a[4][4] =  Ixx - Kpdot;
  a[4][5] = -Ixy;
  a[4][6] = -Izx;

  a[5][1] =  _input.vehicleMass * _centerOfMass[Z];       /* pitch moment */
  a[5][2] = 0.;
  a[5][3] = -_input.vehicleMass * _centerOfMass[X] - Mwdot;
  a[5][4] = -Ixy;
  a[5][5] =  Iyy - Mqdot;
  a[5][6] = -Iyz;

  a[6][1] = -_input.vehicleMass* _centerOfMass[Y];        /* yaw moment */
  a[6][2] =  _input.vehicleMass* _centerOfMass[X] - Nvdot;
  a[6][3] = 0.;
  a[6][4] = -Izx;
  a[6][5] = -Iyz;
  a[6][6] =  Izz - Nrdot;

  if(debug)
    {
      printf("Inertia matrix: ------------ \n") ;
      for(i=1;i<=6;i++){
	for(j=1;j<=6;j++)
	  printf("%6.2f  ", a[i][j]) ;
	printf("\n");
      }
      printf("\n");
    }

  if ( matinv(a,ainv,6,work) == 1 ) {
    strcpy( _eventMsg, "Singular matrix in equations of motion LHS\n");
    _eventLog.write(EventLogIF::Info, _eventMsg);
  }
}


void Simulator::missionStatusCallback(TaskInterface *taskInterface,
				      EventCode eventCode)
{
  Boolean debug = True;

  dprintf("!!!! Simulator::missionStatusCallback() - Got event %d from \"%s\"\n", 
	  eventCode, taskInterface->name());

  dprintf("Event received at _simTime=%.2f", _simTime);

  if (taskInterface == _layeredControl) {

    switch (eventCode) {

    case LayeredControlIF::MissionStarted:
      _missionStarted = True;
      dprintf("Simulator::missionStatusCallback() - mission started\n"); 

      // Synchronize MissionClock with LayeredControl
      //      _missionClock->reset();
      _simTime = 0.;
      break;

    default:
      Syslog::write("Simulator::missionStatusCallback() - unknown event=%d!\n",
		    eventCode);
    }
  }

  return;
}
