vForces = State->GetTs2b()*vFs;
- vDXYZcg(eX) = -(Aircraft->GetXYZrp(eX)
- - MassBalance->GetXYZcg(eX))*inchtoft;
- vDXYZcg(eY) = (Aircraft->GetXYZrp(eY)
- - MassBalance->GetXYZcg(eY))*inchtoft;
- vDXYZcg(eZ) = -(Aircraft->GetXYZrp(eZ)
- - MassBalance->GetXYZcg(eZ))*inchtoft;
+ vDXYZcg = MassBalance->StructuralToBody(Aircraft->GetXYZrp());
vMoments = vDXYZcg*vForces; // M = r X F
tatc=RankineToCelsius(tat);
if (mach < 1) { //calculate total pressure assuming isentropic flow
- pt=p*pow((1 + 0.2*machU*machU),3.5);
+ pt = p*pow((1 + 0.2*machU*machU),3.5);
} else {
// shock in front of pitot tube, we'll assume its normal and use
// the Rayleigh Pitot Tube Formula, i.e. the ratio of total
vPilotAccel.InitMatrix();
if ( Translation->GetVt() > 1 ) {
vPilotAccel = Aerodynamics->GetForces()
- + Propulsion->GetForces()
- + GroundReactions->GetForces();
+ + Propulsion->GetForces()
+ + GroundReactions->GetForces();
vPilotAccel /= MassBalance->GetMass();
- vToEyePt = Aircraft->GetXYZep() - MassBalance->GetXYZcg();
- vToEyePt *= inchtoft;
+ vToEyePt = MassBalance->StructuralToBody(Aircraft->GetXYZep());
vPilotAccel += Rotation->GetPQRdot() * vToEyePt;
vPilotAccel += Rotation->GetPQR() * (Rotation->GetPQR() * vToEyePt);
} else {
}
vPilotAccelN = vPilotAccel/Inertial->gravity();
-
-
+
earthPosAngle += State->Getdt()*Inertial->omega();
return false;
} else {
PropAdvanceCmd.clear();
PropAdvance.clear();
-
unsigned int i;
for (i=0;i<APComponents.size();i++) delete APComponents[i];
// needs to be done like this to convert from structural to body coords.
// CG and RP values are in inches
- vDXYZ(eX) = -(vActingXYZn(eX) - fdmex->GetMassBalance()->GetXYZcg(eX))*inchtoft;
- vDXYZ(eY) = (vActingXYZn(eY) - fdmex->GetMassBalance()->GetXYZcg(eY))*inchtoft;
- vDXYZ(eZ) = -(vActingXYZn(eZ) - fdmex->GetMassBalance()->GetXYZcg(eZ))*inchtoft;
+ vDXYZ = fdmex->GetMassBalance()->StructuralToBody(vActingXYZn);
vM = vMn + vDXYZ*vFb;
const double FGJSBBase::ktstofps = 1.68781;
const double FGJSBBase::inchtoft = 0.08333333;
const double FGJSBBase::in3tom3 = 1.638706E-5;
-const double FGJSBBase::Reng = 1716.0;
+double FGJSBBase::Reng = 1716.0;
const double FGJSBBase::SHRatio = 1.40;
-const string FGJSBBase::needed_cfg_version = "1.60";
+const string FGJSBBase::needed_cfg_version = "1.61";
const string FGJSBBase::JSBSim_version = "0.9.5";
std::queue <FGJSBBase::Message*> FGJSBBase::Messages;
static const double ktstofps;
static const double inchtoft;
static const double in3tom3;
- static const double Reng; // Specific Gas Constant,ft^2/(sec^2*R)
+ static double Reng; // Specific Gas Constant,ft^2/(sec^2*R)
static const double SHRatio;
static const string needed_cfg_version;
static const string JSBSim_version;
MaximumStrutForce = MaximumStrutTravel = 0.0;
SinkRate = GroundSpeed = 0.0;
- vWhlBodyVec = (vXYZ - MassBalance->GetXYZcg()) / 12.0;
- vWhlBodyVec(eX) = -vWhlBodyVec(eX);
- vWhlBodyVec(eZ) = -vWhlBodyVec(eZ);
+ vWhlBodyVec = MassBalance->StructuralToBody(vXYZ);
vLocalGear = State->GetTb2l() * vWhlBodyVec;
if (GearDown) {
- vWhlBodyVec = (vXYZ - MassBalance->GetXYZcg()) / 12.0;
- vWhlBodyVec(eX) = -vWhlBodyVec(eX);
- vWhlBodyVec(eZ) = -vWhlBodyVec(eZ);
+ vWhlBodyVec = MassBalance->StructuralToBody(vXYZ);
// vWhlBodyVec now stores the vector from the cg to this wheel
FCoeff = dynamicFCoeff*fabs(WheelSlip)/WheelSlip;
}
-#if 0
- // A negative force coefficient will result in a force pulling the wheel(s)
- // back instead of trying to stop them from moving.
- if (FCoeff < 0.0)
- FCoeff = 0.0;
-#endif
-
// Compute the vertical force on the wheel using square-law damping (per comment
// in paper AIAA-2000-4303 - see header prologue comments). We might consider
// allowing for both square and linear damping force calculation. Also need to
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+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)));
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
void FGMassBalance::bind(void)
{
typedef double (FGMassBalance::*PMF)(int) const;
inline FGColumnVector3& GetXYZcg(void) {return vXYZcg;}
inline double GetXYZcg(int axis) const {return vXYZcg(axis);}
+ /** Conversion from the structural frame to the body frame.
+ * Converts the argument \parm r given in the reference frame
+ * coordinate system to the body frame. The units of the structural
+ * frame are assumed to be in inches. The unit of the result is in
+ * ft.
+ */
+ FGColumnVector3 StructuralToBody(const FGColumnVector3& r) const;
+
inline void SetEmptyWeight(double EW) { EmptyWeight = EW;}
inline void SetBaseIxx(double bixx) { baseIxx = bixx;}
inline void SetBaseIyy(double biyy) { baseIyy = biyy;}
# else
# include <cmath>
# endif
-# include <iostream>
using std::ostream;
using std::istream;
using std::cerr;
h = Radius - SeaLevelRadius; // Geocentric
- vVRPoffset = State->GetTb2l() * (vVRP - MassBalance->GetXYZcg());
- vVRPoffset /= 12.0; // converted to feet
+ vVRPoffset = State->GetTb2l() * MassBalance->StructuralToBody(Aircraft->GetXYZvrp());
// vVRP - the vector to the Visual Reference Point - now contains the
// offset from the CG to the VRP, in units of feet, in the Local coordinate
LongitudeVRP = vVRPoffset(eEast) / (Radius * cosLat) + Longitude;
LatitudeVRP = vVRPoffset(eNorth) / Radius + Latitude;
- hVRP = vVRPoffset(eDown) + h;
+ hVRP = h - vVRPoffset(eDown);
/*
cout << "Lat/Lon/Alt : " << Latitude << " / " << Longitude << " / " << h << endl;
cout << "Lat/Lon/Alt VRP: " << LatitudeVRP << " / " << LongitudeVRP << " / " << hVRP << endl << endl;
*/
DistanceAGL = Radius - RunwayRadius; // Geocentric
-
+
hoverbcg = DistanceAGL/b;
-
- vMac = State->GetTb2l()*Aircraft->GetXYZrp();
-
- vMac *= inchtoft;
- hoverbmac = (DistanceAGL + vMac(3))/b;
+
+ vMac = State->GetTb2l()*MassBalance->StructuralToBody(Aircraft->GetXYZrp());
+ hoverbmac = (DistanceAGL + vMac(3)) / b;
if (Vt > 0) {
hdot_Vt = RadiusDot/Vt;
OilPressure_psi = N2 * 0.62;
NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
EPR = Seek(&EPR, 1.0, 0.2, 0.2);
+ Augmentation = false;
return 0.0;
}
# include <fstream.h>
# endif
#else
-# if defined(sgi) && !defined(__GNUC__)
+# if defined(sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
# include <fstream.h>
# else
# include <fstream>
}
fdmex = new FGFDMExec( (FGPropertyManager*)globals->get_props() );
-
+
State = fdmex->GetState();
Atmosphere = fdmex->GetAtmosphere();
FCS = fdmex->GetFCS();
SG_LOG( SG_FLIGHT, SG_ALERT, "Halting the sim now, and hoping a solution will present itself soon!");
exit(-1);
}
-
-
+
init_gear();
// Set initial fuel levels if provided.
using namespace JSBSim;
-/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-COMMENTS, REFERENCES, and NOTES [use "class documentation" below for API docs]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@author Tony Peden (Maintained and refined)
@version $Id$
@see main in file JSBSim.cpp (use main() wrapper for standalone usage)
- @see <a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/jsbsim/JSBSim/JSBSim.hxx?rev=HEAD&content-type=text/vnd.viewcvs-markup">
- Header File </a>
- @see <a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/jsbsim/JSBSim/JSBSim.cxx?rev=HEAD&content-type=text/vnd.viewcvs-markup">
- Source File </a>
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models a deadband object.
- Owned and Operated by the FGFCS class.
-
- <COMPONENT NAME="Deadbeat1" TYPE="DEADBAND">
- INPUT {input}
- WIDTH {deadband width}
- GAIN {optional deadband gain}
- MIN {minimum value}
- MAX {maximum value}
- OUTPUT {optional output parameter to set}
- </COMPONENT>
-
+ Here is the format of the deadband control specification:
+ <pre>
+ \<COMPONENT NAME="Deadbeat1" TYPE="DEADBAND">
+ INPUT {input}
+ WIDTH {deadband width}
+ MIN {minimum value}
+ MAX {maximum value}
+ [GAIN {optional deadband gain}]
+ [OUTPUT {optional output parameter to set}]
+ \</COMPONENT>
+ </pre>
+ The WIDTH value is the total deadband region within which an input will
+ produce no output. For example, say that the WIDTH value is 2.0. If the
+ input is between -1.0 and +1.0, the output will be zero.
@author Jon S. Berndt
- @see -
- */
+ @version $Id$
+*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
cc = (2.00*C3 - dt*C2) / denom;
break;
case eOrder2:
- denom = 4.0*C3 + 2.0*C5*dt + C6*dt*dt;
- ca = 4.0*C1 + 2.0*C2*dt + C3*dt*dt / denom;
- cb = 2.0*C3*dt*dt - 8.0*C1 / denom;
- cc = 4.0*C1 - 2.0*C2*dt + C3*dt*dt / denom;
- cd = 2.0*C6*dt*dt - 8.0*C4 / denom;
- ce = 4.0*C3 - 2.0*C5*dt + C6*dt*dt / denom;
+ denom = 4.0*C4 + 2.0*C5*dt + C6*dt*dt;
+ ca = (4.0*C1 + 2.0*C2*dt + C3*dt*dt) / denom;
+ cb = (2.0*C3*dt*dt - 8.0*C1) / denom;
+ cc = (4.0*C1 - 2.0*C2*dt + C3*dt*dt) / denom;
+ cd = (2.0*C6*dt*dt - 8.0*C4) / denom;
+ ce = (4.0*C4 - 2.0*C5*dt + C6*dt*dt) / denom;
break;
case eWashout:
denom = 2.00 + dt*C1;
[TRIGGER \<property>]
\</COMPONENT>
</pre>
-For the integrator, the TRIGGER features the following behavior, if the TRIGGER property value is:
+For the integrator, the TRIGGER features the following behavior, if the TRIGGER
+property value is:
- -1 (or simply less than zero), all previous inputs and outputs are set to 0.0
- 0, no action is taken - the output is calculated normally
- +1 (or simply greater than zero), all previous outputs (only) will be set to 0.0
-
- @author Jon S. Berndt
- @version $Id$
- */
+
+In all the filter specifications above, an [OUTPUT] keyword is also seen. This
+is so that the last component in a "string" can copy its value to the appropriate
+output, such as the elevator, or speedbrake, etc.
+
+@author Jon S. Berndt
+@version $Id$
+*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Encapsulates a gain component for the flight control system.
- */
+ The gain component merely multiplies the input by a gain. The form of the
+ gain component specification is:
+ <pre>
+ \<COMPONENT NAME="name" TYPE="PURE_GAIN">
+ INPUT \<property>
+ GAIN \<value>
+ [OUTPUT \<property>]
+ \</COMPONENT>
+ </pre>
+ Note: as is the case with the Summer component, the input property name may be
+ immediately preceded by a minus sign to invert that signal.
+
+ The scheduled gain component multiplies the input by a variable gain that is
+ dependent on another property (such as qbar, altitude, etc.). The lookup
+ mapping is in the form of a table. This kind of component might be used, for
+ example, in a case where aerosurface deflection must only be commanded to
+ acceptable settings - i.e at higher qbar the commanded elevator setting might
+ be attenuated. The form of the scheduled gain component specification is:
+ <pre>
+ \<COMPONENT NAME="name" TYPE="SCHEDULED_GAIN">
+ INPUT \<property>
+ [GAIN \<value>]
+ SCHEDULED_BY \<property>
+ ROWS \<number_of_rows>
+ \<lookup_value gain_value>
+ ?
+ [OUTPUT \<property>]
+ \</COMPONENT>
+ </pre>
+ An overall GAIN may be supplied that is multiplicative with the scheduled gain.
+
+ Note: as is the case with the Summer component, the input property name may
+ be immediately preceded by a minus sign to invert that signal.
+
+ Here is an example of a scheduled gain component specification:
+ <pre>
+ \<COMPONENT NAME="Pitch Scheduled Gain 1" TYPE="SCHEDULED_GAIN">
+ INPUT fcs/pitch-gain-1
+ GAIN 0.017
+ SCHEDULED_BY fcs/elevator-pos-rad
+ ROWS 22
+ -0.68 -26.548
+ -0.595 -20.513
+ -0.51 -15.328
+ -0.425 -10.993
+ -0.34 -7.508
+ -0.255 -4.873
+ -0.17 -3.088
+ -0.085 -2.153
+ 0 -2.068
+ 0.085 -2.833
+ 0.102 -3.088
+ 0.119 -3.377
+ 0.136 -3.7
+ 0.153 -4.057
+ 0.17 -4.448
+ 0.187 -4.873
+ 0.272 -7.508
+ 0.357 -10.993
+ 0.442 -15.328
+ 0.527 -20.513
+ 0.612 -26.548
+ 0.697 -33.433
+ \</COMPONENT>
+ </pre>
+ In the example above, we see the utility of the overall GAIN value in
+ effecting a degrees-to-radians conversion.
+
+ The aerosurface scale component is a modified version of the simple gain
+ component. The normal purpose
+ for this component is to take control inputs that range from -1 to +1 or
+ from 0 to +1 and scale them to match the expected inputs to a flight control
+ system. For instance, the normal and expected ability of a pilot to push or
+ pull on a control stick is about 50 pounds. The input to the pitch channelb
+ lock diagram of a flight control system is in units of pounds. Yet, the
+ joystick control input is usually in a range from -1 to +1. The form of the
+ aerosurface scaling component specification is:
+<pre>
+ \<COMPONENT NAME="name" TYPE="AEROSURFACE_SCALE">
+ INPUT \<property>
+ MIN \<value>
+ MAX \<value>
+ [GAIN \<value>]
+ [OUTPUT \<property>]
+ \</COMPONENT>
+</pre>
+ Note: as is the case with the Summer component, the input property name may be
+ immediately preceded by a minus sign to invert that signal.
+
+ @author Jon S. Berndt
+ @version $Id$
+*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models a flight control system summing component.
- The Summer component sums multiple inputs. These can be pilot control inputs,
- state variables, or even floating point numbers (e.g. for a bias).
+ The Summer component sums two or more inputs. These can be pilot control
+ inputs or state variables, and a bias can also be added in using the BIAS
+ keyword. The form of the summer component specification is:
+<pre>
+ \<COMPONENT NAME="name" TYPE="SUMMER">
+ INPUT \<property>
+ INPUT \<property>
+ [BIAS \<value>]
+ [?]
+ [CLIPTO \<min> \<max> 1]
+ [OUTPUT \<property>]
+ \</COMPONENT>
+</pre>
+ Note that in the case of an input property the property name may be
+ immediately preceded by a minus sign. Here's an example of a summer
+ component specification:
+<pre>
+ \<COMPONENT NAME="Roll A/P Error summer" TYPE="SUMMER">
+ INPUT velocities/p-rad_sec
+ INPUT -fcs/roll-ap-wing-leveler
+ INPUT fcs/roll-ap-error-integrator
+ CLIPTO -1 1
+ \</COMPONENT>
+</pre>
+ Note that there can be only one BIAS statement per component.
+
@author Jon S. Berndt
@version $Id$
*/
The SWITCH component models a switch - either on/off or a multi-choice rotary
switch. The switch can represent a physical cockpit switch, or can represent a
logical switch, where several conditions might need to be satisfied before a
-particular state is reached. The VALUE of the switch - the output value - is
-chosen depending on the state of the switch. Each switch is comprised of two or
-more TESTs. Each TEST has a VALUE associated with it. The first TEST that
-evaluates to TRUE will set the output value of the switch according to the VALUE
-parameter belonging to that TEST. Each TEST contains one or more CONDITIONS, which
-each must be logically related (if there are more than one) given the value of
-the LOGIC parameter, and which takes the form:
+particular state is reached. The VALUE of the switch - the output value for the
+component - is chosen depending on the state of the switch. Each switch is
+comprised of two or more TESTs. Each TEST has a VALUE associated with it. The
+first TEST that evaluates to TRUE will set the output value of the switch
+according to the VALUE parameter belonging to that TEST. Each TEST contains one
+or more CONDITIONS, which each must be logically related (if there are more than
+one) given the value of the LOGIC parameter, and which takes the form:
property conditional property|value
...
\</COMPONENT\>
</pre>
+
+Here's an example:
+<pre>
+\<COMPONENT NAME="Roll A/P Autoswitch" TYPE="SWITCH">
+ \<TEST LOGIC="DEFAULT" VALUE="0.0">
+ \</TEST>
+ \<TEST LOGIC="AND" VALUE="fcs/roll-ap-error-summer">
+ ap/attitude_hold == 1
+ \</TEST>
+\</COMPONENT>
+</pre>
+The above example specifies that the default value of the component (i.e. the
+output property of the component, addressed by the property, ap/roll-ap-autoswitch)
+is 0.0. If or when the attitude hold switch is selected (property
+ap/attitude_hold takes the value 1), the value of the switch component will be
+whatever value fcs/roll-ap-error-summer is.
+@author Jon S. Berndt
+@version $Id$
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%