DBC Framework
DBC parsing and CAN signal decoding framework
Loading...
Searching...
No Matches
dbc_decoder_test.cpp
1#include "dbc_decoder.h"
2
3#include <QtTest>
4#include <limits>
5#include <sstream>
6
7struct DecodeCase {
8 const char *name;
9 ByteOrder byteOrder;
10 std::uint32_t startBit;
11 std::uint32_t length;
12 ValueType valueType;
13 std::vector<std::uint8_t> data;
14 bool valid;
15 std::int64_t rawValue;
16 double factor;
17 double offset;
18 double physicalValue;
19};
20
21Q_DECLARE_METATYPE(DecodeCase)
22
23class DbcDecoderTest : public QObject {
24 Q_OBJECT
25private Q_SLOTS:
26 void decode_data();
27 void decode();
28 void boundaries();
29};
30
31void DbcDecoderTest::decode_data() {
32 QTest::addColumn<DecodeCase>("test");
33 const DecodeCase cases[] = {
34 {"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
35 {"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned, {0xAC}, true, 11, 1.0, 0.0, 11.0},
36 {"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned, {0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0},
37 {"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned, {0x01, 0x80}, true, 3, 1.0, 0.0, 3.0},
38 {"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned, {0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
39 {"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed, {0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0},
40 {"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
41 {"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL, 1.0, 0.0, 4611686018427387904.0},
42 {"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0},
43 {"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned, {}, false, 0, 1.0, 0.0, 0.0},
44 {"Motorola large start", ByteOrder::Motorola, std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
45 {"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
46 {"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned,
47 std::vector<std::uint8_t>(9U, 0xFF), false, 0, 1.0, 0.0, 0.0},
48 {"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0},
49 {"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
50 {"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}};
51
52 for (const DecodeCase &test: cases)
53 QTest::newRow(test.name) << test;
54}
55
56void DbcDecoderTest::decode() {
57 QFETCH(DecodeCase, test);
58 DecodeSignal signal;
59 signal.name = test.name;
60 signal.byteOrder = test.byteOrder;
61 signal.startBit = test.startBit;
62 signal.length = test.length;
63 signal.valueType = test.valueType;
64 signal.factor = test.factor;
65 signal.offset = test.offset;
66
67 DecodeFrame definition;
68 definition.canId = 0x123U;
69 definition.dlc = static_cast<std::uint8_t>(test.data.size());
70 definition.signals.push_back(signal);
71 DecodeDatabase database;
72 database.frames.push_back(definition);
73 database.frameIndexByKey.emplace(FrameKey(definition.canId, false), 0U);
74
75 RawCanFrame frame;
76 frame.canId = definition.canId;
77 frame.data = test.data;
78 const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
79 QVERIFY2(result.valid, test.name);
80 QCOMPARE(result.definition, &database.frames[0]);
81 QCOMPARE(result.signals.size(), std::size_t(1U));
82 const DecodedSignalValue &decoded = result.signals[0];
83 QCOMPARE(decoded.definition, &database.frames[0].signals[0]);
84 QCOMPARE(decoded.valid, test.valid);
85 if (!test.valid)
86 return;
87
88 if (test.valueType == ValueType::Signed) {
89 const std::int64_t *raw = std::get_if<std::int64_t>(&decoded.rawValue);
90 QVERIFY(raw != nullptr);
91 QCOMPARE(*raw, test.rawValue);
92 } else {
93 const std::uint64_t *raw = std::get_if<std::uint64_t>(&decoded.rawValue);
94 QVERIFY(raw != nullptr);
95 QCOMPARE(*raw, static_cast<std::uint64_t>(test.rawValue));
96 }
97 QCOMPARE(decoded.physicalValue, test.physicalValue);
98}
99
100void DbcDecoderTest::boundaries() {
101 for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
102 for (ValueType type: {ValueType::Unsigned, ValueType::Signed}) {
103 for (std::uint64_t bits: {std::uint64_t(0U), std::uint64_t(0x7FFFFFFFFFFFFFFFULL),
104 std::uint64_t(0x8000000000000000ULL),
105 std::uint64_t(0x8000000000000001ULL),
106 std::uint64_t(0xFFFFFFFFFFFFFFFFULL)}) {
107 DecodeDatabase database;
108 DecodeFrame definition;
109 DecodeSignal signal;
110 signal.startBit = order == ByteOrder::Intel ? 0U : 7U;
111 signal.length = 64U;
112 signal.byteOrder = order;
113 signal.valueType = type;
114 signal.factor = 0.5;
115 signal.offset = 10.0;
116 definition.signals.push_back(signal);
117 database.frames.push_back(definition);
118 database.frameIndexByKey.emplace(FrameKey(0U, false), 0U);
119 RawCanFrame frame;
120 for (unsigned byte = 0U; byte < 8U; ++byte) {
121 const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U;
122 frame.data.push_back(static_cast<std::uint8_t>(bits >> shift));
123 }
124 const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
125 const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL
126 ? static_cast<std::int64_t>(bits)
127 : std::numeric_limits<std::int64_t>::min() +
128 static_cast<std::int64_t>(bits - 0x8000000000000000ULL);
129 const DecodedSignalValue &decoded = result.signals.at(0U);
130 const RawSignalValue expected = type == ValueType::Unsigned
131 ? std::variant<std::int64_t, std::uint64_t>(bits)
132 : std::variant<std::int64_t, std::uint64_t>(signedExpected);
133 const double physical = (type == ValueType::Unsigned
134 ? static_cast<double>(bits)
135 : static_cast<double>(signedExpected)) *
136 0.5 +
137 10.0;
138 std::ostringstream actualText;
139 std::ostringstream expectedText;
140 if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
141 actualText << *signedValue;
142 else
143 actualText << std::get<std::uint64_t>(decoded.rawValue);
144 if (type == ValueType::Unsigned)
145 expectedText << bits;
146 else
147 expectedText << signedExpected;
148 QVERIFY(decoded.valid);
149 QVERIFY(decoded.rawValue == expected);
150 QCOMPARE(decoded.physicalValue, physical);
151 QCOMPARE(actualText.str(), expectedText.str());
152 }
153 }
154 }
155}
156
157int RunDecoderTests(int argc, char **argv) {
158 DbcDecoderTest tests;
159 return QTest::qExec(&tests, argc, argv);
160}
161
162#include "dbc_decoder_test.moc"
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:72
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
ByteOrder
Signal byte order used for runtime decoding.
ValueType
Signal numeric type.
Runtime decode database with fast lookup by CAN ID.
Runtime-ready frame definition.
std::uint8_t dlc
std::uint32_t canId
Runtime-ready signal definition.
std::string name
ValueType valueType
std::uint32_t startBit
ByteOrder byteOrder
std::uint32_t length
Fully decoded frame.
Definition dbc_decoder.h:61
const DecodeFrame * definition
Definition dbc_decoder.h:62
One decoded signal value.
Definition dbc_decoder.h:43
RawSignalValue rawValue
Definition dbc_decoder.h:45
const DecodeSignal * definition
Definition dbc_decoder.h:44
Key for fast frame lookup.
Raw CAN frame used for runtime or trace decoding.
Definition dbc_decoder.h:24
std::vector< std::uint8_t > data
Definition dbc_decoder.h:27
std::uint32_t canId
Definition dbc_decoder.h:25