One Heading Is Not a Measurement
Seeking a reason for a 17 degree heading error on a recent dive, I decided to test for a temperature impact on indicated heading. After a cursory test (heat gun and ice packs), I came up with a conclusion that was interesting, and which failed to give me the smoking gun I sought. The magnetometer drifts with temperature, but carefully measured it came to about 0.43° of heading per °C. On a typical summer lake dive, where the temperature goes from around 21 °C at the surface to around 10 °C at depth, that’s about 5° of heading error. Annoying and worth compensating for, not dangerous, and a long way from the ~17° error that sent me looking in the first place. Temperature was a smaller suspect than I’d feared. Action to take, but nothing too significant.
The number was measured correctly. It was also measured at exactly one heading, and that turned out to matter.
Over the next couple of days I ran the same heat-gun test at north, east, south and west. At one of those headings the compass moved about 2° per °C. So much for case closed.

The test
The setup is dull, which is what you want from a test:
- Park the scooter level on a non-magnetic stand, pointed at a cardinal heading by an ordinary magnetic compass. Not by the unit’s own display, since that’s the thing under test.
- Start logging. Let it sit for a few seconds to get a baseline.
- Heat the nav board with a heat gun for 45–60 seconds.
- Walk away. Let it cool back toward room temperature for 45–60 minutes without touching it.
- Rotate 90° and do it again.
One obvious thing: measure temperature on the IMU, not from the depth sensor. In code, they’re both reported as temperature, source ambiguous, so it’s pretty easy to log the wrong one and do a bunch of math based on a number that absolutely is not correlated with the die temp. The LIS3MDL magnetometer has a temperature sensor on its own die, so that’s the one I used. Measure with the sensor that actually feels the heat.
What the data says
The heat gun took the magnetometer die from about 20 °C up to 45–69 °C, depending on how enthusiastic I was that run. Heading swung hard:
| Pointed | Peak die temp | Heading swing at peak |
|---|---|---|
| North | 53 °C | +41° |
| East | 45 °C | −49° |
| South | 69 °C | −93° |
| West | 55 °C | +65° |
Now, you’re unlikely to see 70C water on a typical dive, and similarly unlikely to see 50C temperature swings in either direction. So 90 degree heading swings are unlikely. But it’s good data to understand what’s going on.
But heading isn’t what the sensor measures. The magnetometer measures a magnetic field in three axes, and heading is calculated from that. So I looked at the raw field instead. That’s where things got simple:
| Pointed | x (µT/°C) | y (µT/°C) | z (µT/°C) |
|---|---|---|---|
| North | 0.48 | 0.40 | 0.29 |
| East | 0.49 | 0.45 | 0.26 |
| South | 0.51 | 0.42 | 0.23 |
| West (1) | 0.51 | 0.43 | 0.32 |
| West (2) | 0.50 | 0.42 | 0.29 |
The field reading drifts the same amount no matter which way the scooter points. Every °C adds roughly the same small vector to the reading: about half a microtesla on each horizontal axis. For scale, the horizontal part of Earth’s field here is about 17–19 µT. It’s the same at every heading, on both heating and cooling, and close to linear all the way up to 69 °C. It isn’t the scooter’s steel or the building. It’s the sensor, and the error travels with it.
A few things I checked before believing any of this:
- The heat gun’s own field. A heat gun is a motor and a big heating coil, both of which make magnetic fields. If the gun were polluting the reading, the field would jump the moment I switched it off. It doesn’t. Field and temperature rise and fall together, smoothly.
- Heating vs cooling. The fast heating leg and the slow cooling leg give the same slope. So there’s no hysteresis, no lag, and nothing permanent: it comes back when it cools. That’s important: it means that leaving your DPV-Nav in the back of a hot car doesn’t invalidate your calibration.
- A bumped unit. Early in the north run the scooter got nudged and tilted about 3°. The body-frame field jumped, as it should, but heading moved less than half a degree, because tilt compensation handled it. Good to know that works.
Why one heading lied
If the sensor error is the same at every heading, why does the heading error change so much?
A compass works out heading from the direction of the horizontal field. Add a small fixed offset to that field and it tips the arrow sideways, but only the part of the offset that’s perpendicular to the field does any tipping. Point the scooter one way and the offset is mostly sideways, so heading moves a lot. Point it 90° away and the same offset lines up with the field, making it look slightly stronger or weaker without turning it at all, and heading barely moves.
So the heading error goes up and down like a sine wave as you turn. With a well-centred calibration on this unit it works out to roughly:
heading error ≈ 2.2° per °C × sin(40° − heading)
Near zero pointing northeast or southwest, worst pointing southeast or northwest. Friday’s test was pointed a little east of north, about 016° magnetic, which is close to the northeast quiet spot. At that heading the effect is roughly a third of its worst. That number was never wrong. It was just a point on a curve, and I’d treated it as the curve.

