PHP function required to convert hex to lat & long - php

I have a question regarding lattitude and longitude encoding, the answer to which my brain refuses to produce.
I need to write a php function that takes the value '1446041F' and '447D1100' (Lat & Lng) does some processing (the bit I cannot fathom) and outputs '52.062297' and '0.191030'.
I am told the Lat & Lng are encoded to 4 bytes from a signed degrees, minutes and decimal minutes with a format as follows;
Latitude: SDDMM.MMMMM where 0≤DD≤90, S = [+|-], 0≤M≤9
Longitude: SDDDMM.MMMMM where 0≤DDD≤180, S = [+|-], 0≤M≤9
See that last bit, I've searched many sites but I still have no idea what that all means.
I'm aware this is a massive shot in the dark and it may be so simple that I am rightfully told to sit in the corner wearing the dunce hat but I am running low on hair to pull out!
Any advice is much appreciated.
Thanks,
Matthew

The examples you gave, 1446041F and 447D1100 are probably 32-bit signed integers in little-endian byte order.
They are to be read as follows:
1446041F -> 0x1F044614 -> 520373780
447D1100 -> 0x00117D44 -> 001146180
They can be interpreted in degrees and minutes like this:
520373780 -> 52 degrees, 03.73780 minutes
1146480 -> 0 degrees, 11.46480 minutes
The following function will convert the hex values you specified to degrees. I assume the values are integers like 0x447D1100 and the like. If I assume wrong and the input values are actually strings, let me know. I put this function into the public domain.
function hextolatlon($hex){
// Assume hex is a value like 0x1446041F or 0x447D1100
// Convert to a signed integer
$h=$hex&0xFF;
$h=($h<<8)|(($hex>>8)&0xFF);
$h=($h<<8)|(($hex>>16)&0xFF);
$h=($h<<8)|(($hex>>24)&0xFF);
$negative=($h>>31)!=0; // Get the sign
if($negative){
$h=~$h;
$h=$h&0x7FFFFFFF;
$h++;
}
// Convert to degrees and minutes
$degrees=floor($h/10000000);
$minutes=$h%10000000;
// Convert to full degrees
$degrees+=($minutes/100000.0) / 60.0;
if($negative)$degrees=-$degrees;
return $degrees;
}

Here's the PHP (the verbosity is for clarity):
function llconv($hex) {
// Pack hex string:
$bin = pack('H*', $hex);
// Unpack into integer (returns array):
$unpacked = unpack('V', $bin);
// Get first (and only) element:
$int = array_shift($unpacked);
// Decimalize minutes:
$degmin = $int / 100000;
// Get degrees:
$deg = (int)($degmin/100);
// Get minutes:
$min = $degmin - $deg*100;
// Return degress:
return round($deg + ($min/60), 6);
}
$long = '1446041F';
$lat = '447D1100';
$iLong = llconv($long);
$iLat = llconv($lat);
print "Out: $iLong x $iLat\n";

Related

PHP Convert a dec to a bit array with a predefined length

I'm getting confused on how to do a simple thing, perhaps someone can help me.
At one point of my code I convert a bit array with 10 alarms (0 or 1) to a decimal and save it.
At another point I load the decimal and want to convert it back to a bit array.
This works however the bit array should be always have a length of 10 even if the decimal length is not 10 bites.
See my code:
// Convert array to dec:
$alarms = array(0,0,1,0,0,0,1,0,0,0);
$str = implode("", $alarms);
$dec = bindec($str);
// Convert back to bit array:
$bin = decbin($dec);
echo $bin;
The result of this code is:
10001000
But should be:
0010001000
Thanks!
Here's an idea of how you might implement this.
<?php
// Input array of bits
$inBits = [0,0,1,0,0,0,1,0,0,0];
// Convert to decimal value
$value = bindec(implode('', $inBits));
// Convert back to string of 0/1, adding padding as needed.
$outBitStr = str_pad(decbin($value), count($inBits), '0', STR_PAD_LEFT);
var_dump(implode('', $inBits) === $outBitStr); // TRUE

Convert IEEE754 to hex in PHP

