<?xml version="1.0" encoding="utf-8"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/"/>

<title>Jeff Epler's blog</title>
<modified>2021-10-25T02:19:30Z</modified>
<tagline>Photos, electronics, cnc, and more</tagline>
<author><name>Jeff Epler</name><email>jepler@unpythonic.net</email></author>
<entry>
<title>CWWVB: Putting what I've learned about WWVB to use in a new decoder</title>
<issued>2021-10-25T02:19:30Z</issued>
<modified>2021-10-25T02:19:30Z</modified>
<id>https://gamma.unpythonic.net/01635128370</id>
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/01635128370"/>
<content type="text/html" mode="escaped">

It's time to write a new WWVB decoder from scratch. This one relies on
regular sampling of the amplitude output of a low-cost WWVB receiver.
Since these receivers already introduce up to 100ms of phase shift, trying to
&amp;quot;&lt;a href=&quot;https://grammarist.com/eggcorns/home-in-hone-in/&quot;&gt;home in on&lt;/a&gt;&amp;quot; the exact start of second isn't too useful, but sampling at 20ms is quite enough to tell the 0/1/Mark symbols apart.


&lt;p&gt;&lt;br&gt;Unlike other decoders I've read about (or written), this one is neither based
on simple pulse lengths (PulseIn) nor does it have a &amp;quot;start of second
acquisition&amp;quot; phase separate from the &amp;quot;receive &amp;amp; decode a full minute&amp;quot; phase.
Instead, the 'start of second' is continuously tracked by statistics over the
last 30-60 seconds of data, and then at the end of each second a symbol is
decoded.

&lt;p&gt;The start of the second is the sample where the discrete derivative of the signal strength is greatest, at an offset of 0.16 here (based on a rather noisy set of input data):

&lt;p&gt;&lt;img src=&quot;https://media.unpythonic.net/emergent-files/01635128370/wwvbstats.png&quot;&gt;

&lt;p&gt;Because the statistic is continuously (but efficiently) tracked, it doesn't matter if the local sampling clock has an error relative to WWVB. This just causes the offset to slowly shift, but doesn't affect decoding.

&lt;p&gt;It's targeted at Cortex M microcontrollers, though it might fit on smaller
micros like those on the classic Arduino.  So far, I've only run it against
logs from the &lt;a href=&quot;https://github.com/wwvb-observatory/wwvb-observatory&quot;&gt;WWVB Observatory&lt;/a&gt;, but it far outperforms my existing CircuitPython WWVB
decoder (source code not online)---In an hour where my existing clock (using a
PulseIn-like strategy) recieved 0 minutes successfully due to storms in the
area, the new algorithm decoded 39 out of 59 minutes.

&lt;p&gt;The C++ code is called CWWVB and it is &lt;a href=&quot;https://github.com/jepler/cwwvb&quot;&gt;up on github&lt;/a&gt;! It's not fully commented, but
it does explain things more closely that this blog post does. My fresh receiver
modules aren't coming before the end of the month, but I'm thinking of doing a
very simple display, such as just showing MM:SS on a 4-digit 8-segment display,
with an &lt;a href=&quot;https://www.adafruit.com/product/3857&quot;&gt;Adafruit Feather M4&lt;/a&gt; for the microcontroller.
</content>
</entry>
<entry>
<title>WWVB Observatory</title>
<issued>2021-10-21T00:23:44Z</issued>
<modified>2021-10-21T00:23:44Z</modified>
<id>https://gamma.unpythonic.net/01634775824</id>
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/01634775824"/>
<content type="text/html" mode="escaped">

A lot of my play coding lately has been related to WWVB, a 60kHz radio time
signal broadcast from near Fort Collins, Colorado, USA.

&lt;p&gt;I'm calling my latest work the &amp;quot;&lt;a href=&quot;https://github.com/wwvb-observatory/wwvb-observatory&quot;&gt;WWVB Observatory&lt;/a&gt;&amp;quot;: I'm
capturing the amplitude signal from an inexpensive &amp;quot;MAS6180C&amp;quot; receiver
connected to a Raspberry Pi 50 times a second, and uploading the result to
github hourly.  The Pi is well-synchronized to the accurate time using NTP, and
while it's not running as real-time software, the 20ms sample rate doesn't seem
to pose any practical problems.

&lt;p&gt;I'm mostly interested in using the data &amp;quot;ex post facto&amp;quot; to develop and measure
the performance of different decoder algorithms, though I haven't started on
that part yet.  Others may have their own ideas.

&lt;p&gt;In principle, this software infrastructure can also be used with other clock
signals compatible with the MAS6180C: DCF77, HGB, MSF, JJY and BPC.

&lt;p&gt;The code and data are &lt;a href=&quot;https://github.com/wwvb-observatory/wwvb-observatory&quot;&gt;on github&lt;/a&gt;.  Right now
I'm hoping to operate it for at least months, and if there's ever another leap
second I fully intend to un-mothball it to try to record that very special
moment that happens only rarely in the past few years.

