18#include <simgear/debug/logstream.hxx>
19#include <simgear/math/SGMath.hxx>
20#include <simgear/misc/ResourceManager.hxx>
21#include <simgear/misc/strutils.hxx>
22#include <simgear/props/props_io.hxx>
23#include <simgear/structure/exception.hxx>
32static void s_Append(std::vector<char>& out,
const void* data,
size_t data_len)
34 size_t pos = out.size();
35 out.resize(pos + data_len);
36 memcpy(&out[pos], data, data_len);
42 s_Append(out, &number,
sizeof(number));
63 std::vector<char>& out,
66 const std::vector<const char*>* path_exclude_prefixes)
70 assert(a->getNameString() == b->getNameString());
71 assert(a->getPath() == b->getPath());
72 assert(a->getIndex() == b->getIndex());
74 if (path_exclude_prefixes) {
75 for (
const char* path_exclude_prefix : *path_exclude_prefixes) {
76 if (simgear::strutils::starts_with(a->getPath(
true ), path_exclude_prefix)) {
77 SG_LOG(SG_SYSTEMS, SG_BULK,
"Ignoring: " << a->getPath(
true ));
83 const std::string a_value = a->getStringValue();
84 const std::string b_value = b->getStringValue();
85 if (a_value != b_value) {
87 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"recording property change:" << a->getPath() <<
": " << b->getStringValue() <<
" => " << a->getStringValue());
90 b->setStringValue(a_value);
94 int bn = b->nChildren();
95 for (
int i = 0;
i < bn; ++
i) {
96 SGPropertyNode* bc = b->getChild(
i);
97 SGPropertyNode* ac = a->getChild(bc->getNameString(), bc->getIndex(),
false );
109 int an = a->nChildren();
110 for (
int i = 0;
i < an; ++
i) {
111 SGPropertyNode* ac = a->getChild(
i);
112 SGPropertyNode* bc = b->getChild(ac->getNameString(), ac->getIndex(),
true );
127 : m_record_extra_properties(
fgGetNode(
"/sim/replay/record-extra-properties", true )),
128 m_record_main_window(
fgGetNode(
"/sim/replay/record-main-window", true )),
129 m_record_main_view(
fgGetNode(
"/sim/replay/record-main-view", true )),
130 m_record_extra_properties_paths(
fgGetNode(
"/sim/replay/record-extra-properties-paths", true ))
139 if (m_record_extra_properties->getBoolValue()) {
143 auto paths = m_record_extra_properties_paths->getChildren(
"path");
144 for (SGPropertyNode* n : paths) {
148 std::string path = n->getStringValue();
149 SGPropertyNode* a =
globals->get_props()->getNode(path,
false );
151 SG_LOG(SG_SYSTEMS, SG_ALERT,
"property does not exist: " << path);
154 SGPropertyNode* b = RecordExtraPropertiesReference->getNode(path,
true );
159 if (m_record_main_window->getBoolValue()) {
162 static std::vector<const char*> s_paths = {
166 "sim/startup/ysize"};
167 for (
const char* path : s_paths) {
168 SGPropertyNode* a =
globals->get_props()->getNode(path,
true );
169 SGPropertyNode* b = RecordExtraPropertiesReference->getNode(path,
true );
174 if (m_record_main_view->getBoolValue()) {
177 static std::vector<const char*> s_excludes = {
178 "/sim/current-view/debug/",
179 "/sim/current-view/raw-orientation",
180 "/sim/current-view/viewer-"};
181 const char* path =
"sim/current-view";
182 SGPropertyNode* a =
globals->get_props()->getNode(path,
true );
183 SGPropertyNode* b = RecordExtraPropertiesReference->getNode(path,
true );
189 SGPropertyNode_ptr m_record_extra_properties;
190 SGPropertyNode_ptr m_record_main_window;
191 SGPropertyNode_ptr m_record_main_view;
192 SGPropertyNode_ptr m_record_extra_properties_paths;
199 m_ReplayMultiplayer(
fgGetNode(
"/sim/replay/multiplayer", true)),
200 m_ReplayExtraProperties(
fgGetNode(
"/sim/replay/replay-extra-properties", true)),
201 m_ReplayMainView(
fgGetNode(
"/sim/replay/replay-main-view", true)),
202 m_ReplayMainWindowPosition(
fgGetNode(
"/sim/replay/replay-main-window-position", true)),
203 m_ReplayMainWindowSize(
fgGetNode(
"/sim/replay/replay-main-window-size", true)),
204 m_RecordContinuous(
fgGetNode(
"/sim/replay/record-continuous", true)),
205 m_RecordExtraProperties(
fgGetNode(
"/sim/replay/record-extra-properties", true)),
206 m_LogRawSpeed(
fgGetNode(
"/sim/replay/log-raw-speed", true)),
207 m_TotalRecordSize(0),
208 m_ConfigName(pConfigName),
209 m_usingDefaultConfig(false),
222 m_usingDefaultConfig =
false;
224 SGPropertyNode_ptr ConfigNode;
225 int Selected = m_RecorderNode->getIntValue(m_ConfigName, 0);
226 SG_LOG(SG_SYSTEMS, SG_INFO,
"FlightRecorder: Recorder configuration #" << std::to_string(Selected));
228 ConfigNode = m_RecorderNode->getChild(
"config", Selected);
230 if (!ConfigNode.valid())
231 ConfigNode = getDefault();
238 m_TotalRecordSize = 0;
240 m_CaptureDouble.clear();
241 m_CaptureFloat.clear();
242 m_CaptureInteger.clear();
243 m_CaptureInt16.clear();
244 m_CaptureInt8.clear();
245 m_CaptureBool.clear();
247 m_ConfigNode = ConfigNode;
249 if (!m_ConfigNode.valid()) {
250 SG_LOG(SG_SYSTEMS, SG_ALERT,
"FlightRecorder: Configuration is invalid. Flight recorder disabled.");
253 const std::string pRecorderName =
254 m_ConfigNode->getStringValue(
"name",
255 "aircraft-specific flight recorder");
256 SG_LOG(SG_SYSTEMS, SG_INFO,
"FlightRecorder: Using custom recorder configuration: " << pRecorderName);
257 m_RecorderNode->setStringValue(
"active-config-name", pRecorderName);
260 initSignalList(
"double", m_CaptureDouble, m_ConfigNode);
261 initSignalList(
"float", m_CaptureFloat, m_ConfigNode);
262 initSignalList(
"int", m_CaptureInteger, m_ConfigNode);
263 initSignalList(
"int16", m_CaptureInt16, m_ConfigNode);
264 initSignalList(
"int8", m_CaptureInt8, m_ConfigNode);
265 initSignalList(
"bool", m_CaptureBool, m_ConfigNode);
269 m_TotalRecordSize =
sizeof(double) * 1 +
270 sizeof(
double) * m_CaptureDouble.size() +
271 sizeof(float) * m_CaptureFloat.size() +
272 sizeof(int) * m_CaptureInteger.size() +
273 sizeof(
short int) * m_CaptureInt16.size() +
274 sizeof(
signed char) * m_CaptureInt8.size() +
275 sizeof(
unsigned char) * ((m_CaptureBool.size() + 7) / 8);
278 m_RecorderNode->setIntValue(
"record-size", m_TotalRecordSize);
279 SG_LOG(SG_SYSTEMS, SG_INFO,
"FlightRecorder: record size is " << m_TotalRecordSize <<
" bytes");
287 unsigned int Count = SignalList.size();
288 for (
unsigned int i = 0;
i < Count;
i++) {
289 if (SignalList[
i].Signal.get() == pProperty) {
297bool FGFlightRecorder::haveProperty(
const SGPropertyNode* pProperty)
299 if (haveProperty(m_CaptureDouble, pProperty))
301 if (haveProperty(m_CaptureFloat, pProperty))
303 if (haveProperty(m_CaptureInteger, pProperty))
305 if (haveProperty(m_CaptureInt16, pProperty))
307 if (haveProperty(m_CaptureInt8, pProperty))
309 if (haveProperty(m_CaptureBool, pProperty))
317FGFlightRecorder::getDefault()
319 SGPropertyNode_ptr ConfigNode;
322 SG_LOG(SG_SYSTEMS, SG_INFO,
"FlightRecorder: No custom configuration. Loading generic default recorder.");
324 const std::string Path = m_RecorderNode->getStringValue(
"default-config",
"");
326 SG_LOG(SG_SYSTEMS, SG_ALERT,
"FlightRecorder: No default flight recorder specified! Check defaults.xml!");
330 SG_LOG(SG_SYSTEMS, SG_ALERT,
"FlightRecorder: Cannot find file '" << Path <<
"'.");
333 readProperties(path, m_RecorderNode->getChild(
"config", 0,
true), 0);
334 ConfigNode = m_RecorderNode->getChild(
"config", 0,
false);
335 }
catch (sg_io_exception& e) {
336 SG_LOG(SG_SYSTEMS, SG_ALERT,
"FlightRecorder: Error reading file '" << Path <<
": " << e.getFormattedMessage());
341 m_usingDefaultConfig =
true;
349void FGFlightRecorder::initSignalList(
const char* pSignalType,
TSignalList& SignalList, SGPropertyNode_ptr BaseNode)
354 processSignalList(pSignalType, SignalList, BaseNode);
356 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"FlightRecorder: " << SignalList.size() <<
" signals of type " << pSignalType);
363void FGFlightRecorder::processSignalList(
const char* pSignalType,
TSignalList& SignalList, SGPropertyNode_ptr SignalListNode,
364 string PropPrefix,
int Count)
367 SGPropertyNode_ptr SignalNode;
370 Count = SignalListNode->getIntValue(
"count", Count);
371 PropPrefix = simgear::strutils::strip(SignalListNode->getStringValue(
"prefix", PropPrefix.c_str()));
372 if ((!PropPrefix.empty()) && (PropPrefix[PropPrefix.size() - 1] !=
'/'))
376 SignalNode = SignalListNode->getChild(
"signal", Index,
false);
377 if (SignalNode.valid() &&
378 (std::string(pSignalType) == SignalNode->getStringValue(
"type",
"float"))) {
379 string PropertyPath = SignalNode->getStringValue(
"property",
"");
380 if (!PropertyPath.empty()) {
381 PropertyPath = PropPrefix + PropertyPath;
382 const std::string pInterpolation = SignalNode->getStringValue(
"interpolation",
"linear");
385 string::size_type IndexPos = PropertyPath.find(
"%i");
386 int SignalCount = Count;
387 if (IndexPos == string::npos)
390 for (
int IndexValue = 0; IndexValue < SignalCount; IndexValue++) {
391 string PPath = PropertyPath;
392 if (IndexPos != string::npos) {
394 snprintf(strbuf, 20,
"%d", IndexValue);
395 PPath = PPath.replace(IndexPos, 2, strbuf);
399 if (!Capture.
Signal.valid()) {
403 if (!m_usingDefaultConfig) {
406 SG_LOG(SG_SYSTEMS, SG_INFO,
"FlightRecorder: Recording non-existent property '" << PPath <<
"'.");
412 if (pInterpolation ==
"discrete")
414 else if ((pInterpolation ==
"angular") ||
415 (pInterpolation ==
"angular-rad"))
417 else if (pInterpolation ==
"angular-deg")
419 else if (pInterpolation ==
"linear")
422 SG_LOG(SG_SYSTEMS, SG_ALERT,
"FlightRecorder: Unsupported interpolation type '" << pInterpolation <<
"' of signal '" << PPath <<
"'");
425 if (haveProperty(Capture.
Signal)) {
426 SG_LOG(SG_SYSTEMS, SG_ALERT,
"FlightRecorder: Property '" << PPath <<
"' specified multiple times. Check flight recorder configuration.");
428 SignalList.push_back(Capture);
433 }
while (SignalNode.valid());
436 simgear::PropertyList Nodes = SignalListNode->getChildren(
"signals");
437 for (
unsigned int i = 0;
i < Nodes.size();
i++) {
438 processSignalList(pSignalType, SignalList, Nodes[
i], PropPrefix, Count);
461 static std::vector<char> s_recent_raw_data;
463 int in_replay =
fgGetInt(
"/sim/replay/replay-state");
467 if (in_replay && !s_recent_raw_data.empty()) {
471 ReplayData->
raw_data = s_recent_raw_data;
476 ReplayData->
raw_data.resize(m_TotalRecordSize);
477 char* pBuffer = &ReplayData->
raw_data.front();
482 double* pDoubles = (
double*)&pBuffer[Offset];
483 unsigned int SignalCount = m_CaptureDouble.size();
484 for (
unsigned int i = 0;
i < SignalCount;
i++) {
485 pDoubles[
i] = m_CaptureDouble[
i].Signal->getDoubleValue();
487 Offset += SignalCount *
sizeof(double);
493 float* pFloats = (
float*)&pBuffer[Offset];
494 unsigned int SignalCount = m_CaptureFloat.size();
495 for (
unsigned int i = 0;
i < SignalCount;
i++) {
496 pFloats[
i] = m_CaptureFloat[
i].Signal->getFloatValue();
498 Offset += SignalCount *
sizeof(float);
503 int* pInt = (
int*)&pBuffer[Offset];
504 unsigned int SignalCount = m_CaptureInteger.size();
505 for (
unsigned int i = 0;
i < SignalCount;
i++) {
506 pInt[
i] = m_CaptureInteger[
i].Signal->getIntValue();
508 Offset += SignalCount *
sizeof(int);
514 short int* pShortInt = (
short int*)&pBuffer[Offset];
515 unsigned int SignalCount = m_CaptureInt16.size();
516 for (
unsigned int i = 0;
i < SignalCount;
i++) {
517 pShortInt[
i] = (
short int)m_CaptureInt16[
i].Signal->getIntValue();
519 Offset += SignalCount *
sizeof(
short int);
525 signed char* pChar = (
signed char*)&pBuffer[Offset];
526 unsigned int SignalCount = m_CaptureInt8.size();
527 for (
unsigned int i = 0;
i < SignalCount;
i++) {
528 pChar[
i] = (
signed char)m_CaptureInt8[
i].Signal->getIntValue();
530 Offset += SignalCount *
sizeof(
signed char);
535 unsigned char* pFlags = (
unsigned char*)&pBuffer[Offset];
536 unsigned int SignalCount = m_CaptureBool.size();
537 int Size = (SignalCount + 7) / 8;
539 memset(pFlags, 0, Size);
540 for (
unsigned int i = 0;
i < SignalCount;
i++) {
541 if (m_CaptureBool[
i].Signal->getBoolValue())
542 pFlags[
i >> 3] |= 1 << (
i & 7);
546 assert(Offset +
sizeof(
double) == m_TotalRecordSize);
551 s_recent_raw_data = ReplayData->
raw_data;
560 bool replayMultiplayer = m_ReplayMultiplayer->getBoolValue();
562 auto multiplayerMessage = m_MultiplayMgr->popMessageHistory();
563 if (!multiplayerMessage) {
566 if (replayMultiplayer) {
572 auto message_header =
reinterpret_cast<const T_MsgHdr*
>(&multiplayerMessage->front());
573 xdr_data_t message_id = message_header->MsgId;
575 SG_LOG(SG_GENERAL, SG_ALERT,
"Not recording chat message: " << std::string(
reinterpret_cast<const T_ChatMsg*
>(message_header + 1)->Text, message_header->MsgLen -
sizeof(*message_header)));
584 if (m_RecordContinuous->getBoolValue()) {
585 if (!m_RecordExtraPropertiesReference) {
586 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"m_RecordPropertiesReference is null");
587 m_RecordExtraPropertiesReference =
new SGPropertyNode;
610 switch (interpolation) {
612 return v1 + ratio * (v2 - v1);
616 double tmp = v2 - v1;
621 return v1 + tmp * ratio;
626 double tmp = v2 - v1;
629 else if (tmp < -SGD_PI)
631 return v1 + tmp * ratio;
643 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Clearing m_RecordExtraPropertiesReference");
644 m_RecordExtraPropertiesReference =
nullptr;
649static void setInt(
const std::string& value,
int* out)
653 *out = std::stoi(value);
654 }
catch (std::exception& e) {
655 SG_LOG(SG_SYSTEMS, SG_ALERT,
"Ignoring failed conversion of '" << value <<
"' to int: " << e.what());
666 out = strtod(s.c_str(), &end);
667 if (errno || !end || *end != 0) {
678 const std::string& path,
679 const std::string& value_next_string,
683 SGPropertyNode*
p =
globals->get_props()->getNode(path,
true );
687 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"p && _pLastBuffer");
691 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"property in frame_prev and frame_next:"
693 const std::string& value_prev_string = p_prev_it->second;
700 if (value_prev_ok && value_next_ok) {
702 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"property is fp");
704 if (simgear::strutils::ends_with(path,
"-deg")) {
706 }
else if (simgear::strutils::ends_with(path,
"-rad")) {
709 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"calling weighting() ratio=" << ratio);
715 SG_LOG(SG_GENERAL, SG_DEBUG,
"Interpolating " << path <<
": [" << value_prev <<
" .. " << value_next <<
"] => " << value_interpolated);
716 globals->get_props()->setDoubleValue(path, value_interpolated);
721 if (!done)
p->setStringValue(path, value_next_string);
728 int* main_window_xpos,
729 int* main_window_ypos,
730 int* main_window_xsize,
731 int* main_window_ysize)
733 const char* pLastBuffer = (_pLastBuffer && !_pLastBuffer->
raw_data.empty()) ? &_pLastBuffer->
raw_data.front() :
nullptr;
734 const char* pBuffer = (_pNextBuffer && !_pNextBuffer->
raw_data.empty()) ? &_pNextBuffer->
raw_data.front() :
nullptr;
740 double NextSimTime = _pNextBuffer->
sim_time;
741 double LastSimTime = _pLastBuffer->
sim_time;
742 double Numerator = SimTime - LastSimTime;
743 double dt = NextSimTime - LastSimTime;
745 if ((Numerator > 0.0) && (dt != 0.0)) {
746 ratio = Numerator / dt;
755 const double* pDoubles = (
const double*)&pBuffer[Offset];
756 const double* pLastDoubles = (
const double*)&pLastBuffer[Offset];
757 unsigned int SignalCount = m_CaptureDouble.size();
759 for (
unsigned int i = 0;
i < SignalCount;
i++) {
760 double v = pDoubles[
i];
762 v =
weighting(m_CaptureDouble[
i].Interpolation, ratio,
765 m_CaptureDouble[
i].Signal->setDoubleValue(v);
768 if (m_LogRawSpeed->getBoolValue() && _pNextBuffer && pLastBuffer) {
773 SGGeod pos_geod = SGGeod::fromDegFt(
777 SGVec3d pos = SGVec3d::fromGeod(pos_geod);
778 static SGVec3d pos_prev;
779 static double t_prev = -1;
781 double dt = t - t_prev;
782 if (t_prev != -1 && dt > 0) {
783 double distance = length(pos - pos_prev);
784 double speed = dt ? distance / dt : -1;
785 SG_LOG(SG_GENERAL, SG_DEBUG,
""
787 <<
" pLastBuffer=" << ((
void*)pLastBuffer) <<
" t_prev=" << std::setprecision(10) << t_prev <<
" t=" << std::setprecision(10) << t <<
" dt=" << dt <<
" distance=" << distance <<
" speed=" << speed);
788 SGPropertyNode* n =
fgGetNode(
"/sim/replay/log-raw-speed-values",
true );
789 n->addChild(
"value")->setDoubleValue(speed);
795 Offset += SignalCount *
sizeof(double);
801 const float* pFloats = (
const float*)&pBuffer[Offset];
802 const float* pLastFloats = (
const float*)&pLastBuffer[Offset];
803 unsigned int SignalCount = m_CaptureFloat.size();
804 for (
unsigned int i = 0;
i < SignalCount;
i++) {
805 float v = pFloats[
i];
807 v =
weighting(m_CaptureFloat[
i].Interpolation, ratio,
810 m_CaptureFloat[
i].Signal->setDoubleValue(v);
812 Offset += SignalCount *
sizeof(float);
817 const int* pInt = (
const int*)&pBuffer[Offset];
818 unsigned int SignalCount = m_CaptureInteger.size();
819 for (
unsigned int i = 0;
i < SignalCount;
i++) {
820 m_CaptureInteger[
i].Signal->setIntValue(pInt[
i]);
822 Offset += SignalCount *
sizeof(int);
828 const short int* pShortInt = (
const short int*)&pBuffer[Offset];
829 unsigned int SignalCount = m_CaptureInt16.size();
830 for (
unsigned int i = 0;
i < SignalCount;
i++) {
831 m_CaptureInt16[
i].Signal->setIntValue(pShortInt[
i]);
833 Offset += SignalCount *
sizeof(
short int);
839 const signed char* pChar = (
const signed char*)&pBuffer[Offset];
840 unsigned int SignalCount = m_CaptureInt8.size();
841 for (
unsigned int i = 0;
i < SignalCount;
i++) {
842 m_CaptureInt8[
i].Signal->setIntValue(pChar[
i]);
844 Offset += SignalCount *
sizeof(
signed char);
849 const unsigned char* pFlags = (
const unsigned char*)&pBuffer[Offset];
850 unsigned int SignalCount = m_CaptureBool.size();
851 int Size = (SignalCount + 7) / 8;
853 for (
unsigned int i = 0;
i < SignalCount;
i++) {
854 m_CaptureBool[
i].Signal->setBoolValue(0 != (pFlags[
i >> 3] & (1 << (
i & 7))));
861 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Pushing multiplayer message to multiplay manager");
862 m_MultiplayMgr->pushMessageHistory(multiplayer_message);
867 bool replay_extra_properties = m_ReplayExtraProperties->getBoolValue();
868 bool replay_main_view = m_ReplayMainView->getBoolValue();
869 bool replay_main_window_position = m_ReplayMainWindowPosition->getBoolValue();
870 bool replay_main_window_size = m_ReplayMainWindowSize->getBoolValue();
872 if (replay_extra_properties) {
874 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"replaying extra property removal: " << extra_property_removed_path);
875 globals->get_props()->removeChild(extra_property_removed_path);
888 if (replay_main_view) {
890 const std::string& path = prop_change.first;
891 const std::string& value = prop_change.second;
892 if (simgear::strutils::starts_with(path,
"/sim/current-view/view-number")) {
893 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"SimTime=" << SimTime <<
" replaying view " << path <<
"=" << value);
894 globals->get_props()->setStringValue(path, value);
900 const std::string& path = prop_change.first;
901 const std::string& value = prop_change.second;
904 }
else if (path ==
"/sim/startup/xpos") {
905 if (replay_main_window_position)
setInt(value, main_window_xpos);
906 }
else if (path ==
"/sim/startup/ypos") {
907 if (replay_main_window_position)
setInt(value, main_window_ypos);
908 }
else if (path ==
"/sim/startup/xsize") {
909 if (replay_main_window_size)
setInt(value, main_window_xsize);
910 }
else if (path ==
"/sim/startup/ysize") {
911 if (replay_main_window_size)
setInt(value, main_window_ysize);
912 }
else if (simgear::strutils::starts_with(path,
"/sim/current-view/")) {
913 if (replay_main_view) {
914 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"SimTime=" << SimTime <<
" replaying view change: " << path <<
"=" << value);
918 }
else if (replay_extra_properties) {
919 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"SimTime=" << SimTime <<
" replaying extra_property change: " << path <<
"=" << value);
920 SGPropertyNode*
p =
globals->get_props()->getNode(path,
true );
922 if (
p && _pLastBuffer) {
923 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"p && _pLastBuffer");
929 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"property in _pLastBuffer and _pNextBuffer:"
931 const std::string& valus_prev = p_prev_it->second;
932 size_t value_prev_len;
933 size_t value_next_len;
934 double value_prev = std::stod(valus_prev, &value_prev_len);
935 double value_next = std::stod(value, &value_next_len);
936 if (value_prev_len == valus_prev.size() && value_next_len == value.size()) {
939 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"property is fp");
941 if (simgear::strutils::ends_with(path,
"-deg")) {
943 }
else if (simgear::strutils::ends_with(path,
"-rad")) {
946 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"calling weighting()");
952 SG_LOG(SG_GENERAL, SG_DEBUG,
"Interpolating " << path <<
": [" << value_prev <<
" .. " << value_next <<
"] => " << value_interpolated);
953 globals->get_props()->setDoubleValue(path, value_interpolated);
958 if (!done)
p->setStringValue(path, value);
965 static const char* InterpolationTypes[] = {
"discrete",
"linear",
"angular-rad",
"angular-deg"};
966 size_t SignalCount = SignalList.size();
967 SGPropertyNode* Signals = root->getNode(
"signals",
true);
968 for (
size_t i = 0;
i < SignalCount;
i++) {
969 SGPropertyNode* SignalProp = Signals->addChild(
"signal");
970 SignalProp->setStringValue(
"type", typeStr);
971 SignalProp->setStringValue(
"interpolation", InterpolationTypes[SignalList[
i].Interpolation]);
972 SignalProp->setStringValue(
"property", SignalList[
i].Signal->getPath());
974 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"FlightRecorder: Have " << SignalCount <<
" signals of type " << typeStr);
975 root->setIntValue(typeStr, SignalCount);
981 root->setStringValue(
"name", m_RecorderNode->getStringValue(
"active-config-name",
""));
983 SignalCount +=
getConfig(root,
"double", m_CaptureDouble);
984 SignalCount +=
getConfig(root,
"float", m_CaptureFloat);
985 SignalCount +=
getConfig(root,
"int", m_CaptureInteger);
986 SignalCount +=
getConfig(root,
"int16", m_CaptureInt16);
987 SignalCount +=
getConfig(root,
"int8", m_CaptureInt8);
988 SignalCount +=
getConfig(root,
"bool", m_CaptureBool);
991 root->setIntValue(
"recorder/signal-count", SignalCount);
FGFlightRecorder(const char *pConfigName)
void getConfig(SGPropertyNode *root)
virtual ~FGFlightRecorder()
FGReplayData * capture(double SimTime, FGReplayData *pRecycledBuffer)
Capture data.
void replay(double SimTime, const FGReplayData *pNextBuffer, const FGReplayData *pLastBuffer, int *main_window_xpos, int *main_window_ypos, int *main_window_xsize, int *main_window_ysize)
Replay.
void resetExtraProperties()
SGPath resolve_aircraft_path(const std::string &branch) const
Given a path to an aircraft-related resource file, resolve it against the appropriate root.
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
static void s_Append(std::vector< char > &out, const void *data, size_t data_len)
static void s_RecordPropertyDiffs(std::vector< char > &out, SGPropertyNode *a, SGPropertyNode *b, const std::vector< const char * > *path_exclude_prefixes)
static double weighting(TInterpolation interpolation, double ratio, double v1, double v2)
Do interpolation as defined by given interpolation type and weighting ratio.
static void string_to_double(const std::string &s, double &out, bool &ok)
static void s_AppendNumber(std::vector< char > &out, int16_t number)
static void setInt(const std::string &value, int *out)
static void s_AppendString(std::vector< char > &out, const std::string &s)
static void replayProperty(const std::string &path, const std::string &value_next_string, const FGReplayData *frame_prev, double ratio)
static std::shared_ptr< RecordExtraProperties > s_record_extra_properties
std::vector< TCapture > TSignalList
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
std::vector< std::shared_ptr< std::vector< char > > > multiplayer_messages
std::vector< char > extra_properties
std::map< std::string, std::string > replay_extra_property_changes
std::vector< std::string > replay_extra_property_removals
std::vector< char > raw_data
TInterpolation Interpolation
SGPropertyNode_ptr Signal