embedded cclinical algorithmsfirmware

How We Ported Parkinson's Algorithms From Python to Embedded C

A validated algorithm and a deployable one are not the same thing.

We learned this properly during the PKG Health engagement. By the time we needed to move three algorithms onto Empatica's wrist device, bradykinesia, dyskinesia, and tremor, we had already spent close to two years refactoring the full algorithm suite from a mix of legacy C/C++, Julia, and TCL into clean, documented Python. That Python code was validated against the certified original, output by output, on real clinical sessions. It worked. It ran correctly in the cloud pipeline. But it could not run on the device itself, and the device is where PKG needed it.

Getting there took about six months.

Why the device needed C, not Python

Empatica's wrist device is a small, battery-powered microcontroller platform, not a computer. There is no Python interpreter running on it, no NumPy, no filesystem to load reference data from, no operating system scheduling background processes for you. Whatever runs on the device has to fit inside a fixed memory budget, execute deterministically inside a real-time loop, and not drain the battery in a day. That rules out an interpreted language with a large runtime. C, or something close to it, is what wearable firmware is written in, because it gives direct control over memory, timing, and power draw.

This is a different problem from writing correct Python. Correct Python that produces the right output on a laptop tells you the algorithm's logic is sound. It tells you nothing about whether that same logic fits in a few kilobytes of RAM, executes inside a fixed interrupt window, or behaves consistently when the accelerometer is sampling continuously for a week.

The Python version was the reference, not a draft

One thing made this project different from a typical rewrite: the Python implementation was not a prototype we were free to reinterpret in C. It was the certified reference. PKG's FDA 510(k) clearance was tied to specific algorithm behavior, and the validated Python version was the documented, tested baseline that behavior had to match. The C version was not allowed to be close enough. It had to reproduce the same outputs, within a tolerance defined for the regulatory submission, on the same clinical datasets already used to validate the Python version.

That meant the C port was not really a rewrite exercise. It was closer to a second, independent validation project that happened to also produce a deployable binary.

What actually breaks when you move from Python to C

A few things came up repeatedly.

Floating point behavior does not always transfer cleanly. NumPy and a hand-written C loop can evaluate the same mathematical expression in a different order, accumulate rounding error differently, and produce outputs that agree to five decimal places on one input and drift slightly on another. For most software that difference does not matter. For an algorithm tied to a specific regulatory clearance, it has to be understood and either eliminated or documented as within tolerance.

Library functions are not free. A one-line NumPy call for a Fourier transform or a digital filter is doing real work under the hood that has to be written by hand, or sourced from a vetted embedded math library, and then validated separately. There is no signal processing library sitting on a microcontroller waiting to be imported.

Memory discipline matters in a way it never does in Python. Dynamic allocation is something you actively avoid in embedded medical firmware. Buffers get sized up front, and every array the algorithm touches has to fit inside a budget that was fixed long before the algorithm was written.

Sampling and processing stop being batch operations. In Python, a validation run reads a full week of sensor data from a file and processes it as one array. On the device, data arrives continuously from an interrupt, a few samples at a time, and the algorithm has to process it that same continuous way, indefinitely, without drifting or leaking memory over days of runtime.

None of this is exotic. It is the standard cost of moving numerical code from a research environment to embedded hardware. What made this engagement specific was that every one of these decisions had to be documented and validated against the certified original before it could ship.

Validation didn't change. Only the target did

The comparison process was the same discipline used for the original Python refactor: run both implementations, the Python reference and the C candidate, against the same clinical input, log every deviation, investigate it, and resolve it before moving forward. The difference was a second constraint layered on top: correct behavior under real device conditions, not just correct output on a test file.

By the end, the bradykinesia, dyskinesia, and tremor algorithms were running directly on the Empatica wrist device, producing the same clinically validated outputs as the certified system they replaced, inside the constraints of a battery-powered microcontroller.

Why this matters beyond one project

Most companies building wearable medical devices eventually hit this exact wall: an algorithm that works, validated in Python or MATLAB during research, and a device that cannot run either. The gap between the two is not conceptual. It is a specific set of engineering decisions around memory, timing, and numerical precision, each of which has to be justified with evidence if the algorithm is tied to a regulatory clearance.

That is the part of this work that does not show up in a research paper or a pitch deck. It is also the part that determines whether a validated algorithm ever actually ships on a device.

Also see: Embedded Firmware Development · Clinical Algorithm Development

← All posts