Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert hex to latitude longitude from dashcam .MOV file

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

like image 457
Rio Marie A. Callos Avatar asked Aug 03 '17 01:08

Rio Marie A. Callos


2 Answers

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

Update thanks to @TDG

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" ) );
}
like image 153
ceilingcat Avatar answered Sep 22 '22 05:09

ceilingcat


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);
    }
like image 26
Mark Zbarjevsky Avatar answered Sep 18 '22 05:09

Mark Zbarjevsky