Example #1
0
FGRotor::FGRotor(FGFDMExec *exec, Element* rotor_element, int num)
                    : FGThruster(exec, rotor_element, num)
{

  FGColumnVector3 location, orientation;
  Element *thruster_element;

  PropertyManager = fdmex->GetPropertyManager();
  dt = fdmex->GetDeltaT();

  /* apply defaults */

  rho = 0.002356; // just a sane value
  
  RPM = 0.0;
  Sense = 1.0;
  tailRotorPresent = false;
  
  effective_tail_col = 0.001; // just a sane value 

  prop_inflow_ratio_lambda =  0.0;
  prop_advance_ratio_mu = 0.0; 
  prop_inflow_ratio_induced_nu = 0.0;
  prop_mr_torque = 0.0;
  prop_thrust_coefficient = 0.0;
  prop_coning_angle = 0.0;

  prop_theta_downwash = prop_phi_downwash = 0.0;

  hover_threshold = 0.0;
  hover_scale = 0.0;

  mr.zero();
  tr.zero();

  // debug stuff
  prop_DumpFlag = 0;

  /* configure */

  Type = ttRotor;
  SetTransformType(FGForce::tCustom);

  // get data from parent and 'mount' the rotor system

  thruster_element = rotor_element->GetParent()->FindElement("sense");
  if (thruster_element) {
    Sense = thruster_element->GetDataAsNumber() >= 0.0 ? 1.0: -1.0;
  }

  thruster_element = rotor_element->GetParent()->FindElement("location");
  if (thruster_element)  location = thruster_element->FindElementTripletConvertTo("IN");
  else          cerr << "No thruster location found." << endl;

  thruster_element = rotor_element->GetParent()->FindElement("orient");
  if (thruster_element)  orientation = thruster_element->FindElementTripletConvertTo("RAD");
  else          cerr << "No thruster orientation found." << endl;

  SetLocation(location);
  SetAnglesToBody(orientation);

  // get main rotor parameters 
  mr.parent = rotor_element;

  int flags = eMain;

  string a_val="";
  a_val = rotor_element->GetAttributeValue("variant");
  if ( a_val == "coaxial" ) {
    flags += eCoaxial;
    cerr << "# found 'coaxial' variant" << endl;
  }  

  if (Sense<0.0) {
    flags += eRotCW;
  }
    
  mr.configure(flags);

  mr.rk.init(0,dt,6);

  // get tail rotor parameters
  tr.parent=rotor_element->FindElement("tailrotor");
  if (tr.parent) {
    tailRotorPresent = true;
  } else {
    tailRotorPresent = false;
    cerr << "# No tailrotor found, assuming a single rotor." << endl;
  }

  if (tailRotorPresent) {
    int flags = eTail;
    if (Sense<0.0) {
      flags += eRotCW;
    }
    tr.configure(flags, &mr);
    tr.rk.init(0,dt,6);
    tr.RpmRatio = tr.NominalRPM/mr.NominalRPM; // 'connect'
  }

  /* remaining parameters */

  // ground effect  
  double c_ground_effect = 0.0;  // uh1 ~ 0.28 , larger values increase the effect
  ground_effect_exp = 0.0;  
  ground_effect_shift = 0.0;

  if (rotor_element->FindElement("cgroundeffect"))
    c_ground_effect = rotor_element->FindElementValueAsNumber("cgroundeffect");

  if (rotor_element->FindElement("groundeffectshift"))
    ground_effect_shift = rotor_element->FindElementValueAsNumberConvertTo("groundeffectshift","FT");

  // prepare calculations, see /TA77/
  if (c_ground_effect > 1e-5) {
    ground_effect_exp = 1.0 / ( 2.0*mr.Radius * c_ground_effect );
  } else {
    ground_effect_exp = 0.0; // disable
  }

  // smooth out jumps in hagl reported, otherwise the ground effect
  // calculation would cause jumps too. 1Hz seems sufficient.
  damp_hagl = Filter(1.0,dt);


  // misc, experimental
  if (rotor_element->FindElement("hoverthreshold"))
    hover_threshold = rotor_element->FindElementValueAsNumberConvertTo("hoverthreshold", "FT/SEC");

  if (rotor_element->FindElement("hoverscale"))
    hover_scale = rotor_element->FindElementValueAsNumber("hoverscale");

  // enable import-export
  bind();

  // unused right now
  prop_rotorbrake->setDoubleValue(0.0);
  prop_freewheel_factor->setDoubleValue(1.0);  

  Debug(0);

}  // Constructor
Example #2
0
FGThruster::FGThruster(FGFDMExec *FDMExec, Element *el, int num ): FGForce(FDMExec)
{
  Element* thruster_element = el->GetParent();
  Element* element;
  FGColumnVector3 location, orientation, pointing;

  Type = ttDirect;
  SetTransformType(FGForce::tCustom);

  Name = el->GetAttributeValue("name");

  GearRatio = 1.0;
  ReverserAngle = 0.0;
  Thrust = 0.0;
  EngineNum = num;
  PropertyManager = FDMExec->GetPropertyManager();

// Determine the initial location and orientation of this thruster and load the
// thruster with this information.

  element = thruster_element->FindElement("location");
  if (element)  location = element->FindElementTripletConvertTo("IN");
  else          cerr << fgred << "      No thruster location found." << reset << endl;

  SetLocation(location);

  string property_name, base_property_name;
  base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNum);

  element = thruster_element->FindElement("pointing");
  if (element)  {

    // This defines a fixed nozzle that has no public interface property to gimbal or reverse it.
    pointing = element->FindElementTripletConvertTo("RAD"); // The specification of RAD here is superfluous,
                                                            // and simply precludes a conversion.
    mT.InitMatrix();
    mT(1,1) = pointing(1);
    mT(2,1) = pointing(2);
    mT(3,1) = pointing(3);

  } else {

  element = thruster_element->FindElement("orient");
  if (element)  orientation = element->FindElementTripletConvertTo("RAD");

  SetAnglesToBody(orientation);
  property_name = base_property_name + "/pitch-angle-rad";
  PropertyManager->Tie( property_name.c_str(), (FGForce *)this, &FGForce::GetPitch, &FGForce::SetPitch);
  property_name = base_property_name + "/yaw-angle-rad";
  PropertyManager->Tie( property_name.c_str(), (FGForce *)this, &FGForce::GetYaw, &FGForce::SetYaw);

  if (el->GetName() == "direct") // this is a direct thruster. At this time
                                 // only a direct thruster can be reversed.
  {
    property_name = base_property_name + "/reverser-angle-rad";
    PropertyManager->Tie( property_name.c_str(), (FGThruster *)this, &FGThruster::GetReverserAngle,
                                                          &FGThruster::SetReverserAngle);
  }

  }


  Debug(0);
}
Example #3
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");
    }
  }

}