Forty years in IT. OSCP. OSCE. Kubernetes clusters in production. A soldering iron in my hand for the first time since the original Xbox modchip era. It started on a Saturday afternoon in November when I cracked open a dead radio-controlled wall clock to harvest the ferrite antenna. By 17:41 that same evening, the time-signal software was running and the watch was attempting to receive. It just wasn't synced yet.
The proper hardware build came four weeks later. Saturday December 6, evening tinkering at the bench. Oscilloscope on the LC tank. By 03:02 AM Sunday December 7, the Casio Wave Ceptor displayed GET SIG and the correct date. Watch synced. From my bedroom in Paphos.
This is the story of those weeks.
DCF77 is the longwave time signal transmitter operated by the Physikalisch-Technische Bundesanstalt in Mainflingen, Germany. It broadcasts the current time on 77.5 kHz, encoded as second-by-second amplitude modulation of the carrier. Every radio-controlled clock you have ever owned in Europe gets its time from this station. From its 50 kW transmitter and 150-meter antenna, the signal nominally covers about 2,000 kilometers — Mainflingen to Cyprus is 2,300 km, well outside the receivable range. No commercial DCF77 watch ever syncs here, day or night.
The plan was not to receive DCF77. The plan was to be DCF77. A miniature one. Bedroom-scale. 50 milliwatts instead of 50 kilowatts. A 4.5-centimeter ferrite rod instead of a 150-meter tower. Efficiency: 0.00012% of the real thing. Functional reach: enough to sync the watch on my nightstand.
The reference project came from harlock974/time-signal on GitHub — a Raspberry Pi-based time signal generator that toggles a GPIO pin at 77.5 kHz, encoding the DCF77 protocol second-by-second. The Pi handles the signal generation in software. Everything between the GPIO pin and the air is your problem.
The ferrite antenna was salvaged from a dead wall-mounted radio clock. This turned out to be a critical and counterintuitive choice. The ferrite from a receiver clock is factory-tuned for a specific time signal — and the one I pulled was originally tuned for JJY (Japan's equivalent station at 40 kHz). I discovered this by measuring on the oscilloscope: the LC resonance was nowhere near 77.5 kHz. After recalculation:
I retuned it by swapping the parallel capacitor until the scope showed clean resonance at 77.5 kHz. Standard value 6.8 nF, later refined to 2.2 nF on the final perfboard after measuring the actual ferrite inductance more precisely.
The driver stage went through evolution. Driving the ferrite directly from a Pi GPIO pin produced a weak, distorted signal — 3.3V at very limited current is not enough to push useful magnetic flux through a coil. The first upgrade was a single MOSFET; this worked but ran hot and produced asymmetric waveforms. The final design is a complementary push-pull pair: BS250 (P-channel) on top, BS170 (N-channel) on the bottom, gates tied together to the GPIO. When GPIO is high, BS170 sinks; when low, BS250 sources. The drain junction drives the LC tank through the 10R series resistor.
This is a textbook Class-D output stage. It is also the moment when I first really understood what "complementary push-pull" means, because I had to wire it on a breadboard, see what happened, and trace the consequences with a probe.
Dead MOSFET, day one. The first BS170 I plugged in was already dead from the factory or from static somewhere in its life. The circuit didn't work. I assumed I had wired it wrong. I rewired everything three times. Eventually I put the scope probe on the drain and saw nothing changing when the GPIO toggled. Replaced the MOSFET with a fresh one from the same tray. Signal appeared instantly. Lesson burned in: trust the measurement, not the assumption.
40 kHz vs 77.5 kHz. The salvaged ferrite was originally tuned for the Japanese JJY40 station. I spent an embarrassing amount of time wondering why the LC tank looked like it was resonating "near" the right frequency but the watch wouldn't sync. The scope eventually showed the actual resonance peak — about 40 kHz, exactly the JJY band. Recalculation and cap swap fixed it.
Cooperative switching artifacts. The push-pull output had high-frequency spikes on every transition — both MOSFETs briefly conducting at the crossover, creating shoot-through current that contaminated the waveform with harmonics. Cleaner sine = more energy at 77.5 kHz = better range. Increasing the series resistor to 22R softened the edges. Adding a 100nF decoupling cap across the 5V rail stabilized the supply against the switching transients. The waveform on the scope went from "kicks" to something approximating a real sine wave.
Timezone confusion. The Pi was running in Europe/Paphos timezone (EET, UTC+2). The watch is hardcoded to interpret DCF77 as CET (UTC+1) and then convert to its configured local time. Result: watch displayed +1 hour from reality. Fix: set the Pi's timezone to Europe/Berlin. The transmitter now broadcasts CET as DCF77 actually does, and the watch correctly converts to EET locally.
The transmitter runs as a systemd service. No screen sessions, no nohup hacks, no ssh-dependent processes:
systemctl enable dcf77 and the transmitter comes up automatically on every boot. If it crashes, it restarts. If the Pi reboots, it restarts. It has now been running continuously for months.
03:02 AM, Sunday December 7. Casio Wave Ceptor about 50 cm from the transmitter. Display: GET SIG. Date: 7-12-2025. The watch was actively receiving and decoding my signal.
This is the moment that matters. Not "the scope shows the right frequency." Not "the LC tank resonates correctly." A Casio Wave Ceptor that had spent its entire life in Cyprus showing "NO SIG" — because DCF77 from Mainflingen never reaches this island — locked onto my 50-milliwatt signal from across the bedroom and started behaving like it was sitting on a kitchen table in Frankfurt. Binary outcome. Synced or didn't sync. Mine.
Range testing followed: roughly 2.5 meters of reliable reception. Beyond that, the watch fails to lock. Adequate for the bedroom, the only environment that matters.
time-signal program (harlock974) running on a Volumio Pi, encoding DCF77 second-by-second. The "100 200 100 200..." patterns are the per-second amplitude codes.
After breadboard validation, I moved to perfboard. First soldering work in over twenty years — the last time was the original Xbox modchip era. No third-hand clamp. No loupe lamp. No proper desoldering gear. Eyes that struggle with 0603 components. The work was slow, the joints were uneven, but every connection passed continuity check. The board sits in a plastic Tupperware case (plastic is RF-transparent at 77.5 kHz; metal would create a Faraday cage and kill the signal) in a cupboard near the bed. It has not been touched since.
The current build has one structural weakness: the ferrite antenna is a receiver-grade part being used as a transmitter. Receiver ferrites are optimized for sensitivity to incoming flux — they have many turns of fine wire on a small core for high inductance per volume. Transmitter antennas need the opposite: low resistance, high current handling, large radiating surface area.
The 50 cm reliable-sync distance is what you get with this compromise. For substantially better range — say, anywhere in the apartment — the antenna should be a purpose-built transmit loop: 10–20 turns of thicker wire wound on a 10–20 cm diameter form, retuned to 77.5 kHz with a parallel capacitor. More current, more radiating area, more flux density at distance.
This is a v2.0 problem. The current build covers the bedroom, which is the only place I need it to cover. Optimization is infinite. Working is binary.
I have spent four decades in IT. I have built things in Kubernetes, in Python, in Rust, in PowerShell, in Bash. I have certifications in offensive security that required me to chain together exploits across multiple systems under time pressure. And yet, until this weekend, I had never built a thing that produced a measurable physical effect in the real world from first principles.
Software has a tendency to lie to you. kubectl get pods says Running, but is the application actually serving traffic? You write a test, the test passes — does the test test what you think it tests? Every layer of abstraction is a place where the system can be subtly broken in a way that looks fine.
A 77.5 kHz transmitter is not subtle. The scope shows a sine wave or it doesn't. The watch syncs or it doesn't. The frequency is right or wrong. Physics does not negotiate, does not gaslight, does not pretend to be working. It either is, or it isn't.
This is the right kind of project for the right kind of brain. The hyperfocus took over completely — I was sketching board layouts in my head while trying to sleep, got up at 5:30 Sunday morning to keep going, and finished the perfboard soldering by mid-afternoon. Forty years of waiting to find out hardware could feel like this.
The DCF77 transmitter still runs. Every morning the watch is on the correct time. Bedroom is calibrated to UTC, atomically, by a Tupperware container in a cupboard. That's the entire summary.
This was the gateway drug. Six months later: BELLA DAC 1.