We're collecting data from a partner's IoT devices and the data comes encoded in a hex string like 1C000000010028 which contains binary data:
Byte 0 bit 0 Boolean
Byte 0 bit 1 Boolean
Byte 0 bit 2 Boolean
Byte 0 bit 3 Boolean
Byte 0 bits 4-7 UInt4
Bytes 1-2 bits 0-15 UInt16
Byte 3 bits 0-7 UInt8
Bytes 4-5 bits 0-15 UInt16
Byte 6 bits 0-7 UInt8
I have never worked with this kind of data and am wondering how to decode / unpack this in PHP. I was guessing that https://www.php.net/manual/de/function.unpack.php would be my friend but I just don't get it. Any help would be much appreciated, thanks!
They say that the input is a hex string like '1C000000010028'.
$code = '1C000000010028';
To use unpack() the data must be a string with binaryData. You can convert it with hex2bin.
$binaryData = hex2bin($code);
// "\x1c\x00\x00\x00\x01\x00\x28"
Now you could use unpack.
$arr = unpack('Cbyte_0/vUInt16_0/Cbyte_1/vUInt16_1/Cbyte_2',$binaryData);
/*
$arr = array (
'byte_0' => 28,
'UInt16_0' => 0,
'byte_1' => 0,
'UInt16_1' => 1,
'byte_2' => 40,
)
*/
Individual data types such as Boolean and UInt4 are not included in the pack/unpack formats. To get this data you have to work with the bit operators.
Just one example of this:
$byte_0bit2 = (bool)($arr['byte_0'] & 0b00000100);
This can lead to further questions, the answers of which can be found here on Stackoverflow.
Related
I get through Modbus some 4-Byte Float Values.
I get this 4 bytes in an array:
Array ( [0] => 67 [1] => 105 [2] => 25 [3] => 154 )
After some Bitconversion I made this:
1000011011010010001100110011010
when putting this binary string into a online converter such as
https://www.binaryconvert.com/result_float.html?hexadecimal=4369199A
I get the value of 2.331E2 which is correct (233.1).
However I fail to convert this binary in a PHP float variable. I tried to point the address to a float variable, but did not succeed.
How can I convert this binary string or the array above into a php float?
Added:
The pointer Idea was stupid, since php does not let me define the type of variable. So I tried a different approach using unpack to a float type. But it does not work either:
// This represents a 4 byte float read from modbus
$array=array(67,105,25,154);
$array=array_reverse($array);
print_r($array);
for ($i=0;$i<count($array);$i++) {
$t+=pow(2,$i*8)*$array[$i];
}
echo '<br>Binary: '.decbin($t). ' - This would be the correct Binary for 233.1';
echo '<br>float: ';
print_r(unpack('f',$t));
This code results in:
Array ( [0] => 154 [1] => 25 [2] => 105 [3] => 67 )
Binary: 1000011011010010001100110011010 - This would be the correct Binary for 233.1
float: Array ( [1] => 6.5189725839687E-10 )
No chance to get my 233.1 :(
I know I come with a possible answer a little bit later but maybe others found useful.
The conversion is a little bit complicated, I worked few hours on it to include in my application.
Firstly I created a function to get value of the Mantissa, then calculated a float number. I didn't created function for the calculation of the exponent and sign.
This example is valid for positive float numbers, for more details read this:
HOW REAL (FLOATING POINT) AND 32-BIT DATA IS ENCODED IN MODBUS RTU MESSAGES
And the script:
$array=array(67,105,25,154);
$result = pow(2,(((($array[0] * 256 + $array[1]) & 32640) >> 7)-127)) * calcMantissa((($array[1] & 127) << 16) + ($array[2] << 8) + $array[3]+1);
echo $result . "\n";
function calcMantissa($nb) {
$retValue = 1;
for ($i = 0; $i < 22; $i++) {
$retValue = $retValue + (($nb & (1 << (22 - $i))) > 0) / (pow(2,($i+1))) ;
}
return $retValue;
}
which gave the following result:
233.10000610352
I am trying to write some binary data (in an array of unsigned chars) to a tcp stream, the data looks like this:
array(44) {
[0]=>int(0)
[1]=>int(44)
[2]=>int(10)
[3]=>int(0)
[4]=>int(5)
[5]=>int(108)
[6]=>int(111)
[7]=>int(103)
...
And what I would like to get to is a string holding the same binary bytes, that I can throw into fwrite() to send these bytes down a TCP connection.
Everything I have tried using join etc, so far has ended up with the ascii equivalent (eg the first byte ends up as 0x30 , an ascii '0', instead of 0x00)
What I need to end up with is binary data in the string ie 0x00, 0x2C, 0x0A not an ascii representation of the data.
Ideas?
You just can use the function "decbin()" to convert your integer into a binary format.
After this you can send the binary code to your TCP connection.
Here is a code example:
<?php
$numbers = array(
0 => "0",
1 => "1",
2 => "2"
);
$bin = decbin($numbers[1]);
$bin = substr("00000000",0,8 - strlen($bin)) . $bin;
echo $bin;
?>
Output is 00000001 for Number 1.
print_r(bin2hex("11111111"));
echo '</br>';
print_r(bindec("11111111"));
Result
131313131313131
255
I want a hexadecimal 16 byte value to do aes encryption.How is the conversion from binary to hex happening in php.I am getting incorrect value using the function.Also when i convert an array of hexadecimal values to string the byte length changes
You get a correct result, it's just not what you want. bin2hex() returns an ASCII string of the hexadecimal representation. A quote from the manual:
Returns an ASCII string containing the hexadecimal representation of str.
So If you want the hexadecimal number you can use this:
print_r(dechex(bindec("11111111")));
The converter to get hexidecimal is dechex(), but it needs a decimal number. To do that we convert you binary string to a decimal number first using bindec() and then pass it into dechex(), e.g:
print_r(dechex(bindec("11111111")));
<?php
$str = "Hello world!";
echo bin2hex($str) . "<br>";
echo pack("H*",bin2hex($str)) . "<br>";
?>
PHP.NET Manual :
http://php.net/manual/en/function.bin2hex.php
Test Your Result : http://www.cs.princeton.edu/courses/archive/fall07/cos109/bc.html
Detailed Explanation:
http://www.computerhope.com/binhex.htm
It's simply 9 * 16 + F where F is 15 (the letters A thru F stand for 10 thru 15). In other words, 0x9F is 159.
It's no different really to the number 314,159 being:
3 * 100,000 (10^5, "to the power of", not "xor")
+ 1 * 10,000 (10^4)
+ 4 * 1,000 (10^3)
+ 1 * 100 (10^2)
+ 5 * 10 (10^1)
+ 9 * 1 (10^0)
for decimal (base 10).
The signedness of such a number is sort of "one level up" from there. The unsigned value of 159 (in 8 bits) is indeed a negative number but only if you interpret it as one.
I am using BEncoded PHP Library to decode the bencoded response from a Bittorrent tracker.
The response of Tracker is:
d5:filesd20:¼€™rÄ2ÞÊþVA .]á^¦d8:completei285e10:downloadedi22911e10:incompletei9eeee
after decoding it using the below code:
require 'bencoded.php';
$be = new BEncoded;
//Response saved in scrape.txt
$data =file_get_contents('scrape.txt');
print_r($be->Decode($data));
the output is:
Array ( [files] => Array ( [¼€™rÄ2ÞÊþVA .]á^¦] => Array ( [complete] => 285 [downloaded] => 22911 [incomplete] => 9 [isDct] => 1 ) [isDct] => 1 ) [isDct] => 1 )
My Problem
my problem in the above output is how to decode those mysterious letters in output.
The link: http://wiki.vuze.com/w/Scrape posted by user3690414 pretty much explains what the different keys stands for.
To interpret the raw bencoded string:
d5:filesd20:¼€™rÄ2ÞÊþVA .]á^¦d8:completei285e10:downloadedi22911e10:incompletei9eeee
you need to understand how bencoding works: https://wiki.theory.org/BitTorrentSpecification#Bencoding
The most essential to know here is that that every entry in a bencoded dictionary is a Key,Value-pair.
Where Key is a byte string
and Value one of the following types: byte string, integer, a list or a dictionary.
With that in mind the raw string can be broken down like this:
d // The first d indicates the start of the Root dictionary
5:files // that has a Key with a 5 byte string name 'files',
d // the value of the 'files'-key is a second dictionary
20:¼€™rÄ2ÞÊþVA .]á^¦ // that has a Key 20 byte = 160 bit big endian SHA1 info-hash
d // the value of that key is a third dictionary
8:complete // that has a Key with a 8 byte string name 'complete',
i285e // the value of that key is a Integer=285
10:downloaded // that has a Key with a 10 byte string name 'downloaded',
i22911e // the value of that key is a Integer=22911
10:incomplete // that has a Key with a 10 byte string name 'incomplete',
i9e // the value of that key is a Integer=9
e // this e indicates the end of the third dictionary
e // this e indicates the end of the second dictionary
e // this e indicates the end of the Root dictionary
Hope this helps to understand the output from 'bencoded.php'.
edit.
If you want to make the 160 bit big endian SHA1 info-hash [¼€™rÄ2ÞÊþVA .]á^¦]
more human readable, I suggest that you output it as 40 byte hex-encoded string:
0xBC801B9D9972C432DECAFE56410F092E5DE15EA6
If you are referring to the mangled key of files array then it's raw infohash - check out the spec:
https://wiki.theory.org/BitTorrentSpecification#Tracker_.27scrape.27_Convention
http://wiki.vuze.com/w/Scrape
I'm pretty inexperienced in PHP so this may be obvious to some of you, but if I call md5($mystring,true) in PHP it says it returns a "raw binary format with a length of 16". So what is that? Is it an array? An array of what? How do I read the individual bits and bytes of that return value?
None of the examples I can find online use it without going straight into base64_encode() or something. I just want to be able to check the fifth bit, or the third byte, for example.
var_dump(md5("string", TRUE));
"Raw binary format" means a string (as strings are binary-safe in PHP):
string(16) "�\����= �(��^{!"
If you want to read a byte out of that, use the $string[5] offset syntax for that. Or to extract a single bit out of the fifth byte for example:
$bit4_from_5th_byte = ord($result[5]) & (1 << 4);
It returns it as a "string" with each Character being a byte of the output. What you might consider instead is using the hex output of md5() without the second parameter and converting substrings of it to numbers using intval() with 16 as the base parameter. Like so:
$hash = md5($raw);
$byte = intval(substr($hash, 0, 2), 16);
An MD5 hash is a 128bit number, e.g. 16 bytes of raw binary data. For legibility, PHP's md5() function defaults to outputting this as a human readable string, where those 16 binary bytes get converted into a 32 character alpha-numeric string.
When you specify that second true parameter for MD5, you're telling PHP to return that raw 128bit number instead of the human readable version.
The RAW output of MD5 is the plain binary string of the hash. You cannot print it to the screen since it's not encoded as actual ASCII characters but binary numbers. This is only useful if you need to store or transfer the hash in a binary format.
To get the individual bits:
$md5 = md5( "b", true );
$l = strlen( $md5 );
$bits = "";
for( $i = 0; $i < $l; ++$i ) {
$num = ord( $md5[$i] );
for( $j = 7; $j >= 0; --$j ) {
$bits .= ( $num & ( 1 << $j ) ) ? "1" : "0";
}
}
echo $bits."\n";
//10010010111010110101111111111110111001101010111000101111111011000011101011010111000111000111011101110101001100010101011110001111
echo asciibin2hex( $bits ) == md5("b"); // true
So we go byte by byte, and since it is in string form, we need to convert it to ASCII number by ord(). Now go through the byte, bit by bit and see which are turned on and which are turned off and concatenate to the bit string. Go to next byte and rinse and repeat until all 128 bits are read.
Read third bit ( from the left ) in third byte:
ord( $md5[2] ) & ( 1 << ( 8 - 3 ) )
returns 1 if the bit is turned on, 0 otherwise