]> git.mxchange.org Git - flightgear.git/blob - utils/GPSsmooth/MIDG_main.cxx
Created a utility (similar to GPSsmooth) that can take a recorded file of
[flightgear.git] / utils / GPSsmooth / MIDG_main.cxx
1 #ifdef HAVE_CONFIG_H
2 #  include <config.h>
3 #endif
4
5 #include <iostream>
6 #include <string>
7
8 #include <plib/net.h>
9 #include <plib/sg.h>
10
11 #include <simgear/io/lowlevel.hxx> // endian tests
12 #include <simgear/timing/timestamp.hxx>
13
14 #include <Network/net_ctrls.hxx>
15 #include <Network/net_fdm.hxx>
16
17 #include "MIDG-II.hxx"
18
19
20 SG_USING_STD(cout);
21 SG_USING_STD(endl);
22 SG_USING_STD(string);
23
24
25 // Network channels
26 static netSocket fdm_sock, ctrls_sock;
27
28 // midg data
29 MIDGTrack track;
30
31 // Default ports
32 static int fdm_port = 5505;
33 static int ctrls_port = 5506;
34
35 // Default path
36 static string file = "";
37
38 // Master time counter
39 float sim_time = 0.0f;
40
41 // sim control
42 SGTimeStamp last_time_stamp;
43 SGTimeStamp current_time_stamp;
44
45 // altitude offset
46 double alt_offset = 0.0;
47
48 bool inited = false;
49
50
51 // The function htond is defined this way due to the way some
52 // processors and OSes treat floating point values.  Some will raise
53 // an exception whenever a "bad" floating point value is loaded into a
54 // floating point register.  Solaris is notorious for this, but then
55 // so is LynxOS on the PowerPC.  By translating the data in place,
56 // there is no need to load a FP register with the "corruped" floating
57 // point value.  By doing the BIG_ENDIAN test, I can optimize the
58 // routine for big-endian processors so it can be as efficient as
59 // possible
60 static void htond (double &x)   
61 {
62     if ( sgIsLittleEndian() ) {
63         int    *Double_Overlay;
64         int     Holding_Buffer;
65     
66         Double_Overlay = (int *) &x;
67         Holding_Buffer = Double_Overlay [0];
68     
69         Double_Overlay [0] = htonl (Double_Overlay [1]);
70         Double_Overlay [1] = htonl (Holding_Buffer);
71     } else {
72         return;
73     }
74 }
75
76 // Float version
77 static void htonf (float &x)    
78 {
79     if ( sgIsLittleEndian() ) {
80         int    *Float_Overlay;
81         int     Holding_Buffer;
82     
83         Float_Overlay = (int *) &x;
84         Holding_Buffer = Float_Overlay [0];
85     
86         Float_Overlay [0] = htonl (Holding_Buffer);
87     } else {
88         return;
89     }
90 }
91
92
93 static void midg2fg( const MIDGpos pos, const MIDGatt att,
94                      FGNetFDM *fdm, FGNetCtrls *ctrls )
95 {
96     unsigned int i;
97
98     // Version sanity checking
99     fdm->version = FG_NET_FDM_VERSION;
100
101     // Aero parameters
102     fdm->longitude = pos.lon_deg * SGD_DEGREES_TO_RADIANS;
103     fdm->latitude = pos.lat_deg * SGD_DEGREES_TO_RADIANS;
104     fdm->altitude = pos.altitude_msl + alt_offset;
105     fdm->agl = -9999.0;
106     fdm->psi = att.yaw_rad; // heading
107     fdm->phi = att.roll_rad; // roll
108     fdm->theta = att.pitch_rad; // pitch;
109
110     fdm->phidot = 0.0;
111     fdm->thetadot = 0.0;
112     fdm->psidot = 0.0;
113     fdm->vcas = pos.speed_kts;
114     fdm->climb_rate = 0; // fps
115     // cout << "climb rate = " << aero->hdota << endl;
116     fdm->v_north = 0.0;
117     fdm->v_east = 0.0;
118     fdm->v_down = 0.0;
119     fdm->v_wind_body_north = 0.0;
120     fdm->v_wind_body_east = 0.0;
121     fdm->v_wind_body_down = 0.0;
122     fdm->stall_warning = 0.0;
123
124     fdm->A_X_pilot = 0.0;
125     fdm->A_Y_pilot = 0.0;
126     fdm->A_Z_pilot = 0.0 /* (should be -G) */;
127
128     // Engine parameters
129     fdm->num_engines = 1;
130     fdm->eng_state[0] = 2;
131     // cout << "state = " << fdm->eng_state[0] << endl;
132     double rpm = ((pos.speed_kts - 15.0) / 65.0) * 2000.0 + 500.0;
133     if ( rpm < 0.0 ) { rpm = 0.0; }
134     if ( rpm > 3000.0 ) { rpm = 3000.0; }
135     fdm->rpm[0] = rpm;
136
137     fdm->fuel_flow[0] = 0.0;
138     fdm->egt[0] = 0.0;
139     // cout << "egt = " << aero->EGT << endl;
140     fdm->oil_temp[0] = 0.0;
141     fdm->oil_px[0] = 0.0;
142
143     // Consumables
144     fdm->num_tanks = 2;
145     fdm->fuel_quantity[0] = 0.0;
146     fdm->fuel_quantity[1] = 0.0;
147
148     // Gear and flaps
149     fdm->num_wheels = 3;
150     fdm->wow[0] = 0;
151     fdm->wow[1] = 0;
152     fdm->wow[2] = 0;
153
154     // the following really aren't used in this context
155     fdm->cur_time = 0;
156     fdm->warp = 0;
157     fdm->visibility = 0;
158
159     // cout << "Flap deflection = " << aero->dflap << endl;
160     fdm->left_flap = 0.0;
161     fdm->right_flap = 0.0;
162
163     fdm->elevator = -fdm->theta * 1.0;
164     fdm->elevator_trim_tab = 0.0;
165     fdm->left_flap = 0.0;
166     fdm->right_flap = 0.0;
167     fdm->left_aileron = fdm->phi * 1.0;
168     fdm->right_aileron = -fdm->phi * 1.0;
169     fdm->rudder = 0.0;
170     fdm->nose_wheel = 0.0;
171     fdm->speedbrake = 0.0;
172     fdm->spoilers = 0.0;
173
174     // Convert the net buffer to network format
175     fdm->version = htonl(fdm->version);
176
177     htond(fdm->longitude);
178     htond(fdm->latitude);
179     htond(fdm->altitude);
180     htonf(fdm->agl);
181     htonf(fdm->phi);
182     htonf(fdm->theta);
183     htonf(fdm->psi);
184     htonf(fdm->alpha);
185     htonf(fdm->beta);
186
187     htonf(fdm->phidot);
188     htonf(fdm->thetadot);
189     htonf(fdm->psidot);
190     htonf(fdm->vcas);
191     htonf(fdm->climb_rate);
192     htonf(fdm->v_north);
193     htonf(fdm->v_east);
194     htonf(fdm->v_down);
195     htonf(fdm->v_wind_body_north);
196     htonf(fdm->v_wind_body_east);
197     htonf(fdm->v_wind_body_down);
198
199     htonf(fdm->A_X_pilot);
200     htonf(fdm->A_Y_pilot);
201     htonf(fdm->A_Z_pilot);
202
203     htonf(fdm->stall_warning);
204     htonf(fdm->slip_deg);
205
206     for ( i = 0; i < fdm->num_engines; ++i ) {
207         fdm->eng_state[i] = htonl(fdm->eng_state[i]);
208         htonf(fdm->rpm[i]);
209         htonf(fdm->fuel_flow[i]);
210         htonf(fdm->egt[i]);
211         htonf(fdm->cht[i]);
212         htonf(fdm->mp_osi[i]);
213         htonf(fdm->tit[i]);
214         htonf(fdm->oil_temp[i]);
215         htonf(fdm->oil_px[i]);
216     }
217     fdm->num_engines = htonl(fdm->num_engines);
218
219     for ( i = 0; i < fdm->num_tanks; ++i ) {
220         htonf(fdm->fuel_quantity[i]);
221     }
222     fdm->num_tanks = htonl(fdm->num_tanks);
223
224     for ( i = 0; i < fdm->num_wheels; ++i ) {
225         fdm->wow[i] = htonl(fdm->wow[i]);
226         htonf(fdm->gear_pos[i]);
227         htonf(fdm->gear_steer[i]);
228         htonf(fdm->gear_compression[i]);
229     }
230     fdm->num_wheels = htonl(fdm->num_wheels);
231
232     fdm->cur_time = htonl( fdm->cur_time );
233     fdm->warp = htonl( fdm->warp );
234     htonf(fdm->visibility);
235
236     htonf(fdm->elevator);
237     htonf(fdm->elevator_trim_tab);
238     htonf(fdm->left_flap);
239     htonf(fdm->right_flap);
240     htonf(fdm->left_aileron);
241     htonf(fdm->right_aileron);
242     htonf(fdm->rudder);
243     htonf(fdm->nose_wheel);
244     htonf(fdm->speedbrake);
245     htonf(fdm->spoilers);
246 }
247
248
249 static void send_data( const MIDGpos pos, const MIDGatt att ) {
250     int len;
251     int ctrlsize = sizeof( FGNetCtrls );
252     int fdmsize = sizeof( FGNetFDM );
253
254     // cout << "Running main loop" << endl;
255
256     FGNetFDM fgfdm;
257     FGNetCtrls fgctrls;
258
259     midg2fg( pos, att, &fgfdm, &fgctrls );
260     len = fdm_sock.send(&fgfdm, fdmsize, 0);
261 }
262
263
264 void usage( const string &argv0 ) {
265     cout << "Usage: " << argv0 << endl;
266     cout << "\t[ --help ]" << endl;
267     cout << "\t[ --file <file_name>" << endl;
268     cout << "\t[ --hertz <hertz> ]" << endl;
269     cout << "\t[ --host <hostname> ]" << endl;
270     cout << "\t[ --broadcast ]" << endl;
271     cout << "\t[ --fdm-port <fdm output port #> ]" << endl;
272     cout << "\t[ --ctrls-port <ctrls output port #> ]" << endl;
273     cout << "\t[ --altitude-offset <meters> ]" << endl;
274 }
275
276
277 int main( int argc, char **argv ) {
278     double hertz = 60.0;
279     string out_host = "localhost";
280     bool do_broadcast = false;
281
282     // process command line arguments
283     for ( int i = 1; i < argc; ++i ) {
284         if ( strcmp( argv[i], "--help" ) == 0 ) {
285             usage( argv[0] );
286             exit( 0 );
287         } else if ( strcmp( argv[i], "--hertz" ) == 0 ) {
288             ++i;
289             if ( i < argc ) {
290                 hertz = atof( argv[i] );
291             } else {
292                 usage( argv[0] );
293                 exit( -1 );
294             }
295         } else if ( strcmp( argv[i], "--file" ) == 0 ) {
296             ++i;
297             if ( i < argc ) {
298                 file = argv[i];
299             } else {
300                 usage( argv[0] );
301                 exit( -1 );
302             }
303         } else if ( strcmp( argv[i], "--host" ) == 0 ) {
304             ++i;
305             if ( i < argc ) {
306                 out_host = argv[i];
307             } else {
308                 usage( argv[0] );
309                 exit( -1 );
310             }
311         } else if ( strcmp( argv[i], "--broadcast" ) == 0 ) {
312           do_broadcast = true;
313         } else if ( strcmp( argv[i], "--fdm-port" ) == 0 ) {
314             ++i;
315             if ( i < argc ) {
316                 fdm_port = atoi( argv[i] );
317             } else {
318                 usage( argv[0] );
319                 exit( -1 );
320             }
321         } else if ( strcmp( argv[i], "--ctrls-port" ) == 0 ) {
322             ++i;
323             if ( i < argc ) {
324                 ctrls_port = atoi( argv[i] );
325             } else {
326                 usage( argv[0] );
327                 exit( -1 );
328             }
329         } else if ( strcmp( argv[i], "--altitude-offset" ) == 0 ) {
330             ++i;
331             if ( i < argc ) {
332                 alt_offset = atof( argv[i] );
333             } else {
334                 usage( argv[0] );
335                 exit( -1 );
336             }
337         } else {
338             usage( argv[0] );
339             exit( -1 );
340         }
341     }
342
343     // Load the track data
344     if ( file == "" ) {
345         cout << "No track file specified" << endl;
346         exit(-1);
347     }
348     track.load( file );
349     cout << "Loaded " << track.possize() << " position records." << endl;
350     cout << "Loaded " << track.attsize() << " attitude records." << endl;
351
352     // Setup up outgoing network connections
353
354     netInit( &argc,argv ); // We must call this before any other net stuff
355
356     if ( ! fdm_sock.open( false ) ) {  // open a UDP socket
357         cout << "error opening fdm output socket" << endl;
358         return -1;
359     }
360     if ( ! ctrls_sock.open( false ) ) {  // open a UDP socket
361         cout << "error opening ctrls output socket" << endl;
362         return -1;
363     }
364     cout << "open net channels" << endl;
365
366     fdm_sock.setBlocking( false );
367     ctrls_sock.setBlocking( false );
368     cout << "blocking false" << endl;
369
370     if ( do_broadcast ) {
371         fdm_sock.setBroadcast( true );
372         ctrls_sock.setBroadcast( true );
373     }
374
375     if ( fdm_sock.connect( out_host.c_str(), fdm_port ) == -1 ) {
376         perror("connect");
377         cout << "error connecting to outgoing fdm port: " << out_host
378              << ":" << fdm_port << endl;
379         return -1;
380     }
381     cout << "connected outgoing fdm socket" << endl;
382
383     if ( ctrls_sock.connect( out_host.c_str(), ctrls_port ) == -1 ) {
384         perror("connect");
385         cout << "error connecting to outgoing ctrls port: " << out_host
386              << ":" << ctrls_port << endl;
387         return -1;
388     }
389     cout << "connected outgoing ctrls socket" << endl;
390
391     int size = track.possize();
392
393     double current_time = track.get_pospt(0).get_seconds();
394     cout << "Track begin time is " << current_time << endl;
395     double end_time = track.get_pospt(size-1).get_seconds();
396     cout << "Track end time is " << end_time << endl;
397     cout << "Duration = " << end_time - current_time << endl;
398
399     double frame_us = 1000000.0 / hertz;
400     if ( frame_us < 0.0 ) {
401         frame_us = 0.0;
402     }
403
404     SGTimeStamp start_time;
405     start_time.stamp();
406     int pos_count = 0;
407     int att_count = 0;
408
409     MIDGpos pos0, pos1;
410     pos0 = pos1 = track.get_pospt( 0 );
411     
412     MIDGatt att0, att1;
413     att0 = att1 = track.get_attpt( 0 );
414     
415     while ( current_time < end_time ) {
416         // cout << "current_time = " << current_time << " end_time = "
417         //      << end_time << endl;
418
419         // Advance position pointer
420         if ( current_time > pos1.get_seconds() ) {
421             pos0 = pos1;
422             ++pos_count;
423             // cout << "count = " << count << endl;
424             pos1 = track.get_pospt( pos_count );
425         }
426         // cout << "p0 = " << p0.get_time() << " p1 = " << p1.get_time()
427         //      << endl;
428
429         // Advance attitude pointer
430         if ( current_time > att1.get_seconds() ) {
431             att0 = att1;
432             ++att_count;
433             // cout << "count = " << count << endl;
434             att1 = track.get_attpt( att_count );
435         }
436         // cout << "p0 = " << p0.get_time() << " p1 = " << p1.get_time()
437         //      << endl;
438
439         double pos_percent;
440         if ( fabs(pos1.get_seconds() - pos0.get_seconds()) < 0.00001 ) {
441             pos_percent = 0.0;
442         } else {
443             pos_percent =
444                 (current_time - pos0.get_seconds()) /
445                 (pos1.get_seconds() - pos0.get_seconds());
446         }
447         // cout << "Percent = " << percent << endl;
448         double att_percent;
449         if ( fabs(att1.get_seconds() - att0.get_seconds()) < 0.00001 ) {
450             att_percent = 0.0;
451         } else {
452             att_percent =
453                 (current_time - att0.get_seconds()) /
454                 (att1.get_seconds() - att0.get_seconds());
455         }
456         // cout << "Percent = " << percent << endl;
457
458         MIDGpos pos = MIDGInterpPos( pos0, pos1, pos_percent );
459         MIDGatt att = MIDGInterpAtt( att0, att1, att_percent );
460         // cout << current_time << " " << p0.lat_deg << ", " << p0.lon_deg
461         //      << endl;
462         // cout << current_time << " " << p1.lat_deg << ", " << p1.lon_deg
463         //      << endl;
464         // cout << (double)current_time << " " << pos.lat_deg << ", "
465         //      << pos.lon_deg << " " << att.yaw_deg << endl;
466         printf( "%.3f  %.4f %.4f %.1f  %.2f %.2f %.2f\n",
467                 current_time,
468                 pos.lat_deg, pos.lon_deg, pos.altitude_msl,
469                 att.yaw_rad * 180.0 / SG_PI,
470                 att.pitch_rad * 180.0 / SG_PI,
471                 att.roll_rad * 180.0 / SG_PI );
472
473         send_data( pos, att );
474
475         // Update the elapsed time.
476         static bool first_time = true;
477         if ( first_time ) {
478             last_time_stamp.stamp();
479             first_time = false;
480         }
481
482         current_time_stamp.stamp();
483         /* Convert to ms */
484         double elapsed_us = current_time_stamp - last_time_stamp;
485         if ( elapsed_us < (frame_us - 2000) ) {
486             double requested_us = (frame_us - elapsed_us) - 2000 ;
487             ulMilliSecondSleep ( (int)(requested_us / 1000.0) ) ;
488         }
489         current_time_stamp.stamp();
490         while ( current_time_stamp - last_time_stamp < frame_us ) {
491             current_time_stamp.stamp();
492         }
493
494         current_time += (frame_us / 1000000.0);
495         last_time_stamp = current_time_stamp;
496     }
497
498     cout << "Processed " << pos_count << " entries in "
499          << (current_time_stamp - start_time) / 1000000 << " seconds." << endl;
500
501     return 0;
502 }