Here’s what that means if you calibrate at 21 °C and then dive colder:
| Heading | per °C | Dive 5 °C colder | Dive 10 °C colder |
|---|---|---|---|
| N | +1.4° | −8° | −19° |
| NE | −0.3° | +2° | +4° |
| E | −1.7° | +10° | +22° |
| SE | −2.2° | +11° | +20° |
| S | −1.4° | +6° | +10° |
| SW | +0.3° | −1° | −2° |
| W | +1.7° | −8° | −14° |
| NW | +2.2° | −11° | −22° |
A 10 °C swing is an ordinary Lake Washington dive. Twenty degrees of heading error over a 500 m leg puts you about 170 m off to one side. That’s not “annoying.” That’s the wrong wreck.
There’s a second amplifier: the calibration
How much a microtesla of drift moves heading depends on how big the calibrated horizontal field is, and a good calibration makes that the same at every heading. On the day of the test, mine wasn’t. The field the unit saw was about 9 µT pointing north and 29 µT pointing south, so north was roughly three times as sensitive as south. With that calibration, 5 °C of warming moved heading +14° at north and only −3.6° at south.
The practical lesson is that “degrees of heading per degree of temperature” isn’t a property of the sensor at all. It’s a property of the sensor, plus the calibration, plus which way you’re pointing.
Fix the cause, not the symptom
My first instinct was to build a correction table: at this heading and this temperature, add this many degrees. It would have worked, sort of, until the next recalibration changed the geometry and quietly invalidated the whole table. Then I’d have to build a 4 hour thermal test into every calibration cycle, which is wholly untenable.
The better question, and it was a good one to be asked, was: if the temperature effect is on the raw magnetic reading, why correct the heading at all?
So the firmware now corrects the reading. Before any calibration math runs, every magnetometer sample has its temperature drift subtracted:
reading -= coefficient × (die temperature − 21 °C)
That’s three numbers for x, y and z, plus a reference temperature, in a small mag_temp.json file on the unit. 21 °C is where every calibration I’ve done so far was collected, so existing calibrations stay valid. It lives at the bottom of the stack, so it survives recalibration, and it fixes everything else that uses the magnetometer: the attitude filter, the dive logs, and the calibration samples themselves. Die temperature now goes in every log, even the smallest, so any future dive can be re-examined.
What the manufacturer says
I went looking for ST’s own temperature coefficient before trusting mine. The LIS3MDL datasheet lists a zero-field offset of ±1 gauss at 25 °C, which is about 100 µT, and gives no temperature coefficient for it at all. The application note (AN4602) says the chip compensates sensitivity for temperature internally. It says nothing about offset.
So “temperature compensated” on a sensor datasheet may mean the gain, not the zero point. The drift that matters here is unspecified, uncorrected by the part, and potentially different from unit to unit. This will be one that needs to be measured on every board until I know what the pattern is.
What I don’t know yet
-
Cold. Every test here was warming up from room temperature. A dive is colder than the calibration. Heating and cooling behaved identically across 20–69 °C, so I’m assuming the line carries on below 20 °C, but that’s an assumption. I’ll test it properly this winter, when Mother Nature does the cooling for me. This weekend’s dive should also tell me more about whether or not this is a problem - I’ll be logging die temp in addition to everything else, so I’ll know how the temperature actually changes during a dive.
-
A heat gun is not a lake. The gun heats the board locally and fast. A dive cools the housing, which is full of air, and both plastic and air move thermal energy slowly. If some of this drift comes from parts near the sensor rather than the sensor itself, it might not follow die temperature on a real dive. The cold test should answer that too.
-
The Bomber dive. Temperature is back on the suspect list for that 18° error. The outbound leg fits: pointing east and getting colder, the model predicts an error of about that size and sign. The return leg doesn’t fit nearly as well. That log predates the die-temperature column, so it’s going to stay a maybe.
-
Making this practical. This took the better part of two days: five runs, about an hour each, mostly waiting, with no prompts from the unit. That’s fine for me. Asking every builder to do it is not. The drift is the same at every heading, so one heading may be enough, and that could turn it into a guided step in the calibration flow instead of an afternoon of babysitting a heat gun. Either way, I need to test at least one or two more boards for thermal coefficient of magnetic interference before I can infer anything like a predictable result.
What I’d tell past me
- A number measured at one point of a cycle is a point, not a coefficient. If a result could depend on direction, measure more than one direction before you write it down.
- Correct the physical quantity, not the thing you compute from it. Heading is downstream. Fix the field.
- Look for the invariant. The heading swings were all over the place. The field drift was the same five times running. The simple number was one layer down.
- “Temperature compensated” doesn’t mean what you hope. Read what the datasheet actually says is compensated.
- Friday’s conclusions are drafts. Especially the reassuring ones.