]> git.mxchange.org Git - flightgear.git/blob - src/Aircraft/replay.cxx
Merge branch 'next' of gitorious.org:fg/flightgear into next
[flightgear.git] / src / Aircraft / replay.cxx
1 // replay.cxx - a system to record and replay FlightGear flights
2 //
3 // Written by Curtis Olson, started July 2003.
4 // Updated by Thorsten Brehm, September 2011.
5 //
6 // Copyright (C) 2003  Curtis L. Olson  - http://www.flightgear.org/~curt
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include <float.h>
29
30 #include <simgear/constants.h>
31 #include <simgear/structure/exception.hxx>
32
33 #include <Main/fg_props.hxx>
34 #include <FDM/fdm_shell.hxx>
35
36 #include "replay.hxx"
37 #include "flightrecorder.hxx"
38
39 /**
40  * Constructor
41  */
42
43 FGReplay::FGReplay() :
44     last_replay_state(0),
45     m_high_res_time(60.0),
46     m_medium_res_time(600.0),
47     m_low_res_time(3600.0),
48     m_medium_sample_rate(0.5), // medium term sample rate (sec)
49     m_long_sample_rate(5.0),   // long term sample rate (sec)
50     m_pRecorder(new FGFlightRecorder("replay-config"))
51 {
52 }
53
54 /**
55  * Destructor
56  */
57
58 FGReplay::~FGReplay()
59 {
60     clear();
61
62     delete m_pRecorder;
63     m_pRecorder = NULL;
64 }
65
66 /**
67  * Clear all internal buffers.
68  */
69 void
70 FGReplay::clear()
71 {
72     while ( !short_term.empty() )
73     {
74         m_pRecorder->deleteRecord(short_term.front());
75         short_term.pop_front();
76     }
77     while ( !medium_term.empty() )
78     {
79         m_pRecorder->deleteRecord(medium_term.front());
80         medium_term.pop_front();
81     }
82     while ( !long_term.empty() )
83     {
84         m_pRecorder->deleteRecord(long_term.front());
85         long_term.pop_front();
86     }
87     while ( !recycler.empty() )
88     {
89         m_pRecorder->deleteRecord(recycler.front());
90         recycler.pop_front();
91     }
92 }
93
94 /** 
95  * Initialize the data structures
96  */
97
98 void
99 FGReplay::init()
100 {
101     disable_replay  = fgGetNode("/sim/replay/disable",      true);
102     replay_master   = fgGetNode("/sim/freeze/replay-state", true);
103     replay_time     = fgGetNode("/sim/replay/time",         true);
104     replay_time_str = fgGetNode("/sim/replay/time-str",     true);
105     replay_looped   = fgGetNode("/sim/replay/looped",       true);
106     speed_up        = fgGetNode("/sim/speed-up",            true);
107     reinit();
108 }
109
110 /** 
111  * Reset replay queues.
112  */
113
114 void
115 FGReplay::reinit()
116 {
117     sim_time = 0.0;
118     last_mt_time = 0.0;
119     last_lt_time = 0.0;
120
121     // Flush queues
122     clear();
123     m_pRecorder->reinit();
124
125     m_high_res_time   = fgGetDouble("/sim/replay/buffer/high-res-time",    60.0);
126     m_medium_res_time = fgGetDouble("/sim/replay/buffer/medium-res-time", 600.0); // 10 mins
127     m_low_res_time    = fgGetDouble("/sim/replay/buffer/low-res-time",   3600.0); // 1 h
128     // short term sample rate is as every frame
129     m_medium_sample_rate = fgGetDouble("/sim/replay/buffer/medium-res-sample-dt", 0.5); // medium term sample rate (sec)
130     m_long_sample_rate   = fgGetDouble("/sim/replay/buffer/low-res-sample-dt",    5.0); // long term sample rate (sec)
131
132     // Create an estimated nr of required ReplayData objects
133     // 120 is an estimated maximum frame rate.
134     int estNrObjects = (int) ((m_high_res_time*120) + (m_medium_res_time*m_medium_sample_rate) +
135                              (m_low_res_time*m_long_sample_rate)); 
136     for (int i = 0; i < estNrObjects; i++)
137     {
138         FGReplayData* r = m_pRecorder->createEmptyRecord();
139         if (r)
140             recycler.push_back(r);
141         else
142         {
143             SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Out of memory!");
144         }
145     }
146     replay_master->setIntValue(0);
147     disable_replay->setBoolValue(0);
148     replay_time->setDoubleValue(0);
149     replay_time_str->setStringValue("");
150 }
151
152 /** 
153  * Bind to the property tree
154  */
155
156 void
157 FGReplay::bind()
158 {
159 }
160
161
162 /** 
163  *  Unbind from the property tree
164  */
165
166 void
167 FGReplay::unbind()
168 {
169     // nothing to unbind
170 }
171
172 static void
173 printTimeStr(char* pStrBuffer,double _Time, bool ShowDecimal=true)
174 {
175     if (_Time<0)
176         _Time = 0;
177     unsigned int Time = (unsigned int) (_Time*10);
178     int h = Time/36000;
179     int m = (Time % 36000)/600;
180     int s = (Time % 600)/10;
181     int d = Time % 10;
182     if (h>0)
183         sprintf(pStrBuffer,"%u:%02u",h,m);
184     else
185         sprintf(pStrBuffer,"%u",m);
186     if (ShowDecimal)
187         sprintf(pStrBuffer,"%s:%02u.%u",pStrBuffer,s,d);
188     else
189         sprintf(pStrBuffer,"%s:%02u",pStrBuffer,s);
190 }
191
192 /** Start replay session
193  */
194 bool
195 FGReplay::start()
196 {
197     // freeze the fdm, resume from sim pause
198     double StartTime = get_start_time();
199     double EndTime = get_end_time();
200     fgSetDouble("/sim/replay/start-time", StartTime);
201     fgSetDouble("/sim/replay/end-time",   EndTime);
202     char StrBuffer[30];
203     printTimeStr(StrBuffer,StartTime,false);
204     fgSetString("/sim/replay/start-time-str", StrBuffer);
205     printTimeStr(StrBuffer,EndTime,false);
206     fgSetString("/sim/replay/end-time-str",   StrBuffer);
207
208     unsigned long buffer_elements =  short_term.size()+medium_term.size()+long_term.size();
209     fgSetDouble("/sim/replay/buffer-size-mbyte",
210                 buffer_elements*m_pRecorder->getRecordSize() / (1024*1024.0));
211     if ((fgGetBool("/sim/freeze/master"))||
212         (0 == replay_master->getIntValue()))
213         fgSetString("/sim/messages/copilot", "Replay active. 'Esc' to stop.");
214     fgSetBool  ("/sim/freeze/master",     0);
215     fgSetBool  ("/sim/freeze/clock",      0);
216     if (0 == replay_master->getIntValue())
217     {
218         replay_master->setIntValue(1);
219         replay_time->setDoubleValue(-1);
220         replay_time_str->setStringValue("");
221     }
222     return true;
223 }
224
225 /** 
226  *  Update the saved data
227  */
228
229 void
230 FGReplay::update( double dt )
231 {
232     int current_replay_state = last_replay_state;
233     timingInfo.clear();
234     stamp("begin");
235
236     if ( disable_replay->getBoolValue() )
237     {
238         current_replay_state = replay_master->getIntValue();
239         replay_master->setIntValue(0);
240         replay_time->setDoubleValue(0);
241         replay_time_str->setStringValue("");
242         disable_replay->setBoolValue(0);
243         speed_up->setDoubleValue(1.0);
244         fgSetString("/sim/messages/copilot", "Replay stopped");
245     }
246
247     int replay_state = replay_master->getIntValue();
248
249     if ((replay_state > 0)&&
250        (last_replay_state == 0))
251     {
252         // replay is starting, suspend FDM
253         /* FIXME we need to suspend/resume the FDM - not the entire FDM shell.
254          * FDM isn't available via the global subsystem manager yet, so need a
255          * method at the FDMshell for now */
256         ((FDMShell*) globals->get_subsystem("flight"))->getFDM()->suspend();
257     }
258     else
259     if ((replay_state == 0)&&
260         (last_replay_state > 0))
261     {
262         if (current_replay_state == 3)
263         {
264             // "my controls!" requested: pilot takes control at current replay position...
265             // May need to uncrash the aircraft here :)
266             fgSetBool("/sim/crashed", false);
267         }
268         else
269         {
270             // replay was active, restore most recent frame
271             replay(DBL_MAX);
272         }
273         // replay is finished, resume FDM
274         ((FDMShell*) globals->get_subsystem("flight"))->getFDM()->resume();
275     }
276
277     // remember recent state
278     last_replay_state = replay_state;
279
280     switch(replay_state)
281     {
282         case 0:
283             // replay inactive, keep recording
284             break;
285         case 1:
286         {
287             // replay active
288             double current_time = replay_time->getDoubleValue();
289             if (current_time<=0.0)
290             {
291                 // initialize start time
292                 double startTime = get_start_time();
293                 double endTime = get_end_time();
294                 fgSetDouble( "/sim/replay/start-time", startTime );
295                 fgSetDouble( "/sim/replay/end-time", endTime );
296                 double duration = fgGetDouble( "/sim/replay/duration" );
297                 if( duration && (duration < (endTime - startTime)) ) {
298                     current_time = endTime - duration;
299                 } else {
300                     current_time = startTime;
301                 }
302             }
303             bool IsFinished = replay( replay_time->getDoubleValue() );
304             if (IsFinished)
305                 current_time = (replay_looped->getBoolValue()) ? -1 : get_end_time()+0.01;
306             else
307                 current_time += dt * speed_up->getDoubleValue();
308             replay_time->setDoubleValue(current_time);
309             char StrBuffer[30];
310             printTimeStr(StrBuffer,current_time);
311             replay_time_str->setStringValue((const char*)StrBuffer);
312             return; // don't record the replay session 
313         }
314         case 2: // normal replay operation
315         case 3: // replay operation, prepare to resume normal flight at current replay position 
316             // replay paused, no-op
317             return; // don't record the replay session
318         default:
319             throw sg_range_exception("unknown FGReplay state");
320     }
321
322     // flight recording
323
324     //cerr << "Recording replay" << endl;
325     sim_time += dt * speed_up->getDoubleValue();
326
327     // sanity check, don't collect data if FDM data isn't good
328     if (!fgGetBool("/sim/fdm-initialized", false)) {
329         return;
330     }
331
332     FGReplayData* r = record(sim_time);
333     if (!r)
334     {
335         SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Out of memory!");
336         return;
337     }
338
339     // update the short term list
340     //stamp("point_06");
341     short_term.push_back( r );
342     //stamp("point_07");
343     FGReplayData *st_front = short_term.front();
344     
345     if (!st_front)
346     {
347         SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Inconsistent data!");
348     }
349
350     if ( sim_time - st_front->sim_time > m_high_res_time ) {
351         while ( sim_time - st_front->sim_time > m_high_res_time ) {
352             st_front = short_term.front();
353             recycler.push_back(st_front);
354             short_term.pop_front();
355         }
356         //stamp("point_08");
357         // update the medium term list
358         if ( sim_time - last_mt_time > m_medium_sample_rate ) {
359             last_mt_time = sim_time;
360             st_front = short_term.front();
361             medium_term.push_back( st_front );
362             short_term.pop_front();
363
364             FGReplayData *mt_front = medium_term.front();
365             if ( sim_time - mt_front->sim_time > m_medium_res_time ) {
366             //stamp("point_09");
367                 while ( sim_time - mt_front->sim_time > m_medium_res_time ) {
368                     mt_front = medium_term.front();
369                     recycler.push_back(mt_front);
370                     medium_term.pop_front();
371                 }
372                 // update the long term list
373                 if ( sim_time - last_lt_time > m_long_sample_rate ) {
374                     last_lt_time = sim_time;
375                     mt_front = medium_term.front();
376                     long_term.push_back( mt_front );
377                     medium_term.pop_front();
378
379                     FGReplayData *lt_front = long_term.front();
380                     if ( sim_time - lt_front->sim_time > m_low_res_time ) {
381                         //stamp("point_10");
382                         while ( sim_time - lt_front->sim_time > m_low_res_time ) {
383                             lt_front = long_term.front();
384                             recycler.push_back(lt_front);
385                             long_term.pop_front();
386                         }
387                     }
388                 }
389             }
390         }
391     }
392
393 #if 0
394     cout << "short term size = " << short_term.size()
395          << "  time = " << sim_time - short_term.front().sim_time
396          << endl;
397     cout << "medium term size = " << medium_term.size()
398          << "  time = " << sim_time - medium_term.front().sim_time
399          << endl;
400     cout << "long term size = " << long_term.size()
401          << "  time = " << sim_time - long_term.front().sim_time
402          << endl;
403 #endif
404    //stamp("point_finished");
405 }
406
407 FGReplayData*
408 FGReplay::record(double time)
409 {
410     FGReplayData* r = NULL;
411
412     if (recycler.size())
413     {
414         r = recycler.front();
415         recycler.pop_front();
416     }
417
418     r = m_pRecorder->capture(time, r);
419
420     return r;
421 }
422
423 /** 
424  * interpolate a specific time from a specific list
425  */
426 void
427 FGReplay::interpolate( double time, const replay_list_type &list)
428 {
429     // sanity checking
430     if ( list.size() == 0 )
431     {
432         // handle empty list
433         return;
434     } else if ( list.size() == 1 )
435     {
436         // handle list size == 1
437         replay(time, list[0]);
438         return;
439     }
440
441     unsigned int last = list.size() - 1;
442     unsigned int first = 0;
443     unsigned int mid = ( last + first ) / 2;
444
445     bool done = false;
446     while ( !done )
447     {
448         // cout << "  " << first << " <=> " << last << endl;
449         if ( last == first ) {
450             done = true;
451         } else if ( list[mid]->sim_time < time && list[mid+1]->sim_time < time ) {
452             // too low
453             first = mid;
454             mid = ( last + first ) / 2;
455         } else if ( list[mid]->sim_time > time && list[mid+1]->sim_time > time ) {
456             // too high
457             last = mid;
458             mid = ( last + first ) / 2;
459         } else {
460             done = true;
461         }
462     }
463
464     replay(time, list[mid+1], list[mid]);
465 }
466
467 /** 
468  *  Replay a saved frame based on time, interpolate from the two
469  *  nearest saved frames.
470  *  Returns true when replay sequence has finished, false otherwise.
471  */
472
473 bool
474 FGReplay::replay( double time ) {
475     // cout << "replay: " << time << " ";
476     // find the two frames to interpolate between
477     double t1, t2;
478
479     if ( short_term.size() > 0 ) {
480         t1 = short_term.back()->sim_time;
481         t2 = short_term.front()->sim_time;
482         if ( time > t1 ) {
483             // replay the most recent frame
484             replay( time, short_term.back() );
485             // replay is finished now
486             return true;
487             // cout << "first frame" << endl;
488         } else if ( time <= t1 && time >= t2 ) {
489             interpolate( time, short_term );
490             // cout << "from short term" << endl;
491         } else if ( medium_term.size() > 0 ) {
492             t1 = short_term.front()->sim_time;
493             t2 = medium_term.back()->sim_time;
494             if ( time <= t1 && time >= t2 )
495             {
496                 replay(time, medium_term.back(), short_term.front());
497                 // cout << "from short/medium term" << endl;
498             } else {
499                 t1 = medium_term.back()->sim_time;
500                 t2 = medium_term.front()->sim_time;
501                 if ( time <= t1 && time >= t2 ) {
502                     interpolate( time, medium_term );
503                     // cout << "from medium term" << endl;
504                 } else if ( long_term.size() > 0 ) {
505                     t1 = medium_term.front()->sim_time;
506                     t2 = long_term.back()->sim_time;
507                     if ( time <= t1 && time >= t2 )
508                     {
509                         replay(time, long_term.back(), medium_term.front());
510                         // cout << "from medium/long term" << endl;
511                     } else {
512                         t1 = long_term.back()->sim_time;
513                         t2 = long_term.front()->sim_time;
514                         if ( time <= t1 && time >= t2 ) {
515                             interpolate( time, long_term );
516                             // cout << "from long term" << endl;
517                         } else {
518                             // replay the oldest long term frame
519                             replay(time, long_term.front());
520                             // cout << "oldest long term frame" << endl;
521                         }
522                     }
523                 } else {
524                     // replay the oldest medium term frame
525                     replay(time, medium_term.front());
526                     // cout << "oldest medium term frame" << endl;
527                 }
528             }
529         } else {
530             // replay the oldest short term frame
531             replay(time, short_term.front());
532             // cout << "oldest short term frame" << endl;
533         }
534     } else {
535         // nothing to replay
536         return true;
537     }
538     return false;
539 }
540
541 /** 
542  * given two FGReplayData elements and a time, interpolate between them
543  */
544 void
545 FGReplay::replay(double time, FGReplayData* pCurrentFrame, FGReplayData* pOldFrame)
546 {
547     m_pRecorder->replay(time,pCurrentFrame,pOldFrame);
548 }
549
550 double
551 FGReplay::get_start_time()
552 {
553     if ( long_term.size() > 0 )
554     {
555         return long_term.front()->sim_time;
556     } else if ( medium_term.size() > 0 )
557     {
558         return medium_term.front()->sim_time;
559     } else if ( short_term.size() )
560     {
561         return short_term.front()->sim_time;
562     } else
563     {
564         return 0.0;
565     }
566 }
567
568 double
569 FGReplay::get_end_time()
570 {
571     if ( short_term.size() )
572     {
573         return short_term.back()->sim_time;
574     } else
575     {
576         return 0.0;
577     } 
578 }