]> git.mxchange.org Git - flightgear.git/blob - src/Aircraft/replay.cxx
Revert "Fix compilation problem with MSVC 2012"
[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             bool ResetTime = (current_time<=0.0);
306             if (ResetTime)
307             {
308                 // initialize start time
309                 double startTime = get_start_time();
310                 double endTime = get_end_time();
311                 fgSetDouble( "/sim/replay/start-time", startTime );
312                 fgSetDouble( "/sim/replay/end-time", endTime );
313                 double duration = fgGetDouble( "/sim/replay/duration" );
314                 if( duration && (duration < (endTime - startTime)) ) {
315                     current_time = endTime - duration;
316                 } else {
317                     current_time = startTime;
318                 }
319             }
320             bool IsFinished = replay( replay_time->getDoubleValue() );
321             if (IsFinished)
322                 current_time = (replay_looped->getBoolValue()) ? -1 : get_end_time()+0.01;
323             else
324                 current_time += dt * speed_up->getDoubleValue();
325             replay_time->setDoubleValue(current_time);
326             char StrBuffer[30];
327             printTimeStr(StrBuffer,current_time);
328             replay_time_str->setStringValue((const char*)StrBuffer);
329
330             // when time skipped (looped replay), trigger listeners to reset views etc
331             if (ResetTime)
332                 replay_master->setIntValue(replay_state);
333
334             return; // don't record the replay session 
335         }
336         case 2: // normal replay operation
337             return; // don't record the replay session
338         default:
339             throw sg_range_exception("unknown FGReplay state");
340     }
341
342     // flight recording
343
344     //cerr << "Recording replay" << endl;
345     sim_time += dt * speed_up->getDoubleValue();
346
347     // sanity check, don't collect data if FDM data isn't good
348     if (!fgGetBool("/sim/fdm-initialized", false)) {
349         return;
350     }
351
352     FGReplayData* r = record(sim_time);
353     if (!r)
354     {
355         SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Out of memory!");
356         return;
357     }
358
359     // update the short term list
360     //stamp("point_06");
361     short_term.push_back( r );
362     //stamp("point_07");
363     FGReplayData *st_front = short_term.front();
364     
365     if (!st_front)
366     {
367         SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Inconsistent data!");
368     }
369
370     if ( sim_time - st_front->sim_time > m_high_res_time ) {
371         while ( sim_time - st_front->sim_time > m_high_res_time ) {
372             st_front = short_term.front();
373             recycler.push_back(st_front);
374             short_term.pop_front();
375         }
376         //stamp("point_08");
377         // update the medium term list
378         if ( sim_time - last_mt_time > m_medium_sample_rate ) {
379             last_mt_time = sim_time;
380             st_front = short_term.front();
381             medium_term.push_back( st_front );
382             short_term.pop_front();
383
384             FGReplayData *mt_front = medium_term.front();
385             if ( sim_time - mt_front->sim_time > m_medium_res_time ) {
386             //stamp("point_09");
387                 while ( sim_time - mt_front->sim_time > m_medium_res_time ) {
388                     mt_front = medium_term.front();
389                     recycler.push_back(mt_front);
390                     medium_term.pop_front();
391                 }
392                 // update the long term list
393                 if ( sim_time - last_lt_time > m_long_sample_rate ) {
394                     last_lt_time = sim_time;
395                     mt_front = medium_term.front();
396                     long_term.push_back( mt_front );
397                     medium_term.pop_front();
398
399                     FGReplayData *lt_front = long_term.front();
400                     if ( sim_time - lt_front->sim_time > m_low_res_time ) {
401                         //stamp("point_10");
402                         while ( sim_time - lt_front->sim_time > m_low_res_time ) {
403                             lt_front = long_term.front();
404                             recycler.push_back(lt_front);
405                             long_term.pop_front();
406                         }
407                     }
408                 }
409             }
410         }
411     }
412
413 #if 0
414     cout << "short term size = " << short_term.size()
415          << "  time = " << sim_time - short_term.front().sim_time
416          << endl;
417     cout << "medium term size = " << medium_term.size()
418          << "  time = " << sim_time - medium_term.front().sim_time
419          << endl;
420     cout << "long term size = " << long_term.size()
421          << "  time = " << sim_time - long_term.front().sim_time
422          << endl;
423 #endif
424    //stamp("point_finished");
425 }
426
427 FGReplayData*
428 FGReplay::record(double time)
429 {
430     FGReplayData* r = NULL;
431
432     if (recycler.size())
433     {
434         r = recycler.front();
435         recycler.pop_front();
436     }
437
438     r = m_pRecorder->capture(time, r);
439
440     return r;
441 }
442
443 /** 
444  * interpolate a specific time from a specific list
445  */
446 void
447 FGReplay::interpolate( double time, const replay_list_type &list)
448 {
449     // sanity checking
450     if ( list.size() == 0 )
451     {
452         // handle empty list
453         return;
454     } else if ( list.size() == 1 )
455     {
456         // handle list size == 1
457         replay(time, list[0]);
458         return;
459     }
460
461     unsigned int last = list.size() - 1;
462     unsigned int first = 0;
463     unsigned int mid = ( last + first ) / 2;
464
465     bool done = false;
466     while ( !done )
467     {
468         // cout << "  " << first << " <=> " << last << endl;
469         if ( last == first ) {
470             done = true;
471         } else if ( list[mid]->sim_time < time && list[mid+1]->sim_time < time ) {
472             // too low
473             first = mid;
474             mid = ( last + first ) / 2;
475         } else if ( list[mid]->sim_time > time && list[mid+1]->sim_time > time ) {
476             // too high
477             last = mid;
478             mid = ( last + first ) / 2;
479         } else {
480             done = true;
481         }
482     }
483
484     replay(time, list[mid+1], list[mid]);
485 }
486
487 /** 
488  *  Replay a saved frame based on time, interpolate from the two
489  *  nearest saved frames.
490  *  Returns true when replay sequence has finished, false otherwise.
491  */
492
493 bool
494 FGReplay::replay( double time ) {
495     // cout << "replay: " << time << " ";
496     // find the two frames to interpolate between
497     double t1, t2;
498
499     if ( short_term.size() > 0 ) {
500         t1 = short_term.back()->sim_time;
501         t2 = short_term.front()->sim_time;
502         if ( time > t1 ) {
503             // replay the most recent frame
504             replay( time, short_term.back() );
505             // replay is finished now
506             return true;
507             // cout << "first frame" << endl;
508         } else if ( time <= t1 && time >= t2 ) {
509             interpolate( time, short_term );
510             // cout << "from short term" << endl;
511         } else if ( medium_term.size() > 0 ) {
512             t1 = short_term.front()->sim_time;
513             t2 = medium_term.back()->sim_time;
514             if ( time <= t1 && time >= t2 )
515             {
516                 replay(time, medium_term.back(), short_term.front());
517                 // cout << "from short/medium term" << endl;
518             } else {
519                 t1 = medium_term.back()->sim_time;
520                 t2 = medium_term.front()->sim_time;
521                 if ( time <= t1 && time >= t2 ) {
522                     interpolate( time, medium_term );
523                     // cout << "from medium term" << endl;
524                 } else if ( long_term.size() > 0 ) {
525                     t1 = medium_term.front()->sim_time;
526                     t2 = long_term.back()->sim_time;
527                     if ( time <= t1 && time >= t2 )
528                     {
529                         replay(time, long_term.back(), medium_term.front());
530                         // cout << "from medium/long term" << endl;
531                     } else {
532                         t1 = long_term.back()->sim_time;
533                         t2 = long_term.front()->sim_time;
534                         if ( time <= t1 && time >= t2 ) {
535                             interpolate( time, long_term );
536                             // cout << "from long term" << endl;
537                         } else {
538                             // replay the oldest long term frame
539                             replay(time, long_term.front());
540                             // cout << "oldest long term frame" << endl;
541                         }
542                     }
543                 } else {
544                     // replay the oldest medium term frame
545                     replay(time, medium_term.front());
546                     // cout << "oldest medium term frame" << endl;
547                 }
548             }
549         } else {
550             // replay the oldest short term frame
551             replay(time, short_term.front());
552             // cout << "oldest short term frame" << endl;
553         }
554     } else {
555         // nothing to replay
556         return true;
557     }
558     return false;
559 }
560
561 /** 
562  * given two FGReplayData elements and a time, interpolate between them
563  */
564 void
565 FGReplay::replay(double time, FGReplayData* pCurrentFrame, FGReplayData* pOldFrame)
566 {
567     m_pRecorder->replay(time,pCurrentFrame,pOldFrame);
568 }
569
570 double
571 FGReplay::get_start_time()
572 {
573     if ( long_term.size() > 0 )
574     {
575         return long_term.front()->sim_time;
576     } else if ( medium_term.size() > 0 )
577     {
578         return medium_term.front()->sim_time;
579     } else if ( short_term.size() )
580     {
581         return short_term.front()->sim_time;
582     } else
583     {
584         return 0.0;
585     }
586 }
587
588 double
589 FGReplay::get_end_time()
590 {
591     if ( short_term.size() )
592     {
593         return short_term.back()->sim_time;
594     } else
595     {
596         return 0.0;
597     } 
598 }