For a project I need to read in information from MQTT. The payload is filled with protobuf information, that needs to be converted.
For a certain value I receive 5.6904566139035E-28 as float. Using http://www.exploringbinary.com/floating-point-converter/ I can convert this when I tick single and raw hexadecimal value, then I receive 12345678, the value I should have (I know what is sent).
But now I need to do that conversion in PHP. I haven't any idea how this could be done. After some reading I figured out it is a Floating Point, but how to convert this like done on that website.
Is there someone that can help me with this!
Thanks a lot!
With the quite cryptic pack and unpack functions, it can be done in a one-liner:
function rawSingleHex($num) {
return strrev(unpack('h*', pack('f', $num))[1]);
}
This "packs" the number as its binary representation, then "unpacks" it in an array with one element: the binary representation in hexadecimal format. This format has the digits in the reversed order, so the function reverses that in the final result.
Call it by passing the floating point number:
echo rawSingleHex(5.6904566139035E-28);
Output:
12345678
Without pack/pack
(this was my original answer, but with the first option being available, this is not the advised way to proceed)
The binary format is explained in Wikipedia's article on the Single-precision floating-point format.
Here is a PHP function that implements the described procedure:
function rawSingleHex($num) {
if ($num == 0) return '00000000';
// set sign bit, and add another, higher one, which will be stripped later
$sign = $num < 0 ? 0x300 : 0x200;
$significant = abs($num);
$exponent = floor(log($significant, 2));
// get 24 most significant binary bits before the comma:
$significant = round($significant / pow(2, $exponent-23));
// exponent has exponent-bias format:
$exponent += 127;
// format: 1 sign bit + 8 exponent bits + 23 significant bits,
// without left-most "1" of significant
$bin = substr(decbin($sign + $exponent), 1) .
substr(decbin($significant), 1);
// assert that result has correct number of bits:
if (strlen($bin) !== 32) {
return "unexpected error";
}
// convert binary representation to hex, with exactly 8 digits
return str_pad(dechex(bindec($bin)), 8, "0", STR_PAD_LEFT);
}
It outputs the same as in the first solution.

How to get log() of a very big number (PHP)?

I've looked at php-big numbers, BC Math, and GMP for dealing with very big numbers in php. But none seem to have a function equivilent to php's log(). For example I want to do this:
$result = log($bigNumber, 2);
Would anyone know of an alternate way to get the log base 2 of a arbitray precision point number in php? Maybe Ive missed a function, or library, or formula.
edit: php-bignumbers seems to have a log base 10 function only log10()
In general if you want to implement your high precision log own calculation, I'd suggest 1st use the basic features of logarithm:
log_a(x) = log_b(x) / log_b(a) |=> thus you can recalulate logarith to any base
log(x*y) = log(x) + log(y)
log(a**n) = n*log(a)
where log_a(x) - meaning logarithm to the base a of x; log means natural logarithm
So log(1000000000000000000000.123) = 21*log(1.000000000000000000000123)
and for high precision of log(1+x)
use algorithm referenced at
http://en.wikipedia.org/wiki/Natural_logarithm#High_precision
One solution combining the suggestions so far would be to use this formula:
log2($num) = log10($num) / log10(2)
in conjunction with php-big numbers since it has a pre-made log10 function.
eg, after installing the php-big numbers library, use:
$log2 = log10($bigNum) / log10(2);
Personally I've decided to use different math/logic so as to not need the log function, and just using bcmath for the big numbers.
One of the great things about base 2 is that counting and shifting become part of the tool set.
So one way to get a 'log2' of a number is to convert it to a binary string and count the bits.
You can accomplish this equivalently by dividing by 2 in a loop. But it seems to me that counting would be more efficient.
gmp_scan0 and gmp_scan1 can be used if you are counting from the right. But you'd have to somehow convert the mixed bits to all ones and zeroes.
But using gmp_strval(num, 2), you can produce a string and do a strpos on it.
if the whole value is being converted, you can do a (strlen - 1) on it.
Obviously this only works when you want an integer log.
I've had a very similar problem just recently.. and so I just scaled the number considerably in order to use the inbuild log to find the fractional part.. (I prefere the log10 for some reason.. don't ask... people are strange, me too)
I hope this is selfexplanatory enough..
it returns a float value (since that's what I needed)
function gmp_log($num, $base=10, $full=true)
{
if($base == 10)
$string = gmp_strval($num);
else
$string = gmp_strval($num,$base);
$intpart = strlen($string)-1;
if(!$full)
return $intpart;
if($base ==10)
{
$string = substr_replace($string, ".", 1, 0);
$number = floatval($string);
$lg = $intpart + log10($number);
return $lg;
}
else
{
$string = gmp_strval($num);
$intpart = strlen($string)-1;
$string = substr_replace($string, ".", 1, 0);
$number = floatval($string);
$lg = $intpart + log10($number);
$lb = $lg / log10($base);
return $lb;
}
}
it's quick, it's dirty... but it works well enough to get the log of some RSA sized integers ;)
usage is straight forward as well
$N = gmp_init("11002930366353704069");
echo gmp_log($N,10)."\n";
echo gmp_log($N,10, false)."\n";
echo gmp_log($N,2)."\n";
echo gmp_log($N,16)."\n";
returns
19.041508364472
19
63.254521604973
15.813630401243

