Extracting GPS numerical values from byte array using PowerShell - arrays

I wish to extract GPS Location data from the EXIF properties of image files. I am using the System.Drawing.Bitmap class to get raw the values. I am able to extract the values I am looking for but they are coming back as bytes, or possibly arrays of bytes and I need help converting the bytes into sensible numbers. Here is what I have so far:
$imagePath = 'C:\temp\picTest\WP_20150918_003.jpg'
$imageProperties = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $imagePath
From the EXIF GPS Tag Reference I know that the property tags I am looking for are:
0x0001 - GPSLatitudeRef - Indicates whether the latitude is north or south latitude.
0x0002 - GPSLatitude - Indicates the latitude.
0x0003 - GPSLongitudeRef - Indicates whether the longitude is east or west longitude.
0x0004 - GPSLongitude - Indicates the longitude.
0x0005 - GPSAltitudeRef - Indicates the altitude used as the reference altitude.
0x0006 - GPSAltitude - Indicates the altitude based on the reference in GPSAltitudeRef.
First I get the GPSLatitudeRef:
$imageProperies.PropertyItems | ? ID -eq 0x0001
and I get:
Id Len Type Value
-- --- ---- -----
1 2 2 {78, 0}
According to the MSDN documentation for System.Drawing library, PropertyItem.Type of "2" is ASCII.
I load the value into a variable:
$GPSLatRef = $imageProperties.PropertyItems| Where ID -eq 0x0001
$GPSLatRef = $GPSLatRef.Value
$GPSLatRef
78
0
Get-Member on the variable returns type System.Byte. I know how to convert byte back into ASCII:
$GPSLatRef = [System.Text.Encoding]::ASCII.GetString($GPSLatRef.Value)
$GPSLatRef
returns:
N
But things get tricky for me with the GPSLatitude value (0x0002):
$imageProperies.PropertyItems | ? ID -eq 0x0002
returns:
Id Len Type Value
-- --- ---- -----
2 24 5 {37, 0, 0, 0...}
Load the value up into a variable:
$GPSLatitiude = $imageProperties.PropertyItems| Where ID -eq 0x0002
$GPSLatitiude.Value
The value returned is:
37
0
0
0
1
0
0
0
40
0
0
0
1
0
0
0
220
182
0
0
232
3
0
0
According to the MSDN documentation referenced above, PropertyItem.Type of "5"
"Specifies that Value data member is an array of pairs of unsigned long integers. Each pair represents a fraction; the first integer is the numerator and the second integer is the denominator.
Looking at the properties of the file itself in Windows Explorer I see the GPS Location values in decimal form.
"37;40;46.812000000005156"
From the value data above, and the description of the data type from the documentation, and comparing to the decimal values from Windows Explorer I can surmise that $GPSLatitude.Value actually shows me three different sets of two integers apiece. For example,
37
0
0
0
= 37
1
0
0
0
= 1
and 37/1 = 37, which matches the decimal value from Windows Explorer so I know I am on the right track.
How can I split the three sets of values from the (array of?) bytes found in the GPSLatitude property? Even though the data in the bytes is described as long integers, the decimal value displayed in the Windows Explorer shows that the outcome could be a number with fifteen digits to the right of the decimal point makes me think that the product of the division of the two long integers in the bytes from the property value might need to be stored in a [double] or perhaps [decimal]?

This snippet will give you Latitude and longitude details, I am using BitConverter for extracting the data from Byte array:
[double]$LatDegrees = (([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 0)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 4)));
[double]$LatMinutes = ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 8)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 12));
[double]$LatSeconds = ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 16)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(2).Value, 20));
[double]$LonDegrees = (([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 0)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 4)));
[double]$LonMinutes = ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 8)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 12));
[double]$LonSeconds = ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 16)) / ([System.BitConverter]::ToInt32( $imageProperties.GetPropertyItem(4).Value, 20));
"Latitude: $LatDegrees;$LatMinutes;$LatSeconds"
"Longitude: $LonDegrees;$LonMinutes;$LonSeconds"

Related

How can I find minimum values from array in matlab?

