How to use¶
Platform support¶
Framework:
Arduino
This library is made to be used with:
ESP32 family
Teensy 4.X family
Protobuf logger¶
The logging systems uses protobuf serialization to log data in a binary file. The binary file is composed timestamped LoggerFrames defined in the protobuf files of libDM_protobuf. The documentation of the protobuf messages can be found here:
The logger frame is currently defined in frame.proto file as:
1message LoggerFrame
2{
3 // Start byte of the data frame.
4 uint32 start_byte = 1;
5
6 // Timestamp of the logger frame in us.
7 uint64 timestamp = 2;
8
9 // Header of the logger frame (with variables names), written one time at the beginning of the log file. char encoded as bytes to avoid fixed size encoding
10 optional bytes binaryHeader = 3 [(nanopb).max_size = 4096];
11
12 // Header of the logger frame (with variables types), written one time at the beginning of the log file. char encoded as bytes to avoid fixed size encoding
13 optional bytes binaryTypes = 4 [(nanopb).max_size = 4096];
14
15 // Binary data of the logger frame.
16 optional bytes binaryData = 5 [(nanopb).max_size = 4096];
17
18 // Header of the protobuf frame (with variables names), written one time at the beginning of the log file. char encoded as bytes to avoid fixed size encoding
19 optional bytes protobufHeader = 6 [(nanopb).max_size = 256];
20
21 // Binary data of the logger protobuf logged messages (RX or TX)
22 optional bytes protobufData = 7 [(nanopb).max_size = 512];
23
24 // Header of the mavlink frame (with variables names), written one time at the beginning of the log file. char encoded as bytes to avoid fixed size encoding
25 optional bytes mavlinkHeader = 8 [(nanopb).max_size = 512];
26
27 // Header of the mavlink frame (with variables ID), written one time at the beginning of the log file. char encoded as bytes to avoid fixed size encoding
28 optional bytes mavlinkID = 9 [(nanopb).max_size = 256];
29
30 // Binary data of the logger mavlink logged messages (RX or TX)
31 optional bytes mavlinkData = 11 [(nanopb).max_size = 280];
32
33 // Binary data of the ulog messages, char encoded as bytes to avoid fixed size encoding
34 optional bytes ulogData = 12 [(nanopb).max_size = 512];
35
36 // End byte of the data frame.
37 uint32 end_byte = 13;
38}
Logging process¶
When the user want to log text data, mavlink_event or protobuf event, he will need to call streamLogger::printlnUlog()
or streamLogger::updateProtobufProtocolLogBuffer()
and provide the data to log. The data will be timestamped and added to a LoggerFrame. This loggerFrame will be added to a ring buffer with the call of streamLogger::writeLog()
and this ringBuffer will be flushed regularly to the binary file.
Extract Contents¶
Tools are available inside libDM_protobuf repo to extract the content of the binary file. If mavlink or protobuf messagery deifnitions are modified, the user will need to regenerate the code using generate_protobuf.sh or generate_mavlink.sh scripts and recompile tools from libDM_protobuf repo using generate_protobuf.sh or build_tools_windows.bat.
The output format provided allow for easy parsing of the data in Matlab or Python. Events or ulogs can be extracted from the json using :
1file = open("output.json", "r")
2data = json.load(file)
To extract the file, just use the script inside protobuf repo/tools:
1python decode_protobuf.py log.binDM
An output folder will be created and all the files extracted inside.
Binary file header¶
To allow decoding of the full file, a header is added at the beginning of the binary file. It contains the following information: - Variable list separated by a comma - Variable type separated by a comma
For example:
SUP_bImmobility,CAP_magTimestamp,CAP_baroTimestamp,OBS_localQuatW,OBS_localQuatX, … ,tuint8,uint32,uint32,float,float, … ,float
Binary file framing¶
The binary file is composed of n frames of the same size, with CR/LF at the end.
Each frame corresponds to one snapshot of all the added variable (with streamLogger::addVariable()
) for one timestamp. When calling streamLogger::updateLogBuffer()
, the internal ringBuffer is appended with the new frame.
To allow easy decoding and avoid reading corrupted line, one start byte and two end bytes are added to each binary message.
start byte : 126
end byte 1:
\r
end byte 1:
\n
How to log data¶
Include files¶
1#include "libDM_timer_tool.hpp"
2#include "libDM_stream_logger.hpp"
Initialize the logger¶
The logger is initialized using a timertool object:
Example
1timerTool myTimer;
2SDIO_CONF sdioConf(generalConf.sdioClock,
3 pinConf.pinSdioClk,
4 pinConf.pinSdioCmd,
5 pinConf.pinSdioDat0,
6 pinConf.pinSdioDat1,
7 pinConf.pinSdioDat2,
8 pinConf.pinSdioDat3,
9 generalConf.sdioMountpoint,
10 false);
11streamLogger streamObj(&myTimer, sdioConf);
Binary file¶
The logging is done using 3 simple steps
Add one time the variable at the begining of the application into log using
streamLogger::addVariable()
Example
1streamObj.addVariable(&OBS_localQuat[0], "OBS_localQuatW");
2streamObj.addVariable(&OBS_localQuat[1], "OBS_localQuatX");
3streamObj.addVariable(&OBS_localQuat[2], "OBS_localQuatY");
4streamObj.addVariable(&OBS_localQuat[3], "OBS_localQuatZ");
5
6streamObj.addVariable(&OBS_dstAltitude, "OBS_dstAltitude");
7streamObj.addVariable(&OBS_spdUp, "OBS_spdUp");
2. Update the log buffer with the frequency of your choice streamLogger::updateLogBuffer()
.
Each call to this function will add a new frame to the log buffer.
Tip
This function is generally called by the main (and fastest) loop of the application.
Example
1streamObj.updateLogBuffer(); // The fastest and most prioritary task updates buffers
Note
On multi-core CPU, a possibility is to use a less prioritary core to write the log to avoid flushing SD delays to impact the main loop.
(optional) Use a task on another thread/core to write/flush the log.
streamLogger::writeLog()
the argument false for writing log.
The method streamLogger::setWriteLogTask()
will allow to specofy a task handle and a size threshold, above which the task will be called to write the log.
Example
1void taskWriteLog(void* pvParameters) // NOLINT(misc-unused-parameters)
2{
3 streamObj.setWriteLogTask(TaskHandleWriteLog); // Write logs every ko of data
4 while (!mainTaskStarted)
5 delay(1);
6 streamObj.printlnUlog(true, true, "Write log task: Started");
7
8 while (true)
9 {
10 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
11 streamObj.writeLog();
12 }
13 vTaskDelete(NULL);
14}
Extracting the data¶
All the tools are now regrouped inside the libDM_protobuf repo. A call to decode_protobuf.py will extract the data from the binary file and create all json files, csv/mat files.
The binary file can be decoded using this compiled code:
An example file can be used to test the script: log1.binDM
The application is used as follows:
Mat file:
1./parse_log -input_file log1.binDM -output_format mat -output_file output.mat
Csv file:
1./parse_log -input_file log1.binDM -output_format csv -output_file output.csv
Note
The mat file creation is around 10x faster than the csv file creation.