Example #1
0
FGLocation::FGLocation(double lon, double lat, double radius)
  : mCacheValid(false)
{
  e2 = c = 0.0;
  a = ec = ec2 = 1.0;
  epa = 0.0;

  mLon = mLat = mRadius = 0.0;
  mGeodLat = GeodeticAltitude = 0.0;

  mTl2ec.InitMatrix();
  mTec2l.InitMatrix();
  mTi2ec.InitMatrix();
  mTec2i.InitMatrix();
  mTi2l.InitMatrix();
  mTl2i.InitMatrix();

  double sinLat = sin(lat);
  double cosLat = cos(lat);
  double sinLon = sin(lon);
  double cosLon = cos(lon);
  mECLoc = FGColumnVector3( radius*cosLat*cosLon,
                            radius*cosLat*sinLon,
                            radius*sinLat );
}
Example #2
0
FGColumnVector3 FGMassBalance::StructuralToBody(const FGColumnVector3& r) const
{
  // Under the assumption that in the structural frame the:
  //
  // - X-axis is directed afterwards,
  // - Y-axis is directed towards the right,
  // - Z-axis is directed upwards,
  //
  // (as documented in http://jsbsim.sourceforge.net/JSBSimCoordinates.pdf)
  // we have to subtract first the center of gravity of the plane which
  // is also defined in the structural frame:
  //
  //   FGColumnVector3 cgOff = r - vXYZcg;
  //
  // Next, we do a change of units:
  //
  //   cgOff *= inchtoft;
  //
  // And then a 180 degree rotation is done about the Y axis so that the:
  //
  // - X-axis is directed forward,
  // - Y-axis is directed towards the right,
  // - Z-axis is directed downward.
  //
  // This is needed because the structural and body frames are 180 degrees apart.

  return FGColumnVector3(inchtoft*(vXYZcg(1)-r(1)),
                         inchtoft*(r(2)-vXYZcg(2)),
                         inchtoft*(vXYZcg(3)-r(3)));
}
Example #3
0
FGColumnVector3 FGPropeller::GetPFactor()
{
  double px=0.0, py, pz;

  py = Thrust * Sense * (GetActingLocationY() - GetLocationY()) / 12.0;
  pz = Thrust * Sense * (GetActingLocationZ() - GetLocationZ()) / 12.0;

  return FGColumnVector3(px, py, pz);
}
Example #4
0
FGColumnVector3 FGPropeller::GetPFactor() const
{
  // These are moments in lbf per ft : the lever arm along Z generates a moment
  // along the pitch direction.
  double p_pitch = Thrust * Sense * (GetActingLocationZ() - GetLocationZ()) / 12.0;
  // The lever arm along Y generates a moment along the yaw direction.
  double p_yaw = Thrust * Sense * (GetActingLocationY() - GetLocationY()) / 12.0;

  return FGColumnVector3(0.0, p_pitch, p_yaw);
}
Example #5
0
bool FGPropagate::InitModel(void)
{
  if (!FGModel::InitModel()) return false;

  // For initialization ONLY:
  VState.vLocation.SetEllipse(in.SemiMajor, in.SemiMinor);
  VState.vLocation.SetAltitudeAGL(4.0);

  VState.dqPQRidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
  VState.dqUVWidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
  VState.dqInertialVelocity.resize(5, FGColumnVector3(0.0,0.0,0.0));
  VState.dqQtrndot.resize(5, FGColumnVector3(0.0,0.0,0.0));

  integrator_rotational_rate = eRectEuler;
  integrator_translational_rate = eAdamsBashforth2;
  integrator_rotational_position = eRectEuler;
  integrator_translational_position = eAdamsBashforth3;

  return true;
}
Example #6
0
void FGLocation::SetPosition(double lon, double lat, double radius)
{
  mCacheValid = false;

  double sinLat = sin(lat);
  double cosLat = cos(lat);
  double sinLon = sin(lon);
  double cosLon = cos(lon);

  mECLoc = FGColumnVector3( radius*cosLat*cosLon,
                            radius*cosLat*sinLon,
                            radius*sinLat );
}
Example #7
0
FGPropagate::FGPropagate(FGFDMExec* fdmex)
  : FGModel(fdmex),
    VehicleRadius(0)
{
  Debug(0);
  Name = "FGPropagate";

  /// These define the indices use to select the various integrators.
  // eNone = 0, eRectEuler, eTrapezoidal, eAdamsBashforth2, eAdamsBashforth3, eAdamsBashforth4};

  integrator_rotational_rate = eRectEuler;
  integrator_translational_rate = eAdamsBashforth2;
  integrator_rotational_position = eRectEuler;
  integrator_translational_position = eAdamsBashforth3;

  VState.dqPQRidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
  VState.dqUVWidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
  VState.dqInertialVelocity.resize(5, FGColumnVector3(0.0,0.0,0.0));
  VState.dqQtrndot.resize(5, FGQuaternion(0.0,0.0,0.0));

  bind();
  Debug(0);
}
Example #8
0
void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
{
  SetSeaLevelRadius(FGIC->GetSeaLevelRadiusFtIC());
  SetTerrainElevation(FGIC->GetTerrainElevationFtIC());

  // Set the position lat/lon/radius
  VState.vLocation.SetPosition( FGIC->GetLongitudeRadIC(),
                          FGIC->GetLatitudeRadIC(),
                          FGIC->GetAltitudeASLFtIC() + FGIC->GetSeaLevelRadiusFtIC() );

  VehicleRadius = GetRadius();
  radInv = 1.0/VehicleRadius;

  // Set the Orientation from the euler angles
  VState.vQtrn = FGQuaternion( FGIC->GetPhiRadIC(),
                        FGIC->GetThetaRadIC(),
                        FGIC->GetPsiRadIC() );

  // Set the velocities in the instantaneus body frame
  VState.vUVW = FGColumnVector3( FGIC->GetUBodyFpsIC(),
                          FGIC->GetVBodyFpsIC(),
                          FGIC->GetWBodyFpsIC() );

  // Set the angular velocities in the instantaneus body frame.
  VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
                          FGIC->GetQRadpsIC(),
                          FGIC->GetRRadpsIC() );

  // Compute the local frame ECEF velocity
  vVel = GetTb2l()*VState.vUVW;

  // Finally, make sure that the quaternion stays normalized.
  VState.vQtrn.Normalize();

  // Recompute the LocalTerrainRadius.
  RecomputeLocalTerrainRadius();
}
Example #9
0
bool FGAccelerometer::Run(void )
{
  // There is no input assumed. This is a dedicated acceleration sensor.

  vRadius = MassBalance->StructuralToBody(vLocation);
    
  //gravitational forces
  vAccel = Propagate->GetTl2b() * FGColumnVector3(0, 0, Inertial->gravity());

  //aircraft forces
  vAccel += (Accelerations->GetUVWdot()
              + Accelerations->GetPQRdot() * vRadius
              + Propagate->GetPQR() * (Propagate->GetPQR() * vRadius));

  // transform to the specified orientation
  vAccel = mT * vAccel;

  Input = vAccel(axis);

  ProcessSensorSignal();

  return true;
}
Example #10
0
bool FGPropagate::InitModel(void)
{
  if (!FGModel::InitModel()) return false;

  // For initialization ONLY:
  SeaLevelRadius = LocalTerrainRadius = Inertial->GetRefRadius();

  VState.vLocation.SetRadius( LocalTerrainRadius + 4.0 );
  VState.vLocation.SetEllipse(Inertial->GetSemimajor(), Inertial->GetSemiminor());
  vOmega = FGColumnVector3( 0.0, 0.0, Inertial->omega() ); // Earth rotation vector

  last2_vPQRdot.InitMatrix();
  last_vPQRdot.InitMatrix();
  vPQRdot.InitMatrix();
  
  last2_vQtrndot = FGQuaternion(0,0,0);
  last_vQtrndot = FGQuaternion(0,0,0);
  vQtrndot = FGQuaternion(0,0,0);

  last2_vUVWdot.InitMatrix();
  last_vUVWdot.InitMatrix();
  vUVWdot.InitMatrix();
  
  last2_vLocationDot.InitMatrix();
  last_vLocationDot.InitMatrix();
  vLocationDot.InitMatrix();

  vOmegaLocal.InitMatrix();

  integrator_rotational_rate = eAdamsBashforth2;
  integrator_translational_rate = eTrapezoidal;
  integrator_rotational_position = eAdamsBashforth2;
  integrator_translational_position = eTrapezoidal;

  return true;
}
Example #11
0
FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, unsigned int num,
                     const struct Inputs& input)
  : FGForce(exec), in(input)
{
  string token;
  Element* element;

  FGPropertyManager* PropertyManager = exec->GetPropertyManager();
  MassBalance = exec->GetMassBalance();

  gasCellJ = FGMatrix33();
  gasCellM = FGColumnVector3();

  Buoyancy = MaxVolume = MaxOverpressure = Temperature = Pressure =
    Contents = Volume = dVolumeIdeal = 0.0;
  Xradius = Yradius = Zradius = Xwidth = Ywidth = Zwidth = 0.0;
  ValveCoefficient = ValveOpen = 0.0;
  CellNum = num;

  // NOTE: In the local system X points north, Y points east and Z points down.
  SetTransformType(FGForce::tLocalBody);

  type = el->GetAttributeValue("type");
  if      (type == "HYDROGEN") Type = ttHYDROGEN;
  else if (type == "HELIUM")   Type = ttHELIUM;
  else if (type == "AIR")      Type = ttAIR;
  else                         Type = ttUNKNOWN;

  element = el->FindElement("location");
  if (element) {
    vXYZ = element->FindElementTripletConvertTo("IN");
  } else {
      std::stringstream error;
    error << "Fatal Error: No location found for this gas cell." << endl;
    throw std::runtime_error(error.str());
  }
  if ((el->FindElement("x_radius") || el->FindElement("x_width")) &&
      (el->FindElement("y_radius") || el->FindElement("y_width")) &&
      (el->FindElement("z_radius") || el->FindElement("z_width"))) {

    if (el->FindElement("x_radius")) {
      Xradius = el->FindElementValueAsNumberConvertTo("x_radius", "FT");
    }
    if (el->FindElement("y_radius")) {
      Yradius = el->FindElementValueAsNumberConvertTo("y_radius", "FT");
    }
    if (el->FindElement("z_radius")) {
      Zradius = el->FindElementValueAsNumberConvertTo("z_radius", "FT");
    }

    if (el->FindElement("x_width")) {
      Xwidth = el->FindElementValueAsNumberConvertTo("x_width", "FT");
    }
    if (el->FindElement("y_width")) {
      Ywidth = el->FindElementValueAsNumberConvertTo("y_width", "FT");
    }
    if (el->FindElement("z_width")) {
      Zwidth = el->FindElementValueAsNumberConvertTo("z_width", "FT");
    }

    // The volume is a (potentially) extruded ellipsoid.
    // However, currently only a few combinations of radius and width are
    // fully supported.
    if ((Xradius != 0.0) && (Yradius != 0.0) && (Zradius != 0.0) &&
        (Xwidth  == 0.0) && (Ywidth  == 0.0) && (Zwidth  == 0.0)) {
      // Ellipsoid volume.
      MaxVolume = 4.0  * M_PI * Xradius * Yradius * Zradius / 3.0;
    } else if  ((Xradius == 0.0) && (Yradius != 0.0) && (Zradius != 0.0) &&
                (Xwidth  != 0.0) && (Ywidth  == 0.0) && (Zwidth  == 0.0)) {
      // Cylindrical volume.
      MaxVolume = M_PI * Yradius * Zradius * Xwidth;
    } else {
      cerr << "Warning: Unsupported gas cell shape." << endl;
      MaxVolume = 
        (4.0  * M_PI * Xradius * Yradius * Zradius / 3.0 +
         M_PI * Yradius * Zradius * Xwidth +
         M_PI * Xradius * Zradius * Ywidth +
         M_PI * Xradius * Yradius * Zwidth +
         2.0  * Xradius * Ywidth * Zwidth +
         2.0  * Yradius * Xwidth * Zwidth +
         2.0  * Zradius * Xwidth * Ywidth +
         Xwidth * Ywidth * Zwidth);
    }
  } else {
      std::stringstream error;
    error << "Fatal Error: Gas cell shape must be given." << endl;
    throw std::runtime_error(error.str());
  }
  if (el->FindElement("max_overpressure")) {
    MaxOverpressure = el->FindElementValueAsNumberConvertTo("max_overpressure",
                                                            "LBS/FT2");
  }
  if (el->FindElement("fullness")) {
    const double Fullness = el->FindElementValueAsNumber("fullness");
    if (0 <= Fullness) { 
      Volume = Fullness * MaxVolume; 
    } else {
      cerr << "Warning: Invalid initial gas cell fullness value." << endl;
    }
  }  
  if (el->FindElement("valve_coefficient")) {
    ValveCoefficient =
      el->FindElementValueAsNumberConvertTo("valve_coefficient",
                                            "FT4*SEC/SLUG");
    ValveCoefficient = max(ValveCoefficient, 0.0);
  }

  // Initialize state
  SetLocation(vXYZ);

  if (Temperature == 0.0) {
    Temperature = in.Temperature;
  }
  if (Pressure == 0.0) {
    Pressure = in.Pressure;
  }
  if (Volume != 0.0) {
    // Calculate initial gas content.
    Contents = Pressure * Volume / (R * Temperature);
    
    // Clip to max allowed value.
    const double IdealPressure = Contents * R * Temperature / MaxVolume;
    if (IdealPressure > Pressure + MaxOverpressure) {
      Contents = (Pressure + MaxOverpressure) * MaxVolume / (R * Temperature);
      Pressure = Pressure + MaxOverpressure;
    } else {
      Pressure = max(IdealPressure, Pressure);
    }
  } else {
    // Calculate initial gas content.
    Contents = Pressure * MaxVolume / (R * Temperature);
  }

  Volume = Contents * R * Temperature / Pressure;
  Mass = Contents * M_gas();

  // Bind relevant properties
  string property_name, base_property_name;

  base_property_name = CreateIndexedPropertyName("buoyant_forces/gas-cell", CellNum);

  property_name = base_property_name + "/max_volume-ft3";
  PropertyManager->Tie( property_name.c_str(), &MaxVolume, false );
  PropertyManager->GetNode()->SetWritable( property_name, false );
  property_name = base_property_name + "/temp-R";
  PropertyManager->Tie( property_name.c_str(), &Temperature, false );
  property_name = base_property_name + "/pressure-psf";
  PropertyManager->Tie( property_name.c_str(), &Pressure, false );
  property_name = base_property_name + "/volume-ft3";
  PropertyManager->Tie( property_name.c_str(), &Volume, false );
  property_name = base_property_name + "/buoyancy-lbs";
  PropertyManager->Tie( property_name.c_str(), &Buoyancy, false );
  property_name = base_property_name + "/contents-mol";
  PropertyManager->Tie( property_name.c_str(), &Contents, false );
  property_name = base_property_name + "/valve_open";
  PropertyManager->Tie( property_name.c_str(), &ValveOpen, false );

  Debug(0);

  // Read heat transfer coefficients
  if (Element* heat = el->FindElement("heat")) {
    Element* function_element = heat->FindElement("function");
    while (function_element) {
      HeatTransferCoeff.push_back(new FGFunction(PropertyManager,
                                                 function_element));
      function_element = heat->FindNextElement("function");
    }
  }

  // Load ballonets if there are any
  if (Element* ballonet_element = el->FindElement("ballonet")) {
    while (ballonet_element) {
      Ballonet.push_back(new FGBallonet(exec,
                                        ballonet_element,
                                        Ballonet.size(),
                                        this, in));
      ballonet_element = el->FindNextElement("ballonet");
    }
  }

}
Example #12
0
bool FGAuxiliary::Run()
{
  double A,B,D;

  if (FGModel::Run()) return true; // return true if error returned from base class
  if (FDMExec->Holding()) return false;

  const FGColumnVector3& vPQR = Propagate->GetPQR();
  const FGColumnVector3& vUVW = Propagate->GetUVW();
  const FGColumnVector3& vUVWdot = Propagate->GetUVWdot();
  const FGColumnVector3& vVel = Propagate->GetVel();

  p = Atmosphere->GetPressure();
  rhosl = Atmosphere->GetDensitySL();
  psl = Atmosphere->GetPressureSL();
  sat = Atmosphere->GetTemperature();

// Rotation

  double cTht = Propagate->GetCosEuler(eTht);
  double sTht = Propagate->GetSinEuler(eTht);
  double cPhi = Propagate->GetCosEuler(ePhi);
  double sPhi = Propagate->GetSinEuler(ePhi);

  vEulerRates(eTht) = vPQR(eQ)*cPhi - vPQR(eR)*sPhi;
  if (cTht != 0.0) {
    vEulerRates(ePsi) = (vPQR(eQ)*sPhi + vPQR(eR)*cPhi)/cTht;
    vEulerRates(ePhi) = vPQR(eP) + vEulerRates(ePsi)*sTht;
  }

// 12/16/2005, JSB: For ground handling purposes, at this time, let's ramp
// in the effects of wind from 10 fps to 30 fps when there is weight on the
// landing gear wheels.

  if (GroundReactions->GetWOW() && vUVW(eU) < 10) {
    vAeroPQR = vPQR;
    vAeroUVW = vUVW;
  } else if (GroundReactions->GetWOW() && vUVW(eU) < 30) {
    double factor = (vUVW(eU) - 10.0)/20.0;
    vAeroPQR = vPQR - factor*Atmosphere->GetTurbPQR();
    vAeroUVW = vUVW - factor*Propagate->GetTl2b()*Atmosphere->GetTotalWindNED();
  } else {
    FGColumnVector3 wind = Propagate->GetTl2b()*Atmosphere->GetTotalWindNED();
    vAeroPQR = vPQR - Atmosphere->GetTurbPQR();
    vAeroUVW = vUVW - wind;
  }

  Vt = vAeroUVW.Magnitude();
  if ( Vt > 0.05) {
    if (vAeroUVW(eW) != 0.0)
      alpha = vAeroUVW(eU)*vAeroUVW(eU) > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0;
    if (vAeroUVW(eV) != 0.0)
      beta = vAeroUVW(eU)*vAeroUVW(eU)+vAeroUVW(eW)*vAeroUVW(eW) > 0.0 ? atan2(vAeroUVW(eV),
             sqrt(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW))) : 0.0;

    double mUW = (vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW));
    double signU=1;
    if (vAeroUVW(eU) != 0.0)
      signU = vAeroUVW(eU)/fabs(vAeroUVW(eU));

    if ( (mUW == 0.0) || (Vt == 0.0) ) {
      adot = 0.0;
      bdot = 0.0;
    } else {
      adot = (vAeroUVW(eU)*vUVWdot(eW) - vAeroUVW(eW)*vUVWdot(eU))/mUW;
      bdot = (signU*mUW*vUVWdot(eV) - vAeroUVW(eV)*(vAeroUVW(eU)*vUVWdot(eU)
              + vAeroUVW(eW)*vUVWdot(eW)))/(Vt*Vt*sqrt(mUW));
    }
  } else {
    alpha = beta = adot = bdot = 0;
  }

  Re = Vt * Aircraft->Getcbar() / Atmosphere->GetKinematicViscosity();

  qbar = 0.5*Atmosphere->GetDensity()*Vt*Vt;
  qbarUW = 0.5*Atmosphere->GetDensity()*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW));
  qbarUV = 0.5*Atmosphere->GetDensity()*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eV)*vAeroUVW(eV));
  Mach = Vt / Atmosphere->GetSoundSpeed();
  MachU = vMachUVW(eU) = vAeroUVW(eU) / Atmosphere->GetSoundSpeed();
  vMachUVW(eV) = vAeroUVW(eV) / Atmosphere->GetSoundSpeed();
  vMachUVW(eW) = vAeroUVW(eW) / Atmosphere->GetSoundSpeed();

