JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
FGOutputFG.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGOutputFG.cpp
4  Author: Bertrand Coconnier
5  Date started: 09/10/11
6  Purpose: Manage output of sim parameters to FlightGear
7  Called by: FGOutput
8 
9  ------------- Copyright (C) 2011 Bertrand Coconnier -------------
10 
11  This program is free software; you can redistribute it and/or modify it under
12  the terms of the GNU Lesser General Public License as published by the Free
13  Software Foundation; either version 2 of the License, or (at your option) any
14  later version.
15 
16  This program is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19  details.
20 
21  You should have received a copy of the GNU Lesser General Public License along
22  with this program; if not, write to the Free Software Foundation, Inc., 59
23  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 
25  Further information about the GNU Lesser General Public License can also be
26  found on the world wide web at http://www.gnu.org.
27 
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 This is the place where you create output routines to dump data for perusal
31 later.
32 
33 HISTORY
34 --------------------------------------------------------------------------------
35 11/09/07 HDW Added FlightGear Socket Interface
36 09/10/11 BC Moved the FlightGear socket in a separate class
37 
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39 INCLUDES
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
41 
42 #include <cstring>
43 
44 #include "FGOutputFG.h"
45 #include "FGXMLElement.h"
46 #include "models/FGAuxiliary.h"
47 #include "models/FGPropulsion.h"
48 #include "models/FGFCS.h"
49 #include "models/propulsion/FGPiston.h"
50 #include "models/propulsion/FGElectric.h"
51 #include "models/propulsion/FGTank.h"
52 
53 #if defined(WIN32) && !defined(__CYGWIN__)
54 # include <windows.h>
55 #else
56 # include <netinet/in.h> // htonl() ntohl()
57 #endif
58 
59 #if !defined (min)
60 # define min(X,Y) X<Y?X:Y
61 #endif
62 
63 static const int endianTest = 1;
64 #define isLittleEndian (*((char *) &endianTest ) != 0)
65 
66 using namespace std;
67 
68 namespace JSBSim {
69 
70 // (stolen from FGFS native_fdm.cxx)
71 // The function htond is defined this way due to the way some
72 // processors and OSes treat floating point values. Some will raise
73 // an exception whenever a "bad" floating point value is loaded into a
74 // floating point register. Solaris is notorious for this, but then
75 // so is LynxOS on the PowerPC. By translating the data in place,
76 // there is no need to load a FP register with the "corruped" floating
77 // point value. By doing the BIG_ENDIAN test, I can optimize the
78 // routine for big-endian processors so it can be as efficient as
79 // possible
80 static void htond (double &x)
81 {
82  if ( isLittleEndian ) {
83  int *Double_Overlay;
84  int Holding_Buffer;
85 
86  Double_Overlay = (int *) &x;
87  Holding_Buffer = Double_Overlay [0];
88 
89  Double_Overlay [0] = htonl (Double_Overlay [1]);
90  Double_Overlay [1] = htonl (Holding_Buffer);
91  } else {
92  return;
93  }
94 }
95 
96 // Float version
97 static void htonf (float &x)
98 {
99  if ( isLittleEndian ) {
100  int *Float_Overlay;
101  int Holding_Buffer;
102 
103  Float_Overlay = (int *) &x;
104  Holding_Buffer = Float_Overlay [0];
105 
106  Float_Overlay [0] = htonl (Holding_Buffer);
107  } else {
108  return;
109  }
110 }
111 
112 
113 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 CLASS IMPLEMENTATION
115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
116 
117 FGOutputFG::FGOutputFG(FGFDMExec* fdmex) :
118  FGOutputSocket(fdmex), outputOptions{false, 1e6}
119 {
120  memset(&fgSockBuf, 0x0, sizeof(fgSockBuf));
121 
122  if (fdmex->GetDebugLevel() > 0) {
123  // Engine status
124  if (Propulsion->GetNumEngines() > FGNetFDM::FG_MAX_ENGINES)
125  cerr << "This vehicle has " << Propulsion->GetNumEngines() << " engines, but the current " << endl
126  << "version of FlightGear's FGNetFDM only supports " << FGNetFDM::FG_MAX_ENGINES << " engines." << endl
127  << "Only the first " << FGNetFDM::FG_MAX_ENGINES << " engines will be used." << endl;
128 
129  // Consumables
130  if (Propulsion->GetNumTanks() > FGNetFDM::FG_MAX_TANKS)
131  cerr << "This vehicle has " << Propulsion->GetNumTanks() << " tanks, but the current " << endl
132  << "version of FlightGear's FGNetFDM only supports " << FGNetFDM::FG_MAX_TANKS << " tanks." << endl
133  << "Only the first " << FGNetFDM::FG_MAX_TANKS << " tanks will be used." << endl;
134 
135  // Gear status
136  if (GroundReactions->GetNumGearUnits() > FGNetFDM::FG_MAX_WHEELS)
137  cerr << "This vehicle has " << GroundReactions->GetNumGearUnits() << " bogeys, but the current " << endl
138  << "version of FlightGear's FGNetFDM only supports " << FGNetFDM::FG_MAX_WHEELS << " bogeys." << endl
139  << "Only the first " << FGNetFDM::FG_MAX_WHEELS << " bogeys will be used." << endl;
140  }
141 }
142 
143 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144 
146 {
147  if (!FGOutputSocket::Load(el)) {
148  return false;
149  }
150 
151  // Check if there is a <time> element
152  Element* time_el = el->FindElement("time");
153  if (time_el) {
154  // Check if the attribute "type" is specified and is set to "simulation"
155  if (time_el->HasAttribute("type") && time_el->GetAttributeValue("type") == "simulation") {
156  outputOptions.useSimTime = true;
157  }
158 
159  // Check if the attribute "resolution" is specified and set to a valid value
160  if (time_el->HasAttribute("resolution")) {
161  if (time_el->GetAttributeValueAsNumber("resolution") <= 1 &&
162  time_el->GetAttributeValueAsNumber("resolution") >= 1e-9) {
163  outputOptions.timeFactor = 1./time_el->GetAttributeValueAsNumber("resolution");
164  } else {
165  return false;
166  }
167  }
168  }
169  return true;
170 }
171 
172 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
173 
174 void FGOutputFG::SocketDataFill(FGNetFDM* net)
175 {
176  unsigned int i;
177 
178  // Version
179  net->version = FG_NET_FDM_VERSION;
180 
181  // Positions
182  net->longitude = Propagate->GetLongitude(); // longitude (radians)
183  net->latitude = Propagate->GetGeodLatitudeRad(); // geodetic (radians)
184  net->altitude = Propagate->GetAltitudeASL()*0.3048; // altitude, above sea level (meters)
185  net->agl = (float)(Propagate->GetDistanceAGL()*0.3048); // altitude, above ground level (meters)
186 
187  net->phi = (float)(Propagate->GetEuler(ePhi)); // roll (radians)
188  net->theta = (float)(Propagate->GetEuler(eTht)); // pitch (radians)
189  net->psi = (float)(Propagate->GetEuler(ePsi)); // yaw or true heading (radians)
190 
191  net->alpha = (float)(Auxiliary->Getalpha()); // angle of attack (radians)
192  net->beta = (float)(Auxiliary->Getbeta()); // side slip angle (radians)
193 
194  // Velocities
195  net->phidot = (float)(Auxiliary->GetEulerRates(ePhi)); // roll rate (radians/sec)
196  net->thetadot = (float)(Auxiliary->GetEulerRates(eTht)); // pitch rate (radians/sec)
197  net->psidot = (float)(Auxiliary->GetEulerRates(ePsi)); // yaw rate (radians/sec)
198  net->vcas = (float)(Auxiliary->GetVcalibratedKTS()); // VCAS, knots
199  net->climb_rate = (float)(Propagate->Gethdot()); // altitude rate, ft/sec
200  net->v_north = (float)(Propagate->GetVel(eNorth)); // north vel in NED frame, fps
201  net->v_east = (float)(Propagate->GetVel(eEast)); // east vel in NED frame, fps
202  net->v_down = (float)(Propagate->GetVel(eDown)); // down vel in NED frame, fps
203 //---ADD METHOD TO CALCULATE THESE TERMS---
204  net->v_body_u = (float)(Propagate->GetUVW(1)); // ECEF speed in body axis
205  net->v_body_v = (float)(Propagate->GetUVW(2)); // ECEF speed in body axis
206  net->v_body_w = (float)(Propagate->GetUVW(3)); // ECEF speed in body axis
207 
208  // Accelerations
209  net->A_X_pilot = (float)(Auxiliary->GetPilotAccel(1)); // X body accel, ft/s/s
210  net->A_Y_pilot = (float)(Auxiliary->GetPilotAccel(2)); // Y body accel, ft/s/s
211  net->A_Z_pilot = (float)(Auxiliary->GetPilotAccel(3)); // Z body accel, ft/s/s
212 
213  // Stall
214  net->stall_warning = 0.0; // 0.0 - 1.0 indicating the amount of stall
215  net->slip_deg = (float)(Auxiliary->Getbeta(inDegrees)); // slip ball deflection, deg
216 
217  net->num_engines = min(FGNetFDM::FG_MAX_ENGINES,Propulsion->GetNumEngines()); // Number of valid engines
218 
219  for (i=0; i<net->num_engines; i++) {
220  FGEngine* engine = Propulsion->GetEngine(i);
221  if (engine->GetRunning())
222  net->eng_state[i] = 2; // Engine state running
223  else if (engine->GetCranking())
224  net->eng_state[i] = 1; // Engine state cranking
225  else
226  net->eng_state[i] = 0; // Engine state off
227 
228  switch (engine->GetType()) {
229  case (FGEngine::etRocket):
230  break;
231  case (FGEngine::etPiston):
232  {
233  FGPiston* piston_engine = static_cast<FGPiston*>(engine);
234  net->rpm[i] = (float)(piston_engine->getRPM());
235  net->fuel_flow[i] = (float)(piston_engine->getFuelFlow_gph());
236  net->fuel_px[i] = 0; // Fuel pressure, psi (N/A in current model)
237  net->egt[i] = (float)(piston_engine->GetEGT());
238  net->cht[i] = (float)(piston_engine->getCylinderHeadTemp_degF());
239  net->mp_osi[i] = (float)(piston_engine->getManifoldPressure_inHg());
240  net->oil_temp[i] = (float)(piston_engine->getOilTemp_degF());
241  net->oil_px[i] = (float)(piston_engine->getOilPressure_psi());
242  net->tit[i] = 0; // Turbine Inlet Temperature (N/A for piston)
243  }
244  break;
245  case (FGEngine::etTurbine):
246  break;
247  case (FGEngine::etTurboprop):
248  break;
249  case (FGEngine::etElectric):
250  net->rpm[i] = static_cast<float>(static_cast<FGElectric*>(engine)->getRPM());
251  break;
252  case (FGEngine::etUnknown):
253  break;
254  }
255  }
256 
257  net->num_tanks = min(FGNetFDM::FG_MAX_TANKS, Propulsion->GetNumTanks()); // Max number of fuel tanks
258 
259  for (i=0; i<net->num_tanks; i++) {
260  net->fuel_quantity[i] = (float)(((FGTank *)Propulsion->GetTank(i))->GetContents());
261  }
262 
263  net->num_wheels = min(FGNetFDM::FG_MAX_WHEELS, GroundReactions->GetNumGearUnits());
264 
265  for (i=0; i<net->num_wheels; i++) {
266  net->wow[i] = GroundReactions->GetGearUnit(i)->GetWOW();
267  if (GroundReactions->GetGearUnit(i)->GetGearUnitDown())
268  net->gear_pos[i] = 1; //gear down, using FCS convention
269  else
270  net->gear_pos[i] = 0; //gear up, using FCS convention
271  net->gear_steer[i] = (float)(GroundReactions->GetGearUnit(i)->GetSteerNorm());
272  net->gear_compression[i] = (float)(GroundReactions->GetGearUnit(i)->GetCompLen());
273  }
274 
275  // Environment
276  if (outputOptions.useSimTime) {
277  // Send simulation time with specified resolution
278  net->cur_time = static_cast<uint32_t>(FDMExec->GetSimTime()*outputOptions.timeFactor);
279  } else {
280  // Default to sending constant dummy value to ensure backwards-compatibility
281  net->cur_time = 1234567890u;
282  }
283 
284  net->warp = 0; // offset in seconds to unix time
285  net->visibility = 25000.0; // visibility in meters (for env. effects)
286 
287  // Control surface positions (normalized values)
288  net->elevator = (float)(FCS->GetDePos(ofNorm)); // Norm Elevator Pos, --
289  net->elevator_trim_tab = (float)(FCS->GetPitchTrimCmd()); // Norm Elev Trim Tab Pos, --
290  net->left_flap = (float)(FCS->GetDfPos(ofNorm)); // Norm Flap Pos, --
291  net->right_flap = (float)(FCS->GetDfPos(ofNorm)); // Norm Flap Pos, --
292  net->left_aileron = (float)(FCS->GetDaLPos(ofNorm)); // Norm L Aileron Pos, --
293  net->right_aileron = (float)(FCS->GetDaRPos(ofNorm)); // Norm R Aileron Pos, --
294  net->rudder = (float)(FCS->GetDrPos(ofNorm)); // Norm Rudder Pos, --
295  net->nose_wheel = (float)(FCS->GetDrPos(ofNorm)); // *** FIX *** Using Rudder Pos for NWS, --
296  net->speedbrake = (float)(FCS->GetDsbPos(ofNorm)); // Norm Speedbrake Pos, --
297  net->spoilers = (float)(FCS->GetDspPos(ofNorm)); // Norm Spoiler Pos, --
298 
299  // Convert the net buffer to network format
300  if ( isLittleEndian ) {
301  net->version = htonl(net->version);
302 
303  htond(net->longitude);
304  htond(net->latitude);
305  htond(net->altitude);
306  htonf(net->agl);
307  htonf(net->phi);
308  htonf(net->theta);
309  htonf(net->psi);
310  htonf(net->alpha);
311  htonf(net->beta);
312 
313  htonf(net->phidot);
314  htonf(net->thetadot);
315  htonf(net->psidot);
316  htonf(net->vcas);
317  htonf(net->climb_rate);
318  htonf(net->v_north);
319  htonf(net->v_east);
320  htonf(net->v_down);
321  htonf(net->v_body_u);
322  htonf(net->v_body_v);
323  htonf(net->v_body_w);
324 
325  htonf(net->A_X_pilot);
326  htonf(net->A_Y_pilot);
327  htonf(net->A_Z_pilot);
328 
329  htonf(net->stall_warning);
330  htonf(net->slip_deg);
331 
332  for (i=0; i<net->num_engines; ++i ) {
333  net->eng_state[i] = htonl(net->eng_state[i]);
334  htonf(net->rpm[i]);
335  htonf(net->fuel_flow[i]);
336  htonf(net->fuel_px[i]);
337  htonf(net->egt[i]);
338  htonf(net->cht[i]);
339  htonf(net->mp_osi[i]);
340  htonf(net->tit[i]);
341  htonf(net->oil_temp[i]);
342  htonf(net->oil_px[i]);
343  }
344  net->num_engines = htonl(net->num_engines);
345 
346  for (i=0; i<net->num_tanks; ++i ) {
347  htonf(net->fuel_quantity[i]);
348  }
349  net->num_tanks = htonl(net->num_tanks);
350 
351  for (i=0; i<net->num_wheels; ++i ) {
352  net->wow[i] = htonl(net->wow[i]);
353  htonf(net->gear_pos[i]);
354  htonf(net->gear_steer[i]);
355  htonf(net->gear_compression[i]);
356  }
357  net->num_wheels = htonl(net->num_wheels);
358 
359  net->cur_time = htonl( net->cur_time );
360  net->warp = htonl( net->warp );
361  htonf(net->visibility);
362 
363  htonf(net->elevator);
364  htonf(net->elevator_trim_tab);
365  htonf(net->left_flap);
366  htonf(net->right_flap);
367  htonf(net->left_aileron);
368  htonf(net->right_aileron);
369  htonf(net->rudder);
370  htonf(net->nose_wheel);
371  htonf(net->speedbrake);
372  htonf(net->spoilers);
373  }
374 }
375 
376 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
377 
379 {
380  int length = sizeof(fgSockBuf);
381 
382  if (socket == 0) return;
383  if (!socket->GetConnectStatus()) return;
384 
385  SocketDataFill(&fgSockBuf);
386  socket->Send((char *)&fgSockBuf, length);
387 }
388 }
JSBSim::FGFDMExec
Encapsulates the JSBSim simulation executive.
Definition: FGFDMExec.h:185
JSBSim::FGPropagate::GetAltitudeASL
double GetAltitudeASL(void) const
Returns the current altitude above sea level.
Definition: FGPropagate.cpp:523
JSBSim::FGOutputFG::Print
void Print(void) override
Generate the output.
Definition: FGOutputFG.cpp:378
JSBSim::FGLGear::GetCompLen
double GetCompLen(void) const
Gets the current compressed length of the gear in feet.
Definition: FGLGear.h:257
JSBSim::FGFCS::GetPitchTrimCmd
double GetPitchTrimCmd(void) const
Gets the pitch trim command.
Definition: FGFCS.h:264
JSBSim::Element::HasAttribute
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
Definition: FGXMLElement.h:155
JSBSim::Element::GetAttributeValue
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Definition: FGXMLElement.cpp:260
JSBSim::Element::FindElement
Element * FindElement(const std::string &el="")
Searches for a specified element.
Definition: FGXMLElement.cpp:389
JSBSim::FGPropulsion::GetEngine
FGEngine * GetEngine(unsigned int index) const
Retrieves an engine object pointer from the list of engines.
Definition: FGPropulsion.h:133
JSBSim::FGFDMExec::GetSimTime
double GetSimTime(void) const
Returns the cumulative simulation time in seconds.
Definition: FGFDMExec.h:542
JSBSim::FGFCS::GetDfPos
double GetDfPos(int form=ofRad) const
Gets the flaps position.
Definition: FGFCS.h:314
JSBSim::FGPropagate::Gethdot
double Gethdot(void) const
Returns the current altitude rate.
Definition: FGPropagate.h:416
JSBSim::FGAuxiliary::GetVcalibratedKTS
double GetVcalibratedKTS(void) const
Returns Calibrated airspeed in knots.
Definition: FGAuxiliary.h:127
JSBSim::FGOutputSocket::Load
bool Load(Element *el) override
Init the output directives from an XML file.
Definition: FGOutputSocket.cpp:115
JSBSim::FGLGear::GetWOW
bool GetWOW(void) const
Gets the Weight On Wheels flag value.
Definition: FGLGear.h:255
JSBSim::FGPropulsion::GetNumTanks
unsigned int GetNumTanks(void) const
Retrieves the number of tanks defined for the aircraft.
Definition: FGPropulsion.h:138
JSBSim::FGFCS::GetDePos
double GetDePos(int form=ofRad) const
Gets the elevator position.
Definition: FGFCS.h:294
JSBSim::FGPropagate::GetVel
const FGColumnVector3 & GetVel(void) const
Retrieves the velocity vector.
Definition: FGPropagate.h:183
JSBSim::FGFCS::GetDspPos
double GetDspPos(int form=ofRad) const
Gets the spoiler position.
Definition: FGFCS.h:309
JSBSim::Element::GetAttributeValueAsNumber
double GetAttributeValueAsNumber(const std::string &key)
Retrieves an attribute value as a double precision real number.
Definition: FGXMLElement.cpp:279
JSBSim::FGFCS::GetDsbPos
double GetDsbPos(int form=ofRad) const
Gets the speedbrake position.
Definition: FGFCS.h:304
JSBSim::FGOutputFG::Load
bool Load(Element *) override
Evaluate the output directives from an XML file.
Definition: FGOutputFG.cpp:145
JSBSim::FGPropulsion::GetNumEngines
unsigned int GetNumEngines(void) const
Retrieves the number of engines defined for the aircraft.
Definition: FGPropulsion.h:127
JSBSim::FGPropulsion::GetTank
FGTank * GetTank(unsigned int index) const
Retrieves a tank object pointer from the list of tanks.
Definition: FGPropulsion.h:144
JSBSim::FGFCS::GetDaRPos
double GetDaRPos(int form=ofRad) const
Gets the right aileron position.
Definition: FGFCS.h:289
JSBSim::FGGroundReactions::GetGearUnit
FGLGear * GetGearUnit(int gear) const
Gets a gear instance.
Definition: FGGroundReactions.h:110
JSBSim::FGPiston
Models a Supercharged Piston engine.
Definition: FGPiston.h:223
JSBSim::FGPropagate::GetUVW
const FGColumnVector3 & GetUVW(void) const
Retrieves the body frame vehicle velocity vector.
Definition: FGPropagate.h:195
JSBSim::FGPropagate::GetEuler
const FGColumnVector3 & GetEuler(void) const
Retrieves the Euler angles that define the vehicle orientation.
Definition: FGPropagate.h:251
JSBSim::FGGroundReactions::GetNumGearUnits
int GetNumGearUnits(void) const
Gets the number of gears.
Definition: FGGroundReactions.h:105
JSBSim::FGEngine
Base class for all engines.
Definition: FGEngine.h:103
JSBSim::FGFCS::GetDaLPos
double GetDaLPos(int form=ofRad) const
Gets the left aileron position.
Definition: FGFCS.h:284
JSBSim::Element
Definition: FGXMLElement.h:143
JSBSim::FGFCS::GetDrPos
double GetDrPos(int form=ofRad) const
Gets the rudder position.
Definition: FGFCS.h:299
JSBSim::FGOutputSocket
Implements the output to a socket.
Definition: FGOutputSocket.h:66