&lt;p&gt;One sub-part of the WWVB Observatory is an independent library for working
with the leap second database known as &amp;quot;leap-seconds.list&amp;quot;.  I've uploaded it
to &lt;a href=&quot;https://github.com/jepler/leapseconddata&quot;&gt;github&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://pypi.org/project/leapseconddata/&quot;&gt;pypi&lt;/a&gt;.

&lt;p&gt;I still need to break out my &amp;quot;advanced Linux timekeeping APIs&amp;quot; library,
erroneously called &amp;quot;&lt;a href=&quot;https://github.com/wwvb-observatory/wwvb-observatory/blob/main/src/clock_nanosleep.py&quot;&gt;clock_nanosleep.py&lt;/a&gt;&amp;quot;. It wraps clock_nanosleep, clock_gettime, clock_settime and
ntp_adjtime.  clock_nanosleep is interesting because you can sleep until a
particular &lt;b&gt;deadline&lt;/b&gt; specified against a particular timesource (UTC, TAI,
or monotonic being the useful options). Sleeping until a deadline is a
fundamental building block of &amp;quot;realtime-ish&amp;quot; code like the WWVB Observatory.
</content>
</entry>
<entry>
<title>My experience adding type annotations to a 2.5k-line Python library</title>
<issued>2021-10-05T23:41:03Z</issued>
<modified>2021-10-05T23:41:03Z</modified>
<id>https://gamma.unpythonic.net/01633477263</id>
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/01633477263"/>
<content type="text/html" mode="escaped">
The &lt;a href=&quot;https://github.com/jepler/wwvbpy&quot;&gt;wwvb package for Python&lt;/a&gt;
has been a focus of my recent hobby-time programming. I've used it as a place
to educate myself about the ins and outs of maintaining a Python package.
In the past, I used it to learn about using pylint, black &amp;amp; code coverage
to improve the quality of Python code.  Most recently, I added type annotations
through the whole package until &lt;tt&gt;mypy --strict&lt;/tt&gt; was happy with
the whole wwvb package and uwwvb module.

&lt;p&gt;The annotations were added in two steps: See pull requests &lt;a href=&quot;https://github.com/jepler/wwvbpy/pull/7&quot;&gt;#7&lt;/a&gt; and &lt;a href=&quot;https://github.com/jepler/wwvbpy/pull/8&quot;&gt;#8&lt;/a&gt;. Together, these PRs contained 320 insertions and 223 deletions across 14 python files, plus 6 insertions in 2 other files related to CI. I did the work during a part of a day, probably under 4 hours of time spent.  Since the package currently contains exactly 2500 physical lines of Python code, adding type annotations touched or added over 10% of physical lines!</content>
</entry>
<entry>
<title>Quick CircuitPython Driver for ES100 WWVB Receiver</title>
<issued>2021-06-24T01:43:49Z</issued>
<modified>2021-06-24T01:43:49Z</modified>
<id>https://gamma.unpythonic.net/01624499029</id>
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/01624499029"/>
<content type="text/html" mode="escaped">

I picked up an &lt;a href=&quot;https://www.universal-solder.ca/product/universal-solder-everset-es100-wwvb-bpsk-atomic-clock-starter-kit/&quot;&gt;ES100 WWVB receiver kit&lt;/a&gt;
and wrote a quick &amp;amp; dirty library to interact with it in CircuitPython.

&lt;p&gt;I'm not super thrilled with how the chip works; I imagined that the
date &amp;amp; time registers would act like an RTC after a successful reception,
but instead they just mark the second when reception &amp;amp; decoding completed
and are cleared to zero as soon as a new reception attempt is kicked off.

&lt;p&gt;Still, I'll have to figure out a clock to put it inside.  I am still
thinking of doing an edge-lit display version of the Roman Solar Clock,
so maybe that's where it'll go.

&lt;p&gt;The library is &lt;tt &gt;&lt;a href=&quot;https://media.unpythonic.net/emergent-files/01624499029/jepler_es100.py&quot;&gt;jepler_es100.py&lt;/a&gt;&lt;/tt&gt; and the example is &lt;tt &gt;&lt;a href=&quot;https://media.unpythonic.net/emergent-files/01624499029/code_es100.py&quot;&gt;code_es100.py&lt;/a&gt;&lt;/tt&gt; (rename to code.py).
I ran it on a Feather nRF52840 Expess with CircuitPython 6.3, but it should
work on a range of boards.

&lt;p&gt;Because the ES100 just locks up the I2C bus if you &amp;quot;repeated-start&amp;quot; it, I had
to use my custom rolled register library instead of &lt;tt &gt;adafruit_register&lt;/tt&gt;.
I did build it on top of &lt;tt &gt;adafruit_bus_device&lt;/tt&gt;.

