{"id":20629957,"url":"https://github.com/academe/msf-atomic-clock-decoder","last_synced_at":"2026-04-28T16:35:49.062Z","repository":{"id":43100292,"uuid":"511060514","full_name":"academe/msf-atomic-clock-decoder","owner":"academe","description":"Decode MSF atomic clock transmissions using ESP8266 and interrupts","archived":false,"fork":false,"pushed_at":"2022-07-27T22:32:10.000Z","size":41,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-17T07:07:17.857Z","etag":null,"topics":["arduino","arduino-library","cpp","esp8266"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/academe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-07-06T08:51:50.000Z","updated_at":"2022-07-17T13:23:56.000Z","dependencies_parsed_at":"2022-07-08T10:22:56.389Z","dependency_job_id":null,"html_url":"https://github.com/academe/msf-atomic-clock-decoder","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/academe%2Fmsf-atomic-clock-decoder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/academe%2Fmsf-atomic-clock-decoder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/academe%2Fmsf-atomic-clock-decoder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/academe%2Fmsf-atomic-clock-decoder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/academe","download_url":"https://codeload.github.com/academe/msf-atomic-clock-decoder/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242583300,"owners_count":20153422,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["arduino","arduino-library","cpp","esp8266"],"created_at":"2024-11-16T14:06:31.443Z","updated_at":"2026-04-28T16:35:44.043Z","avatar_url":"https://github.com/academe.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# msf-atomic-clock-decoder\n\nDecode MSF atomic clock transmissions using ESP8266 and interrupts.\n\nThis is a WIP propject, with lots of refactoring, exploration and trying things out.\n\nThe end aim is for a library to support decoding of MSF-60 (UK) atomic clock transmissions on an ESP8266, and ESP32.\n\nThe library will use interrupts, so the ESP/Arduino can carry on doing other things, such as handling the clock display.\n\nThe main components are:\n\n    carrier on/off timer -\u003e state machine bit extractor -\u003e date and time constructor -\u003e clock display\n\n## State Diagrams\n\nThe MSF transmitter switches the carrier on and off one or more times a second.\nIt splits time into 100mS intervals and changes the state of the carrier only\non one of those intervals.\n\nA 100mS division of a second will be referred to as a \"div\" in this library.\nEach second is divided into ten 100mS or deciseconds, or \"divs\".\nEach are labeled 1 to 10 in the state machine that drives the library.\n\n### Success Path Transistions\n\nEach transition is marked with the carrier state (`ON` or `OFF`) and the\nnumber of divs is has stayed in that state (each div is 100mS).\n\nThe success path from `wait_next_second` back to itself will contain\nexactly ten divs, adding up to one second. Each transition will be a\nstate change of the carrier from `ON` to `OFF`, or from `OFF` to `ON`.\n\n```mermaid\nstateDiagram-v2\n    [*] --\u003e wait_minute_marker_start\n    wait_minute_marker_start --\u003e wait_minute_marker_end : OFF 5\n    wait_minute_marker_end --\u003e wait_next_second : ON 5\\nBITS=11\n    wait_next_second --\u003e wait_0X_bit_1_end : OFF 1\n    wait_next_second --\u003e wait_10_end : OFF 2\n    wait_next_second --\u003e wait_11_end : OFF 3\n    wait_next_second --\u003e wait_minute_marker_end : OFF 5\n    wait_11_end --\u003e wait_next_second : ON 7\\nBITS=11\n    wait_10_end --\u003e wait_next_second : ON 8\\nBITS=10\n    wait_01_bit_2_end --\u003e wait_01_end : OFF 1\n    wait_0X_bit_1_end --\u003e wait_next_second : ON 9\\nBITS=00\n    wait_0X_bit_1_end --\u003e wait_01_bit_2_end : ON 1\n    wait_01_end --\u003e wait_next_second : ON 7\\nBITS=01\n```\n\n### Error Transition Recovery\n\nIf a carrier state for an unexpected number of divs occurs, then the\nminute lock is considered lost.\nThe state returns to waiting for the miniute marker start (5 divs of\ncarrier `OFF`) to get a lock on a new minute.\nLater there may be patterns that can be used to lock back on to the\nminiute earlier, or places where a small timing error can be ignored\nbecause they don't affect the bit stream being read.\n\n```mermaid\nstateDiagram-v2\n    wait_minute_marker_start --\u003e wait_minute_marker_end\n    wait_11_end --\u003e wait_minute_marker_start\n    wait_10_end --\u003e wait_minute_marker_start\n    wait_01_bit_2_end --\u003e wait_minute_marker_start\n    wait_0X_bit_1_end --\u003e wait_minute_marker_start\n    wait_01_end --\u003e wait_minute_marker_start\n    wait_minute_marker_end --\u003e wait_next_second\n```\n\n## (Binary Coded Decimal) BCD Decoding\n\nThe BCD encoded digits in the bit stream are listed in the table below.\n\n| Digit Number | Value Name | Bit A From | Bit A To | Magnitude |\n| ------------ | ---------- | ---------- | -------- | --------- |\n| 0            | Year       | 17         | 20       | Tens 0-9  |\n| 1            | Year       | 21         | 24       | Units 0-9 |\n| 2            | Month      | 25         | 25       | Tens 0-1  |\n| 3            | Month      | 26         | 29       | Units 0-9 |\n| 4            | Month Day  | 30         | 31       | Tens 0-3  |\n| 5            | Month Day  | 32         | 35       | Units 0-9 |\n| 6            | Week Day   | 36         | 38       | Units 0-6 |\n| 7            | Hour       | 39         | 40       | Tens 0-2  |\n| 8            | Hour       | 41         | 44       | Units 0-9 |\n| 9            | Minute     | 45         | 47       | Tens 0-5  |\n| 10           | Minute     | 48         | 51       | Units 0-9 |\n\nSo bits 17 to 15 will give is a stream of 11 decimal digits that can be\nconstructed into the year, month, day of month, day of week, hour and minute.\nEach series of bits representing one digit, are binary encoded MSB first, and\nthe length of the sequence will depend on the magnitude of the sequence.\n\nThe digits are organised into this table as a step to developing a\ndata-driven BCD decoder.\n\n# When Does The Time Apply?\n\nThis is a simple question, which does not seem to be well defined in the\nspecification at all. Here is what we do know.\n\nThe definition of a minute, is broadcast over the previous minute.\nSo the date and time, to the minute, is captured over the course of a minute,\nthat that date an time applies immediately the next minute starts.\n\nNow, when does a minute start? Is it the end of the minute marker, or\nthe beginning of the minute marker?\n\nLogic would suggest the minute starts at the *end* of the minute marker.\nEach second broadcasts data and state. You cannot predict what that second\nis going to broadcast until it has been broadcast, so it makes sense that\nthe end of the second, when it has finished broadcasting, is the point\nat which the state that second represents now applies.\n\nHowever, it seems that is not the case, based on other people's research.\nMy own observations confirm also that the minute starts at the *beginning*\nof the minute marker, the moment the carrier drops for its 500mS silence.\nOf course, you don't know it has dropped for 500mS under *after* it has\ndone so, so how can you tell the minute marker has started, marking the\nstart of the next minute?\n\nThis is solved using an alternate minute marker. For seconds 52 to 59,\nbit A will broadcast `01111110`. The end of that last `0` will be a\ndropped carier for the start of the 500mS minute marker, and *that*\nis the point the next minute starts. That is when the seconds count\nresets back to zero.\nSince this state machine looks at state changes at the end of seconds\nrather than at the start of seconds, then the new minute starts at the\nend of the last bit A `0`.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facademe%2Fmsf-atomic-clock-decoder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Facademe%2Fmsf-atomic-clock-decoder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facademe%2Fmsf-atomic-clock-decoder/lists"}