patternphpMinor
Converting a 32bit number in a proprietary timestamp with PHP
Viewed 0 times
number32bitproprietarywithphptimestampconverting
Problem
This is a copy of a posting I made on SO, it was suggested I post it here all I've changed from that post is I'm now doing the bitwise AND using a decimal number rather than using bindec("111") to create the mask. The output is identical.
I have a 32 bit number represented as a HEX string that I need to convert to a timestamp following these rules:
What I want to check is that I'm not doing something glaringly stupid? Does my conversion look sane given the timestamp specification?
I've put together the following code to do this. I know PHP isn't the best for this sort of work but it's part of a larger legacy application all written in PHP.
The output for the last entry in the file is:
```
Incoming timestamp 2DAC1B0A
(as binary 00101101101011000001101
I have a 32 bit number represented as a HEX string that I need to convert to a timestamp following these rules:
What I want to check is that I'm not doing something glaringly stupid? Does my conversion look sane given the timestamp specification?
I've put together the following code to do this. I know PHP isn't the best for this sort of work but it's part of a larger legacy application all written in PHP.
$t is taken from an XML file of readings, one every 5 mins.// convert the hex string to a string representation of the binary number
// sprintf pads leading zeros, for debugging only
$binStr = sprintf('%032s', base_convert($t, 16, 2));
//get a decimal representation so we can use the bitwise operator on it
$decimal = base_convert($t, 16, 10);
// get the first 6 bits
$min = $decimal & 63; // 111111 in binary
// drop the first 8 bits then apply a mask to the next 5
$hour = ($decimal >> 8) & 31; // 11111 in binary
// drop the first 15 bits then apply a mask to the next 1
$daylightSaving = ($decimal >> 15) & 1;
// etc
$day = ($decimal >> 16) & 31; // 11111
$yearLsb = ($decimal >> 21) & 7; //111
$month = ($decimal >> 24) & 15; //1111
$yearMsb = ($decimal >> 28) & 15; //1111
Logger::debug(
"Incoming timestamp " . $t . " \n" .
" (as binary " . $binStr . ") converted to: \n" .
" Day: " . $day . "\n" .
" Month: " . $month . "\n" .
" Year: " . $yearMsb . $yearLsb . "\n" .
" Hour: " . $hour . "\n" .
" Min: " . $min . "\n" .
" Daylight saving: ". $daylightSaving
);The output for the last entry in the file is:
```
Incoming timestamp 2DAC1B0A
(as binary 00101101101011000001101
Solution
Not being a PHP expert, but messing with bitwise operations a lot, I have some advice for you:
First, the specification for your operations should be embedded with the code as a comment. Having to look up a specification that may or may not be the same as the one you used when programming it, is not useful... and changing between screens/pages to inspect the code vs. the specification is also not much fun. Something like:
This makes it easier to compare/validate your code.
My next suggestion is to shift the data as you pull values off... This way you don't need to do 'brain-mushing' thinking when doing the manipulation.
Then, the next suggestion is to set up some 'constants' for your masks:
Now, the example above becomes:
Then, I recommend you process the fields you don't need, and it helps you keep track of the specification:
Follow the same pattern for the other bits...
This is a relatively concise way to do things, actually, and you only need to keep one 'dimension' (the length of each data value) of the problem in your head, and you can ignore the offset.
This leads to fewer bugs, and better understanding and readability.
I cannot find a definitive reference whether PHP supports a definitive
First, the specification for your operations should be embedded with the code as a comment. Having to look up a specification that may or may not be the same as the one you used when programming it, is not useful... and changing between screens/pages to inspect the code vs. the specification is also not much fun. Something like:
// Input data is 4 bytes with following bit-specification:
// Byte0 (least significant)
// 0-5 : minutes
// 6 : reserved
// 7 : valid
// Byte1
// 0-4 : hours
// ........This makes it easier to compare/validate your code.
My next suggestion is to shift the data as you pull values off... This way you don't need to do 'brain-mushing' thinking when doing the manipulation.
// get the first 6 bits
$min = $decimal & 63; // 111111 in binary
$decimal = $decimal >> 6; // get rid of those 6 bits,
// now we only need to worry about low bits.Then, the next suggestion is to set up some 'constants' for your masks:
$mask6bits = (1 << 6) - 1; // 00111111 in binary.Now, the example above becomes:
// get the first 6 bits
$min = $decimal & mask6bits;
$decimal = $decimal >> 6; // shift off minutesThen, I recommend you process the fields you don't need, and it helps you keep track of the specification:
// get the first 6 bits
$min = $decimal & mask6bits;
$decimal = $decimal >> 6; // shift off minutes
$decimal = $decimal >> 1; // shift off reserved
$decimal = $decimal >> 1; // shift off valid
$hours = $decimal & mask5bits;
$decimal = $decimal >> 5; // shift off the hours.Follow the same pattern for the other bits...
This is a relatively concise way to do things, actually, and you only need to keep one 'dimension' (the length of each data value) of the problem in your head, and you can ignore the offset.
This leads to fewer bugs, and better understanding and readability.
I cannot find a definitive reference whether PHP supports a definitive
>>= assignment operator. If it does, the lines $decimal = $decimal >> 5 can be replaced with $decimal >>= 5Code Snippets
// Input data is 4 bytes with following bit-specification:
// Byte0 (least significant)
// 0-5 : minutes
// 6 : reserved
// 7 : valid
// Byte1
// 0-4 : hours
// ........// get the first 6 bits
$min = $decimal & 63; // 111111 in binary
$decimal = $decimal >> 6; // get rid of those 6 bits,
// now we only need to worry about low bits.$mask6bits = (1 << 6) - 1; // 00111111 in binary.// get the first 6 bits
$min = $decimal & mask6bits;
$decimal = $decimal >> 6; // shift off minutes// get the first 6 bits
$min = $decimal & mask6bits;
$decimal = $decimal >> 6; // shift off minutes
$decimal = $decimal >> 1; // shift off reserved
$decimal = $decimal >> 1; // shift off valid
$hours = $decimal & mask5bits;
$decimal = $decimal >> 5; // shift off the hours.Context
StackExchange Code Review Q#37051, answer score: 2
Revisions (0)
No revisions yet.