// Position

  Vground = sqrt( vVel(eNorth)*vVel(eNorth) + vVel(eEast)*vVel(eEast) );

  psigt = atan2(vVel(eEast), vVel(eNorth));
  if (psigt < 0.0) psigt += 2*M_PI;
  gamma = atan2(-vVel(eDown), Vground);

  tat = sat*(1 + 0.2*Mach*Mach); // Total Temperature, isentropic flow
  tatc = RankineToCelsius(tat);

  if (MachU < 1) {   // Calculate total pressure assuming isentropic flow
    pt = p*pow((1 + 0.2*MachU*MachU),3.5);
  } else {
    // Use Rayleigh pitot tube formula for normal shock in front of pitot tube
    B = 5.76*MachU*MachU/(5.6*MachU*MachU - 0.8);
    D = (2.8*MachU*MachU-0.4)*0.4167;
    pt = p*pow(B,3.5)*D;
  }

  A = pow(((pt-p)/psl+1),0.28571);
  if (MachU > 0.0) {
    vcas = sqrt(7*psl/rhosl*(A-1));
    veas = sqrt(2*qbar/rhosl);
  } else {
    vcas = veas = 0.0;
  }

  vPilotAccel.InitMatrix();
  if ( Vt > 1.0 ) {
     vAircraftAccel = Aerodynamics->GetForces()
                    + Propulsion->GetForces()
                    + GroundReactions->GetForces()
                    + ExternalReactions->GetForces()
                    + BuoyantForces->GetForces();

     vAircraftAccel /= MassBalance->GetMass();
     // Nz is Acceleration in "g's", along normal axis (-Z body axis)
     Nz = -vAircraftAccel(eZ)/Inertial->gravity();
     vToEyePt = MassBalance->StructuralToBody(Aircraft->GetXYZep());
     vPilotAccel = vAircraftAccel + Propagate->GetPQRdot() * vToEyePt;
     vPilotAccel += vPQR * (vPQR * vToEyePt);
  } else {
     // The line below handles low velocity (and on-ground) cases, basically
     // representing the opposite of the force that the landing gear would
     // exert on the ground (which is just the total weight). This eliminates
     // any jitter that could be introduced by the landing gear. Theoretically,
     // this branch could be eliminated, with a penalty of having a short
     // transient at startup (lasting only a fraction of a second).
     vPilotAccel = Propagate->GetTl2b() * FGColumnVector3( 0.0, 0.0, -Inertial->gravity() );
     Nz = -vPilotAccel(eZ)/Inertial->gravity();
  }

  vPilotAccelN = vPilotAccel/Inertial->gravity();

  // VRP computation
  const FGLocation& vLocation = Propagate->GetLocation();
  FGColumnVector3 vrpStructural = Aircraft->GetXYZvrp();
  FGColumnVector3 vrpBody = MassBalance->StructuralToBody( vrpStructural );
  FGColumnVector3 vrpLocal = Propagate->GetTb2l() * vrpBody;
  vLocationVRP = vLocation.LocalToLocation( vrpLocal );

  // Recompute some derived values now that we know the dependent parameters values ...
  hoverbcg = Propagate->GetDistanceAGL() / Aircraft->GetWingSpan();

  FGColumnVector3 vMac = Propagate->GetTb2l()*MassBalance->StructuralToBody(Aircraft->GetXYZrp());
  hoverbmac = (Propagate->GetDistanceAGL() + vMac(3)) / Aircraft->GetWingSpan();

  // when all model are executed, 
  // please calculate the distance from the initial point

  CalculateRelativePosition();

  return false;
}