I want to extract the two points (i.e their values) which are marked with black outline in figure. These minima points are 2 and 5. Then after extraction these marked points coordinates I want to calculate the distance between them.
The code that I am using to plot average values of image, calculate minimas and locations is
I1=imread('open.jpg');
I2=rgb2gray(I1);
figure, title('open');
plot(1:size(I2,1), mean(I2,2));
hold on
horizontalAverages = mean(I2 , 2);
plot(1:size(I2,1) , horizontalAverages)
[Minimas locs] = findpeaks(-horizontalAverages)
plot(locs , -1*Minimas , 'r*')
Minima
-86.5647
-80.3647
-81.3588
-106.9882
-77.0765
-77.8235
-92.2353
-106.2235
-115.3118
-98.3706
locs =
30
34
36
50
93
97
110
121
127
136
It is a bit unclear from your question what you are actually looking for, but the following one liner will get you the local minima:
% Some dummy data
x = 1:11;
y = [3 2 1 0.5 1 2 1 0 1 2 3];
min_idx = ([0 sign(diff(y))] == -1) & ([sign(diff(y)) 0] == 1);
figure
plot(x, y);
hold on;
scatter(x(min_idx), y(min_idx))
hold off;
Use the 'findpeaks' function, if you have the signal processing toolbox.
[y,locs]=findpeaks(-x)
will find the local minima. This function has a ton of options to handle all kinds of special cases, so is very useful.

what is the function of $value & 15

I have been going over a perl book i have recently purchased, and while reading I noticed a block of code that confused me..
use integer;
$value = 257;
while($value){
unshift #digits, (0..9,a..f)[$value & 15];
$value /= 16;
}
print digits;
the book mentions the purpose was to reverse the order of digits. however, new to perl I am having trouble figuring out what [$value & 15] is doing.
It's a bitwise and operation.
What it's doing is performing a bitwise and using the value of 15 and whatever value is contained in $value.
The resulting value is the decimal value that corresponds to the result of a bitwise and with the lower 4 bits of the value.
Ex:
$value = 21
which has a binary representation of: 0b10101
Performing a bitwise and with 15 means that any bits in $value will be zeroed if they are either outside the lower 4 bit range, or contain no 1's in the lower 4 bits.
The result is:
0b10101
&
0b 1111
-------
0b00101 = 5
Looking up the truth tables for performing bitwise operations will help with stuff like this in the future, but when performing an AND with any value, the result is only true, when both bits are 1, 0 otherwise.
V1 | V2 | V1 & V2
-----------------
0 | 0 | 0
0 | 1 | 0
1 | 0 | 0
1 | 1 | 1

How to reference indexes of one array with all values from another array in Matlab