&lt;p&gt;&lt;p&gt;&lt;b&gt;Files currently attached to this page:&lt;/b&gt;
&lt;table cellpadding=5 style=&quot;width:auto!important; clear:none!important&quot;&gt;&lt;col&gt;&lt;col style=&quot;text-align: right&quot;&gt;&lt;tr bgcolor=#eeeeee&gt;&lt;td&gt;&lt;a href=&quot;https://media.unpythonic.net/emergent-files/01624499029/code_es100.py&quot;&gt;code_es100.py&lt;/a&gt;&lt;/td&gt;&lt;td&gt;1.1kB&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor=#dddddd&gt;&lt;td&gt;&lt;a href=&quot;https://media.unpythonic.net/emergent-files/01624499029/jepler_es100.py&quot;&gt;jepler_es100.py&lt;/a&gt;&lt;/td&gt;&lt;td&gt;3.3kB&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;
</content>
</entry>
<entry>
<title>WWVB clock progress</title>
<issued>2011-09-12T20:29:28Z</issued>
<modified>2011-09-12T20:29:28Z</modified>
<id>https://gamma.unpythonic.net/01315859368</id>
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/01315859368"/>
<content type="text/html" mode="escaped">
&lt;a href=&quot;http://timeguy.com&quot;&gt;Chris&lt;/a&gt; was kind enough to pass along to me a
&lt;a href=&quot;http://www.sparkfun.com/products/10060&quot;&gt;commercial WWVB receiver&lt;/a&gt;.
This module was a bit of a pain to develop for, because most of the time the
interference from a nearby laptop computer is enough to seriously compromise
reception, and almost all of the time having a USB cable running from the
microcontroller to the laptop will kill reception outright.</content>
</entry>
<entry>
<title>wwvbpy: WWVB timecode generator in python</title>
<issued>2011-07-25T13:41:29Z</issued>
<modified>2011-07-25T13:41:29Z</modified>
<id>https://gamma.unpythonic.net/01311601289</id>
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/01311601289"/>
<content type="text/html" mode="escaped">

&lt;div style=&quot;float:right;clear:right&quot;&gt;&lt;!-- timecode.png--&gt;&lt;div class=albumouter style=width:306px id=&gt;&lt;div class=albumimage style=&quot;width:306px;margin-left:0.0px;&quot;&gt;&lt;a href=&quot;https://media.unpythonic.net/emergent-files/01311601289/timecode.png&quot; class=&quot;thickbox&quot; rel=&quot;album&quot; title=&quot;Timecode for the 1998 leap second&quot;&gt;&lt;img src=&quot;https://media.unpythonic.net/emergent-files/01311601289/timecode-small.jpg&quot; width=300 height=72&gt;&lt;/a&gt;&lt;div &gt;&lt;div style=&quot;float: right&quot; &gt;&lt;a href=&quot;https://media.unpythonic.net/emergent-files/01311601289/timecode.png&quot;&gt;&lt;img class=zoom src=&quot;https://media.unpythonic.net/emergent-files/default/zoom.png&quot;&gt;&lt;/a&gt;&lt;/div&gt;&lt;a href=&quot;https://media.unpythonic.net/emergent-files/01311601289/timecode.png&quot;&gt;Timecode for the 1998 leap second&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
A few weeks ago, I posted about a &lt;a href=&quot;https://gamma.unpythonic.net/01308865139&quot;&gt;WWVB timecode
generator written in C&lt;/a&gt;.  Unfortunately, this timecode generator did not
have a clear license permitting modification or redistribution, so I
felt I was unable to incorporate it into a project of my own.

&lt;p&gt;Thus was born my own timecode generator, called wwvbpy.  Its primary
output mode is compatible with the &amp;quot;wwvb2.c&amp;quot; that inspired it.  It also
has a few features that wwvb2.c didn't: automatic handling of DST, DUT1,
and leap seconds.  DST is handled according to the operating system's
rules for Denver.  DUT1 and leap seconds are handled using data from
IERS (As a result, my program's DUT1 does not exactly match past
broadcast data on WWVB, as the data NIST broadcasts is &amp;quot;an average value
for an extended range of dates&amp;quot;).

&lt;p&gt;It also has a set of tests of interesting times, such as the first and
second days after a DST change, the last and last-but-one days of leap
and non-leap years, a historical leap second, etc.  (where possible,
these test vectors were originally generated by wwvb2; however, some of
the tests—such as the DST tests—had to be hand-generated, as wwvb2
couldn't generate them; besides this limitation, I also uncovered a bug
in wwvb2 where non-leap years were treated as having 364 days and
leap-years were treated as having 365!)

&lt;p&gt;An option to output the timecode data to a serial device is contemplated
but not finished; ultimately, this would work together with an
Arduino/AVR firmware to produce a logic-level and/or 60kHz modulated
version of the signal for testing hardware devices.

&lt;p&gt;wwvbpy is covered by the GNU GPL v2+.  It can be obtained from my public
git repository: &lt;a href=&quot;http://git.unpy.net/view?p=wwvbpy.git;a=summary&quot;&gt;http://git.unpy.net/view?p=wwvbpy.git;a=summary&lt;/a&gt;.
</content>
</entry>
</feed>
