2010-11-30 183 views
2

我需要通过串行线(RS-232)接收来自Arduino的十六进制编码单精度大端浮点值。如何将它们转换为Python双精度浮点数的浮点数?单精度大端浮点值到Python浮点数(双精度,大端)

Arduino发送类似“8192323E”的东西,在Python中我想要0.174387。我发现“Convert hex to float”,但它似乎都不适用于单精度浮标。

从链接的页面,这看起来很有希望:

from ctypes import * 

def convert(s): 
    i = int(s, 16)     # convert from hex to a Python int 
    cp = pointer(c_int(i))   # make this into a c integer 
    fp = cast(cp, POINTER(c_float)) # cast the int pointer to a float pointer 
    return fp.contents.value   # dereference the pointer, get the float 

但它仍然无法与我的单精度浮点数工作。

在Java(Processing)我已经能够做到这一点:

float decodeFloat(String inString) { 
    byte [] inData = new byte[4]; 

    inString = inString.substring(2, 10); // discard the leading "f:" 
    inData[0] = (byte) unhex(inString.substring(0, 2)); 
    inData[1] = (byte) unhex(inString.substring(2, 4)); 
    inData[2] = (byte) unhex(inString.substring(4, 6)); 
    inData[3] = (byte) unhex(inString.substring(6, 8)); 

    int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | ((inData[1] & 0xff) << 8) | (inData[0] & 0xff); 
    //unhex(inString.substring(0, 8)); 
    return Float.intBitsToFloat(intbits); 
} 

供您参考,这是Arduino的运行实现十六进制编码的C代码。

void serialFloatPrint(float f) { 
    byte * b = (byte *) &f; 
    Serial.print("f:"); 
    for(int i=0; i<4; i++) { 

    byte b1 = (b[i] >> 4) & 0x0f; 
    byte b2 = (b[i] & 0x0f); 

    char c1 = (b1 < 10) ? ('0' + b1) : 'A' + b1 - 10; 
    char c2 = (b2 < 10) ? ('0' + b2) : 'A' + b2 - 10; 

    Serial.print(c1); 
    Serial.print(c2); 
    } 
} 
+0

你可以举一些如何使用ctypes代码解码浮点数的例子吗?我的第一个怀疑是字节顺序。您可能需要交换字节才能正确解码。 – 2010-11-30 15:13:21

+0

在其流线: F:FFFF7F7F F:FFFF7FFF F:8192323E F:00000000 虽然ctypes的代码读取: 'FFFF7F7F' 楠 'FFFF7FFF' 楠 '8192323E' -5.37040237597e-38 '00000000' 0。0 – 2010-11-30 15:20:41

+1

由于Arduino处理器是小端(AFAICT)处理器,运行在其上的C代码在执行十六进制编码时首先处理低位字节 - 因此,当所有四个字节按照生成的顺序连接在一起时,它们将回退到正常方式写入和解释多字节十六进制(和多位十进制)数字。这与Java解码器代码“起作用”(以及我的答案)一致。 – martineau 2010-12-01 11:53:21

回答

4
>>> struct.unpack('<f', '\x81\x92\x32\x3e') 
(0.17438699305057526,) 
0

事实上,你需要反转的字节顺序。看:

>>> convert('8192323E') 
-5.370402375965945e-38 
>>> convert('3E329281') 
0.17438699305057526 
0

它看起来像十六进制字符串值的数据序列具有低位字节第一(little-endian的?)。至少,这是方式Java代码 - 你说的作品 - 将把它:

inData[0] = (byte) unhex(inString.substring(0, 2)); // gets _first_ two bytes 
    inData[1] = (byte) unhex(inString.substring(2, 4)); // and the next two 
    inData[2] = (byte) unhex(inString.substring(4, 6)); // etc... 
    inData[3] = (byte) unhex(inString.substring(6, 8)); 

    // this shifts the bytes at the end of the hex string more the the leading ones 
    int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | 
       ((inData[1] & 0xff) << 8) | (inData[0] & 0xff); 

Python的int()函数需要时通过的(十六进制或十进制)的字符串高阶字节来第一次出现。所以,你应该能够通过添加类似解决您的convert()功能:

def convert(s): 
    s = ''.join(s[i:i+2] for i in range(8,-2,-2)) # put low byte pairs first 
    i = int(s, 16)     # convert from hex to a Python int 
     ... 

附近开始进行修复。

更新

或者你可以修复serialFloatPrint()功能上的Arduino运行一个小端架构做好这方面的十六进制编码。