diff --git a/README.md b/README.md index 026085fa..1b039db9 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ The `examples/` directory contains example applications demonstrating how to use - `external_cmake_project` - Download a copy of the FusionEngine Client library from the public repository and import it into a CMake project using `FetchContent`. - `generate_data` - Generate a binary file containing a fixed set of messages. +- `lband_decode` - Example of decoding RTCM corrections from a recorded file containing LBandFrameMessage. +- `request_version` - Simulate sending a request for a version info message, and waiting for a response. - `tcp_client` - Connect to a device over TCP and display the received FusionEngine messages. - `udp_client` - Connect to a device over UDP and display the received FusionEngine messages. diff --git a/examples/BUILD b/examples/BUILD index 9b535792..ce0020e2 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -6,6 +6,7 @@ filegroup( srcs = [ "//generate_data", "//message_decode", + "//request_version", "//tcp_client", "//udp_client", ] diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index cf83972d..84ffc17b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,8 +1,9 @@ add_subdirectory(common) add_subdirectory(generate_data) -add_subdirectory(message_decode) add_subdirectory(lband_decode) +add_subdirectory(message_decode) +add_subdirectory(request_version) add_subdirectory(tcp_client) add_subdirectory(udp_client) diff --git a/examples/common/print_message.cc b/examples/common/print_message.cc index 33a4b7c7..e3d72a73 100644 --- a/examples/common/print_message.cc +++ b/examples/common/print_message.cc @@ -107,6 +107,17 @@ void PrintMessage(const MessageHeader& header, const void* payload_in) { sv.azimuth_deg); printf(" In solution: %s\n", sv.usage > 0 ? "yes" : "no"); } + } else if (header.message_type == MessageType::VERSION_INFO) { + auto& contents = *reinterpret_cast(payload); + payload += sizeof(contents); + + double system_time = contents.system_time_ns * 1e-9; + printf( + "Version info message @ System time %.3f seconds. [sequence=%u, " + "size=%zu B]\n", + system_time, header.sequence_number, message_size); + printf(" Firmware version: %.*s\n", contents.fw_version_length, + reinterpret_cast(payload)); } else { printf("Received message type %s. [sequence=%u, %zu bytes]\n", to_string(header.message_type), header.sequence_number, @@ -114,6 +125,17 @@ void PrintMessage(const MessageHeader& header, const void* payload_in) { } } +/******************************************************************************/ +void PrintHex(const void* data, size_t data_len_bytes) { + const uint8_t* data_ptr = static_cast(data); + for (size_t i = 0; i < data_len_bytes; ++i) { + printf("%02x", data_ptr[i]); + if (i < data_len_bytes - 1) { + printf(" "); + } + } +} + } // namespace examples } // namespace fusion_engine } // namespace point_one diff --git a/examples/common/print_message.h b/examples/common/print_message.h index 8b889921..5f8c1986 100644 --- a/examples/common/print_message.h +++ b/examples/common/print_message.h @@ -20,6 +20,8 @@ namespace examples { void PrintMessage(const fusion_engine::messages::MessageHeader& header, const void* payload); +void PrintHex(const void* data, size_t data_len_bytes); + } // namespace examples } // namespace fusion_engine } // namespace point_one diff --git a/examples/request_version/BUILD b/examples/request_version/BUILD new file mode 100644 index 00000000..f98bcbd1 --- /dev/null +++ b/examples/request_version/BUILD @@ -0,0 +1,12 @@ +package(default_visibility = ["//visibility:public"]) + +cc_binary( + name = "request_version", + srcs = [ + "request_version.cc", + ], + deps = [ + "//common:print_message", + "@fusion_engine_client", + ], +) diff --git a/examples/request_version/CMakeLists.txt b/examples/request_version/CMakeLists.txt new file mode 100644 index 00000000..06f12ba4 --- /dev/null +++ b/examples/request_version/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(request_version request_version.cc) +target_link_libraries(request_version PUBLIC fusion_engine_client) +target_link_libraries(request_version PUBLIC print_message) diff --git a/examples/request_version/request_version.cc b/examples/request_version/request_version.cc new file mode 100644 index 00000000..eca05435 --- /dev/null +++ b/examples/request_version/request_version.cc @@ -0,0 +1,138 @@ +/**************************************************************************/ /** +* @brief Simulate sending a request for a version info message, and waiting for +* a response. +* @file +******************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include "../common/print_message.h" + +using namespace point_one::fusion_engine::examples; +using namespace point_one::fusion_engine::messages; +using namespace point_one::fusion_engine::parsers; + +bool message_found = false; + +// Enforce a 4-byte aligned address. +alignas(4) uint8_t storage[4096]; + +// Fake Send/Receive functions. +/******************************************************************************/ +void SendData(void* data, size_t data_len_bytes) { + (void)data; + (void)data_len_bytes; +} + +/******************************************************************************/ +size_t ReceiveData(uint8_t* buffer, size_t read_size) { + static size_t offset = 0; + if (offset + read_size < sizeof(storage)) { + // We're using this data as if it were received from the device. + memcpy(buffer, storage + offset, read_size); + offset += read_size; + return read_size; + } else { + return 0; + } +} + +/******************************************************************************/ +int main(int argc, const char* argv[]) { + if (argc != 1) { + printf("Usage: %s\n", argv[0]); + printf(R"EOF( +Simulate sending a version request, and parsing the response. +)EOF"); + return 0; + } + + ////////////////////////////////////////////////////////////////////////////// + // Write a VersionInfoMessage request. + ////////////////////////////////////////////////////////////////////////////// + + uint8_t* buffer = storage; + auto header = reinterpret_cast(buffer); + buffer += sizeof(MessageHeader); + *header = MessageHeader(); + + header->sequence_number = 0; + header->message_type = MessageType::MESSAGE_REQUEST; + header->payload_size_bytes = sizeof(MessageRequest); + + auto req_message = reinterpret_cast(buffer); + *req_message = MessageRequest(); + + req_message->message_type = VersionInfoMessage::MESSAGE_TYPE; + + header->crc = CalculateCRC(storage); + + printf("Sending VersionInfoMessage request:\n "); + PrintHex(storage, sizeof(MessageHeader) + sizeof(MessageRequest)); + // This data would be sent over serial to the device. + SendData(storage, sizeof(MessageHeader) + sizeof(MessageRequest)); + + printf("\n"); + + ////////////////////////////////////////////////////////////////////////////// + // Generate an example response of the data a device would send back. + ////////////////////////////////////////////////////////////////////////////// + + static constexpr char VERSION_STR[] = {'t', 'e', 's', 't'}; + + // @ref ReceiveData will read data from `storage`. + buffer = storage; + header = reinterpret_cast(buffer); + buffer += sizeof(MessageHeader); + *header = MessageHeader(); + + header->sequence_number = 0; + header->message_type = MessageType::VERSION_INFO; + header->payload_size_bytes = sizeof(VersionInfoMessage) + sizeof(VERSION_STR); + + auto version_message = reinterpret_cast(buffer); + *version_message = VersionInfoMessage(); + version_message->fw_version_length = sizeof(VERSION_STR); + buffer += sizeof(VersionInfoMessage); + + char* version_str_ptr = reinterpret_cast(buffer); + // NOTE: Not NULL terminated. + memcpy(version_str_ptr, VERSION_STR, sizeof(VERSION_STR)); + + header->crc = CalculateCRC(storage); + + ////////////////////////////////////////////////////////////////////////////// + // Receive example response. + ////////////////////////////////////////////////////////////////////////////// + + printf("Waiting for response\n"); + // In a real application, you'd need to do the bookkeeping to trigger a + // timeout if no response is received after a couple seconds. + bool has_timed_out = false; + uint8_t read_buffer[10]; + + FusionEngineFramer framer(1024); + framer.SetMessageCallback( + [](const MessageHeader& header, const void* payload) { + // Ignore messages besides the expected response type. + if (header.message_type == VersionInfoMessage::MESSAGE_TYPE) { + PrintMessage(header, payload); + message_found = true; + } + }); + + while (!has_timed_out && !message_found) { + size_t data_read = ReceiveData(read_buffer, sizeof(read_buffer)); + framer.OnData(read_buffer, data_read); + } + + printf("Response received.\n"); + + return 0; +}