New to Matlab and am trying to convert a binary message to characters in Matlab. I was able to translate the binary message to a single column in decimal (without the bin2dec function, as I was instructed not to), with each number corresponding to a character in a string array.
I am having trouble getting the character string array to show its values at the values for the indexes of another array. It works when I reference individual indexes of an array (e.g. message = char_array(decimal(1)) returns a value of k), but not when I try to reference the whole column or the whole array of decimal (e.g. message = char_array(decimal(:,:)) and message = char_array(decimal(:,1)) do not work and give the same error). I know this can accomplished with for loops, but we have been instructed not to use those yet.
Is there some other trick to getting the entire message to display at once? This needs to work in such a way that it could be used for thousands of letters at once with one simple line of code.
The code so far:
clear;clc
% Array of characters (first value is a space, last is ! which denotes the end of the message)
char_array = ' abcdefghijklmnopqrstuvwxyz!';
% The secret message in binary
secret = [0 1 1 0 0;
0 0 0 0 1;
1 0 0 0 0;
0 1 0 0 0;
1 0 0 1 0;
0 1 1 1 1;
0 0 0 0 1;
0 1 0 0 1;
0 0 1 1 1;
0 0 0 0 0;
1 0 0 1 0;
0 1 1 1 1;
0 0 0 1 1;
0 1 0 1 1;
1 0 0 1 1;
1 1 0 1 1];
% Assigns a decimal value to each column to help convert binary values to decimal
conv = [16 8 4 2 1];
% Converts binary numbers in each column of secret message to a decimal value
convert = [secret(:,1)*conv(1), secret(:,2)*conv(2), secret(:,3)*conv(3), secret(:,4)*conv(4), secret(:,5)*conv(5)];
% Converts unadded decimal values into columns to prepare for addition
binary = convert';
% Adds numbers in each column, then transposes it to a single column of decimal values representing values of each row of original message
decimal = sum(binary)';
% Should display character from character array for each index value of "decimal", but returns "Subscript indices must either be real positive integers or logicals." error each time. Works for individual entries such as decimal(1), though.
message = char_array(decimal(:,:))
My code was fine. The problem was that one of the values from decimal was zero, and you cannot reference an array value of zero! :)
If you add 1 to each value of decimal to compensate for the space at the beginning of the character array, it yields the message correctly.
Towards the bottom of the code:
% Adds numbers in each column, then transposes it to a single column of decimal values representing values of each row of original message
decimal = sum(binary)';
% Adds 1 to each value of "decimal"
decimal2 = decimal+1;
message = char_array(decimal2(:,:))
Which decodes the message properly. (And yes, Laphroaig does indeed rock!)
Fast matrix multiplication based technique for a generic compact one-liner solution -
message = char_array(secret*(2.^(size(secret,2)-1:-1:0)')+1)

C GPIO hex numbering

I have been given the following bit of code as an example:
Make port 0 bits 0-2 outputs, other to be inputs.
FIO0DIR = 0x00000007;
Set P0.0, P0.1, P0.2 all low (0)
FIO0CLR = 0x00000007;
I have been told that the port has 31 LED's attached to it. I cant understand why, to enable the first 3 outputs, it is 0x00000007 not 0x00000003?
These GPIO config registers are bitmaps.
Use your Windows calculator to convert the hex to binary:
0x00000007 = 111, or with 32 bits - 00000000000000000000000000000111 // three outputs
0x00000003 = 11, or with 32 bits - 00000000000000000000000000000011 // only two outputs
Because the value you write to the register is a binary bit-mask, with a bit being one meaning "this is an output". You don't write the "number of outputs I'd like to have", you are setting 8 individual flags at the same time.
The number 7 in binary is 00000111, so it has the lower-most three bits set to 1, which here seems to mean "this is an output". The decimal value 3, on the other hand, is just 00000011 in binary, thus only having two bits set to 1, which clearly is one too few.
Bits are indexed from the right, starting at 0. The decimal value of bit number n is 2n. The decimal value of a binary number with more than one bit set is simply the sum of all the values of all the set bits.
So, for instance, the decimal value of the number with bits 0, 1 and 2 set is 20 + 21 + 22 = 1 + 2 + 4 = 7.
Here is an awesome ASCII table showing the 8 bits of a byte and their individual values:
+---+---+---+---+---+---+---+---+
index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---+---+---+---+---+---+---+---+
value |128| 64| 32| 16| 8 | 4 | 2 | 1 |
+---+---+---+---+---+---+---+---+

How to replace bits in a bitfield without affecting other bits using C

I wanted to replace bit/bits (more than one) in a 32/64 bit data field without affecting other bits. Say for example:
I have a 64-bit register where bits 5 and 6 can take values 0, 1, 2, and 3.
5:6
---
0 0
0 1
1 0
1 1
Now, when I read the register, I get say value 0x146 (0001 0 10 0 0110). Now I want to change the value at bit position 5 and 6 to 01. (Right now it is 10, which is 2 in decimal, and I want to replace it to 1 e 01) without other bits getting affected and write back the register with only bits 5 and 6 modified (so it becomes 126 after changing).
I tried doing this:
reg_data = 0x146
reg_data |= 1 << shift // In this case, 'shift' is 5
If I do this, the value at bit positions 5 and 6 will become 11 (0x3), not 01 (0x1) which I wanted.
How do I go about doing read, modify, and write?
How do I replace only certain bit/bits in a 32/64 bit fields without affecting the whole data of the field using C?
Setting a bit is okay, but more than one bit, I am finding it little difficult.
Use a bitmask. It is sort of like:
new_value = 0, 1, 2 or 3 // (this is the value you will set in)
bit_mask = (3<<5) // (mask of the bits you want to set)
reg_data = (reg_data & (~bit_mask)) | (new_value<<5)
This preserves the old bits and OR's in the new ones.
reg_data &= ~( (1 << shift1) | (1 << shift2) );
reg_data |= ( (1 << shift1) | (1 << shift2) );
The first line clears the two bits at (shift1, shift2) and the second line sets them.
Here is a generic process which acts on a long array, considering it a long bitfield, and addresses each bit position individually:
#define set_bit(arr,x) ((arr[(x)>>3]) |= (0x01 << ((x) & 0x07)))
#define clear_bit(arr,x) (arr[(x)>>3] &= ~(0x01 << ((x) & 0x07)))
#define get_bit(arr,x) (((arr[(x)>>3]) & (0x01 << ((x) & 0x07))) != 0)
It simply takes the index, uses the lower three bits of the index to identify eight different bit positions inside each location of the char array, and the upper remainder bits addresses in which array location does the bit denoted by x occur.
To set a bit, you need to OR the target word with another word with 1 in that specific bit position and 0 in all other with the the target. All 0's in the other positions ensure that the existing 1's in the target are as it is during OR, and the 1 in the specific positions ensures that the target gets the 1 in that position. If we have mask = 0x02 = 00000010 (1 byte) then we can OR this to any word to set that bit position:
target = 1 0 1 1 0 1 0 0
OR + + + + + + + +
mask 0 0 0 0 0 0 1 0
---------------
answer 1 0 1 1 0 1 1 0
To clear a bit, you need to AND the target word with another word with 0 in that specific bit position and 1 in all. All 1's in all other bit positions ensure that during AND the target preserves its 0's and 1's as they were in those locations, and a 0 in the bit position to be cleared would also set that bit position 0 in the target word. If we have the same mask = 0x02, then we can prepare this mask for clearing by ~mask:
mask = 0 0 0 0 0 0 1 0
~mask = 1 1 1 1 1 1 0 1
AND . . . . . . . .
target 1 0 1 1 0 1 1 0
---------------
answer 1 0 1 1 0 1 0 0
Apply a mask against the bitfield to maintain the bits that you do not want to change. This will also clear out the bits that you will be changing.
Ensure that you have a bitfield that contains only the bits that you want to set/clear.
Either use the or operator to "or" the two bitfields, or just simply add them.
For instance, if you wanted to only change bits 2 thru 5 based on input of 0 thru 15.
byte newVal = (byte)value & 0x0F;
newVal = (byte)value << 2;
oldVal = oldVal & 0xC3;
oldVal = oldval + newVal;
The question was about how to implement it in C, but as all searches for "replace bits" lead to here, I will supply my implementation in VB.NET.
It has been unit test tested. For those who are wondering what the ToBinaryString extension looks like: Convert.ToString(value,2)
''' <summary>
''' Replace the bits in the enumValue with the bits in the bits parameter, starting from the position that corresponds to 2 to the power of the position parameter.
''' </summary>
''' <param name="enumValue">The integer value to place the bits in.</param>
''' <param name="bits">The bits to place. It must be smaller or equal to 2 to the power of the position parameter.</param>
'''<param name="length">The number of bits that the bits should replace.</param>
''' <param name="position">The exponent of 2 where the bits must be placed.</param>
''' <returns></returns>
''' <remarks></remarks>'
<Extension>
Public Function PlaceBits(enumValue As Integer, bits As Integer, length As Integer, position As Integer) As Integer
If position > 31 Then
Throw New ArgumentOutOfRangeException(String.Format("The position {0} is out of range for a 32 bit integer.",
position))
End If
Dim positionToPlace = 2 << position
If bits > positionToPlace Then
Throw New ArgumentOutOfRangeException(String.Format("The bits {0} must be smaler than or equal to {1}.",
bits, positionToPlace))
End If
'Create a bitmask (a series of ones for the bits to retain and a series of zeroes for bits to discard).'
Dim mask As Integer = (1 << length) - 1
'Use for debugging.'
'Dim maskAsBinaryString = mask.ToBinaryString'
'Shift the mask to left to the desired position'
Dim leftShift = position - length + 1
mask <<= leftShift
'Use for debugging.'
'Dim shiftedMaskAsBinaryString = mask.ToBinaryString'
'Shift the bits to left to the desired position.'
Dim shiftedBits = bits << leftShift
'Use for debugging.'
'Dim shiftedBitsAsBinaryString = shiftedBits.ToBinaryString'
'First clear (And Not) the bits to replace, then set (Or) them.'
Dim result = (enumValue And Not mask) Or shiftedBits
'Use for debugging.'
'Dim resultAsBinaryString = result.ToBinaryString'
Return result
End Function
You'll need to do that one bit at a time. Use the or, like you're currently doing, to set a bit to one, and use the following to set something to 0:
reg_data &= ~ (1 << shift)
You can use this dynamic logic for any number of bit and in any bit field.
Basically, you have three parts in a bit sequence of number -
MSB_SIDE | CHANGED_PART | LSB_SIDE
The CHANGED_PART can be moved up to the extreme MSB or LSB side.
The steps to replace a number of bit(s) are as follows -
Take only the MSB_SIDE part and replace all the remaining bits with 0.
Update the new bit sequence by adding your desired bit sequence in particular position.
Update the entire bit sequence with LSB_SIDE of the original bit sequence.
org_no = 0x53513C;
upd_no = 0x333;
start_pos = 0x6, bit_len = 0xA;
temp_no = 0x0;
temp_no = org_no & (0xFFFFFFFF << (bit_len + start_pos)); // This is step 1
temp_no |= upd_no << start_pos; // This is step 2
org_no = temp_no | (org_no & ~(0xFFFFFFFF << start_pos)); // This is step 3`
Note: The masking with 0xFFFFFFFF is considered as 32 bit. You can change accordingly with your requirement.

Resources