PhaseScope turns a stereo sound card into a sensitive differential phase instrument with a real-time anomaly detector, NTP-disciplined event timestamps, and built-in immunity to buffer glitches and clock drift.
pip install numpy sounddevice PySide6 pyqtgraph
python phasescope.py
Requires Python 3.10+ on Windows (also runs on macOS/Linux).
What it genuinely detects
- Vibration and footsteps — a coil near a magnet is a crude geophone; motion induces EMF directly into the probe channel.
- A magnet moving anywhere near the coil (enormous signal).
- Inductance changes — slide a ferrite rod into the coil, or warm the coil with your hand, and watch the phase walk.
- Electromagnetic interference — mains hum bursts, motors, phone chargers, fluorescent lights switching.
- Slow thermal drift of components (the drift-compensation feature exists for exactly this).
- Gravity/Scalar anomalies
Why not sync phase to an atomic clock over the internet? NTP over the internet is good to a few milliseconds and jitters by tens of milliseconds. The effects you care about are nanoseconds and below — a million times smaller than the sync jitter. Internet time can never be the phase reference. PhaseScope still syncs to NTP (pool.ntp.org and fallbacks), but uses it for what it is actually good for: putting absolute UTC timestamps on logged events, so two stations in different houses or cities can compare logs and look for coincidences. Coincidence between independent stations is the same logic hobbyist cosmic-ray networks use, and it is the only cheap way to separate "something local shook my desk" from "something larger happened."
The reference arm replaces the atomic clock. The Left channel travels through a plain wire; the Right channel travels through your experiment. Both are generated by the same DAC, sampled by the same ADC, clocked by the same crystal, and pass through the same buffers. The software measures the phase of each and subtracts. Everything common to both arms — clock drift, buffer slips, CPU stalls, USB jitter, converter temperature — cancels exactly. What survives the subtraction is what happened only in the probe arm. This is the same philosophy as a laser interferometer: never measure a time against a clock when you can measure a signal against a copy of itself.
Why a CPU hiccup cannot fake an anomaly (your buffering concern, handled three ways):
- A dropped or skipped buffer shifts both channels by the same amount, so the differential phase is unaffected.
- The audio driver reports every overrun/underrun, and the app additionally watches for clipping and amplitude dropouts. Affected samples are masked — shown as gaps, excluded from statistics, never counted as events. You will see a yellow "buffer hiccup absorbed" notice instead of a false alarm.
- A pilot "heartbeat" — a tiny known 0.5 Hz phase wiggle injected on the probe channel — is continuously recovered and verified, proving the whole chain is alive and at full sensitivity. (It is subtracted from the data, so it never pollutes the measurement.) If the heartbeat fades, the app tells you the instrument has a problem rather than reporting silence as "no anomalies."
The processing itself is light: one sine generation and one complex multiply per audio block, a few IIR filters at ~47 measurements/second, and a plot redraw at 30 fps. A modest laptop idles along at a few percent CPU.
- A sound device with stereo line output and stereo line input. Desktop motherboards usually have both (green out, blue in). Laptop mic jacks are usually mono and have AGC — they will not work well. The easy fix is any cheap USB audio interface with stereo RCA/line in (the classic Behringer UCA202-style device, ~$30, is perfect).
- Two 3.5 mm TRS male screw-terminal breakout adapters (a few dollars each), or one stereo cable you are willing to cut and solder.
- Hookup wire for the reference arm.
- Magnet wire (28–34 AWG) for the probe coil: 50–500 turns. Wind it on a ferrite rod, a bolt, a pen, or directly into a loose bundle.
- Optional: a neodymium magnet placed near (not glued to) the coil to turn it into a vibration sensor.
Line-level signals are around 1 V — completely safe to handle.
SOUND CARD LINE OUT SOUND CARD LINE IN
Tip (Left) ──────────────────────── Tip (Left) REFERENCE arm
Ring (Right) ───────[ probe coil ]─── Ring (Right) PROBE arm
Sleeve (Gnd) ──────────────────────── Sleeve (Gnd) shared ground
- Keep the reference wire and the probe path roughly the same length and route them together where possible; separate only at the coil.
- The coil sits in series in the Right channel. Coil DC resistance up to a few hundred ohms is fine; the input impedance of a line-in is ~10 kΩ.
- Twist the ground wire with the signal wires to reduce hum pickup.
- For the magnet experiment, fix the coil to the desk and place the magnet a few millimetres away on a separate support.
- Sound Control Panel → Playback → your output → Properties → Advanced: set 24-bit, 48000 Hz. Do the same for the Recording device. Mismatched rates force Windows to resample, which smears phase.
- On both devices, disable every enhancement/effect ("Audio Enhancements" off, spatial sound off).
- Recording device → Levels: microphone boost to 0 dB; disable AGC, noise suppression and echo cancellation if the driver offers them. AGC is the single biggest enemy of a stable phase measurement.
- In PhaseScope, pick the [Windows WASAPI] entries for both input and output, choose 48000 Hz / buffer 1024, and optionally tick WASAPI exclusive to bypass the Windows mixer entirely (the app falls back to shared mode automatically if exclusive fails).
- Press Start. Both level traces should sit between −30 and −6 dBFS; adjust the output Level slider and the Windows input gain until they do.
- Pilot lock should climb toward ~100 % within ~20 s.
- Wait ~30 s for "learning noise baseline…" to become "quiet — watching", then press Zero.
- Tap the desk. The anomaly trace should spike and an event should appear in the table, timestamped in UTC.
- Wave a magnet near the coil — expect a huge, satisfying response.
Any candidate anomaly must survive these, in order:
- Null arm test. Replace the coil with a plain wire matching the reference arm. The instrument should now show only its noise floor. Any "anomalies" that persist are in your audio chain, not your experiment.
- Shield test. Put the coil inside a grounded foil/biscuit-tin Faraday enclosure. If a signal disappears, it was EM pickup.
- Mass/vibration test. Put the coil on foam or hang it; if a signal changes, it was mechanical.
- Thermal test. Log overnight with drift compensation off and watch the slow wander correlate with room temperature.
- Coincidence test. Run two complete stations on different computers, different sound cards, ideally different buildings, both logging to CSV. Only events that appear in both logs within NTP accuracy (a few tens of milliseconds) at the same UTC time deserve a second look — and even then, check for shared causes (mains transients, weather, seismic activity; earthquake catalogs are public and make a great cross-check).
- Δ phase — the smoothed differential phase, in millidegrees, relative to your last Zero.
- Equiv. timing shift — the same number expressed as time delay through the probe arm at the tone frequency (Δt = Δφ / 2πf). With a quiet setup you will watch this sit at single-digit picoseconds, which is a fun thing to be able to say about a sound card.
- Anomaly σ — how unusual the current value is versus the recent noise (robust median/MAD statistics, so spikes don't inflate their own yardstick). Events open at the threshold and are logged with UTC start, duration, and peak.
- Pilot lock — health of the end-to-end chain, in percent of the injected heartbeat recovered.
- Clock skew — the crystal-frequency difference between your output and input devices, in ppm. Watch it cancel: it can be tens of ppm while the differential trace stays flat. That flatness is the whole design.
- XRUNS / DROPS — buffer incidents detected and masked. Nonzero numbers are fine; they tell you the protection is working.
| Symptom | Likely cause / fix |
|---|---|
| Only mono input devices listed | Laptop mic jack — use a USB interface with stereo line-in |
| Levels fine but phase trace wanders wildly | AGC or enhancements still on; redo section 5 |
| Pilot lock low, R level much lower than L | Coil resistance too high or bad breakout contact |
| Stream won't open | Match sample rates in Windows, pick same host API for in/out, try bigger buffer, untick exclusive |
| Frequent XRUNS | Raise buffer to 2048/4096, plug USB audio directly into the PC (no hub), close DAW/browser audio apps |
| Huge 50/60 Hz-ish wobble | Ground loop or hum pickup — twist wires, move coil away from mains cables, try a tone that isn't near a mains harmonic |
| Phase jumps when you touch nothing | Someone walked by, a compressor/fridge kicked in, or your chair moved — congratulations, the instrument works |
- Wind two coils and compare day vs night noise spectra.
- Build a sensitive doorbell/seismograph and calibrate it against footsteps at known distances.
- Measure the inductance change of a ferrite as a magnet approaches (classic permeability physics, quantifiable with this rig).
- Set up the two-station coincidence network with a friend and actually run it for a month.