]> git.mxchange.org Git - flightgear.git/blob - src/FDM/ExternalNet.cxx
Added support for rotational rates and body access (pilot relative)
[flightgear.git] / src / FDM / ExternalNet.cxx
1 // ExternalNet.hxx -- an net interface to an external flight dynamics model
2 //
3 // Written by Curtis Olson, started November 2001.
4 //
5 // Copyright (C) 2001  Curtis L. Olson  - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23 #include <simgear/debug/logstream.hxx>
24 #include <simgear/io/lowlevel.hxx> // endian tests
25
26 #include <Main/fg_props.hxx>
27
28 #include "ExternalNet.hxx"
29
30
31 // FreeBSD works better with this included last ... (?)
32 #if defined(WIN32) && !defined(__CYGWIN__)
33 #  include <windows.h>
34 #else
35 #  include <netinet/in.h>       // htonl() ntohl()
36 #endif
37
38
39 // The function htond is defined this way due to the way some
40 // processors and OSes treat floating point values.  Some will raise
41 // an exception whenever a "bad" floating point value is loaded into a
42 // floating point register.  Solaris is notorious for this, but then
43 // so is LynxOS on the PowerPC.  By translating the data in place,
44 // there is no need to load a FP register with the "corruped" floating
45 // point value.  By doing the BIG_ENDIAN test, I can optimize the
46 // routine for big-endian processors so it can be as efficient as
47 // possible
48 static void htond (double &x)   
49 {
50     if ( sgIsLittleEndian() ) {
51         int    *Double_Overlay;
52         int     Holding_Buffer;
53     
54         Double_Overlay = (int *) &x;
55         Holding_Buffer = Double_Overlay [0];
56     
57         Double_Overlay [0] = htonl (Double_Overlay [1]);
58         Double_Overlay [1] = htonl (Holding_Buffer);
59     } else {
60         return;
61     }
62 }
63
64
65 static void global2raw( FGRawCtrls *raw ) {
66     int i;
67
68 #if 0
69     // these can probably get wiped as soon as David spots them. :-)
70     static const SGPropertyNode *aileron
71         = fgGetNode("/controls/aileron");
72     static const SGPropertyNode *elevator
73         = fgGetNode("/controls/elevator");
74     static const SGPropertyNode *elevator_trim
75         = fgGetNode("/controls/elevator-trim");
76     static const SGPropertyNode *rudder
77         = fgGetNode("/controls/rudder");
78     static const SGPropertyNode *flaps
79         = fgGetNode("/controls/flaps");
80 #endif
81
82     char buf[256];
83
84     // fill in values
85     raw->version = FG_RAW_CTRLS_VERSION;
86     raw->aileron = fgGetDouble( "/controls/aileron" );
87     raw->elevator = fgGetDouble( "/controls/elevator" );
88     raw->elevator_trim = fgGetDouble( "/controls/elevator-trim" );
89     raw->rudder = fgGetDouble( "/controls/rudder" );
90     raw->flaps = fgGetDouble( "/controls/flaps" );
91     for ( i = 0; i < FG_MAX_ENGINES; ++i ) {
92         sprintf( buf, "/controls/throttle[%d]", i );
93         raw->throttle[i] = fgGetDouble( buf );
94         sprintf( buf, "/controls/mixture[%d]", i );
95         raw->mixture[i] = fgGetDouble( buf );
96         sprintf( buf, "/controls/propeller-pitch[%d]", i );
97         raw->prop_advance[i] = fgGetDouble( buf );
98     }
99     for ( i = 0; i < FG_MAX_WHEELS; ++i ) {
100         sprintf( buf, "/controls/brakes[%d]", i );
101         raw->brake[i] = fgGetDouble( buf );
102     }
103     raw->hground = fgGetDouble( "/environment/ground-elevation-m" );
104     raw->magvar = fgGetDouble("/environment/magnetic-variation-deg");
105     raw->speedup = fgGetInt("/sim/speed-up");
106
107     // convert to network byte order
108     raw->version = htonl(raw->version);
109     htond(raw->aileron);
110     htond(raw->elevator);
111     htond(raw->elevator_trim);
112     htond(raw->rudder);
113     htond(raw->flaps);
114     for ( i = 0; i < FG_MAX_ENGINES; ++i ) {
115         htond(raw->throttle[i]);
116         htond(raw->mixture[i]);
117         htond(raw->prop_advance[i]);
118     }
119     for ( i = 0; i < FG_MAX_WHEELS; ++i ) {
120         htond(raw->brake[i]);
121     }
122     htond(raw->hground);
123     htond(raw->magvar);
124     raw->speedup = htonl(raw->speedup);
125 }
126
127
128 static void net2global( FGNetFDM *net ) {
129     // Convert to the net buffer from network format
130     net->version = ntohl(net->version);
131     htond(net->longitude);
132     htond(net->latitude);
133     htond(net->altitude);
134     htond(net->phi);
135     htond(net->theta);
136     htond(net->psi);
137     htond(net->phidot);
138     htond(net->thetadot);
139     htond(net->psidot);
140     htond(net->vcas);
141     htond(net->climb_rate);
142     htond(net->A_X_pilot);
143     htond(net->A_Y_pilot);
144     htond(net->A_Z_pilot);
145     net->cur_time = ntohl(net->cur_time);
146     net->warp = ntohl(net->warp);
147     htond(net->visibility);
148
149     if ( net->version == FG_NET_FDM_VERSION ) {
150         // cout << "pos = " << net->longitude << " " << net->latitude << endl;
151         // cout << "sea level rad = " << cur_fdm_state->get_Sea_level_radius() << endl;
152         cur_fdm_state->_updateGeodeticPosition( net->latitude,
153                                                 net->longitude,
154                                                 net->altitude
155                                                   * SG_METER_TO_FEET );
156         cur_fdm_state->_set_Euler_Angles( net->phi,
157                                           net->theta,
158                                           net->psi );
159         cur_fdm_state->_set_Euler_Rates( net->phidot,
160                                          net->thetadot,
161                                          net->psidot );
162         cur_fdm_state->_set_V_calibrated_kts( net->vcas );
163         cur_fdm_state->_set_Climb_Rate( net->climb_rate );
164         cur_fdm_state->_set_Accels_Pilot_Body( net->A_X_pilot,
165                                                net->A_Y_pilot,
166                                                net->A_Z_pilot );
167         /* these are ignored for now  ... */
168         /*
169         if ( net->cur_time ) {
170             fgSetLong("/sim/time/cur-time-override", net->cur_time);
171         }
172
173         globals->set_warp( net->warp );
174         last_warp = net->warp;
175         */
176     } else {
177         SG_LOG( SG_IO, SG_ALERT, "Error: version mismatch in net2global()" );
178         SG_LOG( SG_IO, SG_ALERT,
179                 "\tread " << net->version << " need " << FG_NET_FDM_VERSION );
180         SG_LOG( SG_IO, SG_ALERT,
181                 "\tsomeone needs to upgrade net_fdm.hxx and recompile." );
182     }
183 }
184
185
186 FGExternalNet::FGExternalNet( double dt, int dop, int dip, int cp,
187                               string host )
188 {
189     set_delta_t( dt );
190
191     valid = true;
192
193     data_in_port = dip;
194     data_out_port = dop;
195     cmd_port = cp;
196     fdm_host = host;
197
198     /////////////////////////////////////////////////////////
199     // Setup client udp connection (sends data to remote fdm)
200
201     if ( ! data_client.open( false ) ) {
202         SG_LOG( SG_FLIGHT, SG_ALERT, "Error opening client data channel" );
203         valid = false;
204     }
205
206     // fire and forget
207     data_client.setBlocking( false );
208
209     if ( data_client.connect( fdm_host.c_str(), data_out_port ) == -1 ) {
210         printf("error connecting to %s:%d\n", fdm_host.c_str(), data_out_port);
211         valid = false;
212     }
213
214     /////////////////////////////////////////////////////////
215     // Setup server udp connection (for receiving data)
216
217     if ( ! data_server.open( false ) ) {
218         SG_LOG( SG_FLIGHT, SG_ALERT, "Error opening client server channel" );
219         valid = false;
220     }
221
222     // we want to block for incoming data in order to syncronize frame
223     // rates.
224     data_server.setBlocking( false /* don't block while testing */ );
225     // data_server.setBlocking( true /* don't block while testing */ );
226
227     // if we bind to fdm_host = "" then we accept messages from
228     // anyone.
229     if ( data_server.bind( fdm_host.c_str(), data_in_port ) == -1 ) {
230         printf("error binding to port %d\n", data_in_port);
231         valid = false;
232     }
233 }
234
235
236 FGExternalNet::~FGExternalNet() {
237     data_client.close();
238     data_server.close();
239 }
240
241
242 // Initialize the ExternalNet flight model, dt is the time increment
243 // for each subsequent iteration through the EOM
244 void FGExternalNet::init() {
245     // cout << "FGExternalNet::init()" << endl;
246
247     // Explicitly call the superclass's
248     // init method first.
249     common_init();
250
251     double lon = fgGetDouble( "/position/longitude-deg" );
252     double lat = fgGetDouble( "/position/latitude-deg" );
253     double ground = fgGetDouble( "/environment/ground-elevation-m" );
254     double heading = fgGetDouble("/orientation/heading-deg");
255
256     char cmd[256];
257
258     sprintf( cmd, "/longitude-deg?value=%.8f", lon );
259     new HTTPClient( fdm_host.c_str(), cmd_port, cmd );
260 //     cout << "before loop()" << endl;
261     netChannel::loop(0);
262 //     cout << "here" << endl;
263
264     sprintf( cmd, "/latitude-deg?value=%.8f", lat );
265     new HTTPClient( fdm_host.c_str(), cmd_port, cmd );
266     netChannel::loop(0);
267
268     sprintf( cmd, "/ground-m?value=%.8f", ground );
269     new HTTPClient( fdm_host.c_str(), cmd_port, cmd );
270     netChannel::loop(0);
271
272     sprintf( cmd, "/heading-deg?value=%.8f", heading );
273     new HTTPClient( fdm_host.c_str(), cmd_port, cmd );
274     netChannel::loop(0);
275
276     sprintf( cmd, "/reset?value=ground" );
277     new HTTPClient( fdm_host.c_str(), cmd_port, cmd );
278     netChannel::loop(0);
279 }
280
281
282 // Run an iteration of the EOM.  This is a NOP here because the flight
283 // model values are getting filled in elsewhere (most likely from some
284 // external source.)
285 void FGExternalNet::update( int multiloop ) {
286     int length;
287     int result;
288
289     // Send control positions to remote fdm
290     length = sizeof(ctrls);
291     global2raw( &ctrls );
292     if ( data_client.send( (char *)(& ctrls), length, 0 ) != length ) {
293         SG_LOG( SG_IO, SG_ALERT, "Error writing data." );
294     }
295
296     // Read next set of FDM data (blocking enabled to maintain 'sync')
297     length = sizeof(fdm);
298     if ( (result = data_server.recv( (char *)(& fdm), length, 0)) >= 0 ) {
299         SG_LOG( SG_IO, SG_DEBUG, "Success reading data." );
300         net2global( &fdm );
301     }
302 }