使用PHP在专有时间戳中转换32位数字

时间:2013-12-10 15:45:32

标签: php

我有一个32位数字表示为HEX字符串,我需要按照这些规则转换为时间戳:

Timestamp specification

我想检查的是,我没有做一些明显愚蠢的事情?鉴于时间戳规范,我的转换是否合理?

我已经将以下代码放在一起来执行此操作。我知道PHP不是这类工作的最佳选择,但它是用PHP编写的更大的遗留应用程序的一部分。

$t取自读数的XML文件,每5分钟一次。

    // convert the hex string to binary
    // sprintf pads leading zeros
    $binStr = sprintf('%032s', base_convert($t, 16, 2));

    //get a decimal representation so we can use the bitwise operator on it
    // I know I could have converted straight to decimal, but I'm debugging
    $decimal = bindec($binStr);

     // get the first 6 bits
    $min = $decimal & bindec('111111');

    // drop the first 8 bits then apply a mask to the next 5
    $hour = ($decimal >> 8) & bindec('11111');

    // drop the first 15 bits then apply a mask to the next 1
    $daylightSaving = ($decimal >> 15) & 1; 

    // etc
    $day  = ($decimal >> 16) & bindec('11111');      
    $yearLsb  = ($decimal >> 21) & bindec('111');
    $month = ($decimal >> 24) & bindec('1111');
    $yearMsb  = ($decimal >> 28) & bindec('1111');

    Logger::debug(
        $t . " Incoming timestamp converted to  \n" . 
        $binStr . " then converted to  \n" . 
        " Day: " . $day . "\n" . 
        " Month: " . $month . "\n" . 
        " Year: " . $yearMsb . $yearLsb . "\n" . 
        " Hour: " . $hour . "\n" . 
        " Min: " . $min . "\n" . 
        " Daylight saving: ". $daylightSaving
    );

文件中最后一个条目的输出是:

2DAC1B0A Incoming timestamp converted to
00101101101011000001101100001010 then converted to
 Day: 12
 Month: 13
 Year: 25
 Hour: 27
 Min: 10
 Daylight saving: 0

我知道该文件是在2013年11月12日上午11:25创建的,读数是11:10到11:25,文件中的最后一个是上午11点10分。

分钟与文件中的分数相匹配,我以5分钟为增量获得10到25。如果我针对其他文件运行它,这一天也是准确的。

设备制造商有可能希望我们购买一些昂贵的软件来进行这种解析,这已经成为了时间段规范的一部分,但这是一个选项,我在32位整数中使用了很多可能的组合而且我不能看看如何从提供的时间戳中获取我需要的所有数字。

2 个答案:

答案 0 :(得分:3)

我找到了一个C代码并将其转换为PHP。结果有点不同,但也显然是错误的。但也许这有助于你。

您找到的C代码here。搜索Compound CP32。 (没有行号)

$hex = 0x2DAC1B0A;
$dt0 = $hex & 0xff;
$dt1 = ($hex >> 8) & 0xff;
$dt2 = ($hex >> 16) & 0xff;
$dt3 = ($hex >> 24) & 0xff;

$min = $dt0 & 0x3F;
$hour  = $dt1 & 0x1F;
$day  = $dt2 & 0x1F;
$mon   = ($dt3 & 0x0F) - 1;
$year  = (($dt2 & 0xE0) >> 5) | (($dt3 & 0xF0) >> 1);
$dayLightSaving = ($dt1 & 0x80) ? 1 : 0;

echo $min.'<br>'; // 10
echo $hour.'<br>'; // 27
echo $day.'<br>'; // 12
echo $mon.'<br>'; // 12
echo $year.'<br>'; // 21
echo $dayLightSaving; // 0

答案 1 :(得分:0)

年份被解码(或更确切地说,显示)错误:

" Year: " . $yearMsb . $yearLsb . "\n" . 

看起来你假设这一年是BCD。

要计算你应该做的正确年份:

$year = $yearLSB | ($yearMSB << 3);

请注意,EN 13757(您尝试解码的MBUS标准)也定义了“百年”(世纪)字段:

$hy = ($decimal >> 13) & 0x03;
$year = 1900 + 100 * $hy + $year;

if ($year < 1980)
  $year += 100;

if更正了旧仪表的时间戳,甚至标准本身也是如此。

我没有看到错误的小时的原因,因为($hour >> 8) & 0x1F应该是正确的。同样适用于其他字段。我猜$decimal本身的值与仪表发送的值不匹配。尽量避免转换为蜇伤/从蜇伤转换。