Tesla Dashcam SEI Decoder
This tool walks you through exactly how telemetry data is hidden inside a Tesla dashcam video file โ and how a forensic examiner reads it. It uses one verified reference MP4 file with confirmed, independently decoded values.
Every byte shown here is from a real Tesla dashcam clip. Every value has been verified against Tesla's own SEI Explorer tool. This is not a simulation โ it is a learning tool built on real forensic data.
Decoded and built by Noel Lowdon, Harper Shaw Investigation Consultants Ltd in collaboration with Claude (Anthropic) โ an AI assistant used to perform, verify, and explain the decode process and build this interactive tool.
This tool has been made freely available to support learning and professional development within the vehicle forensics community. It is built on a single verified reference file and is intended to illustrate how Tesla dashcam SEI telemetry is structured and decoded โ not to serve as a substitute for validated casework tools or peer-reviewed methodology.
Every effort has been made to ensure the content is accurate. All decoded values have been independently verified against the Tesla SEI Explorer. However, vehicle systems and file formats can vary, and this tool should be treated as educational material rather than definitive technical guidance.
If you spot an inaccuracy, have a correction, or want to contribute to improving the content, please contact training@harpershaw.co.uk โ feedback from the community is genuinely welcomed and helps keep this resource accurate for everyone.
Road to USB
This section gives a broad overview of the recording chain. Each step will be covered in greater depth as this resource develops. Where things are not fully confirmed or tested that is stated clearly.
The dashcam lens focuses light onto an image sensor which converts it to electrical signals. The processor turns those signals into a video frame. This happens repeatedly while the vehicle is moving and recording is active.
Raw video data is far too large to store directly. The processor compresses it using the H.264 standard โ a published international video compression format. The compressed output is structured into NAL units โ discrete chunks of data each with a one byte header identifying what type of data it contains. The compression is lossy, meaning some fine visual detail is discarded, but the vehicle telemetry embedded alongside it is not compressed in the same way.
At some point during or around each frame capture the dashcam system reads vehicle data from the vehicle's systems โ speed, steering angle, GPS coordinates, gear state, brake status, indicator status, and autopilot state among others.
The vehicle data is encoded using Protocol Buffers โ a compact binary format published by Google โ and wrapped in an SEI unit. SEI stands for Supplemental Enhancement Information and is a reserved H.264 unit type intended for vendor defined custom data. A UUID identifier is included at the start of each SEI unit so tools that know what to look for can find and decode it. In the reference file there is one SEI unit for each of the 2,174 frames โ these are the bytes you see in the Hex Map tab.
The NAL units are written into the mdat block of an MP4 container frame by frame as recording continues. The file grows continuously. The moov index โ the file's table of contents containing timestamps and frame locations โ is only written when recording stops cleanly. Until then the file exists as raw data without a navigable index.
If power to the dashcam is cut abruptly the moov index may never be written. Without it a standard video player cannot read the file and reports it as unreadable or corrupt. Whether the frame data and telemetry recorded up to that point is recoverable depends entirely on the physical condition of the drive and the specific circumstances of the power loss.
The data is stored in flash memory โ a type of storage with no moving parts that retains data without power. Flash is not vulnerable to physical shock in the same way a spinning hard drive is. However physical damage to the drive from a collision may still prevent access even if the underlying data is intact. A physically damaged drive is worth specialist examination. Flash memory storage will be covered in more depth as this resource develops.
Now you know how the data gets onto the drive the following sections explain what it looks like once it is there. What is hex? explains the notation used to read binary data. MP4 container shows the file structure. NAL and SEI explains how the telemetry is embedded. IEEE 754 shows how bytes become decimal numbers. The Hex Map tab shows you those bytes from the actual reference file.
Binary
The foundation everything else in this tool is built on. Understanding binary makes hex, IEEE 754, and protobuf readable rather than mysterious.
A transistor is a tiny switch. It is either conducting electricity or it is not. Modern processors contain billions of them. Each one holds a single 1 or 0. Stack eight of them together and you have a byte โ a number from 0 to 255. That is the entire system.
Imagine a row of four light switches. Each switch is either on (1) or off (0). But instead of each switch controlling a single light, each switch is worth a value โ and the values double from right to left. The rightmost switch is worth 1. The next is worth 2. The next 4. The leftmost 8. To store a number you flip whichever switches add up to it and leave the rest off.
1000 in binary, read it right to left: the 0s in positions 1, 2 and 4 are off, the 1 in position 8 is on. Result: 8.| Decimal | 8 | 4 | 2 | 1 | Binary | How you get there |
|---|
10000010 โ appears in the vehicle speed decode. It is the exponent byte. And 130 in decimal, once the IEEE 754 bias is applied, gives you the scale of the speed value. Every byte in the hex map is built exactly this way.Every byte you see in the Hex Map tab is eight of these switches. The colour-coded bytes for vehicle speed, GPS coordinates, steering angle โ all of them are groups of bytes, each byte a pattern of 1s and 0s encoding a value. The next section โ IEEE 754 โ shows exactly how four of those bytes combine to store a decimal number like 12.511 m/s. Now that you know what the bytes are made of, the decode makes sense from the ground up.
What is hexadecimal?
The Binary section explains that computers store everything as patterns of 1s and 0s โ eight per byte. The problem is that long strings of binary are almost impossible for humans to read and work with. Hexadecimal โ hex for short โ is the solution. It is a compact, human readable notation for binary that forensic tools use to display the contents of any file.
Hex uses sixteen symbols โ the digits 0 to 9 and the letters A to F. Each hex character represents exactly four binary digits. Two hex characters represent exactly eight binary digits โ one complete byte. So every byte in a file is always exactly two hex characters. No more, no fewer.
Binary is like reading every letter of every word spelled out individually โ every sentence would take a page. Hex is like reading the words themselves. The information is identical but one is practical and one is not. When a hex editor shows you 82 2D 48 41 you are looking at exactly four bytes. If it showed you the binary equivalent โ 10000010 00101101 01001000 01000001 โ you would count mistakes. Hex is not a different thing from binary. It is binary written in a form humans can actually work with.
In a hex editor like 010 Editor, every byte in every file is visible as two hex characters. A video file, a Word document, a dashcam clip โ all of them are ultimately a sequence of bytes. A hex editor shows you those bytes directly without any software interpretation. What you see is what is physically stored.
A hex byte has two characters. The left character sits in the sixteens column โ multiply it by 16. The right character sits in the ones column โ just add it as is. That is all there is to it.
82 โ left digit 8, right digit 2 โ (8 ร 16) + 2 โ 128 + 2 โ 130
FF โ left digit F (= 15), right digit F (= 15) โ (15 ร 16) + 15 โ 240 + 15 โ 255
41 โ left digit 4, right digit 1 โ (4 ร 16) + 1 โ 64 + 1 โ 65 (the letter A in ASCII)
It works exactly like decimal โ in the number 82 the 8 means 8 ร 10 = 80 and the 2 is just 2. Hex works identically but the left column is worth 16 instead of 10. That is the only difference.
00 = 0 ยท 0A = 10 ยท 0C = 12 ยท 10 = 16 ยท 41 = 65 ยท 59 = 89 ยท 82 = 130 ยท FF = 255
Every unit of digital storage is built from the same foundation โ the bit. The hierarchy below shows how units stack up, and where each one appears in this tool.
| โHiยทLoโ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F |
| 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 1A | 1B | 1C | 1D | 1E | 1F |
| 2 | 20 | 21! | 22" | 23# | 24$ | 25% | 26& | 27' | 28( | 29) | 2A* | 2B+ | 2C, | 2D- | 2E. | 2F/ |
| 3 | 300 | 311 | 322 | 333 | 344 | 355 | 366 | 377 | 388 | 399 | 3A: | 3B; | 3C< | 3D= | 3E> | 3F? |
| 4 | 40@ | 41A | 42B | 43C | 44D | 45E | 46F | 47G | 48H | 49I | 4AJ | 4BK | 4CL | 4DM | 4EN | 4FO |
| 5 | 50P | 51Q | 52R | 53S | 54T | 55U | 56V | 57W | 58X | 59Y | 5AZ | 5B[ | 5C\ | 5D] | 5E^ | 5F_ |
| 6 | 60` | 61a | 62b | 63c | 64d | 65e | 66f | 67g | 68h | 69i | 6Aj | 6Bk | 6Cl | 6Dm | 6En | 6Fo |
| 7 | 70p | 71q | 72r | 73s | 74t | 75u | 76v | 77w | 78x | 79y | 7Az | 7B{ | 7C| | 7D} | 7E~ | 7F |
| 8 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 8A | 8B | 8C | 8D | 8E | 8F |
| 9 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 9A | 9B | 9C | 9D | 9E | 9F |
| A | A0 | A1 | A2 | A3 | A4 | A5 | A6 | A7 | A8 | A9 | AA | AB | AC | AD | AE | AF |
| B | B0 | B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8 | B9 | BA | BB | BC | BD | BE | BF |
| C | C0 | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | CA | CB | CC | CD | CE | CF |
| D | D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 | D9 | DA | DB | DC | DD | DE | DF |
| E | E0 | E1 | E2 | E3 | E4 | E5 | E6 | E7 | E8 | E9 | EA | EB | EC | ED | EE | EF |
| F | F0 | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | FA | FB | FC | FD | FE | FF |
| Unit | Equal to | Example |
|---|---|---|
| Kilobyte (KB) | 1,024 bytes | The moov index in the reference file is 273 KB |
| Megabyte (MB) | 1,024 KB | The mdat block in the reference file is 79 MB |
| Gigabyte (GB) | 1,024 MB | Tesla vehicles are typically supplied with a 128 GB USB drive |
| Terabyte (TB) | 1,024 GB | โ |
Because computers count in base 2. The nearest power of 2 to 1,000 is 2ยนโฐ = 1,024. Storage sizes follow powers of 2 โ the same reason bytes have 8 bits and not 10. This also explains why a drive advertised as 128 GB by a manufacturer shows as approximately 119 GB in an operating system โ manufacturers count in decimal (128,000,000,000 bytes) while operating systems report in binary (128 ร 2ยณโฐ bytes). Same drive, different counting system.
Bitmask
When data is stored in binary, individual bytes often carry more than one piece of information packed together. Rather than waste a whole byte on a single small value, engineers pack multiple values into the same byte โ each occupying a different group of bits.
Imagine a traffic light junction controller that needs to send the status of four sets of lights โ North, South, East, West โ to a monitoring system. Rather than sending four separate signals it packs all four into one byte. Each light gets two bits โ enough to represent red, amber, green, or off. One byte, four junctions, eight bits, all sent in a single transmission.
The monitoring system receives the byte and needs to extract just the North light status. It does not care about the other six bits. So it applies a mask that covers exactly those two bit positions and zeros out everything else. What comes out is just the North status, isolated cleanly from the rest.
This is exactly what happens in this file. The NAL header byte carries both the NAL reference indicator and the NAL unit type packed together. The protobuf tag byte carries both the field number and the wire type. Bitmasking is how you pull them apart.
A bitmask uses the AND (&) operation. AND compares two bits at a time and follows one rule โ the only way to get a 1 out is if both inputs are 1. Everything else produces 0.
| Input A | Input B | Result |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 0 | 0 |
Set 1 in the mask where you want to keep a bit. Set 0 where you want to discard it. ANDing with 0 always produces 0 regardless of the input โ the bit is wiped. ANDing with 1 always produces whatever the input was โ the bit is kept.
| Mask | Binary | Keeps | Used for |
|---|---|---|---|
0x1F | 00011111 | Bottom 5 bits | NAL unit type from NAL header byte |
0x07 | 00000111 | Bottom 3 bits | Wire type from protobuf tag byte |
The NAL header byte 0x67 packs three values together. The bottom 5 bits carry the NAL unit type. To extract them apply the mask 0x1F (00011111):
Result: 00000111 = 7 = NAL type 7 (SPS). The top three bits (011) were discarded. The bottom five survived intact.
The protobuf tag byte 0x25 packs the field number (top 5 bits) and wire type (bottom 3 bits) together. To extract the wire type apply the mask 0x07 (00000111):
Result: 00000101 = 5 = wire type 5 (32-bit fixed). Four data bytes follow.
Once the wire type bits are extracted by masking, the field number sits in the top 5 bits of the original byte. To read it as a plain number, shift all the bits right by 3 positions โ this discards the bottom three bits and moves the field number down to where it can be read directly.
0x25 = 00100101 โ shift right 3 โ 00000100 = 4 = field 4 (vehicle speed)
Mask for wire type + shift for field number. Together they fully decode any protobuf tag byte. This is covered in detail in the Protobuf section.
Bitmasking appears in two places in this tool. The NAL & SEI section uses 0x1F to extract the NAL unit type from each header byte. The Protobuf section uses 0x07 to extract the wire type from each tag byte. Both stepper walkthroughs show the AND operation applied to the actual bytes from the reference file.
The MP4 container format
An MP4 file is a container โ a structured wrapper holding multiple types of data in labelled sections called boxes. Every box starts with its size in bytes, then a four-character name. Boxes nest inside each other like Russian dolls.
Imagine a removal van. Open the back: labelled boxes. Open a box: sealed envelopes. Open an envelope: a letter. A video file works the same way โ nested containers. The video on screen is just one box. Inside the others is data that never appears during normal playback.
The reference file has four top-level boxes:
| Box | Offset | Size | What it contains |
|---|---|---|---|
| ftyp | 0x00 | 32 B | File Type โ declares this is MP4. Always first. |
| free | 0x20 | 8 B | Empty padding. Nothing significant. |
| mdat | 0x28 | 79 MB | Media Data โ all video frames and embedded SEI telemetry. |
| moov | end | 273 KB | Movie index โ timestamps, frame locations. Written last when recording stops cleanly. |
Tesla records continuously into mdat and only writes moov when recording stops cleanly. Power cut at impact โ moov may never be written โ file appears unreadable in a standard video player. The raw mdat block and the SEI telemetry units inside it should theoretically be present on the drive โ however recoverability depends on the physical condition of the device and the precise point of power loss. Absence of a readable file is not necessarily absence of data.
NAL units and SEI telemetry
Inside mdat the video stream is divided into chunks called NAL units (Network Abstraction Layer). Each starts with a one-byte header identifying its type. In the avcC format used here, each NAL unit is also preceded by a 4-byte length field.
NAL units are standardised shipping containers. The one-byte header is the label โ Type 5 is a video frame, Type 7 is config paperwork, Type 6 is a reserved container for custom cargo. The manufacturer fills Type 6 with telemetry data, one per frame, attached to every frame of dashcam footage unseen by normal video players.
| Type byte | Name | Contents |
|---|---|---|
| 0x07 | SPS | Codec config โ resolution, frame rate, profile. Written once at stream start. |
| 0x08 | PPS | Picture config. Housekeeping, not video content. |
| 0x06 | SEI | Supplemental Enhancement Information โ vendor custom data. One per frame here. |
| 0x05 | IDR Frame | A complete keyframe โ the actual video content. |
Each SEI unit contains a 16-byte UUID starting 42 42 42 69, followed by 66 bytes of protobuf-encoded telemetry. The SEI Explorer tool recognises this UUID and decodes it across all 2,174 frames.
Searching the 79MB file for 42 42 42 69 08 returned 2,175 hits โ 2,174 genuine SEI units plus one coincidental byte match in the compressed video data. The genuine count matches MediaInfo's frame count exactly.
IEEE 754 floating point
IEEE 754 is the published international standard for storing decimal numbers in binary โ used in virtually every processor, language, and file format on earth. Used here for vehicle speed, steering angle, accelerator pedal position, and GPS coordinates.
This section assumes familiarity with binary โ why computers use 0s and 1s, how bit positions work, and why each column doubles in value. If any of that is unfamiliar, the Binary section covers it in full with interactive examples. This section picks up from there.
82 2D 48 41. The file uses little endian byte order โ least significant byte first. Reverse them before applying IEEE 754: 41 48 2D 82.Little-endian stores the smallest part of a number first โ like writing a date as day/month/year instead of year/month/day. IEEE 754 expects the bytes in big endian order (most significant first). Always reverse multi-byte values from this file before decoding.
41 โ 01000001, 48 โ 01001000, 2D โ 00101101, 82 โ 10000010. Concatenate to get 32 bits.The 8 exponent bits need to represent both very large and very small numbers โ meaning positive and negative exponents. But 8 bits can only store 0 to 255, with no negatives. The solution is a bias of 127 โ like a thermometer where 0ยฐC is physically marked at position 127. Any stored value above 127 is a positive exponent. Below 127 is negative. Always subtract 127 to find the actual exponent.
10000010. Each bit position is worth a power of 2. A 1 bit contributes its value. A 0 bit contributes nothing.| Position | Bit | Worth | Contribution |
|---|---|---|---|
| 7 (leftmost) | 1 | 2โท = 128 | 128 |
| 6 | 0 | 2โถ = 64 | 0 |
| 5 | 0 | 2โต = 32 | 0 |
| 4 | 0 | 2โด = 16 | 0 |
| 3 | 0 | 2ยณ = 8 | 0 |
| 2 | 0 | 2ยฒ = 4 | 0 |
| 1 | 1 | 2ยน = 2 | 2 |
| 0 (rightmost) | 0 | 2โฐ = 1 | 0 |
| Total (stored exponent) | 128 + 2 = 130 | ||
| Subtract bias (127) | 130 โ 127 = 3 | ||
| Why 127? It sits exactly in the middle of 0โ255 โ the range 8 bits can hold. Values above 127 give a positive exponent. Values below 127 give a negative exponent. 127 was deliberately chosen so the exponent can represent both very large and very small numbers using the same 8 bits. Not an arbitrary number โ the midpoint of the range. | |||
| Actual exponent | 3 โ multiply by 2ยณ = 8 | ||
The exponent 3 means multiply 2 by itself 3 times: 2 ร 2 ร 2 = 8. That is all it means. If the exponent were 10 it would be ten 2s: 2 ร 2 ร 2 ร 2 ร 2 ร 2 ร 2 ร 2 ร 2 ร 2 = 1,024. If the exponent were 1 it would be just one 2 = 2. If it were 0 it would be zero 2s = 1 (anything to the power of zero is always 1). Negative exponents go the other way โ instead of multiplying by 2 you divide by 2. Exponent โ1 = 1 รท 2 = 0.5. Exponent โ3 = 1 รท 2 รท 2 รท 2 = 0.125. The reason it is always 2 and never 3 or 10 is exactly what the base 2 section above explains โ computers only understand 2 states, so the only scale that makes sense is powers of 2.
It is a Latin word meaning makeweight โ the small extra weight a trader added to a scale to bring it to an exact balance after the main weights were set. The mathematician Henry Briggs used it in the 17th century for logarithm tables. The mantissa was the fractional part that gave you the precise digits after the exponent set the rough scale. IEEE 754 uses it for exactly the same reason โ the exponent sets the scale, the mantissa fine-tunes it to the exact value. Some textbooks use the word significand instead, which is more descriptive but less commonly used.
Every row in the table below has a bit value of 1 or 0.
Bit = 1 โ Add that row's decimal value to the running total.
Bit = 0 โ Skip it. It contributes nothing. The running total does not change.
The power notation (2โปยน, 2โปยฒ, 2โปยณโฆ) and the fraction notation (1/2, 1/4, 1/8โฆ) are exactly the same value written two different ways. Both columns are shown throughout so you can see they are identical.
10010000010110110000100. Every position shown:| Position | Bit | Power | Same as fraction | Decimal value | Action (1=add, 0=skip) | Running total |
|---|---|---|---|---|---|---|
| โ | โ | โ | โ | 1.000000 | Implied 1 โ always add | 1.000000 |
| 1 | 1 | 2โปยน | 1/2 | 0.500000 | โ Add | 1.500000 |
| 2 | 0 | 2โปยฒ | 1/4 | 0.250000 | โ Skip | 1.500000 |
| 3 | 0 | 2โปยณ | 1/8 | 0.125000 | โ Skip | 1.500000 |
| 4 | 1 | 2โปโด | 1/16 | 0.062500 | โ Add | 1.562500 |
| 5 | 0 | 2โปโต | 1/32 | 0.031250 | โ Skip | 1.562500 |
| 6 | 0 | 2โปโถ | 1/64 | 0.015625 | โ Skip | 1.562500 |
| 7 | 0 | 2โปโท | 1/128 | 0.007813 | โ Skip | 1.562500 |
| 8 | 0 | 2โปโธ | 1/256 | 0.003906 | โ Skip | 1.562500 |
| 9 | 0 | 2โปโน | 1/512 | 0.001953 | โ Skip | 1.562500 |
| 10 | 1 | 2โปยนโฐ | 1/1,024 | 0.000977 | โ Add | 1.563477 |
| 11 | 0 | 2โปยนยน | 1/2,048 | 0.000488 | โ Skip | 1.563477 |
| 12 | 1 | 2โปยนยฒ | 1/4,096 | 0.000244 | โ Add | 1.563721 |
| 13 | 1 | 2โปยนยณ | 1/8,192 | 0.000122 | โ Add | 1.563843 |
| 14 | 0 | 2โปยนโด | 1/16,384 | 0.000061 | โ Skip | 1.563843 |
| 15 | 1 | 2โปยนโต | 1/32,768 | 0.000031 | โ Add | 1.563873 |
| 16 | 1 | 2โปยนโถ | 1/65,536 | 0.000015 | โ Add | 1.563889 |
| 17 | 0 | 2โปยนโท | 1/131,072 | 0.000008 | โ Skip | 1.563889 |
| 18 | 0 | 2โปยนโธ | 1/262,144 | 0.000004 | โ Skip | 1.563889 |
| 19 | 0 | 2โปยนโน | 1/524,288 | 0.000002 | โ Skip | 1.563889 |
| 20 | 0 | 2โปยฒโฐ | 1/1,048,576 | 0.000001 | โ Skip | 1.563889 |
| 21 | 0 | 2โปยฒยน | 1/2,097,152 | 0.0000005 | โ Skip | 1.563889 |
| 22 | 1 | 2โปยฒยฒ | 1/4,194,304 | 0.0000002 | โ Add | 1.563889 |
| 23 | 0 | 2โปยฒยณ | 1/8,388,608 | 0.0000001 | โ Skip | 1.563889 |
| Mantissa total (implied 1 + all 23 bits) | 1.5639 | |||||
Imagine 23 people queuing at a bank counter. Before they joined the queue, each person was designated either a 1 or a 0 โ assigned when they arrived, not their choice.
Each person has an amount to pay in. The person at the front of the queue โ person 1, first at the counter โ has 50p. The person behind them has 25p. The next 12.5p. Every person has exactly half the amount of the person in front of them. By the time you reach the back of the queue the amounts are vanishingly small โ but they are still real and they still count.
Before the queue even starts moving, the cashier reaches under the counter and pays in ยฃ1 from the till. "That one is always included. No exceptions. Every time." That is the implied leading 1 โ it is never held by anyone in the queue, never written in the binary, but always paid in first without fail.
Then the cashier works through the queue one person at a time from the front.
If a person was designated 1 โ the cashier takes their amount and pays it into the account.
If a person was designated 0 โ the cashier waves them past. They keep their amount. Nothing goes into the account.
When all 23 people have been processed, the cashier looks at the account balance โ the ยฃ1 paid in at the start, plus whatever the 1-designated people contributed. That total is your mantissa value.
We understand Noel has no difficulty grasping the value at the front of the queue, but would rather not be the one standing at the back.
The exponent is the zoom level โ it sets the scale of the number. The mantissa is what you see through the lens โ the actual digits. Change the zoom (exponent) and the same digits (mantissa) represent a completely different magnitude. A speed of 0.07 m/s and 45 m/s have very similar mantissa values but different exponents.
82 2D 48 41.1 bit
8 bits
23 bits
1 = negative
= 1.5639
GPS latitude and longitude follow exactly the same three-step process โ sign, exponent, mantissa โ but use 8 bytes instead of 4. The bit split is 1 sign bit, 11 exponent bits, 52 mantissa bits. The bias is 1023 instead of 127 (again sitting in the middle of 0โ2047). The result is roughly 15 significant figures of precision instead of 7 โ enough to locate a vehicle to sub-millimetre accuracy rather than within 1โ2 metres.
Protocol Buffers
Google's Protocol Buffers โ protobuf for short โ is a compact binary format for structured data. It is used here to encode the vehicle telemetry inside each SEI unit. Unlike JSON or XML which spell out field names as readable text, protobuf uses numbers โ making it significantly smaller and faster to decode.
Every field in a protobuf message is written as a tag followed by the data. The tag is always one byte. The data that follows depends on what the tag says.
The tag byte packs two pieces of information into one byte โ the field number and the wire type. If you have read the Bitmask section you already know how this works. The bottom 3 bits hold the wire type. The top 5 bits hold the field number. You extract them using AND masking and bit shifting.
Wire type โ AND with 0x07 (bottom 3 bits): tag & 0x07 = wire type
Field number โ shift right by 3: tag >> 3 = field number
Example โ tag byte 0x25 (vehicle speed)
0x25 in binary: 00100101
Wire type: 00100101 & 00000111 = 00000101 = 5 (32-bit fixed โ 4 bytes follow)
Field number: 00100101 >> 3 = 00000100 = 4 (vehicle_speed_mps)
Not sure how the binary arithmetic works? See the Bitmask section.
The wire type tells the decoder how the data is formatted and how many bytes to read. Without it the decoder would not know where one field ends and the next begins. Three wire types appear in this file:
| Wire type | Name | Bytes | Used for |
|---|---|---|---|
0 | Varint | Variable | Frame sequence number, gear state, version |
1 | 64-bit fixed | Always 8 | GPS latitude, longitude, heading |
5 | 32-bit fixed | Always 4 | Vehicle speed, steering, accelerator, acceleration |
Wire types 2, 3, and 4 exist in the protobuf specification but do not appear in this file.
Protobuf has an important space-saving rule โ if a field holds its default value it is not written to the binary at all. For a booleanBooleanA value that can only be true or false. In protobuf, a boolean field with the value false is not written to the binary at all โ absence means false, not missing data. the default is false. For an integerIntegerA whole number with no fractional part โ positive, negative, or zero. In protobuf, integers are stored as varints. Frame sequence number and gear state are integers. the default is zero. For an enumEnumShort for enumeration. A field that can only hold one of a fixed set of named values. Gear state is an enum โ it can only be GEAR_DRIVE, GEAR_REVERSE, GEAR_NEUTRAL, or GEAR_PARK. the default is the first value in the list.
This is why the brake, blinker, and autopilot fields are absent from every frame in this file. Their default is false. Protobuf does not write them. Absence is not missing data โ it is the encoding precisely and verifiably stating that the value was false.
Any examiner familiar with the Protocol Buffers specification can independently confirm this. The specification is published and freely available. It is not possible for a boolean field to be absent while its value was true โ if it were true, the byte sequence for that field would be present in the binary.
For whole numbers, protobuf uses a variable length encoding called a varint. Instead of always using 4 bytes for a number, it uses only as many bytes as needed. The number 1 needs one byte. The number 150,030 needs three.
Each byte in a varint contributes 7 data bits. The 8th bit โ the most significant bit โ is a continuation flag. If it is 1, more bytes follow. If it is 0, this is the last byte.
Each runner carries 7 bits of the number. If they pass the baton on (MSB = 1) the next runner carries more. If they cross the finish line (MSB = 0) the number is complete. The decoder keeps reading bytes until it finds one with MSB = 0.
Tag 0x18 = field 3, wire type 0 (varint). Three data bytes follow: 8E 94 09
| Byte | Binary | MSB | Status | 7 data bits |
|---|---|---|---|---|
8E | 10001110 | 1 | More follows | 0001110 |
94 | 10010100 | 1 | More follows | 0010100 |
09 | 00001001 | 0 | Last byte | 0001001 |
Concatenate the 7-bit groups in reverse order โ protobuf varints are little-endian, least significant group first:
0001001 ยท 0010100 ยท 0001110 = 000100100101000001110 = 150,030
Tesla SEI Explorer CSV: frame_seq_no = 150030. Manual decode: 150,030. Match confirmed. This frame sequence number indicates approximately 69 minutes of prior recording existed on the drive before this clip begins.
| Tag | Field | Wire type | Data type |
|---|---|---|---|
0x08 | 1 โ version | 0 (varint) | Integer |
0x10 | 2 โ gear state | 0 (varint) | Enum |
0x18 | 3 โ frame sequence no. | 0 (varint) | Integer |
0x25 | 4 โ vehicle speed | 5 (32-bit) | IEEE 754 float |
0x2D | 5 โ accelerator pedal | 5 (32-bit) | IEEE 754 float |
0x35 | 6 โ steering angle | 5 (32-bit) | IEEE 754 float |
0x59 | 11 โ latitude | 1 (64-bit) | IEEE 754 double |
0x61 | 12 โ longitude | 1 (64-bit) | IEEE 754 double |
0x69 | 13 โ heading | 1 (64-bit) | IEEE 754 double |
0x71 | 14 โ accel X | 5 (32-bit) | IEEE 754 float |
0x79 | 15 โ accel Y | 5 (32-bit) | IEEE 754 float |
0x7D | 16 โ accel Z | 5 (32-bit) | IEEE 754 float |
GPS coordinates โ 64-bit doubles
Speed and steering use 32-bit floats โ 4 bytes, ~7 significant figures. GPS coordinates use 64-bit doubles โ 8 bytes, ~15 significant figures. The conversion is identical to IEEE 754 float but with exponent bias 1023 instead of 127.
A 32-bit float locates you to within ~1โ2 metres. A 64-bit double locates you to within a fraction of a millimetre. The CSV value reads 53.72891032171984 not 53.72891 โ those extra digits are real data, not noise. For forensic GPS evidence that precision is essential.
Verified values from frame 1: latitude 53.7289ยฐN, longitude -1.6932ยฐW โ Brighouse/Halifax area, West Yorkshire. By frame 2,174 heading has rotated from 253ยฐ to 215ยฐ, consistent with steering angle data showing a gradual left turn.
Why this methodology stands up
Vehicle driven โ SEI written per frame โ USB recovered โ MP4 located โ container parsed (MediaInfo) โ binary examined (010 Editor) โ SEI units located by binary search โ bytes decoded (IEEE 754 + protobuf) โ values verified (SEI Explorer CSV) โ findings reported. Every step documented. Every value traceable to a specific byte offset. Every conversion using published mathematics checkable by any qualified examiner independently.
Data written at time of recording. SEI is embedded per-frame by the vehicle's own systems. Cannot be added retrospectively without re-encoding the entire stream, which changes the file hash.
Three independent tools, consistent results. MediaInfo timestamps match encoded dates. Hand-decoded bytes match the SEI Explorer CSV exactly. 010 Editor confirms the container structure MediaInfo reported.
False values provably absent, not assumed. Protobuf omits fields at their default value. Brake=false, indicators=false, autopilot=NONE are absent from every frame's binary payload. Absence is the proof โ not missing data, but the encoding working exactly as the published specification says.
As we say at Harper Shaw: assume nothing, believe nobody, check everything.