The method

How every monitor is checked,in full.

Each hour, every monitor in Delhi and the NCR goes through three checks. Here is what each one asks, its exact formula and thresholds, how they combine into one answer, how they did on two months of real data, and where to get the numbers yourself.

← Back to the live map

The three checks

Three questions, asked every hour.Each one, with a real example.

Physics

Can this reading even be real?

Fine dust (PM2.5) is part of all dust (PM10), so it can never be bigger. A monitor that says otherwise, or repeats the same number for hours, is broken.

Formula and thresholds

Each hour fails if any of these is true:

  • PM2.5 > 1.05 × PM10 (5% allowed for instrument noise)
  • PM10 outside 1–2000 µg/m³, or PM2.5 outside 1–1500 µg/m³
  • Stuck: the same PM2.5, PM10, NO2 or CO value for 3 or more hours in a row

Over the last 7 days (at least 24 hours with PM data): worth a look if more than 1% of hours fail, doesn't add up if more than 5% fail.

Uses PM2.5 and PM10.

Neighbours

Does it agree with the monitors around it?

Hour by hour, against the four nearest. Daytime air mixes, so some difference is normal; we only flag a monitor that drifts far more than the rest of the city.

Formula and thresholds
  • Neighbours: the 4 nearest monitors with data within 12 km (great-circle distance). Monitors closer than 0.5 km are skipped, because they are twins on the same site.
  • Hourly gap: g = ln(PM10 ÷ median PM10 of the neighbours), when at least 2 neighbours report that hour.
  • Daily contrast: d = median g over 11:00–16:59, minus median g over 22:00–05:59 (at least 4 hours each). Its weekly value is the median d over the last 7 days.
  • Mixing correction: by day, air mixes higher, so a monitor that reads high at night drifts towards its neighbours. We fit one Theil–Sen line of d against the night gap across the city, and score each monitor's distance from that line.
  • Score: robust z = (residual − median) ÷ (1.4826 × MAD) across all monitors. Worth a look at |z| ≥ 2, doesn't add up at |z| ≥ 3.
  • Two passes: monitors flagged in the first pass are taken out of everyone's reference and out of the city line in the second.

Uses PM10. Medians and MAD, not means and standard deviations, so one bad hour or one bad monitor can't move the reference.

History

Has it suddenly changed?

We compare the last 7 days with the 3 weeks before. A sudden jump against its own past means something changed at that monitor.

Formula and thresholds
  • The same daily contrast d as Neighbours, for PM10 (log ratio) and for humidity (percentage points).
  • Shift: median d over the last 7 days, minus median d over the 21 days before. This needs at least 4 recent days and 10 earlier days.
  • Score: z = shift ÷ robust SD of the earlier 21 days, where robust SD = 1.4826 × MAD. Its floor is 0.02 for PM10 and 1 point for humidity, so a very steady monitor isn't flagged for a tiny change. The larger |z| of the two measures counts.
  • Worth a look at |z| ≥ 2, doesn't add up at |z| ≥ 3.

Uses PM10 and humidity. A sudden humidity shift against the neighbours usually means something changed at the monitor itself, such as its inlet, its dryer or where it stands.

How the three checks become one answer

A monitor's answer is the most serious of its three checks. So a single check can flag a monitor, even one about humidity, and the monitor's card always names the check and the measure that raised it. Physics uses PM2.5 and PM10; Neighbours uses PM10; History uses PM10 and humidity.

The number you act on is PM2.5, the fine dust that health advice is based on. The checks lean on PM10 because the coarse dust it measures is what dust control and water spraying change the most.

When fewer than 2 neighbours, or too few days of data, are available, a check answers not enough data instead of guessing. A monitor gets that answer only if none of its checks can run.

Tested on real data

Two months, every day, scored as this site would.Here is how often it raises a monitor.

Every day from 1 October to 30 November 2025 was scored at 17:00 IST from the public OpenAQ archive, with the same thresholds as the live site.

On an ordinary day

Doesn't add up20.8% of monitors
Worth a look26.6%
Agrees with neighbours52.6%
Flags from the physics check7.6 a day
Flags from neighbours and history2.9 a day

Most flags are readings that can't be real, such as PM2.5 above PM10 or a sensor stuck on one value: a fault whatever the air is doing.

Catching a planted drop

Daytime PM10 cutFlagged
20%10 of 30
30%29 of 30
40%30 of 30
60%30 of 30

Each quiet monitor in turn had its daytime PM10 lowered for a week, and the whole city was scored again.

Every number here comes from src/validate.py; the full report is docs/VALIDATION.md.

Open data

The numbers are public.Download today's answers.

Every monitor's status and latest readings, updated every hour. Use them freely; cite the source.

CSV columns

ColumnDescription
idOpenAQ location ID
nameStation name
operatorAgency (CPCB, DPCC, HSPCB, UPPCB, IMD, IITM)
lat / lonCoordinates (WGS84)
statusOverall verdict: ok, watch, flag or nodata
physics_statusPhysics check result
neighbours_statusNeighbour-comparison check result
history_statusHistory check result
pm25 / pm10Latest hourly reading in µg/m³ (may be blank)
no2 / coLatest NO₂ (µg/m³) and CO (mg/m³)
relativehumidityLatest relative humidity (%)
neighbours_pm25 / neighbours_pm10Median of the four nearest monitors' latest readings
last_readingIST hour of the monitor's newest PM reading; with none in the last 3 hours, status is nodata
band / neighbours_bandCPCB AQI band of the 24-hour average, for this monitor and for its four neighbours
pm25_24h / neighbours_pm25_24hThe 24-hour average PM2.5 in µg/m³, for this monitor and for its four neighbours
complete_days_7dHow many of the last 7 days had readings in 16 or more hours, the minimum CPCB needs to count a day
data_throughIST timestamp of the latest scored hour

Our results (status, checks, this CSV) are released under CC BY 4.0. Underlying PM readings are from OpenAQ and subject to their terms. Suggested citation: Ground Truth, thegoodengineers, , https://github.com/thegoodengineers/ground-truth.

Questions

Still wondering?Plain answers.

Does a red answer mean someone tampered with the monitor?

No. It means the monitor's numbers don't add up against physics, its neighbours or its own past. Sensors drift, break and get moved. We tested whether the monitors named in the news in October 2025 show a spraying pattern, and with our method they don't stand out.

Which number should I use if my monitor is flagged?

Use what the four nearest monitors read right now. The panel shows it next to your monitor's own reading. It is the middle value of real monitors, not an official reading.

How fresh is the data?

The check runs every hour on new readings from the CPCB and DPCC monitors, via OpenAQ. The time of the latest reading is at the top of the page.

Why does a polluted area look cleaner in the afternoon?

By day the air mixes higher, so pollution near busy roads spreads out and a hotspot reads closer to its neighbours. We correct for this so it isn't mistaken for a problem.

Is the code open?

Yes. Everything, including the data analysis behind these checks, is on GitHub.