The Staircase Cosine: What a DAC Taught Me About Debugging
The oscilloscope trace looked wrong. Iâd implemented a straightforward derivative filter, fed it a clean sine wave, and expected a smooth cosine shifted 90 degrees in phase. Instead, I was staring at something that looked like a staircase trying to impersonate a curve.
My first instinct was to check the code. Then the filter coefficients. Then the sampling rate. Everything checked out. The math was correct. The implementation was correct. So why did my âperfect cosineâ look like it was built from LEGO bricks?
The Hardware Constraint Nobody Mentioned
The answer wasnât in my codeâit was in the hardware. The lab setup used a 12-bit digital-to-analog converter outputting at 750 Hz. For a 20 Hz signal, that means roughly 37 discrete voltage levels per cycle. Each âstepâ in my staircase was the DAC jumping to its next available output value.
The DAC canât output voltages between its discrete levels. Itâs not smoothly tracing a curveâitâs hopping from one flat step to the next. The smooth mathematical function exists only in theory; the physical output is fundamentally quantized.
What made the staircase especially pronounced was that differentiation acts as a high-pass filter. Those sharp step transitions contain high-frequency components, and the derivative operation amplifies them. The quantization artifacts barely visible in the original sine wave became the dominant feature of the output.
Expected: Actual:
___ _|
/ \ | |_
/ \ _| |
/ \ | |_
/ \ _| |
The code was right. The model was right. The constraint was the 12-bit resolution of physical hardware that no amount of clever programming could overcome.
The Same Pattern, Different Domain
This wasnât an isolated incident. Earlier in the same session, I needed a Google Sheets formula to calculate magnitude from experimental data. The formula itself is trivial: =SQRT(A2^2 + B2^2). Any first-year student knows this.
But the lab instructions referenced values âaâ and âbâ without explaining what they represented. Were these vector components? Real and imaginary parts of a complex measurement? Separate trials to be averaged? The formula depends entirely on the answer, and that answer was buried in a PDF I hadnât fully read.
When I asked Claude for help, the response was a clarifying question: âWhat do âaâ and âbâ represent in your experimental context?â Exactly the right questionâand one I couldnât answer without going back to the source material.
Both problems shared the same structure. The execution was trivial once you understood the constraints. Understanding the constraints required context that lived outside the code.
What I Actually Changed
After catching myself asking for help without providing enough background, I started including a brief context dump at the beginning of each task.
Instead of: âWrite a formula to calculate magnitude from columns A and B.â
I started writing: âThis is post-lab analysis for a digital filtering experiment. Columns A and B contain the real and imaginary components of the frequency response measured at each test frequency. I need magnitude in dB for comparison against the theoretical transfer function.â
The difference in response quality was immediate. The same capability was always thereâI just wasnât activating it properly.
This sounds obvious in retrospect. Itâs the same principle as writing good documentation or clear commit messages. But when youâre deep in a problem, itâs easy to forget that your collaborator doesnât share your mental context.
The Constraint You Canât Code Around
The staircase cosine still bothers me. Not because I couldnât fix itâyou can add analog reconstruction filters, increase sampling rate, or use a higher-resolution DAC. What bothers me is how long I spent looking for a bug that didnât exist.
The code was doing exactly what it should. The model accurately predicted the behavior. The limitation was physical, not logical, and no amount of debugging would reveal it because there was nothing broken to find.
Working with AI tools has a similar failure mode. When Claude gives an unhelpful response, my instinct is to rephrase the question or try a different prompt. Sometimes that works. But sometimes the problem isnât the promptâitâs missing context that Iâve internalized so deeply I forget it needs to be stated explicitly.
The DAC canât output voltages it doesnât have. Claude canât use context it wasnât given. Both are constraints worth remembering the next time your output doesnât match your expectations.