LCOV - code coverage report
Current view: top level - core - dbc_decoder.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 94.9 % 156 148
Test Date: 2026-10-20 13:34:58 Functions: 100.0 % 12 12
Branches: 72.8 % 158 115

             Branch data     Line data    Source code
       1                 :             : /**
       2                 :             :  * @file dbc_decoder.cpp
       3                 :             :  * @brief CAN signal extraction and physical value conversion.
       4                 :             :  *
       5                 :             :  * Created: 2026-03-13
       6                 :             :  * Author: Deeaitch (Dim. Himro)
       7                 :             :  *
       8                 :             :  * Licensed under the MIT License.
       9                 :             :  * See LICENSE file in the project root for full license text.
      10                 :             :  */
      11                 :             : 
      12                 :             : #include "dbc_decoder.h"
      13                 :             : #include "j1939.h"
      14                 :             : #include "signal_layout.h"
      15                 :             : #include <cmath>
      16                 :             : #include <stdexcept>
      17                 :             : #include <unordered_set>
      18                 :             : #include <utility>
      19                 :             : 
      20                 :          32 : std::shared_ptr<const DecodeFrame> DbcDecoder::FindFrame(const DecodeDatabase &database,
      21                 :             :                                                          std::uint32_t canId,
      22                 :             :                                                          bool isExtended) const {
      23         [ +  - ]:          32 :     return ResolveFrame(database, canId, isExtended).definition;
      24                 :             : }
      25                 :             : 
      26                 :        6440 : PgnLookupResult DbcDecoder::ResolveFrame(const DecodeDatabase &database,
      27                 :             :                                          std::uint32_t canId, bool isExtended) {
      28         [ +  - ]:        6440 :     const std::shared_ptr<const DecodeFrame> exact = database.FindFrame(FrameKey(canId, isExtended));
      29         [ +  + ]:        6440 :     if (exact)
      30                 :        6335 :         return {exact, false};
      31   [ +  +  +  + ]:         105 :     if (!isExtended || canId > 0x1FFFFFFFU)
      32                 :          25 :         return {};
      33         [ +  - ]:          80 :     return database.FindFrameByPgn(ExtractJ1939Pgn(canId));
      34                 :        6440 : }
      35                 :             : 
      36                 :        6396 : DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
      37                 :             :                                      const RawCanFrame &frame) const {
      38                 :        6396 :     return DecodeImpl(database, frame, nullptr);
      39                 :             : }
      40                 :             : 
      41                 :          12 : DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
      42                 :             :                                      const RawCanFrame &frame,
      43                 :             :                                      const std::vector<SignalKey> &selection) const {
      44                 :          12 :     return DecodeImpl(database, frame, &selection);
      45                 :             : }
      46                 :             : 
      47                 :        6408 : DecodedFrameValue DbcDecoder::DecodeImpl(const DecodeDatabase &database,
      48                 :             :                                          const RawCanFrame &frame,
      49                 :             :                                          const std::vector<SignalKey> *selection) const {
      50                 :        6408 :     DecodedFrameValue result;
      51                 :        6408 :     result.key = FrameKey(frame.canId, frame.isExtended);
      52         [ +  - ]:        6408 :     const PgnLookupResult match = ResolveFrame(database, frame.canId, frame.isExtended);
      53         [ +  + ]:        6408 :     if (match.ambiguous) {
      54                 :           8 :         result.status = FrameDecodeStatus::AmbiguousFrame;
      55                 :           8 :         return result;
      56                 :             :     }
      57                 :        6400 :     const std::shared_ptr<const DecodeFrame> definition = match.definition;
      58         [ +  + ]:        6400 :     if (definition == nullptr)
      59                 :          55 :         return result;
      60                 :        6345 :     result.key = definition->GetKey();
      61                 :             : 
      62                 :        6345 :     std::vector<const DecodeSignal *> selected;
      63         [ +  + ]:        6345 :     if (selection != nullptr) {
      64         [ +  - ]:           8 :         selected.reserve(selection->size());
      65                 :           8 :         std::unordered_set<SignalKey, SignalKeyHasher> keys;
      66         [ +  + ]:          12 :         for (const SignalKey &key: *selection) {
      67         [ +  + ]:           8 :             if (!(key.frame == result.key))
      68   [ +  -  +  - ]:           4 :                 throw std::invalid_argument("Selected signal belongs to another frame: " + key.name);
      69   [ +  -  -  + ]:           4 :             if (!keys.insert(key).second)
      70   [ #  #  #  # ]:           0 :                 throw std::invalid_argument("Duplicate selected signal: " + key.name);
      71         [ +  - ]:           4 :             const std::shared_ptr<const DecodeSignal> signal = database.FindSignal(key);
      72         [ -  + ]:           4 :             if (!signal)
      73   [ #  #  #  # ]:           0 :                 throw std::invalid_argument("Unknown selected signal: " + key.name);
      74         [ +  - ]:           4 :             selected.push_back(signal.get());
      75                 :           4 :         }
      76                 :           8 :     }
      77                 :             : 
      78                 :        6341 :     result.definition = definition;
      79                 :        6341 :     result.valid = true;
      80                 :        6341 :     result.status = frame.data.size() < definition->dlc
      81         [ +  + ]:        6341 :                         ? FrameDecodeStatus::InsufficientPayload
      82                 :             :                         : FrameDecodeStatus::Valid;
      83                 :             : 
      84         [ +  - ]:        6341 :     const SelectorState selector = ReadSelector(*definition, frame);
      85         [ +  + ]:        6341 :     const std::size_t count = selection != nullptr ? selected.size() : definition->signals.size();
      86         [ +  - ]:        6341 :     result.signals.reserve(count);
      87         [ +  + ]:       12716 :     for (std::size_t index = 0U; index < count; ++index) {
      88         [ +  + ]:        6375 :         const DecodeSignal &signal = selection != nullptr ? *selected[index] : definition->signals[index];
      89         [ +  - ]:        6375 :         DecodedSignalValue decoded = DecodeOneSignal(signal, definition->dlc, frame, selector);
      90   [ +  +  +  + ]:        6375 :         if (!decoded.valid && decoded.status != SignalDecodeStatus::Inactive &&
      91         [ +  + ]:          31 :             result.status == FrameDecodeStatus::Valid)
      92                 :           5 :             result.status = FrameDecodeStatus::SignalErrors;
      93                 :        6375 :         decoded.definition = std::shared_ptr<const DecodeSignal>(definition, &signal);
      94         [ +  - ]:        6375 :         decoded.key = SignalKey(result.key, signal.name);
      95         [ +  - ]:        6375 :         result.signals.push_back(std::move(decoded));
      96                 :        6375 :     }
      97                 :             : 
      98                 :        6341 :     return result;
      99                 :        6420 : }
     100                 :             : 
     101                 :        6396 : bool DbcDecoder::IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc) {
     102   [ +  +  +  - ]:        6396 :     if ((signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola) ||
     103   [ +  +  +  - ]:        6396 :         (signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed) ||
     104         [ +  + ]:        6396 :         (signal.multiplexing.role != MultiplexRole::Unconditional &&
     105         [ +  + ]:          71 :          signal.multiplexing.role != MultiplexRole::Selector &&
     106         [ -  + ]:          29 :          signal.multiplexing.role != MultiplexRole::Branch))
     107                 :           0 :         return false;
     108                 :             : 
     109                 :        6396 :     return IsValidSignalLayout(signal.startBit, signal.length,
     110                 :        6396 :                               signal.byteOrder == ByteOrder::Intel, dlc);
     111                 :             : }
     112                 :             : 
     113                 :        6341 : DbcDecoder::SelectorState DbcDecoder::ReadSelector(const DecodeFrame &definition,
     114                 :             :                                                    const RawCanFrame &frame) {
     115                 :        6341 :     SelectorState selector;
     116                 :        6341 :     std::size_t count = 0U;
     117         [ +  + ]:       12716 :     for (const DecodeSignal &signal: definition.signals) {
     118         [ +  + ]:        6375 :         if (signal.multiplexing.role == MultiplexRole::Selector) {
     119                 :          21 :             selector.definition = &signal;
     120                 :          21 :             ++count;
     121                 :             :         }
     122                 :             :     }
     123         [ +  + ]:        6341 :     if (count != 1U)
     124                 :        6320 :         return selector;
     125                 :             : 
     126                 :          21 :     const DecodeSignal &signal = *selector.definition;
     127         [ +  - ]:          42 :     selector.valid = signal.valueType == ValueType::Unsigned &&
     128         [ +  - ]:          21 :                      IsValidDefinition(signal, definition.dlc);
     129   [ +  -  +  + ]:          21 :     selector.available = selector.valid && ExtractUnsigned(frame.data, signal, selector.value);
     130                 :          21 :     return selector;
     131                 :             : }
     132                 :             : 
     133                 :        6375 : DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal,
     134                 :             :                                                std::uint8_t dlc,
     135                 :             :                                                const RawCanFrame &frame,
     136                 :             :                                                const SelectorState &selector) {
     137                 :        6375 :     DecodedSignalValue decoded;
     138                 :        6375 :     std::uint64_t unsignedValue = 0U;
     139                 :             : 
     140   [ +  -  +  -  :       12750 :     if (!IsValidDefinition(signal, dlc) ||
                   -  + ]
     141   [ +  +  -  + ]:        6375 :         (signal.multiplexing.role != MultiplexRole::Unconditional && !selector.valid)) {
     142                 :           0 :         return decoded;
     143                 :             :     }
     144         [ +  + ]:        6375 :     if (signal.multiplexing.role == MultiplexRole::Branch) {
     145   [ +  +  -  + ]:          29 :         if (selector.definition->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector.definition->length)) {
     146                 :           0 :             return decoded;
     147                 :             :         }
     148         [ +  + ]:          29 :         if (!selector.available) {
     149                 :           3 :             decoded.status = SignalDecodeStatus::SelectorUnavailable;
     150                 :           3 :             return decoded;
     151                 :             :         }
     152         [ +  + ]:          26 :         if (signal.multiplexing.value != selector.value) {
     153                 :          13 :             decoded.status = SignalDecodeStatus::Inactive;
     154                 :          13 :             return decoded;
     155                 :             :         }
     156                 :             :     }
     157   [ +  -  +  + ]:        6359 :     if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
     158                 :          23 :         decoded.status = SignalDecodeStatus::InsufficientPayload;
     159                 :          23 :         return decoded;
     160                 :             :     }
     161                 :             : 
     162         [ +  + ]:        6336 :     if (signal.valueType == ValueType::Signed)
     163         [ +  - ]:          81 :         decoded.rawValue = SignExtend(unsignedValue, signal.length);
     164                 :             :     else
     165                 :        6255 :         decoded.rawValue = unsignedValue;
     166                 :             : 
     167                 :             :     double numericValue;
     168         [ +  + ]:        6336 :     if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
     169                 :          81 :         numericValue = static_cast<double>(*signedValue);
     170                 :             :     else
     171         [ +  - ]:        6255 :         numericValue = static_cast<double>(std::get<std::uint64_t>(decoded.rawValue));
     172                 :        6336 :     decoded.physicalValue = numericValue * signal.factor + signal.offset;
     173         [ +  + ]:        6336 :     if (!std::isfinite(decoded.physicalValue)) {
     174                 :           5 :         decoded.status = SignalDecodeStatus::PhysicalConversionFailed;
     175                 :           5 :         return decoded;
     176                 :             :     }
     177                 :        6331 :     decoded.valid = true;
     178                 :        6331 :     decoded.status = SignalDecodeStatus::Valid;
     179                 :             : 
     180                 :        6331 :     return decoded;
     181                 :           0 : }
     182                 :             : 
     183                 :        6380 : bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data,
     184                 :             :                                  const DecodeSignal &signal,
     185                 :             :                                  std::uint64_t &value) {
     186   [ +  -  -  + ]:        6380 :     if ((signal.length == 0U) || (signal.length > 64U))
     187                 :           0 :         return false;
     188                 :             : 
     189         [ +  + ]:        6380 :     if (signal.byteOrder == ByteOrder::Intel)
     190                 :        3242 :         return ExtractIntel(data, signal.startBit, signal.length, value);
     191                 :             : 
     192                 :        3138 :     return ExtractMotorola(data, signal.startBit, signal.length, value);
     193                 :             : }
     194                 :             : 
     195                 :        3242 : bool DbcDecoder::ExtractIntel(const std::vector<std::uint8_t> &data,
     196                 :             :                               std::uint32_t startBit,
     197                 :             :                               std::uint32_t length,
     198                 :             :                               std::uint64_t &value) {
     199                 :        3242 :     value = 0U;
     200                 :             : 
     201         [ +  + ]:      105558 :     for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
     202                 :      102334 :         const std::uint64_t absoluteBit = std::uint64_t(startBit) + bitIndex;
     203                 :      102334 :         const std::uint64_t byteIndex = absoluteBit / 8U;
     204                 :      102334 :         const std::uint32_t bitInByte = absoluteBit % 8U;
     205                 :             : 
     206         [ +  + ]:      102334 :         if (byteIndex >= data.size())
     207                 :          18 :             return false;
     208                 :             : 
     209                 :             :         const std::uint64_t bitValue =
     210                 :      102316 :                 (static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
     211                 :             : 
     212                 :      102316 :         value |= bitValue;
     213                 :             :     }
     214                 :             : 
     215                 :        3224 :     return true;
     216                 :             : }
     217                 :             : 
     218                 :        3138 : bool DbcDecoder::ExtractMotorola(const std::vector<std::uint8_t> &data,
     219                 :             :                                  std::uint32_t startBit,
     220                 :             :                                  std::uint32_t length,
     221                 :             :                                  std::uint64_t &value) {
     222                 :             :     /*
     223                 :             :    * DBC Motorola bit numbering:
     224                 :             :    * - startBit points to the most significant bit of the signal
     225                 :             :    * - inside a byte, bit numbering goes 7..0
     226                 :             :    * - crossing byte boundary moves to the next byte, bit 7
     227                 :             :    * Example: a signal starting at 7 follows 7..0, 15..8, 23..16.
     228                 :             :    */
     229                 :             : 
     230                 :        3138 :     value = 0U;
     231                 :             : 
     232                 :        3138 :     std::size_t byteIndex = startBit / 8U;
     233                 :        3138 :     std::uint32_t bitInByte = startBit % 8U;
     234                 :             : 
     235         [ +  + ]:      104732 :     for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
     236         [ +  + ]:      101601 :         if (byteIndex >= data.size())
     237                 :           7 :             return false;
     238                 :             : 
     239                 :      101594 :         value <<= 1U;
     240                 :      101594 :         value |= static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U);
     241                 :             : 
     242         [ +  + ]:      101594 :         if (bitInByte == 0U) {
     243                 :       12688 :             ++byteIndex;
     244                 :       12688 :             bitInByte = 7U;
     245                 :             :         } else
     246                 :       88906 :             --bitInByte;
     247                 :             :     }
     248                 :             : 
     249                 :        3131 :     return true;
     250                 :             : }
     251                 :             : 
     252                 :          81 : std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
     253   [ +  -  -  + ]:          81 :     if ((bitLength == 0U) || (bitLength > 64U))
     254                 :           0 :         return 0;
     255                 :             : 
     256                 :          81 :     const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
     257                 :          81 :     const std::uint64_t valueMask = (bitLength == 64U)
     258         [ +  + ]:          81 :                                             ? ~std::uint64_t(0U)
     259                 :          68 :                                             : (std::uint64_t(1U) << bitLength) - 1U;
     260                 :          81 :     value &= valueMask;
     261                 :             : 
     262         [ +  + ]:          81 :     if ((value & signMask) == 0U)
     263                 :          41 :         return static_cast<std::int64_t>(value);
     264                 :             : 
     265                 :             :     // The complemented magnitude fits int64_t, including for INT64_MIN.
     266                 :          40 :     return -1 - static_cast<std::int64_t>((~value) & valueMask);
     267                 :             : }
        

Generated by: LCOV version 2.0-1