Workaround needed, PHP dechex maximum integer [duplicate]

I have some large HEX values that I want to display as regular numbers, I was using hexdec() to convert to float, and I found a function on PHP.net to convert that to decimal, but it seems to hit a ceiling, e.g.:
$h = 'D5CE3E462533364B';
$f = hexdec($h);
echo $f .' = '. Exp_to_dec($f);
Output: 1.5406319846274E+19 = 15406319846274000000
Result from calc.exe = 15406319846273791563
Is there another method to convert large hex values?
As said on the hexdec manual page:
The function can now convert values
that are to big for the platforms
integer type, it will return the value
as float instead in that case.
If you want to get some kind of big integer (not float), you'll need it stored inside a string. This might be possible using BC Math functions.
For instance, if you look in the comments of the hexdec manual page, you'll find this note
If you adapt that function a bit, to avoid a notice, you'll get:
function bchexdec($hex)
{
$dec = 0;
$len = strlen($hex);
for ($i = 1; $i <= $len; $i++) {
$dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
}
return $dec;
}
(This function has been copied from the note I linked to; and only a bit adapted by me)
And using it on your number:
$h = 'D5CE3E462533364B';
$f = bchexdec($h);
var_dump($f);
The output will be:
string '15406319846273791563' (length=20)
So, not the kind of big float you had ; and seems OK with what you are expecting:
Result from calc.exe =
15406319846273791563
Hope this help ;-)
And, yes, user notes on the PHP documentation are sometimes a real gold mine ;-)
hexdec() switches from int to float when the result is too large to be represented as an int. If you want arbitrarily long values, you're probably going to have to roll your own conversion function to change the hex string to a GMP integer.
function gmp_hexdec($n) {
$gmp = gmp_init(0);
$mult = gmp_init(1);
for ($i=strlen($n)-1;$i>=0;$i--,$mult=gmp_mul($mult, 16)) {
$gmp = gmp_add($gmp, gmp_mul($mult, hexdec($n[$i])));
}
return $gmp;
}
print gmp_strval(gmp_hexdec("D5CE3E462533364B"));
Output: 15406319846273791563
$num = gmp_init( '0xD5CE3E462533364B' ); // way to input a number in gmp
echo gmp_strval($num, 10); // display value in decimal
That's the module to use. Convert it to a function and then use on your numbers.
Note: provide these hex numbers as strings so:
$num = "0x348726837469972346"; // set variable
$gmpnum = gmp_init("$num"); // gmp number format
echo gmp_strval($gmpnum, 10); // convert to decimal and print out
1.5406319846274E+19 is a limited representation of you number. You can have a more complete one by using printf()
printf("%u\n", hexdec($h));
...will output "15406319846273792000". PHP uses floats for such big numbers, so you may lose a bit of precision. If you have to work with arbitrary precision numbers, you may try the bcmath extension. By splitting the hex into two 32-bit words (which should be safe on most systems) you should be able to get more precision. For instance:
$f = bcadd(bcmul(hexdec(substr($h, 0, -8)), 0x100000000), hexdec(substr($h, 8)));
...would set $f to 15406319846273791563.
Convert HEX to DEC is easy.. But, reconstruct back hexadecimal number is very hard.
Try to use base_convert ..
$hexadecimal = base_convert(2826896153644826, 10, 16);
// result: a0b0c0d0e0f1a
Run into this issue while storing 64-bit keys in MySQL database. I was able to get a bit perfect conversion to a 64-bit signed integer (PHP limitation) using a few binary operators: (This code is 16x faster than bchexdec function and resulting variables are using half the memory on average).
function x64toSignedInt($k){
$left = hexdec(substr($k,0,8));
$right = hexdec(substr($k,8,8));
return (int) ($left << 32) | $right;
}
MySQL signed BIGINT datatype is a great match for this as an index or storage in general. HEX(column) is a simple way to convert it back to HEX within the SQL query for use elsewhere.
This solution also uses the BC Math Functions. However, an algorithm is used which does without the bcpow function. This function is a bit shorter and faster than the accepted solution, tested on PHP 7.4.
function hexDecBc(string $hex) : string
{
for ($dec = '0', $i = 0; $i < strlen($hex); $i++) {
$dec = bcadd(bcmul($dec,'16'),(string)hexdec($hex[$i]));
}
return $dec;
}
Make sure to enable gmp extension. ext-gmp
$number = gmp_strval(gmp_init('0x03....')); // outputs: 1234324....
Doesn't intval(var, base) take care of it?
From the PHP Manual.

