I have an OEM dashcam that saves videos in .MOV format. I want to extract the embedded gps data from the video files programmatically. Upon opening the .mov file in a hex editor I found data packets with freeGPS
headers and I can confirm that for my 7 seconds sample video there are 7 packets so I know this where the gps data comes from.
I already found the date and time but I got stuck with converting the hex values to latitude longitude. Below are the hex values and their equivalent coordinates when extracted using Registrator Viewer.
273108AC1C7996404E,0D022B873EA3C74045 - 14.637967,121.041475 516B9A771C7996404E,0D022B873EA3C74045 - 14.637963,121.041475 B9FC87F41B7996404E,52499D803EA3C74045 - 14.637955,121.041472 B9FC87F41B7996404E,52499D803EA3C74045 - 14.637955,121.041472 B459F5B91A7996404E,C442AD693EA3C74045 - 14.637935,121.041460 1DEBE2361A7996404E,ACADD85F3EA3C74045 - 14.637927,121.041455 08CE19511A7996404E,4FD1915C3EA3C74045 - 14.637928,121.041453
The bolded bytes directly translates to @N and @E so I think they are not part of the conversion. I already tried the below answers but I did not succeed in getting the correct coordinates.
How to convert GPS Longitude and latitude from hex
How to convert my binary (hex) data to latitude and longitude?
I already sent an email to the dashcam provider asking for their protocol documentation but it does not look like they have one since they sent Registrator Viewer when I asked for their own video player.
I will also include the first freeGPS
packet in case I am looking at the wrong place.
00 00 80 00 66 72 65 65 47 50 53 20 98 00 00 00 78 2E 78 78 00 00 00 00 00 00 00 00 00 00 00 00 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 27 00 00 00 41 00 00 00 27 31 08 AC 1C 79 96 40 4E 00 00 00 00 00 00 00 0D 02 2B 87 3E A3 C7 40 45 00 00 00 00 00 00 00 8F C2 F5 28 5C 8F E2 3F 48 E1 7A 14 AE 07 68 40 11 00 00 00 06 00 00 00 14 00 00 00 76 00 00 00 88 00 00 00 DE 00 00 00 4D 00 00 00 49 00 00 00 4F 00 00 00 2D 00 00 00 2B 00 00 00 00 00 00 00
Data in bold extracted in order : freeGPS, time, latitude@N?, longitude@E?, date
I can confirm that time and date are correct. The speed is supposed to be 1km/h but I can't also find that.
Thanks in advance for those who can help.
EDIT: Here is the link for the test video. Test Video
Not sure how helpful this is but if you convert the hex string (including the 0x40 at the end) into a little endian IEEE-754 double precision real, then you get approximately 100X the lat/lon.
In C, I did something like
printf( "%f\n", *(double*)"\x27\x31\x08\xAC\x1C\x79\x96\x40" );
printf( "%f\n", *(double*)"\x0D\x02\x2B\x87\x3E\xA3\xC7\x40" );
and got out
1438.278000
12102.488500
If the 1438.278
is interpreted as 14 degrees 38.278 minutes then you get a decimal value of 14.6379666666666667
. If 12102.4885
is interpreted as 121 degrees and 2.4885 minutes, the decimal equivalent is 121.041475
.
Some example C code to do this
#include<stdio.h>
double convert( double input ) {
int i = input/100;
return ( input - i*100 ) / 60 + i;
}
int main(){
printf( "%f\n", convert( *(double*)"\x27\x31\x08\xAC\x1C\x79\x96\x40" ) );
printf( "%f\n", convert( *(double*)"\x0D\x02\x2B\x87\x3E\xA3\xC7\x40" ) );
}
Found this explanation of GPS data format, worked for me.
public static ViofoGpsPoint Parse(uint offset, uint size, byte[] file)
{
byte[] data = new byte[size];
Array.Copy(file, offset, data, 0, size);
uint pos = 0;
uint size1 = Box.ReadUintBE(data, pos); pos += 4;
string type = Encoding.ASCII.GetString(data, (int)pos, 4); pos += 4;
string magic = Encoding.ASCII.GetString(data, (int)pos, 4); pos += 4;
if (size != size1 || type != "free" || magic != "GPS ")
return null;
ViofoGpsPoint gps = new ViofoGpsPoint();
//# checking for weird Azdome 0xAA XOR "encrypted" GPS data.
//This portion is a quick fix.
uint payload_size = 254;
if (data[pos] == 0x05)
{
if (size < 254)
payload_size = size;
byte[] payload = new byte[payload_size];
pos += 6; //???
for (int i = 0; i < payload_size; i++)
{
payload[i] = (byte)(file[pos + i] ^ 0xAA);
}
}
else if ((char)data[pos] == 'L')
{
const uint OFFSET_V2 = 48, OFFSET_V1 = 16;
pos = OFFSET_V2;
//# Datetime data
int hour = (int)Box.ReadUintLE(data, pos); pos += 4;
int minute = (int)Box.ReadUintLE(data, pos); pos += 4;
int second = (int)Box.ReadUintLE(data, pos); pos += 4;
int year = (int)Box.ReadUintLE(data, pos); pos += 4;
int month = (int)Box.ReadUintLE(data, pos); pos += 4;
int day = (int)Box.ReadUintLE(data, pos); pos += 4;
try { gps.Date = new DateTime(2000 + year, month, day, hour, minute, second); }
catch (Exception err) { Debug.WriteLine(err.ToString()); return null; }
//# Coordinate data
char active = (char)data[pos]; pos++;
gps.IsActive = (active == 'A');
gps.Latitude_hemisphere = (char)data[pos]; pos++;
gps.Longtitude_hemisphere = (char)data[pos]; pos++;
gps.Unknown = data[pos]; pos++;
float lat = Box.ReadFloatLE(data, pos); pos += 4;
gps.Latitude = FixCoordinate(lat, gps.Latitude_hemisphere);
float lon = Box.ReadFloatLE(data, pos); pos += 4;
gps.Longtitude = FixCoordinate(lon, gps.Longtitude_hemisphere);
gps.Speed = Box.ReadFloatLE(data, pos); pos += 4;
gps.Bearing = Box.ReadFloatLE(data, pos); pos += 4;
return gps;
}
return null;
}
/// <summary>
/// # Novatek stores coordinates in odd DDDmm.mmmm format
/// </summary>
/// <param name="coord"></param>
/// <param name="hemisphere"></param>
/// <returns></returns>
private static double FixCoordinate(double coord, char hemisphere)
{
double minutes = coord % 100.0;
double degrees = coord - minutes;
double coordinate = degrees / 100.0 + (minutes / 60.0);
if (hemisphere == 'S' || hemisphere == 'W')
return -1 * (coordinate);
else
return (coordinate);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With