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