]> git.mxchange.org Git - flightgear.git/blob - src/Aircraft/replay.cxx
702a8f9137ef4935cc0a8799e0bf048670a5456d
[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 = _Time*10;
177     unsigned int h = Time/36000;
178     unsigned int m = (Time % 36000)/600;
179     unsigned int s = (Time % 600)/10;
180     unsigned int d = Time % 10;
181
182     int len;
183     if (h>0)
184         len = sprintf(pStrBuffer,"%u:%02u:%02u",h,m,s);
185     else
186         len = sprintf(pStrBuffer,"%u:%02u",m,s);
187
188     if (len < 0)
189     {
190         *pStrBuffer = 0;
191         return;
192     }
193
194     if (ShowDecimal)
195         sprintf(&pStrBuffer[len],".%u",d);
196 }
197
198 /** Start replay session
199  */
200 bool
201 FGReplay::start()
202 {
203     // freeze the fdm, resume from sim pause
204     double StartTime = get_start_time();
205     double EndTime = get_end_time();
206     fgSetDouble("/sim/replay/start-time", StartTime);
207     fgSetDouble("/sim/replay/end-time",   EndTime);
208     char StrBuffer[30];
209     printTimeStr(StrBuffer,StartTime,false);
210     fgSetString("/sim/replay/start-time-str", StrBuffer);
211     printTimeStr(StrBuffer,EndTime,false);
212     fgSetString("/sim/replay/end-time-str",   StrBuffer);
213
214     unsigned long buffer_elements =  short_term.size()+medium_term.size()+long_term.size();
215     fgSetDouble("/sim/replay/buffer-size-mbyte",
216                 buffer_elements*m_pRecorder->getRecordSize() / (1024*1024.0));
217     if ((fgGetBool("/sim/freeze/master"))||
218         (0 == replay_master->getIntValue()))
219         fgSetString("/sim/messages/copilot", "Replay active. 'Esc' to stop.");
220     fgSetBool  ("/sim/freeze/master",     0);
221     fgSetBool  ("/sim/freeze/clock",      0);
222     if (0 == replay_master->getIntValue())
223     {
224         replay_master->setIntValue(1);
225         replay_time->setDoubleValue(-1);
226         replay_time_str->setStringValue("");
227     }
228     return true;
229 }
230
231 /** 
232  *  Update the saved data
233  */
234
235 void
236 FGReplay::update( double dt )
237 {
238     int current_replay_state = last_replay_state;
239     timingInfo.clear();
240     stamp("begin");
241
242     if ( disable_replay->getBoolValue() )
243     {
244         if (fgGetBool("/sim/freeze/master",false)||
245             fgGetBool("/sim/freeze/clock",false))
246         {
247             fgSetBool("/sim/freeze/master",false);
248             fgSetBool("/sim/freeze/clock",false);
249             last_replay_state = 1;
250         }
251         else
252         if ((replay_master->getIntValue() != 3)||
253             (last_replay_state == 3))
254         {
255             current_replay_state = replay_master->getIntValue();
256             replay_master->setIntValue(0);
257             replay_time->setDoubleValue(0);
258             replay_time_str->setStringValue("");
259             disable_replay->setBoolValue(0);
260             speed_up->setDoubleValue(1.0);
261             speed_up->setDoubleValue(1.0);
262             if (fgGetBool("/sim/replay/mute",false))
263             {
264                 fgSetBool("/sim/sound/enabled",true);
265                 fgSetBool("/sim/replay/mute",false);
266             }
267             fgSetString("/sim/messages/copilot", "Replay stopped. Your controls!");
268         }
269     }
270
271     int replay_state = replay_master->getIntValue();
272     if ((replay_state == 0)&&
273         (last_replay_state > 0))
274     {
275         if (current_replay_state == 3)
276         {
277             // take control at current replay position ("My controls!").
278             // May need to uncrash the aircraft here :)
279             fgSetBool("/sim/crashed", false);
280         }
281         else
282         {
283             // normal replay exit, restore most recent frame
284             replay(DBL_MAX);
285         }
286
287         // replay is finished
288         last_replay_state = replay_state;
289         return;
290     }
291
292     // remember recent state
293     last_replay_state = replay_state;
294
295     switch(replay_state)
296     {
297         case 0:
298             // replay inactive, keep recording
299             break;
300         case 1: // normal replay
301         case 3: // prepare to resume normal flight at current replay position 
302         {
303             // replay active
304             double current_time = replay_time->getDoubleValue();
305             if (current_time<=0.0)
306             {
307                 // initialize start time
308                 double startTime = get_start_time();
309                 double endTime = get_end_time();
310                 fgSetDouble( "/sim/replay/start-time", startTime );
311                 fgSetDouble( "/sim/replay/end-time", endTime );
312                 double duration = fgGetDouble( "/sim/replay/duration" );
313                 if( duration && (duration < (endTime - startTime)) ) {
314                     current_time = endTime - duration;
315                 } else {
316                     current_time = startTime;
317                 }
318             }
319             bool IsFinished = replay( replay_time->getDoubleValue() );
320             if (IsFinished)
321                 current_time = (replay_looped->getBoolValue()) ? -1 : get_end_time()+0.01;
322             else
323                 current_time += dt * speed_up->getDoubleValue();
324             replay_time->setDoubleValue(current_time);
325             char StrBuffer[30];
326             printTimeStr(StrBuffer,current_time);
327             replay_time_str->setStringValue((const char*)StrBuffer);
328             return; // don't record the replay session 
329         }
330         case 2: // normal replay operation
331             return; // don't record the replay session
332         default:
333             throw sg_range_exception("unknown FGReplay state");
334     }
335
336     // flight recording
337
338     //cerr << "Recording replay" << endl;
339     sim_time += dt * speed_up->getDoubleValue();
340
341     // sanity check, don't collect data if FDM data isn't good
342     if (!fgGetBool("/sim/fdm-initialized", false)) {
343         return;
344     }
345
346     FGReplayData* r = record(sim_time);
347     if (!r)
348     {
349         SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Out of memory!");
350         return;
351     }
352
353     // update the short term list
354     //stamp("point_06");
355     short_term.push_back( r );
356     //stamp("point_07");
357     FGReplayData *st_front = short_term.front();
358     
359     if (!st_front)
360     {
361         SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Inconsistent data!");
362     }
363
364     if ( sim_time - st_front->sim_time > m_high_res_time ) {
365         while ( sim_time - st_front->sim_time > m_high_res_time ) {
366             st_front = short_term.front();
367             recycler.push_back(st_front);
368             short_term.pop_front();
369         }
370         //stamp("point_08");
371         // update the medium term list
372         if ( sim_time - last_mt_time > m_medium_sample_rate ) {
373             last_mt_time = sim_time;
374             st_front = short_term.front();
375             medium_term.push_back( st_front );
376             short_term.pop_front();
377
378             FGReplayData *mt_front = medium_term.front();
379             if ( sim_time - mt_front->sim_time > m_medium_res_time ) {
380             //stamp("point_09");
381                 while ( sim_time - mt_front->sim_time > m_medium_res_time ) {
382                     mt_front = medium_term.front();
383                     recycler.push_back(mt_front);
384                     medium_term.pop_front();
385                 }
386                 // update the long term list
387                 if ( sim_time - last_lt_time > m_long_sample_rate ) {
388                     last_lt_time = sim_time;
389                     mt_front = medium_term.front();
390                     long_term.push_back( mt_front );
391                     medium_term.pop_front();
392
393                     FGReplayData *lt_front = long_term.front();
394                     if ( sim_time - lt_front->sim_time > m_low_res_time ) {
395                         //stamp("point_10");
396                         while ( sim_time - lt_front->sim_time > m_low_res_time ) {
397                             lt_front = long_term.front();
398                             recycler.push_back(lt_front);
399                             long_term.pop_front();
400                         }
401                     }
402                 }
403             }
404         }
405     }
406
407 #if 0
408     cout << "short term size = " << short_term.size()
409          << "  time = " << sim_time - short_term.front().sim_time
410          << endl;
411     cout << "medium term size = " << medium_term.size()
412          << "  time = " << sim_time - medium_term.front().sim_time
413          << endl;
414     cout << "long term size = " << long_term.size()
415          << "  time = " << sim_time - long_term.front().sim_time
416          << endl;
417 #endif
418    //stamp("point_finished");
419 }
420
421 FGReplayData*
422 FGReplay::record(double time)
423 {
424     FGReplayData* r = NULL;
425
426     if (recycler.size())
427     {
428         r = recycler.front();
429         recycler.pop_front();
430     }
431
432     r = m_pRecorder->capture(time, r);
433
434     return r;
435 }
436
437 /** 
438  * interpolate a specific time from a specific list
439  */
440 void
441 FGReplay::interpolate( double time, const replay_list_type &list)
442 {
443     // sanity checking
444     if ( list.size() == 0 )
445     {
446         // handle empty list
447         return;
448     } else if ( list.size() == 1 )
449     {
450         // handle list size == 1
451         replay(time, list[0]);
452         return;
453     }
454
455     unsigned int last = list.size() - 1;
456     unsigned int first = 0;
457     unsigned int mid = ( last + first ) / 2;
458
459     bool done = false;
460     while ( !done )
461     {
462         // cout << "  " << first << " <=> " << last << endl;
463         if ( last == first ) {
464             done = true;
465         } else if ( list[mid]->sim_time < time && list[mid+1]->sim_time < time ) {
466             // too low
467             first = mid;
468             mid = ( last + first ) / 2;
469         } else if ( list[mid]->sim_time > time && list[mid+1]->sim_time > time ) {
470             // too high
471             last = mid;
472             mid = ( last + first ) / 2;
473         } else {
474             done = true;
475         }
476     }
477
478     replay(time, list[mid+1], list[mid]);
479 }
480
481 /** 
482  *  Replay a saved frame based on time, interpolate from the two
483  *  nearest saved frames.
484  *  Returns true when replay sequence has finished, false otherwise.
485  */
486
487 bool
488 FGReplay::replay( double time ) {
489     // cout << "replay: " << time << " ";
490     // find the two frames to interpolate between
491     double t1, t2;
492
493     if ( short_term.size() > 0 ) {
494         t1 = short_term.back()->sim_time;
495         t2 = short_term.front()->sim_time;
496         if ( time > t1 ) {
497             // replay the most recent frame
498             replay( time, short_term.back() );
499             // replay is finished now
500             return true;
501             // cout << "first frame" << endl;
502         } else if ( time <= t1 && time >= t2 ) {
503             interpolate( time, short_term );
504             // cout << "from short term" << endl;
505         } else if ( medium_term.size() > 0 ) {
506             t1 = short_term.front()->sim_time;
507             t2 = medium_term.back()->sim_time;
508             if ( time <= t1 && time >= t2 )
509             {
510                 replay(time, medium_term.back(), short_term.front());
511                 // cout << "from short/medium term" << endl;
512             } else {
513                 t1 = medium_term.back()->sim_time;
514                 t2 = medium_term.front()->sim_time;
515                 if ( time <= t1 && time >= t2 ) {
516                     interpolate( time, medium_term );
517                     // cout << "from medium term" << endl;
518                 } else if ( long_term.size() > 0 ) {
519                     t1 = medium_term.front()->sim_time;
520                     t2 = long_term.back()->sim_time;
521                     if ( time <= t1 && time >= t2 )
522                     {
523                         replay(time, long_term.back(), medium_term.front());
524                         // cout << "from medium/long term" << endl;
525                     } else {
526                         t1 = long_term.back()->sim_time;
527                         t2 = long_term.front()->sim_time;
528                         if ( time <= t1 && time >= t2 ) {
529                             interpolate( time, long_term );
530                             // cout << "from long term" << endl;
531                         } else {
532                             // replay the oldest long term frame
533                             replay(time, long_term.front());
534                             // cout << "oldest long term frame" << endl;
535                         }
536                     }
537                 } else {
538                     // replay the oldest medium term frame
539                     replay(time, medium_term.front());
540                     // cout << "oldest medium term frame" << endl;
541                 }
542             }
543         } else {
544             // replay the oldest short term frame
545             replay(time, short_term.front());
546             // cout << "oldest short term frame" << endl;
547         }
548     } else {
549         // nothing to replay
550         return true;
551     }
552     return false;
553 }
554
555 /** 
556  * given two FGReplayData elements and a time, interpolate between them
557  */
558 void
559 FGReplay::replay(double time, FGReplayData* pCurrentFrame, FGReplayData* pOldFrame)
560 {
561     m_pRecorder->replay(time,pCurrentFrame,pOldFrame);
562 }
563
564 double
565 FGReplay::get_start_time()
566 {
567     if ( long_term.size() > 0 )
568     {
569         return long_term.front()->sim_time;
570     } else if ( medium_term.size() > 0 )
571     {
572         return medium_term.front()->sim_time;
573     } else if ( short_term.size() )
574     {
575         return short_term.front()->sim_time;
576     } else
577     {
578         return 0.0;
579     }
580 }
581
582 double
583 FGReplay::get_end_time()
584 {
585     if ( short_term.size() )
586     {
587         return short_term.back()->sim_time;
588     } else
589     {
590         return 0.0;
591     } 
592 }