{"id":41887525,"url":"https://github.com/mkmcc/watch-calibration","last_synced_at":"2026-01-25T13:35:38.627Z","repository":{"id":138834999,"uuid":"51495552","full_name":"mkmcc/watch-calibration","owner":"mkmcc","description":"estimate the accuracy of a clock from a sound recording","archived":false,"fork":false,"pushed_at":"2025-04-15T21:35:15.000Z","size":331,"stargazers_count":17,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-15T22:28:13.797Z","etag":null,"topics":["calibration","data-analysis","fourier-analysis","signal-processing","watches"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mkmcc.png","metadata":{"files":{"readme":"readme.org","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-02-11T05:14:26.000Z","updated_at":"2025-04-15T22:13:43.000Z","dependencies_parsed_at":"2023-05-06T08:15:46.434Z","dependency_job_id":null,"html_url":"https://github.com/mkmcc/watch-calibration","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mkmcc/watch-calibration","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkmcc%2Fwatch-calibration","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkmcc%2Fwatch-calibration/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkmcc%2Fwatch-calibration/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkmcc%2Fwatch-calibration/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mkmcc","download_url":"https://codeload.github.com/mkmcc/watch-calibration/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkmcc%2Fwatch-calibration/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28753477,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T10:25:12.305Z","status":"ssl_error","status_checked_at":"2026-01-25T10:25:11.933Z","response_time":113,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calibration","data-analysis","fourier-analysis","signal-processing","watches"],"created_at":"2026-01-25T13:35:37.938Z","updated_at":"2026-01-25T13:35:38.613Z","avatar_url":"https://github.com/mkmcc.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"* Watch Calibration\n\n  This is a small program that analyzes a recording of a watch or\n  clock and tells you how slow or fast it runs.  You can use this to\n  regulate a mechanical watch using, eg, a cell phone to record its\n  ticking.\n\n* usage\n  \n  #+BEGIN_EXAMPLE\n  $ python fourier.py recording.wav\n\n  initial guess: 6.0       p/m 0.00606  Hz\n  updated guess: 5.9998549 p/m 1.88e-05 Hz\n  error is -2.1 seconds / day\n  #+END_EXAMPLE\n  \n  The file =recording.wav= should be a mono sound file.  My cell phone\n  has a \"voice memo\" program that records audio using the built-in\n  microphone.  It's plenty sensitive for this purpose.\n\n* examples\n\n  With a good-quality recording, you'll see something like this:\n\n  [[./test-results/plots/strong-signal.png]]\n\n  The top panel shows the last 10 seconds on the recording... this\n  lets you evaluate the quality of your data.  Here, you can clearly\n  see the individual ticks, and that this watch ticks 6 times per\n  second.  The signal-to-noise ratio is probably 10ish or so in this\n  example.\n\n  The bottom left plot shows the Fourier transform of the recording,\n  zoomed in to the range of 0.5-10 Hz.  The spike in this plot\n  corresponds to the tick frequency of the watch.  It's close to 6\n  ticks/per second, where it should be... but how close?\n\n  The bottom right plot shows a fit to the peak, used to estimate the\n  frequency to high precision.  This precision easily matches the\n  frequency stability of a mechanical watch (which varies slightly due\n  to differences in temperature and orientation, among other things).\n\n  With a (very) low quality recording, you'll instead see something\n  like this:\n\n  [[./test-results/plots/weak-signal.png]]\n\n  Here, the individual ticks are buried in the noise... but the\n  program still picks them out!  You need a longer recording to reach\n  the same level of precision, however.\n\n* accuracy\n\n  [[./test-results/plots/error-plot.png]]\n\n  I've tested the program using synthetic signals generated a couple\n  of different ways.  The results are summarized in the plot above.\n\n  The fractional uncertainty in the measurement scales falls with the\n  length of the recording to the (3/2) power.  It also depends on the\n  quality of your measurement.  So, while you want to make as good of\n  a recording as you can, it may be easier to make up for quality with\n  quantity.  I find that 5 minutes of crappy data is about as good as\n  30 seconds of perfect data.  But leaving your watch and phone alone\n  in a sock drawer for 5 minutes is a lot easier than going out and\n  buying a better microphone.  So it's up to you.\n\n  If the data is so bad that you can't even hear ticks in the\n  recording, you may be in trouble, though.  Try again in a quieter\n  place, or find a better microphone.  (And, of course, make sure your\n  clock actually ticks.)\n\n  The dotted black line shows measurements of a real watch.  I wanted\n  to adjust it to run with a few seconds per day and found a recording\n  of 30 seconds or one minute was adequate for the task.  You nudge\n  the regulator bar, record for a minute, nudge again, and so on until\n  you get an answer you're happy with.  I got it regulated to within\n  about 2 seconds per day, which was my goal.  That translates to an\n  error of less than a hundredth of a tick over the span of the\n  recording, which is pretty impressive, I think!\n\n  [[./test-results/plots/audacity-test.png]]\n\n  The plot above shows a test using a synthetic 'tick' signal\n  generated using Audacity.  This shows that the uncertainty reported\n  by the program is a pretty meaningful representation of the true,\n  \"1-σ\" uncertainty.\n\n* how it works\n  \n  The code is pretty straightforward, and most of the effort went into\n  making it run predictably without any human intervention.  I Fourier\n  transform the input signal to measure the peak frequency.  Before\n  transforming, i hit the signal with a Gaussian window... I choose\n  the shape of the window such that frequency peaks will have a\n  Gaussian shape with a width of ~3 frequency bins.  This can be fit\n  accurately with a Gaussian profile, yielding a good estimate of the\n  true frequency.  You can look at the plots to get a sense of how\n  well it's working.\n\n  I use a scipy function to automatically identify the peaks, which\n  seems to work pretty well.\n\n  And I attempt to identify an \"optimum\" length for the FFT.  No\n  problems with it so far.\n\n  Results aren't sensitive to the sampling frequency, unless it's too\n  low to adequately resolve the 'ticking' sound of the clock.  But the\n  program runs pretty fast, so there's no reason not to just use the\n  44.1 kHz sampling rate that's pretty standard.\n\n* todo\n  \n  1. Automatically suggest whether the user needs a longer recording,\n     or if we have enough data to make an accurate measurement.\n  2. improve handing of noise?\n  3. improve the (3/2) scaling?  if that's even possible?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkmcc%2Fwatch-calibration","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmkmcc%2Fwatch-calibration","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkmcc%2Fwatch-calibration/lists"}