1 // replay.cxx - a system to record and replay FlightGear flights
3 // Written by Curtis Olson, started July 2003.
4 // Updated by Thorsten Brehm, September 2011.
6 // Copyright (C) 2003 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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.
30 #include <simgear/constants.h>
31 #include <simgear/structure/exception.hxx>
33 #include <Main/fg_props.hxx>
36 #include "flightrecorder.hxx"
42 FGReplay::FGReplay() :
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"))
66 * Clear all internal buffers.
71 while ( !short_term.empty() )
73 m_pRecorder->deleteRecord(short_term.front());
74 short_term.pop_front();
76 while ( !medium_term.empty() )
78 m_pRecorder->deleteRecord(medium_term.front());
79 medium_term.pop_front();
81 while ( !long_term.empty() )
83 m_pRecorder->deleteRecord(long_term.front());
84 long_term.pop_front();
86 while ( !recycler.empty() )
88 m_pRecorder->deleteRecord(recycler.front());
94 * Initialize the data structures
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);
110 * Reset replay queues.
122 m_pRecorder->reinit();
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)
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++)
137 FGReplayData* r = m_pRecorder->createEmptyRecord();
139 recycler.push_back(r);
142 SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Out of memory!");
145 replay_master->setIntValue(0);
146 disable_replay->setBoolValue(0);
147 replay_time->setDoubleValue(0);
148 replay_time_str->setStringValue("");
152 * Bind to the property tree
162 * Unbind from the property tree
172 printTimeStr(char* pStrBuffer,double _Time, bool ShowDecimal=true)
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;
184 len = sprintf(pStrBuffer,"%u:%02u:%02u",h,m,s);
186 len = sprintf(pStrBuffer,"%u:%02u",m,s);
195 sprintf(&pStrBuffer[len],".%u",d);
198 /** Start replay session
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);
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);
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())
224 replay_master->setIntValue(1);
225 replay_time->setDoubleValue(-1);
226 replay_time_str->setStringValue("");
232 * Update the saved data
236 FGReplay::update( double dt )
238 int current_replay_state = last_replay_state;
242 if ( disable_replay->getBoolValue() )
244 if (fgGetBool("/sim/freeze/master",false)||
245 fgGetBool("/sim/freeze/clock",false))
247 fgSetBool("/sim/freeze/master",false);
248 fgSetBool("/sim/freeze/clock",false);
249 last_replay_state = 1;
252 if ((replay_master->getIntValue() != 3)||
253 (last_replay_state == 3))
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))
264 fgSetBool("/sim/sound/enabled",true);
265 fgSetBool("/sim/replay/mute",false);
267 fgSetString("/sim/messages/copilot", "Replay stopped. Your controls!");
271 int replay_state = replay_master->getIntValue();
272 if ((replay_state == 0)&&
273 (last_replay_state > 0))
275 if (current_replay_state == 3)
277 // take control at current replay position ("My controls!").
278 // May need to uncrash the aircraft here :)
279 fgSetBool("/sim/crashed", false);
283 // normal replay exit, restore most recent frame
287 // replay is finished
288 last_replay_state = replay_state;
292 // remember recent state
293 last_replay_state = replay_state;
298 // replay inactive, keep recording
300 case 1: // normal replay
301 case 3: // prepare to resume normal flight at current replay position
304 double current_time = replay_time->getDoubleValue();
305 bool ResetTime = (current_time<=0.0);
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;
317 current_time = startTime;
320 bool IsFinished = replay( replay_time->getDoubleValue() );
322 current_time = (replay_looped->getBoolValue()) ? -1 : get_end_time()+0.01;
324 current_time += dt * speed_up->getDoubleValue();
325 replay_time->setDoubleValue(current_time);
327 printTimeStr(StrBuffer,current_time);
328 replay_time_str->setStringValue((const char*)StrBuffer);
330 // when time skipped (looped replay), trigger listeners to reset views etc
332 replay_master->setIntValue(replay_state);
334 return; // don't record the replay session
336 case 2: // normal replay operation
337 return; // don't record the replay session
339 throw sg_range_exception("unknown FGReplay state");
344 //cerr << "Recording replay" << endl;
345 sim_time += dt * speed_up->getDoubleValue();
347 // sanity check, don't collect data if FDM data isn't good
348 if (!fgGetBool("/sim/fdm-initialized", false)) {
352 FGReplayData* r = record(sim_time);
355 SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Out of memory!");
359 // update the short term list
361 short_term.push_back( r );
363 FGReplayData *st_front = short_term.front();
367 SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Inconsistent data!");
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();
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();
384 FGReplayData *mt_front = medium_term.front();
385 if ( sim_time - mt_front->sim_time > m_medium_res_time ) {
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();
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();
399 FGReplayData *lt_front = long_term.front();
400 if ( sim_time - lt_front->sim_time > m_low_res_time ) {
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();
414 cout << "short term size = " << short_term.size()
415 << " time = " << sim_time - short_term.front().sim_time
417 cout << "medium term size = " << medium_term.size()
418 << " time = " << sim_time - medium_term.front().sim_time
420 cout << "long term size = " << long_term.size()
421 << " time = " << sim_time - long_term.front().sim_time
424 //stamp("point_finished");
428 FGReplay::record(double time)
430 FGReplayData* r = NULL;
434 r = recycler.front();
435 recycler.pop_front();
438 r = m_pRecorder->capture(time, r);
444 * interpolate a specific time from a specific list
447 FGReplay::interpolate( double time, const replay_list_type &list)
450 if ( list.size() == 0 )
454 } else if ( list.size() == 1 )
456 // handle list size == 1
457 replay(time, list[0]);
461 unsigned int last = list.size() - 1;
462 unsigned int first = 0;
463 unsigned int mid = ( last + first ) / 2;
468 // cout << " " << first << " <=> " << last << endl;
469 if ( last == first ) {
471 } else if ( list[mid]->sim_time < time && list[mid+1]->sim_time < time ) {
474 mid = ( last + first ) / 2;
475 } else if ( list[mid]->sim_time > time && list[mid+1]->sim_time > time ) {
478 mid = ( last + first ) / 2;
484 replay(time, list[mid+1], list[mid]);
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.
494 FGReplay::replay( double time ) {
495 // cout << "replay: " << time << " ";
496 // find the two frames to interpolate between
499 if ( short_term.size() > 0 ) {
500 t1 = short_term.back()->sim_time;
501 t2 = short_term.front()->sim_time;
503 // replay the most recent frame
504 replay( time, short_term.back() );
505 // replay is finished now
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 )
516 replay(time, medium_term.back(), short_term.front());
517 // cout << "from short/medium term" << endl;
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 )
529 replay(time, long_term.back(), medium_term.front());
530 // cout << "from medium/long term" << endl;
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;
538 // replay the oldest long term frame
539 replay(time, long_term.front());
540 // cout << "oldest long term frame" << endl;
544 // replay the oldest medium term frame
545 replay(time, medium_term.front());
546 // cout << "oldest medium term frame" << endl;
550 // replay the oldest short term frame
551 replay(time, short_term.front());
552 // cout << "oldest short term frame" << endl;
562 * given two FGReplayData elements and a time, interpolate between them
565 FGReplay::replay(double time, FGReplayData* pCurrentFrame, FGReplayData* pOldFrame)
567 m_pRecorder->replay(time,pCurrentFrame,pOldFrame);
571 FGReplay::get_start_time()
573 if ( long_term.size() > 0 )
575 return long_term.front()->sim_time;
576 } else if ( medium_term.size() > 0 )
578 return medium_term.front()->sim_time;
579 } else if ( short_term.size() )
581 return short_term.front()->sim_time;
589 FGReplay::get_end_time()
591 if ( short_term.size() )
593 return short_term.back()->sim_time;