Project site · Download a release · Documentation
Pendulum tracks leg movements during sleep, using a smartwatch worn at the ankle, and measures the rhythm between them. If your legs jerk or twitch at night — the thing a bed partner notices and you sleep through — this records how often it happens and how regular it is. Those movements are the motor sign that sleep physicians look for in restless legs syndrome (RLS) and periodic limb movement disorder (PLMD), and most people who have them never know, because they happen during sleep.
It measures; it does not interpret. It can show you that your legs move in a regular rhythm at night, and it cannot tell you what that means. That distinction is the whole design. A sleep physician diagnoses restless legs syndrome from your waking symptoms, using five clinical criteria; the movements Pendulum measures are a supporting criterion, not the diagnosis. What this application can honestly give you is a real measurement, taken over several nights, that you can put in front of a doctor instead of a description from memory.
The sleep period itself comes from a second, independent device through Health Connect, because one sensor cannot honestly measure both the movements and the sleep they happen in.
A pendulum's period does not depend on how far it swings. Huygens proved it in 1656, and it is why pendulums became clocks: the amplitude decays, the period holds.
The same split runs through this project. The conventional measure of these movements is a count per hour of sleep, and it is unstable — 43 % variation from one night to the next. The interval between movements varies by 3.6 %. Twelve times less, and with no denominator to argue about, since an interval is computed from the movement times alone. So Pendulum tracks the period and treats the count as something to hand a physician, not something to follow.
It is a real measurement. The accelerometer readings are real, the processing chain follows published scoring rules, and the numbers mean something. Used over several nights, it can give you a well-founded reason to book an appointment — or a well-founded reason not to worry.
It is not a medical device, an official health application, or a diagnosis. It has not been reviewed or approved by any health authority, it is not affiliated with any medical body, and it has never been validated against a sleep study. Its numbers are on a different scale from the ones a laboratory produces, for reasons explained in Limits.
So: take the result to a physician. Do not take a treatment decision from it, and do not let a reassuring number stop you from seeing someone if you have symptoms.
Status: pre-alpha. All four modules build. The two pure-JVM modules (binary format, signal processing) carry 195 unit tests; the phone and watch applications run on emulators and are covered by instrumented tests of the guard rails. What does not exist yet is a single night of real data: no accelerometer has been worn at an ankle by this software, and nothing has been validated against polysomnography — nor is there a plan that would make that possible for an individual.
Screens: docs/08-screens.md.
Full set with commentary: docs/08-screens.md.
Pendulum deliberately requires two devices, and this is not a limitation that can be engineered away.
| Where | What | Why |
|---|---|---|
| Ankle | A Wear OS watch (developed against a Pixel Watch 3) | The only class of consumer device whose raw accelerometer a third-party app can read at 50 Hz for eight hours. Fitbit, Oura, Whoop and Garmin expose aggregated vendor metrics, never the raw sensor. |
| Wrist, finger, or under the mattress | Any source that writes sleep sessions to Health Connect | It provides the denominator. Consumer wearables estimate total sleep time well and sleep stages poorly — and total sleep time is all the index needs. |
It is the accelerometer that does the work, not a gyroscope. A gyroscope costs 3 to 40 times more current depending on mode and adds nothing for a movement lasting 0.5 to 10 seconds.
The clinical convention is the PLM index — movements per hour of sleep. Pendulum computes it, and puts it in the report you would hand to a physician, because that is the number a sleep specialist reads.
But it is not the number the app tracks over time, for three reasons:
- Stability. Night-to-night variability of the hourly count is 43.2 % ± 37.1; of the mean log inter-movement interval, 3.6 % ± 3.7 (Skeba et al., Sleep Med 2016, PMID 26847989). Twelve times less.
- No denominator. The inter-movement interval and the periodicity index are computed from the movement onsets alone. The hourly count needs hours of sleep — and when that estimate is derived from the same accelerometer that supplies the movements, the metric becomes circular and self-amplifying.
- Scale. 39 % of movements scored on EMG produce no detectable motion at an ankle-worn sensor (Terrill et al., EMBC 2013, PMID 24111321). An accelerometric count is a different quantity, not a noisy estimate of the EMG one, so the 15/h clinical threshold does not transfer.
Fourteen nights, synthetic — the scatter is drawn from the published night-to-night variability, not from recorded data. Same person, same disorder, both panels: the count says something different every night, the rhythm says the same thing. That is the whole argument.
So the tracked quantity is the fundamental rhythm in seconds, recovered by deconvolving the harmonics of the inter-movement interval distribution. A missed movement merges two 21 s intervals into one 42 s interval — a harmonic, not noise. The mixture model that separates them also returns the estimated miss rate, which doubles as a night-comparability check.
pendulum/
├─ format/ Kotlin JVM append-only chunk codec, CRC-protected, wire structures
├─ algo/ Kotlin JVM integrity, timeline, DSP, detection, sleep mask, indices, synthetic truth
├─ wear/ Android foreground capture service, incremental sync
└─ phone/ Android ingestion, Health Connect, storage, Compose UI
Everything testable on a JVM lives outside the Android modules. algo depends on nothing at all —
not on Android, not even on format — so it can be exercised against synthetic signals with
injected ground truth.
./gradlew :format:test :algo:test # 195 unit tests, pure JVM
./gradlew :wear:assembleDebug :phone:assembleDebug
./gradlew :phone:connectedDebugAndroidTest # guard rails, needs a device or emulatorRequires JDK 17 and an Android SDK with platform 36. On WSL, if your build directory sits on a drvfs/9p Windows mount, Gradle will
fail on chmod; either remount with the metadata option or point -Ppendulum.buildRoot at a native
filesystem path.
Documentation lives in docs/. Start with docs/README.md, which is a
short index with three suggested reading orders.
| Document | Contents |
|---|---|
docs/01-overview.md |
What the project is, how a night flows through it, design decisions, guard rails, roadmap |
docs/02-science.md |
The phenomenon, the scoring rules, why periodicity rather than a count, and what is genuinely unknown |
docs/03-algorithm.md |
The processing chain stage by stage, rejected alternatives, a worked numerical example, full parameter tables |
docs/04-architecture.md |
Module map, capture on the watch, chunk format, transfer protocol, energy budget |
docs/05-devices.md |
Choosing the hardware and the sleep source; Health Connect integration |
docs/06-interface.md |
Interface principles, screens, how an uncertain number is displayed |
docs/07-validation.md |
How it is tested, the synthetic ground truth, and the current test status including what fails |
docs/08-screens.md |
Screenshots of the running application, and the three defects only a real render exposed |
docs/09-release.md |
Installing a release, and cutting the next one |
docs/references.md |
Bibliography, marked by whether each source was read in full, as an abstract, or not at all |
The original working documents are in French under docs/fr/. They are more detailed
than the English set and remain the authoritative record; where the two disagree, the French text
is correct.
These are not disclaimers added for form. They are the reasons the output must not be read as a diagnosis.
- A leg sensor cannot diagnose restless legs syndrome. The diagnosis is clinical — five IRLSSG criteria based on waking symptoms. Periodic limb movements are a supporting criterion, nothing more.
- The AASM issues a strong recommendation against actigraphy as a replacement for EMG in diagnosing periodic limb movement disorder (Smith et al., JCSM 2018).
- Without a respiratory channel, respiratory-related leg movements cannot be excluded. In the presence of sleep apnoea the index is structurally overestimated.
- A unilateral sensor misses movements of the opposite leg, biasing the count downward. This does not cancel the previous bias; do not assume the two compensate.
- A single night means nothing. In confirmed patients, the 15/h threshold is exceeded on only about a third of individual nights. The interface refuses to draw a trend below three nights, by design and not by warning.
Pendulum is dual-licensed.
- Source code is available under the GNU Affero General Public License v3.0 — see
LICENSE. You may use, study, modify and redistribute it freely, including for research, provided derivative works remain under the AGPL, and provided that a modified version offered to users over a network makes its complete source available to those users. - Documentation in
docs/is licensed under CC BY-NC-SA 4.0 — seeLICENSE-docs.
Commercial licensing. If you want to incorporate Pendulum into a closed-source or commercial
product without the obligations of the AGPL, a separate commercial licence is available. Open an
issue titled commercial licence to start the conversation.
Being honest about what this does and does not achieve: a licence cannot compel anyone to share revenue. It can only make a commercial user's cheapest legal path lead through a negotiation. Nothing here prevents a company from reading the specification and reimplementing the method independently — copyright protects the expression, not the algorithm.
Contributions are welcome, and require signing a Contributor Licence Agreement — see
CONTRIBUTING.md. Without it the project would lose the ability to offer
commercial licences at all, since that requires holding the rights to the whole work.
No consumer application or open-source project performing this measurement was found. The precedents are medical and out of reach (SOMNOwatch), discontinued (Philips PAM-RL, Actiwatch), or single-subject academic prototypes. The feasibility argument rests on Spektor et al., Clocks & Sleep 2024, which validates a unilateral ankle triaxial accelerometer against polysomnography — with the important caveat that its scoring was manual, not algorithmic.
Note also US patent 10,335,085 (Johns Hopkins), which mentions an accelerometer held on the leg by a strap for detecting periodic leg movements. Its claims and status have not been examined. This is raised as a fact worth knowing, not as legal advice.