rhumb line calculation - javascript to php

This example and java script code is from link text
Look at the section on rhumb lines.
Given a start point and a distance d along constant bearing θ, this will calculate the destination point. If you maintain a constant bearing along a rhumb line, you will gradually spiral in towards one of the poles.
Formula:
α = d/R (angular distance)
lat2 = lat1 + α.cos(θ)
Δφ = ln(tan(lat2/2+π/4)/tan(lat1/2+π/4)) [= the ‘stretched’ latitude difference]
if E:W line q = cos(lat1)
otherwise q = Δlat/Δφ
Δlon = α.sin(θ)/q
lon2 = (lon1+Δlon+π) % 2.π − π
where ln is natural log and % is modulo, Δlon is taking shortest route (<180°), and R is the earth’s radius
JavaScript:
lat2 = lat1 + d*Math.cos(brng);
var dPhi = Math.log(Math.tan(lat2/2+Math.PI/4)/Math.tan(lat1/2+Math.PI/4));
var q = (!isNaN(dLat/dPhi)) ? dLat/dPhi : Math.cos(lat1); // E-W line gives dPhi=0
var dLon = d*Math.sin(brng)/q;
// check for some daft bugger going past the pole, normalise latitude if so
if (Math.abs(lat2) > Math.PI/2) lat2 = lat2>0 ? Math.PI-lat2 : -(Math.PI-lat2);
lon2 = (lon1+dLon+Math.PI)%(2*Math.PI) - Math.PI;
I am trying to convert it into php syntax but I am not getting the desired result. I have the latitude part working fine. I also included my test data.
MY PHP CODE
// test data
$R = 6371;
$tlatitude = 50.7;
$tlongitude = -105.214;
$theading = 124;
$d = 50;
$projlat = $tlatitude + rad2deg(($d/$R)*COS(deg2rad($theading)));
//Δφ = ln(tan(lat2/2+π/4)/tan(lat1/2+π/4))
$delta_phi = log(tan(deg2rad($projlat/2) + pi()/4)/(tan(deg2rad($tlatitude/2) + pi()/4)));
//q = Δlat/Δφ
$delta_lat = deg2rad($projlat - $tlatitude);
$q = $delta_lat/$delta_phi;
//Δlon = α.sin(θ)/q
$delta_long = rad2deg($d/$R*sin(deg2rad($theading))/$q);
$projlong = $tlongitude + $delta_long;
I get $projlong = -104.84
according to the referenced page the answer should be -104.63.
Now I am trying to get this to work disregarding the east-west and over the pole possibilities.
I had some problems when making distance calculations where my errors would grow quite a bit after a while. I discovered that if I made a cast to (double) in my code the precision increased. I have not looked in to the C-code in PHP to see what caused this though. I could after this scrap my BC-version of the code.
If you need additional precision please check out the BC-functions in PHP.
http://php.net/manual/en/book.bc.php
Also, please remember that the order that you make calculations in a computer will affect your precision. That is, the calculation bellow
$d/$R*sin(deg2rad($theading))/$q
will not render the same result as
$d*sin(deg2rad($theading))/$q/$R
and this can also give a third result
$d*sin(deg2rad($theading))/($q*$R)
This has to do with the limited precision for numbers close to zero (0) in computers.
javascript has more precision than php
look at this joke http://ru2.php.net/manual/en/function.doubleval.php
I once needed to check some IBAN with the Luhn's algorithm.
My javascript code worked nicely.
But my php failed, so after some research, i found the joke and had to recode basic operations (add, sub, compute, divide, modulo) based on string and not number.
Maybe should you recode it too, to get your expected precision.
We should not use php for high precision calculations.

Categories