(int32/uint32)整数压缩与解压缩算法---VB编码

本文介绍了一种网络包中无符号整数的压缩与解压缩算法,通过对整数进行逻辑分段,实现了不同范围内的整数压缩,从而减少了网络传输的数据量,提高了传输效率。文章详细解释了压缩与解压缩的过程,并提供了Python实现代码。

对网络包进行压缩可以减少网络带宽,提高传输效率, 很多时候一些报文中包含一些无符号整数,比如报文头中的长度信息.正常情况下一个无符号整数占用32位

0x0000 0000 到 0xFFFF FFFF .

由于一个无符号整数始终占据着4字节内存,就算是1也会占据着4个字节,其实只要1字节就可以保存了.(要想压缩整数,基本算法思想是压缩在4字节之内也就是1,2,3,4.

把无符号整数进行逻辑分段:

0x00 ~ 0xFF 0~255 1Byte

0x100 ~ 0xFFFF 256~65535 2Byte

0x10000 ~ 0xFFFFFF 65536~16777215 3Byte

0x1000000 ~ 0xFFFFFFFF 16777216~4294967295 4Byt 

通过上面可以大致看出1~4字节的逻辑分层.把无符号整数进行压缩在一个字符数组内,定义一个char buf[4];

当在0x00~0xFF之间时:

因为是只有1个字节只要保存在buf[0] 这个字符内。

当在0x100~0xFFFF之间时:

这个范围是2个字节,把前8位保存于数组buf[0]内,后8位保存于数组buf[1]内,通过位操作即可。

当在0x100~0xFFFFFF之间时也类似。

当在0x1000000 ~ 0xFFFFFFFF这个范围不进行压缩。因为必须占用4个字节,所以没什么意义。

下面看下压缩代码:

 

// 输入无符号整数data,压缩到1-4个字节后存入buf,返回压缩后的字节数
bool cb_compress(unsigned int data, char* buf, unsigned int& len) { 
    if (data <= 0xFF) { 
        buf[0] = (char)data; 
        len = 1; 
    } else if (data <= 0xFFFF) { 
        buf[0] = (char)((data & 0xFF00 )>> 8); 
        buf[1] = (char)((data & 0x00FF)); 
        len = 2; 
    } else if (data <= 0xFFFFFF) { 
       buf[0] = (char)((data & 0x00FF0000) >> 16); 
       buf[1] = (char)((data & 0x0000FF00) >> 8); 
       buf[2] = (char)(data & 0x000000FF); 
       len = 3; 
    } else {
       // 超过 0xFFFFFF 的不进行压缩
       cout << "Invaild Data" << endl; 
    } 
    return TRUE; 
}

对压缩过的数据进行解压缩,只要和压缩操作相反就可以了。不过要特别注意符号位的问题!

// 输入字节数组和字节数,输出一个整数data 
bool cb_unCompress(char* buf, int len, unsigned int& data) { 
    if (3 == len) { 
        data = (unsigned int)(((buf[0] << 16) & 0x00FFFFFF) 
                                | ((buf[1] << 8) & 0x00FFFF) 
                                | (buf[2] & 0x0000FF) & 0x00FFFFFF); 
    } else if (2 == len) { 
        data = (unsigned int)((((buf[0] << 8) & 0x00FFFF)) 
                                | (buf[1] & 0x0000FF) & 0x00FFFF); // 0x00FF 
    } else if (1 == len){ 
        data = (unsigned int)buf[0] & 0x000000FF; 
    } else { 
        cout << "unCompress Error" << endl; 
    }
    return TRUE; 
}

压缩图示:

如果你使用的Python,你可以从struct.pack这个方法进行入手。

比如:压缩2个字节:

buf = (c_char * 2)() 
buf[0] = struct .pack("B", (data & 0xFF00) >> 8); 
buf[1] = struct.pack("B", data & 0x00FF);

解压缩进行upack即可:

_data0 = stuct.unpack("B" , data[0])[0] 
_data1 = struct.unpack("B", data[1])[0] 
unpack_data = _data0 << 8 | _data1 & 0x00FF

还有种算法是通过每7位一保存,前面1位为符号位(非有符号的符号位,而是如果为0表示后面没有数据,1表示有数据)

bool cb_compress(unsigned int data, char *buf, unsigned int &len) { 
    len = 0; 
    // 32bit分成5段,每段7bit
    for (int i = 4; i >= 0; --i) { 
        //得到第i段的数值
        char c = (data >> (i*7)) & 0x7f; 
        if (c == 0x00 && !len) {
            //最左的几段值为0,不保存
            continue; 
        } 
        
        //最右段
        if (i == 0) { 
            c &= 0x7f; 
        } else {//并非最右段,首bit标记为1表示后面有数据 
            c |= 0x80; 
        } 
        buf[len] = c; 
        len++; 
    } 
    
    //Invaild Data 
    if (!len) { 
        len++; 
        buf[0] = 0; 
    } 
    return TRUE; 
}
// 评:上述压缩过程如果知道最终len是多少,那么只需要把最左非0段的首位bit置1即可,
// 在字符流的解码过程中,只要遇到了首位bit为1的段就知道是一个新的数据开始了。

 

如果检查为1 表示后面7位需要和下面一段数据进行拼接,0表示后面7位为最后7位。 所以可能压缩后位1~5字节内。

解压缩:

bool cb_unCompress(char *buf, int len, unsigned int &i) { 
    i = 0; 
    for (int index = 0; index < (int)len; ++index) { 
        char c = *(buf + index); //取出压缩数据 
        i = i << 7; 
        c &= 0x7f; 
        i |= c; //多于1位进行左移解压缩 
    } 
    return TRUE; 
}

评:当把一个整数的bit串分成5段后,最左段是不够8bit的,那么对于int型的整数,可以把它当做uint型整数来压缩和解压缩。

Python实现压缩与解压缩:( 本段程序,参考http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html)

def Compress(self, data): 
    if data <= 0x7F: 
        bytes_1Arr = (c_char * 1)() 
        bytes_1Arr = struct.pack("B", data)
        return bytes_1Arr 
    elif data <= 0x3FFF: 
        bytes_2Arr = (c_char * 2)() 
        bytes_2Arr[0] = struct.pack("B", (data & 0xFF00)>>8 | 0x80)
        bytes_2Arr[1] = struct.pack("B", data & 0x00FF); 
        return bytes_2Arr 
    elif data <= 0xffff: bytes_3Arr = (c_char * 4)() 
        bytes_3Arr[0] = struct.pack("B", (data & 0xFF000000)>>24 | 0xC0)
        bytes_3Arr[1] = struct.pack("B", (data & 0x00FF0000)>>16) 
        bytes_3Arr[2] = struct.pack("B", (data & 0x0000FF00)>>8)
        bytes_3Arr[3] = struct.pack("B", (data & 0x000000FF)) 
        return bytes_3Arr 
    else: 
        print "Invaild Data"

def unCompress(self, data): 
    _len = len(data) 
    if _len == 1: 
        return struct.unpack("B", data[0]) 
    elif _len == 2: 
        _data0 = struct.unpack("B", data[0]) 
        _data1 = struct.unpack("B", data[1]) 
        return ((_data0[0] & 0x3F)<<8 | _data1[0]) & 0x00FF 
    elif _len == 4: 
        _data0 = struct.unpack("B", data[0])[0] 
        _data1 = struct.unpack("B", data[1])[0] 
        _data2 = struct.unpack("B", data[2])[0] 
        _data3 = struct.unpack("B", data[3])[0] 
        return ((_data0 & 0x1F)<<24 | _data1<<16 | _data2<<8 | _data3)
    else: 
        print "unCompress Error!"

另外需要注意的是 Python返回后的是个元组 需要 用 .value来取值

图示:

-> 1000 0001 | 0000 0000

<think>嗯,我现在需要解析用户提供的Svpwm2函数中case 1部分的算法。首先,用户提到Ualfa和Ubeta的范围是24位,这可能涉及到变量在计算时的位宽处理。让我先仔细看一下代码的结构,特别是case 1的部分。 首先,函数里定义了一些变量,比如Va、Vb、Vc、T1、T2、Tsum等,都是Int32类型。然后是扇区选择的逻辑,根据Va、Vb、Vc的正负确定Sector的值。接着,对Va、Vb、Vc进行了右移操作,可能是为了调整数值范围以适应后续计算。例如,Va被右移9位,Vb和Vc分别右移12位,这可能涉及到将24位的输入数据缩小到更低的位数,比如15位或更少,以避免计算溢出。 在case 1中,代码将T1设为Vc,T2设为Vb,然后计算Tsum为T1+T2。如果Tsum超过了Limit(28580),则进入调整逻辑。这里使用了TempDiv1来保存T1乘以Limit的结果,然后通过循环右移TempDiv1和Tsum,直到Tsum不超过32767(即15位有符号数的最大值)。之后,调用__builtin_divsd进行除法运算,得到调整后的T1,T2则为Limit减去T1。 调整完T1和T2后,计算Tb、Ta、Tc。Tb的计算式是(32768 + T1 + T2) >> 1,这可能是在进行某种中间值的计算,比如将两个时间相加并取平均,加上32768可能涉及到四舍五入或者调整符号。然后Ta是Tb减去T1,Tc是Ta减去T2。 我需要理解这些步骤背后的数学原理。SVPWM(空间矢量脉宽调制)通常涉及将三相电压转换为两个相邻的非零矢量和零矢量,通过不同的时间组合来合成所需的电压矢量。扇区的划分通常是根据矢量的位置决定的,这里case 1对应某个特定的扇区。 在SVPWM中,每个扇区的处理方式不同,通常需要计算相邻矢量的作用时间T1和T2,以及零矢量的时间T0。总时间Tsum = T1 + T2 + T0应该不超过PWM周期,否则需要进行过调制处理,即按比例缩小T1和T2,使得Tsum不超过Limit(这里可能是PWM周期的最大值)。 在case 1中,当Tsum超过Limit时,代码对T1和T2进行调整。这里可能是在进行饱和处理,即保持T1和T2的比例,但将它们的总和缩放到Limit。具体来说,原来的T1和T2的总和超过了Limit,所以需要按比例缩小。这里的处理方式是用T1乘以Limit,然后除以Tsum,得到新的T1,而T2则为Limit - T1。不过,用户代码中使用了一个循环右移的方法来调整数值,可能是为了在定点数运算中避免除法运算的高开销,或者处理除法的精度问题。 例如,当Tsum太大时,将其右移直到它适合16位有符号数的范围(15位),同时TempDiv1也相应右移,这样除法运算可以被处理为较小的数值,避免溢出。然后使用内置函数__builtin_divsd进行有符号除法,得到调整后的T1。这可能是一种定点数运算中的缩放技巧,确保计算过程中不会溢出,同时保持尽可能高的精度。 接下来,计算Tb、Ta、Tc时,使用了(32768 + T1 + T2) >> 1。32768是2^15,右移一位相当于除以2,但加上这个数可能是为了处理四舍五入的问题。例如,在整数运算中,加上0.5后再取整,这里用32768可能相当于加上0.5的定点表示。例如,假设数值是Q15格式,那么32768可能代表1,但这里可能需要更仔细的位分析。或者,这里的32768可能是用来调整符号,或者确保中间结果在移位时正确。 例如,假设T1和T2都是Q15格式的16位整数,那么它们的和可能超过16位,所以加上32768(即1<<15)然后右移一位,可能是在进行四舍五入的平均值计算。例如,当两个数相加时,加上1<<(n-1)然后右移n位,可以近似四舍五入。这里加上32768(相当于Q15中的1)然后右移1位,相当于除以2时四舍五入到最近的整数。 然后Ta = Tb - T1,这可能对应着某个PWM通道的占空比计算。例如,在SVPWM中,各个相的占空比时间可能由基本矢量的作用时间和零矢量的分配方式决定。不同的扇区对应不同的计算方式,这里case 1的处理可能对应特定的矢量组合。 我需要验证这些步骤是否符合SVPWM的标准算法。通常,SVPWM的每个扇区对应的两个相邻矢量的作用时间由Clarke变换后的Ualpha和Ubeta计算得到,比如使用公式: T1 = √3 * Ts * (Ubeta * cos(θ) - Ualpha * sin(θ)) / Vdc T2 = √3 * Ts * (Ualpha * sin(θ) - Ubeta * cos(θ)) / Vdc 其中θ是扇区的起始角度,Ts是PWM周期,Vdc是直流母线电压。不过,在具体实现中,可能通过不同的坐标变换和简化来避免三角函数运算,尤其是在定点运算中。 用户代码中的Vb和Vc在初始计算中被赋值为Ualfa2和Ubeta2的线性组合,可能对应于不同扇区中的系数。例如,在扇区1中,可能需要特定的线性组合来计算T1和T2。例如,Va、Vb、Vc的初始计算可能判断矢量的方向有关,而后续的右移操作可能是为了调整数值范围。 例如,在扇区选择后,case 1中计算Vc和Vb的右移后的值,可能是为了得到对应的时间T1和T2。例如,Vc和Vb可能是经过缩放后的系数,用于直接计算时间。这里可能涉及到将电压矢量分解到相邻的两个基本矢量上的比例,进而得到作用时间。 当Tsum超过Limit时,需要进行缩放,以保证总时间不超过PWM周期。这时,将T1和T2按比例缩小,使得它们的和等于Limit。代码中的处理方式是通过将T1乘以Limit,然后除以Tsum,得到新的T1,然后T2=Limit-T1。这符合比例缩放的逻辑,但实现中为了避免大数除法,采用了右移的方式,将分子和分母同时右移直到分母在16位范围内,这样可以使用内置的除法指令。 例如,当Tsum超过32767(即15位最大值),则循环右移,直到Tsum不大于32767。同时,TempDiv1也被同样右移,保持比例不变。例如,假设原来的Tsum是32位,右移n次后变成16位,而TempDiv1也被右移n次,这样T1*Limit的结果在除法时不会溢出,且结果正确。 然后,计算Tb、Ta、Tc时,可能是在分配三个PWM通道的占空比时间。例如,在扇区1中,可能对应某两个相的时间较长,而第三个相的时间较短,或者根据矢量作用顺序来分配时间。例如,SVPWM通常将每个PWM周期分为七段,包括两个矢量作用时间和零矢量时间。这里可能是在计算每个相的占空比对应的计数值,以便设置PWM寄存器的比较值。 例如,Tb的计算可能对应某个相的高电平时间,而Ta和Tc则根据T1和T2进行调整。具体的分配方式可能依赖于扇区,不同的扇区会有不同的公式。 总结来说,case 1中的算法主要处理以下几个步骤: 1. 根据扇区选择对应的矢量作用时间T1和T2。 2. 检查总时间是否超过限制,如果超过,则按比例调整T1和T2。 3. 调整后的时间用于计算三个相的PWM占空比。 在调整过程中,通过右移操作处理大数除法,确保计算不会溢出,同时保持比例关系。之后的占空比计算可能涉及将总时间分配到各个相,并考虑PWM计数器的中心对齐模式或其他特定模式。 可能的疑问点包括: - Va、Vb、Vc的计算和右移的位数是否有特定的缩放比例? - Limit的值28580是如何确定的?是否PWM周期相关? - 32768的使用是否Q15格式或其他定点格式有关? - 各个变量(如Ta、Tb、Tc)如何对应到实际的PWM输出? 需要进一步分析代码中的数值处理和位操作,以及SVPWM算法的具体实现步骤,才能更准确地解释每个步骤的作用。</think>以下是针对`Svpwm2`函数中**case 1**部分的算法解析,结合SVPWM原理和代码实现逻辑: --- ### **1. 核心变量作用** - **输入变量**:`Ualfa`和`Ubeta`为24bit定点数,表示Clarke变换后的两相电压。 - **关键中间变量**: - `Va`, `Vb`, `Vc`:通过线性组合和位运算调整后的电压分量。 - `T1`, `T2`:相邻非零矢量的作用时间。 - `Tsum`:总时间(需限制在PWM周期内)。 - `Limit = 28580`:PWM周期最大计数值(假设为固定频率下的计数值)。 --- ### **2. 扇区选择逻辑** - **扇区判定**:通过`Va`, `Vb`, `Vc`的正负组合确定电压矢量所在的扇区。 - **case 1对应扇区**:当`Va > 0`且`Vb ≤ 0`, `Vc ≤ 0`时,进入扇区1。 --- ### **3. 电压分量调整** - **右移运算**: - `Va = Ubeta2 >> 9`:将24bit的`Ubeta`缩小到15bit(右移9位相当于除以512)。 - `Vb = (Ualfa2 * 7 + Ubeta2 * 4) >> 12`:线性组合后缩小到12bit。 - `Vc = (Ubeta2 * 4 - Ualfa2 * 7) >> 12`:同上。 - **目的**:将24bit输入缩放到适合后续定点运算的范围(避免溢出)。 --- ### **4. 时间计算限幅** - **原始时间分配**: ```c T1 = Vc; // 对应矢量Vc的作用时间 T2 = Vb; // 对应矢量Vb的作用时间 Tsum = T1 + T2; ``` - **过调制处理**: - 若`Tsum > Limit`,按比例缩小`T1`和`T2`: 1. **循环右移**:将`T1 * Limit`和`Tsum`同步右移,直到`Tsum ≤ 32767`(15bit有符号数最大值)。 2. **定点除法**:通过`__builtin_divsd`计算新的`T1 = (T1 * Limit) / Tsum`。 3. **更新`T2`**:`T2 = Limit - T1`,确保`T1 + T2 = Limit`。 --- ### **5. PWM占空比生成** - **占空比分配**: ```c Tb = (32768 + T1 + T2) >> 1; // 中间值计算(四舍五入) Ta = Tb - T1; // 相位A的占空比 Tc = Ta - T2; // 相位C的占空比 ``` - **关键点**: - **32768的作用**:在右移前加0.5(Q15格式的0.5对应32768),实现四舍五入。 - **最终输出**:`Ta`, `Tb`, `Tc`为三相PWM的比较值,需映射到具体计时器寄存器。 --- ### **6. 数学原理验证** - **SVPWM时间公式**: 在扇区1中,标准SVPWM的作用时间为: $$ T_1 = \frac{\sqrt{3} T_s}{V_{dc}} \cdot U_{\beta}, \quad T_2 = \frac{\sqrt{3} T_s}{V_{dc}} \cdot \left( \frac{\sqrt{3}}{2} U_{\alpha} + \frac{1}{2} U_{\beta} \right) $$ 代码中通过`Vc`和`Vb`的线性组合(如`7*Ualfa ± 4*Ubeta`)近似实现系数匹配。 --- ### **7. 定点运算优化** - **位运算替代浮点**:通过右移和整数乘法代替浮点运算,提升实时性。 - **缩放比例**:输入24bit → 中间15/12bit → 输出适配PWM分辨率(如16bit)。 --- ### **总结** case 1的算法实现了: 1. **扇区判定**:确定电压矢量位置。 2. **时间计算**:生成相邻矢量的作用时间。 3. **过调制处理**:按比例限幅保证总时间不超限。 4. **占空比分配**:生成三相PWM波形所需的计时值。 该代码是SVPWM在定点DSP/MCU中的典型实现,兼顾精度实时性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值