<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>dnim.dev</title>
    <link>https://dnim.dev</link>
    <description>Notes on precision timing, audio, embedded systems, and debugging by measurement. Written by JR (dniminenn).</description>
    <language>en-ca</language>
    <atom:link href="https://dnim.dev/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Putting back the bass that was never recorded</title>
      <link>https://dnim.dev/blog/measured-audio-mastering</link>
      <guid isPermaLink="true">https://dnim.dev/blog/measured-audio-mastering</guid>
      <description>I prepped a music set for a 1500-watt DIY rig and synthesized a missing sub-bass octave into tracks rolled off below 40 Hz. Measure first, fix only what the numbers justify, don&apos;t touch anything else.</description>
      <pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate>
      <category>Audio</category>
    </item>
    <item>
      <title>What is putting those bytes on the wire?</title>
      <link>https://dnim.dev/blog/ceph-read-amplification</link>
      <guid isPermaLink="true">https://dnim.dev/blog/ceph-read-amplification</guid>
      <description>The storage interface on a production cluster was pulling 4 to 12x more than the public interface where client requests arrive. Getting to a defensible number meant distrusting everything, including my own profiler, which was off by 30x.</description>
      <pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate>
      <category>Systems debugging</category>
    </item>
    <item>
      <title>A Dovecot optimization the compiler had been deleting all along</title>
      <link>https://dnim.dev/blog/dovecot-attr-pure-panic</link>
      <guid isPermaLink="true">https://dnim.dev/blog/dovecot-attr-pure-panic</guid>
      <description>An IMAP THREAD command was panicking inside Dovecot&apos;s array code. The crash was real, but the comment above it described an optimization that hadn&apos;t run in any -O2 build ever shipped. ATTR_PURE turned it into dead code, and the same annotation decided which line showed up in the backtrace.</description>
      <pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate>
      <category>Systems debugging</category>
    </item>
    <item>
      <title>A Ceph cluster I throw away on every reboot</title>
      <link>https://dnim.dev/blog/cephplayground</link>
      <guid isPermaLink="true">https://dnim.dev/blog/cephplayground</guid>
      <description>I wanted a real Ceph endpoint (S3, CephFS, RBD) to test app code against, without a k8s cluster or a single byte hitting my SSD. One container, ~30 seconds, all in RAM. The version sweep is where it got interesting.</description>
      <pubDate>Sun, 24 May 2026 00:00:00 GMT</pubDate>
      <category>Systems debugging</category>
    </item>
    <item>
      <title>The $20 stratum 1 that chrony kept benching</title>
      <link>https://dnim.dev/blog/esp32-stratum-1-ntp</link>
      <guid isPermaLink="true">https://dnim.dev/blog/esp32-stratum-1-ntp</guid>
      <description>I built a GPS-disciplined NTP server on a $20 ESP32 with single-nanosecond internal timing. Then chrony refused to use it. The fix was not in the clock. It was in everything I had assumed about serving the time.</description>
      <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
      <category>Precision timing</category>
    </item>
    <item>
      <title>You cannot average percentiles</title>
      <link>https://dnim.dev/blog/cannot-average-percentiles</link>
      <guid isPermaLink="true">https://dnim.dev/blog/cannot-average-percentiles</guid>
      <description>Finding an IMAP server&apos;s real capacity means driving load from many hosts at once. The hard part isn&apos;t generating the load. It&apos;s combining their latencies into one honest P99 without quietly lying about it.</description>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <category>Systems debugging</category>
    </item>
    <item>
      <title>The frozen clock that nobody noticed</title>
      <link>https://dnim.dev/blog/ts2phc-go-frozen-clock</link>
      <guid isPermaLink="true">https://dnim.dev/blog/ts2phc-go-frozen-clock</guid>
      <description>A GPS-disciplined NTP server stopped being disciplined, and every monitor stayed green. The giveaway was a freq_ppb value that repeated byte-for-byte across three seconds, which a live servo never does.</description>
      <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
      <category>Precision timing</category>
    </item>
    <item>
      <title>130 frames per second, derived from a wire</title>
      <link>https://dnim.dev/blog/gometer-130fps-led</link>
      <guid isPermaLink="true">https://dnim.dev/blog/gometer-130fps-led</guid>
      <description>I built a Go daemon that drives a music visualizer on a Pi, then found out the frame rate wasn&apos;t mine to pick. The LED protocol had already decided it. The real work was a latency budget I had to measure before the lights felt locked to the music.</description>
      <pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate>
      <category>Audio</category>
    </item>
    <item>
      <title>ts2phc and gpsd cannot share a serial port</title>
      <link>https://dnim.dev/blog/jankgps-ts2phc-gpsd-demux</link>
      <guid isPermaLink="true">https://dnim.dev/blog/jankgps-ts2phc-gpsd-demux</guid>
      <description>One USB serial port, two programs that both need exclusive access. ts2phc wants RMC-only for PTP clock discipline; gpsd wants everything else. I wrote a demuxer that gives each one a synthetic port shaped exactly the way it expects.</description>
      <pubDate>Sat, 07 Mar 2026 00:00:00 GMT</pubDate>
      <category>Precision timing</category>
    </item>
    <item>
      <title>The spare cores in a BeagleBone keep better time than its kernel</title>
      <link>https://dnim.dev/blog/bbb-pru-pps-timestamping</link>
      <guid isPermaLink="true">https://dnim.dev/blog/bbb-pru-pps-timestamping</guid>
      <description>The standard Linux PPS driver timestamps the GPS pulse in an interrupt handler and pays ~20 µs of jitter for it. The BeagleBone has two 200 MHz real-time cores that Linux never touches. I moved the timestamp into one of those, and the clock offset dropped into the low nanoseconds.</description>
      <pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate>
      <category>Precision timing</category>
    </item>
    <item>
      <title>Tidal stopped serving lossless, so I read the token</title>
      <link>https://dnim.dev/blog/tidal-24bit-pkce</link>
      <guid isPermaLink="true">https://dnim.dev/blog/tidal-24bit-pkce</guid>
      <description>Upstream plugin started pulling 320 kbps instead of lossless FLAC, and swapping client IDs did nothing. The access token had frozen its entitlements at mint time, long before any request went out.</description>
      <pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate>
      <category>Audio</category>
    </item>
    <item>
      <title>The game that rebooted the hypervisor</title>
      <link>https://dnim.dev/blog/gpu-passthrough-host-reboot</link>
      <guid isPermaLink="true">https://dnim.dev/blog/gpu-passthrough-host-reboot</guid>
      <description>A friend&apos;s gaming VM hard-reset its Proxmox host the instant a game launched, with nothing in the logs. The cause was below every layer we kept editing: a power transient at game launch that tripped a PCIe fault and reset the box.</description>
      <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
      <category>Systems debugging</category>
    </item>
    <item>
      <title>The furnace controller that deadlocked when the WiFi dropped</title>
      <link>https://dnim.dev/blog/rp2040-furnace-deadlock</link>
      <guid isPermaLink="true">https://dnim.dev/blog/rp2040-furnace-deadlock</guid>
      <description>An RP2040 furnace controller would freeze at random, always when the WiFi dropped. The culprit was a connectivity probe that opened a TCP connection to decide whether it could open one. A timeout would have hidden it; removing the probe fixed it.</description>
      <pubDate>Sun, 30 Nov 2025 00:00:00 GMT</pubDate>
      <category>Embedded systems</category>
    </item>
    <item>
      <title>The GPS that answers 0xFF when it has nothing to say</title>
      <link>https://dnim.dev/blog/gpsd-i2c-chrony-shm</link>
      <guid isPermaLink="true">https://dnim.dev/blog/gpsd-i2c-chrony-shm</guid>
      <description>Reading a u-blox GPS over I2C frees the UART, but the I2C interface has no concept of idle. When the module has nothing to send, every read comes back as 0xFF filler. This is the small bridge that turns that into a chrony refclock.</description>
      <pubDate>Mon, 27 Oct 2025 00:00:00 GMT</pubDate>
      <category>Precision timing</category>
    </item>
    <item>
      <title>One socket for the whole subnet</title>
      <link>https://dnim.dev/blog/goscan-one-socket-subnet</link>
      <guid isPermaLink="true">https://dnim.dev/blog/goscan-one-socket-subnet</guid>
      <description>A host scanner that opened a raw socket and a goroutine per host fell over on anything bigger than a /24. The rewrite uses one socket and two goroutines no matter how many hosts, with an ARP fast path and an unprivileged fallback.</description>
      <pubDate>Mon, 03 Mar 2025 00:00:00 GMT</pubDate>
      <category>Systems debugging</category>
    </item>
    <item>
      <title>My weather station lies about the sun</title>
      <link>https://dnim.dev/blog/wsrepeater-sensor-calibration</link>
      <guid isPermaLink="true">https://dnim.dev/blog/wsrepeater-sensor-calibration</guid>
      <description>A cheap Ecowitt station reports UV and solar radiation that read high and jump around. The repeater that forwards it to Weather Underground smooths and scales those channels first, and never lets a slow upstream API stall the station.</description>
      <pubDate>Thu, 29 Aug 2024 00:00:00 GMT</pubDate>
      <category>Embedded systems</category>
    </item>
  </channel>
</rss>