Files
dbc/core/dbc_decoder.cpp
T
2026-10-07 18:50:11 -04:00

201 lines
6.8 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"
const DecodeFrame *DbcDecoder::FindFrame(const DecodeDatabase &database,
std::uint32_t canId,
bool isExtended) const {
const FrameKey key(canId, isExtended);
const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it =
database.frameIndexByKey.find(key);
if (it == database.frameIndexByKey.end())
return nullptr;
const std::size_t index = it->second;
if (index >= database.frames.size())
return nullptr;
return &database.frames[index];
}
DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
const RawCanFrame &frame) const {
DecodedFrameValue result;
const DecodeFrame *definition = FindFrame(database, frame.canId, frame.isExtended);
if (definition == nullptr)
return result;
result.definition = definition;
result.valid = true;
const SelectorState selector = ReadSelector(*definition, frame);
for (const DecodeSignal &signal : definition->signals)
result.signals.push_back(DecodeOneSignal(signal, frame, selector));
return result;
}
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 &&
signal.length > 0U && signal.length <= 64U;
selector.available = selector.valid && ExtractUnsigned(frame.data, signal, selector.value);
return selector;
}
DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal,
const RawCanFrame &frame,
const SelectorState &selector) {
DecodedSignalValue decoded;
std::uint64_t unsignedValue = 0U;
decoded.definition = &signal;
if (signal.length == 0U || signal.length > 64U ||
(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;
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::uint32_t absoluteBit = startBit + bitIndex;
const std::uint32_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);
}