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