221 lines
8.1 KiB
C++
221 lines
8.1 KiB
C++
/**
|
|
* @file dbc_decoder.cpp
|
|
* @brief
|
|
*
|
|
* Created: 2026-03-13
|
|
* Author: Deeaitch (Dim. Himro)
|
|
*
|
|
* Licensed under the MIT License.
|
|
* See LICENSE file in the project root for full license text.
|
|
*/
|
|
|
|
#include "dbc_decoder.h"
|
|
#include "signal_layout.h"
|
|
#include <cmath>
|
|
#include <utility>
|
|
|
|
std::shared_ptr<const DecodeFrame> DbcDecoder::FindFrame(const DecodeDatabase &database,
|
|
std::uint32_t canId,
|
|
bool isExtended) const {
|
|
return database.FindFrame(FrameKey(canId, isExtended));
|
|
}
|
|
|
|
DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
|
|
const RawCanFrame &frame) const {
|
|
DecodedFrameValue result;
|
|
result.key = FrameKey(frame.canId, frame.isExtended);
|
|
const std::shared_ptr<const DecodeFrame> definition = FindFrame(database, frame.canId, frame.isExtended);
|
|
|
|
if (definition == nullptr)
|
|
return result;
|
|
|
|
result.definition = definition;
|
|
result.valid = true;
|
|
result.status = frame.data.size() < definition->dlc
|
|
? FrameDecodeStatus::InsufficientPayload
|
|
: FrameDecodeStatus::Valid;
|
|
|
|
const SelectorState selector = ReadSelector(*definition, frame);
|
|
result.signals.reserve(definition->signals.size());
|
|
for (const DecodeSignal &signal: definition->signals) {
|
|
DecodedSignalValue decoded = DecodeOneSignal(signal, definition->dlc, frame, selector);
|
|
if (!decoded.valid && decoded.status != SignalDecodeStatus::Inactive &&
|
|
result.status == FrameDecodeStatus::Valid)
|
|
result.status = FrameDecodeStatus::SignalErrors;
|
|
decoded.definition = std::shared_ptr<const DecodeSignal>(definition, &signal);
|
|
decoded.key = SignalKey(result.key, signal.name);
|
|
result.signals.push_back(std::move(decoded));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool DbcDecoder::IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc) {
|
|
if (signal.length == 0U || signal.length > 64U ||
|
|
(signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola) ||
|
|
(signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed) ||
|
|
(signal.multiplexing.role != MultiplexRole::Unconditional &&
|
|
signal.multiplexing.role != MultiplexRole::Selector &&
|
|
signal.multiplexing.role != MultiplexRole::Branch))
|
|
return false;
|
|
|
|
return IsValidSignalLayout(signal.startBit, signal.length,
|
|
signal.byteOrder == ByteOrder::Intel, dlc);
|
|
}
|
|
|
|
DbcDecoder::SelectorState DbcDecoder::ReadSelector(const DecodeFrame &definition,
|
|
const RawCanFrame &frame) {
|
|
SelectorState selector;
|
|
std::size_t count = 0U;
|
|
for (const DecodeSignal &signal: definition.signals) {
|
|
if (signal.multiplexing.role == MultiplexRole::Selector) {
|
|
selector.definition = &signal;
|
|
++count;
|
|
}
|
|
}
|
|
if (count != 1U)
|
|
return selector;
|
|
|
|
const DecodeSignal &signal = *selector.definition;
|
|
selector.valid = signal.valueType == ValueType::Unsigned &&
|
|
IsValidDefinition(signal, definition.dlc);
|
|
selector.available = selector.valid && ExtractUnsigned(frame.data, signal, selector.value);
|
|
return selector;
|
|
}
|
|
|
|
DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal,
|
|
std::uint8_t dlc,
|
|
const RawCanFrame &frame,
|
|
const SelectorState &selector) {
|
|
DecodedSignalValue decoded;
|
|
std::uint64_t unsignedValue = 0U;
|
|
|
|
if (!IsValidDefinition(signal, dlc) ||
|
|
(signal.multiplexing.role != MultiplexRole::Unconditional && !selector.valid)) {
|
|
return decoded;
|
|
}
|
|
if (signal.multiplexing.role == MultiplexRole::Branch) {
|
|
if (selector.definition->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector.definition->length)) {
|
|
return decoded;
|
|
}
|
|
if (!selector.available) {
|
|
decoded.status = SignalDecodeStatus::SelectorUnavailable;
|
|
return decoded;
|
|
}
|
|
if (signal.multiplexing.value != selector.value) {
|
|
decoded.status = SignalDecodeStatus::Inactive;
|
|
return decoded;
|
|
}
|
|
}
|
|
if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
|
|
decoded.status = SignalDecodeStatus::InsufficientPayload;
|
|
return decoded;
|
|
}
|
|
|
|
if (signal.valueType == ValueType::Signed)
|
|
decoded.rawValue = SignExtend(unsignedValue, signal.length);
|
|
else
|
|
decoded.rawValue = unsignedValue;
|
|
|
|
double numericValue;
|
|
if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
|
|
numericValue = static_cast<double>(*signedValue);
|
|
else
|
|
numericValue = static_cast<double>(std::get<std::uint64_t>(decoded.rawValue));
|
|
decoded.physicalValue = numericValue * signal.factor + signal.offset;
|
|
if (!std::isfinite(decoded.physicalValue)) {
|
|
decoded.status = SignalDecodeStatus::PhysicalConversionFailed;
|
|
return decoded;
|
|
}
|
|
decoded.valid = true;
|
|
decoded.status = SignalDecodeStatus::Valid;
|
|
|
|
return decoded;
|
|
}
|
|
|
|
bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data,
|
|
const DecodeSignal &signal,
|
|
std::uint64_t &value) {
|
|
if ((signal.length == 0U) || (signal.length > 64U))
|
|
return false;
|
|
|
|
if (signal.byteOrder == ByteOrder::Intel)
|
|
return ExtractIntel(data, signal.startBit, signal.length, value);
|
|
|
|
return ExtractMotorola(data, signal.startBit, signal.length, value);
|
|
}
|
|
|
|
bool DbcDecoder::ExtractIntel(const std::vector<std::uint8_t> &data,
|
|
std::uint32_t startBit,
|
|
std::uint32_t length,
|
|
std::uint64_t &value) {
|
|
value = 0U;
|
|
|
|
for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
|
|
const std::uint64_t absoluteBit = std::uint64_t(startBit) + bitIndex;
|
|
const std::uint64_t byteIndex = absoluteBit / 8U;
|
|
const std::uint32_t bitInByte = absoluteBit % 8U;
|
|
|
|
if (byteIndex >= data.size())
|
|
return false;
|
|
|
|
const std::uint64_t bitValue =
|
|
(static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
|
|
|
|
value |= bitValue;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DbcDecoder::ExtractMotorola(const std::vector<std::uint8_t> &data,
|
|
std::uint32_t startBit,
|
|
std::uint32_t length,
|
|
std::uint64_t &value) {
|
|
/*
|
|
* DBC Motorola bit numbering:
|
|
* - startBit points to the most significant bit of the signal
|
|
* - inside a byte, bit numbering goes 7..0
|
|
* - crossing byte boundary moves to the next byte, bit 7
|
|
* Example: a signal starting at 7 follows 7..0, 15..8, 23..16.
|
|
*/
|
|
|
|
value = 0U;
|
|
|
|
std::size_t byteIndex = startBit / 8U;
|
|
std::uint32_t bitInByte = startBit % 8U;
|
|
|
|
for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
|
|
if (byteIndex >= data.size())
|
|
return false;
|
|
|
|
value <<= 1U;
|
|
value |= static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U);
|
|
|
|
if (bitInByte == 0U) {
|
|
++byteIndex;
|
|
bitInByte = 7U;
|
|
} else
|
|
--bitInByte;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
|
|
if ((bitLength == 0U) || (bitLength > 64U))
|
|
return 0;
|
|
|
|
const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
|
|
const std::uint64_t valueMask = (bitLength == 64U)
|
|
? ~std::uint64_t(0U)
|
|
: (std::uint64_t(1U) << bitLength) - 1U;
|
|
value &= valueMask;
|
|
|
|
if ((value & signMask) == 0U)
|
|
return static_cast<std::int64_t>(value);
|
|
|
|
// The complemented magnitude fits int64_t, including for INT64_MIN.
|
|
return -1 - static_cast<std::int64_t>((~value) & valueMask);
|
|
}
|