根据.dbc文件解析can报文信号数据

实现背景

因为维护一个比较恶心的CanIDS检测项目遇到的一个问题,写代码的人不知道去哪高就了,只能通过网络研究下.dbc大概的意思。这方面的东西大家可以自行在网上搜索,有一部分写的还算不错吧(我看了几天结合Vector Candb++工具算是基本看明白了)。如果一些基本知识不了解应该是看不懂这篇记录的

以往的项目.dbc文件比较简单而且都是intel格式的,没有被人发现问题,这个辣鸡项目却是Motorola格式。而正好被人发现获取的信号数据不对。所以就有了以下这些代码。

需要说明的是这个代码没有对一些参数做边界值和有效性判断,因为原项目代码就是这么烂,所以就假设输入数据是正确的。

基本知识

从Vector 搬运过来的介绍

In CAN databases signals can be defined with a byte order for either Motorola or Intel processors. The individual signals within a message may also have different byte orders.
Byte order for Motorola processors (Big Endian)
Byte order for Intel processors (Little Endian)
Bit significance (Bit Order)
Within a byte the significance of bits is the same in both formats:
msb: most significant bit; lsb: least significant bit

比较重要的就是 msb就是最高有效位, lsb就是最低有效位。
我处理的这个dbc文件里提供的核心数据是msb,和信号长度。

.dbc部分数据

BO_ 586 CDC_CHS62: 8 CDC
SG_ EPSFailState : 0|1@0+ (1,0) [0|1] “” ADC
SG_ EPSSteerMode : 5|2@0+ (1,0) [0|3] “” ADC
SG_ EPS_LKS_CtrlAvailable : 9|2@0+ (1,0) [0|3] “” ADC
SG_ EPS_LKS_CtrlActive : 10|1@0+ (1,0) [0|1] “” ADC
SG_ EPS_LKS_AbortReq : 11|1@0+ (1,0) [0|1] “” ADC
SG_ EPS_LKS_AbortSource : 14|3@0+ (1,0) [0|7] “” ADC
SG_ EPSHandsOffDetnSts : 15|1@0+ (1,0) [0|1] “” ADC
SG_ EPS_APA_CtrlAvailable : 25|2@0+ (1,0) [0|3] “” ADC
SG_ EPSHandsOffDetnStsValid : 26|1@0+ (1,0) [0|1] “” ADC
SG_ EPS_APA_AbortReq : 27|1@0+ (1,0) [0|1] “” ADC
SG_ EPS_APA_AbortSource : 30|3@0+ (1,0) [0|7] “” ADC
SG_ EPSLKATorqOvrlDlvd : 23|9@0+ (0.1,-15) [-15|15] “Nm” ADC
SG_ StrCtlReqFdk : 34|1@0+ (1,0) [0|1] “” ADC
SG_ DriverTakeOvStrSta : 35|1@0+ (1,0) [0|1] “” ADC
SG_ EPSLKATorqOvrlDlvdValid : 36|1@0+ (1,0) [0|1] “” ADC
SG_ EPSCtrlState : 39|3@0+ (1,0) [0|7] “” ADC
SG_ EPSDrvInputTrqValue : 46|11@0+ (0.01,-10) [-10|10] “Nm” ADC
SG_ CDC_CHS62_AliveCounter : 51|4@0+ (1,0) [0|15] “” ADC
SG_ CDC_CHS62_CheckSum : 63|8@0+ (1,0) [0|255] “” ADC

Candb++解析的信号布局

根据.dbc文件解析can报文信号数据_第1张图片

核心代码部分

通过msb和信号长度获取关键数据

获取lsb, lastByteOffset(最后一行和首字节的偏移), line(有效位所占行数);

int getLsb(int msb, int length, int* lsb, int* lastByteOffset, int* line)
{
    int startBit;
    if (length > 32)
        return 0;
    // 判断是否是多行
    if ((msb % 8 + 1) >= length)
    {
        startBit = msb - length + 1;
        *lsb = startBit;
        *lastByteOffset = msb / 8;
        *line = 1;
    }else {
        startBit = msb - msb % 8;
        int tempLength = (length - (msb % 8 + 1));
        // 减去第一行的长度,决定了endBit位置
        if (tempLength % 8 == 0)
        {
            *lsb = startBit + tempLength / 8 * 8;
            *line = tempLength / 8 + 1;
        }else {
            *lsb = startBit + 8 + tempLength / 8 * 8 + (8 - tempLength % 8);
            *line = tempLength / 8 + 2;
        }
        *lastByteOffset = *lsb / 8;
    }
    return 1;
}

通过上一步的关键数据获取信号

unsigned int getMask(int bitSize)
{
    unsigned int mask = 0;
    for (int i = 0; i < bitSize; i++)
    {
        mask = mask | (0x1 << i);
    }
    return mask;
}

unsigned int getSigData(unsigned char* input, int inputLength, int lsb, int lastByteOffset, int line, int msb, int length)
{
    unsigned int ret = 0;
    if (lastByteOffset + 1 > inputLength)
        return false;
    for (int i = 0; i < line; i++)
    {
        if (i == 0) {
            ret = *(input + lastByteOffset) >> (lsb % 8);
            // 存在无效位所以需要去除
            if (line == 1 && length < 8 - lsb % 8) {
                ret &= getMask(length);
            }
        }
        else {
            // 存在无效位所以需要去除
            if ((i == line - 1) && msb % 8 < 7) {
                ret += (* (input + lastByteOffset - i) & getMask(msb % 8 + 1)) << (8 - (lsb % 8) + (i - 1) * 8);
            }else {
                ret += *(input + lastByteOffset - i) << (8 - (lsb % 8) + (i - 1) * 8);
            }
        }
    }
    return ret;
}

实际测试代码

    unsigned char data[] = { 0x3E,0x60,0x56,0x78,0x90,0xf3,0x13,0x56 };

    while (true) {
        int msb, length;
        std::cout << "----------------------------" << std::endl;
        std::cin >> msb >> length;
        
        int lsb, lastByteOffset, line;
        int ret = getLsb(msb, length, &lsb, &lastByteOffset, &line);
        if (!ret) {
            std::cout << "error style\n";
            return 1;
        }
        std::cout << lsb << std::endl;
        std::cout << lastByteOffset << std::endl;
        std::cout << line << std::endl;
        std::cout << "*****************************"<< std::endl;

        std::cout << getSigData(data, sizeof(data), lsb, lastByteOffset, line, msb, length) << std::endl;

        std::cout << "----------------------------" << std::endl;
    }

注意

以上代码经过简单测试是没问题的,注意使用场景需要是Motorola格式的信号数据,并且.dbc文件需要提供的是msb。(个人猜测有可能有的.dbc文件会提供lsb+长度的数据格式)。

你可能感兴趣的:(c,信号处理)