WhereisMyRide · phone sensors · Oct – Nov 2026
I turned my phone into a lab instrument for Istanbul's metro.
WhereisMyRide started as an app to answer "where is my train right now?" — no live positions are published for Istanbul's metro. To answer it, the app recorded every sensor a phone has while I rode. The app is finished now; plenty of good transit apps already exist. But the recordings turned out to be the interesting part: a phone on a train seat can see the tunnel's hills, the air the train pushes, and the current in its motors. This page is what it saw, and the maths that gets from raw sensor readings to each number.
Chapter 1 · accelerometer
Speed from a sensor that drifts.
Speed is the integral of acceleration. The catch: the phone can't separate the train's push from gravity leaking in through a tiny tilt, and an integral never forgets an error.
A tilt error \(\delta\theta\) puts a slice of gravity into the measured acceleration:
$$\delta a = g\,\sin\delta\theta \;\approx\; g\,\delta\theta, \qquad \delta\theta = 0.1^\circ \;\Rightarrow\; \delta a \approx 0.017\ \mathrm{m/s^2} \;\Rightarrow\; \delta v \approx 1.5\ \mathrm{m/s}\ \text{after } 90\ \mathrm{s}$$So the error is modelled as an offset plus a slow drift, \(e(t) = c_0 + c_1 t\), and taken out of the integral:
$$v(t) = A(t) - c_0\,t - \tfrac{1}{2}\,c_1\,t^2, \qquad A(t) = \int_0^{t} a_{\mathrm{m}}(\tau)\,\mathrm{d}\tau$$Two facts are known for every run of length \(T\) between stations \(L\) apart: the train is stopped at both ends, and it covers the track distance. They fix \(c_0\) and \(c_1\) exactly:
$$v(T) = 0, \qquad \int_0^{T} v(t)\,\mathrm{d}t = L \quad\Longrightarrow\quad \begin{bmatrix} T & \tfrac{1}{2}T^2 \\[2pt] \tfrac{1}{2}T^2 & \tfrac{1}{6}T^3 \end{bmatrix} \begin{bmatrix} c_0 \\ c_1 \end{bmatrix} = \begin{bmatrix} A(T) \\ \int_0^{T} A\,\mathrm{d}t - L \end{bmatrix}$$\(|c_1|\,T\) — how much the error had to change over the run — grades it: under \(0.15\ \mathrm{m/s^2}\) is good, under \(0.6\) rough, above that the phone moved and the run is thrown out. On the run below it is 0.082.
One run, stretch S5 → S6. Android's ready-made "linear acceleration" is a sensor fusion that slowly mistakes a long, steady push for the phone tilting — integrated, it puts the train 6 m from where it started (0.3% of the distance). The raw accelerometer, pinned as above, is the blue line.
Limit. The pin fixes the start, end and area under the curve, not its shape. The line's top speed is 80 km/h, yet 2 stretches peak above it (up to 88.9) — a shape error of about 10%, right inside the ±5–10% predicted before the first ride.
Chapter 2 · gyroscope + barometer
The tunnel in 3D.
Turn rate gives the bends; air pressure at each platform gives the height. Together they rebuild the tunnel — its own page, with a 3D model you can turn around.
The gyroscope measures rotation \(\boldsymbol{\omega}\) in the phone's own axes. Projected on the measured "up" \(\hat{\mathbf{g}}\), and minus the bias \(b\) read while the train stands still, it is the train's turn rate. Integrated with speed, it draws the plan:
$$\psi(t) = \int_0^{t} \big(\boldsymbol{\omega}\cdot\hat{\mathbf{g}} - b\big)\,\mathrm{d}\tau, \qquad x(t) = \int_0^{t} v\cos\psi\,\mathrm{d}\tau, \qquad y(t) = \int_0^{t} v\sin\psi\,\mathrm{d}\tau$$Height comes from hydrostatics, read only in the calm air of a platform:
$$\Delta h = -\frac{\Delta p}{\rho\,g}, \qquad \rho \approx 1.2\ \mathrm{kg/m^3} \;\Rightarrow\; 1\ \mathrm{m} \leftrightarrow 11.8\ \mathrm{Pa}$$Bends match the map to about 1° on most stretches; station heights repeat within 0.6 m; the tunnel rides the hills above it.
Chapter 3 · barometer
Bernoulli in a tunnel.
Why read the barometer only at platforms? Because while the train moves, the pressure in the car swings by more than any hill. The swing isn't noise — it follows the train's speed.
Air rushing past a moving train has dynamic pressure \(q\). By Bernoulli, faster air means lower static pressure, so the car's pressure should dip in proportion to \(q\):
$$q = \tfrac{1}{2}\,\rho\,v^2, \qquad p_{\mathrm{car}}(t) - p_{\mathrm{line}}(t) \;\approx\; -\,C\,q(t)$$where \(p_{\mathrm{line}}\) is the straight line between the two platform readings. At the line's top speed, \(q\) is already 296 Pa — the same as 25 m of height. That is why the slope between stations can't be read from a barometer on a moving train.
Same run as above. Correlation on this run: −0.71.
Check. Any run starts and ends at rest with a hump between, so anything hump-shaped would correlate. Across 12 clean runs, the dip follows \(q\) with a median correlation of −0.63, against −0.32 for a plain hump; with the hump shape taken out, \(q\) still gives −0.74. It is the speed.
Limit. \(C\) changes from run to run (range −0.92 to −0.16 in correlation): pressure waves from the train entering and leaving tunnels, other trains, and open ventilation shafts all add their own swings.
Chapter 4 · magnetometer
A compass that reads the motors.
The motors draw hundreds of amperes through cables in the car. A current makes a magnetic field — and the phone's compass is sitting in it.
The field of a long straight current \(I\) at distance \(r\):
$$B = \frac{\mu_0\, I}{2\pi\, r}, \qquad \mu_0 = 4\pi\times10^{-7}\ \mathrm{T\,m/A}$$The supply voltage \(U\) is fixed, so the current follows the power the motors deliver, and power is force times speed:
$$I = \frac{P}{U} = \frac{F\,v}{U}, \qquad F \approx m\,a \quad\Longrightarrow\quad B \;\propto\; a\,v$$The typical peak while pulling is 158 µT — more than three times Earth's field. Taking the cables \(r \approx 1\) m away (an assumption; the real geometry is unknown), that is a current of order
$$I = \frac{2\pi\, r\, B}{\mu_0} \approx \frac{2\pi \times 1\ \mathrm{m} \times 158\ \mu\mathrm{T}}{4\pi\times10^{-7}\ \mathrm{T\,m/A}} \approx 800\ \mathrm{A}$$Same run. When the train brakes, the field turns negative: the current reverses, so the motors are working as generators. Whether that energy goes back into the line or into on-board resistors, a compass can't tell.
Check. If \(B \propto a\,v\), the field should follow power better than acceleration alone. Across 12 runs: median correlation 0.85 with \(a\,v\) against 0.75 with \(a\), and power wins on 10 of 12.
Limit. The current estimate is an order of magnitude: \(r\) is guessed, and the cables are not one straight wire. \(F \approx m\,a\) also ignores slope and rolling resistance.
Chapter 5 · energy and comfort
Brakes, gravity and comfort.
Between two standstills at stations A and B, everything the motors put in, plus the height lost, must leave through the brakes and drag:
$$W_{\mathrm{brakes}} + W_{\mathrm{drag}} = W_{\mathrm{motors}} + m\,g\,(h_A - h_B)$$On the steepest downhill that height alone is 398 J per kg of train. If the slope is even, most of it lands on the final stop, on top of the motion energy \(\tfrac{1}{2}v^2\) at the moment braking starts.
Ride comfort follows ISO 2631-1: each axis is filtered with a frequency weighting — \(W_d\) horizontal, \(W_k\) vertical — then combined:
$$a_w = \sqrt{\frac{1}{T}\int_0^{T} a_W^2(t)\,\mathrm{d}t}, \qquad a_v = \sqrt{a_{w,x}^2 + a_{w,y}^2 + a_{w,z}^2}$$On the tunnel page: where gravity brakes for free, and where the brakes absorb almost twice the train's motion energy.
The predictions, graded
Written from datasheets before the first ride. Then the rides graded them.
Before the first recording, I wrote down what each sensor should be able to resolve, from typical datasheet figures. These are the ones the data can grade.
| Sensor | Predicted | Measured | Grade |
|---|---|---|---|
| Accelerometer | Speed within ±5–10%, probably biased low by Android's sensor fusion | Fusion absorbed nearly the whole push (6 m of 1,937); raw + pinned works, peaks ~10% high | right, and worse |
| Gyroscope | Curves yes, map accuracy no: 1–4.5° drift, 10–60 m sideways per stretch | Turns within about 1° on 6 of 8 stretches once the bias is read at rest; 30–130 m off the map at worst | right |
| Barometer, standing | Station height differences to about 0.5 m | Repeat within 0.6 m on separate rides | right |
| Barometer, moving | Measures tunnel aerodynamics, not slope (\(q \approx 240\) Pa at 20 m/s) | The dip follows \(\tfrac{1}{2}\rho v^2\) — median correlation −0.63 | right |
| Magnetometer | Traction current visible, 100× above noise | Up to 217 µT against ~0.2 µT noise; follows power | right |
| Comfort (ISO 2631) | Only comparable within one phone position | Not tested — no ride was recorded in two positions at once. Kept as a rule: comfort is only compared between runs with the phone in the same position | untested |
What's not here
Privacy, and the parts I left out.
These are my own rides. Station names, dates and how often I rode a stretch would add up to where I go, so the pages keep only measurements: stations are numbered, numbers are pooled across runs, and the bus, walking and Bluetooth recordings the app also made stay private.
Why it ended
The app is done. The measurements were the point.
There are plenty of good transit apps, and competing with them wasn't what I wanted to spend time on. What I wanted was to take a system I use every day apart with the tools I had in my pocket — and a phone turned out to be a surprisingly good lab.