I have taken a similar approach to Wayne's.

When WWVB blinks off my plan is to have a single GPS receiver in the house with 
a good antenna and to distribute from it a digital signal that will key little 
60 kHz units for each clock.


Attached is source code (well commented) for an Adafruit GPS module and Arduino 
processor to do that.


The protocol for bits 57 and 58 is this: 57 goes on 24 hours before DST comes 
on and 58 goes on at 0000 hrs (GMT) on the day of the change.  They both stay 
on until DST is to go off, then 57 goes off 24 hours before the change and 58 
goes off on the day of.


Andy Backus


________________________________
From: time-nuts <[email protected]> on behalf of Wayne Holder 
<[email protected]>
Sent: Wednesday, September 5, 2018 3:01 AM
To: Discussion of precise time and frequency measurement
Subject: Re: [time-nuts] WWVB Signal Generator

I've reworked my WWVB Simulator so it can now run on a slightly modified,
328-based Arduino (swapped in a 16.36 MHz crystal for the standard 16 MHz.)
 The new code is also now using a GPS module to set the time from the
GPS $GPRMC message and my BALDR clock just syncs up nicely.  I used a
GlobalTopGPS module (similar to the one used in this Adafruit GPS module
<https://www.adafruit.com/product/746>) but, with minor changes, it should

work with any GPS module.  The main change needed is configuring the
message used to set the module to only send $GPRMC messages (at a 1 second
interval) and suppress the others NMEA messages.

The last step is to to set the DST status bits (57 and 58) in the WWVB
message, but I'm little bit confused as to when to precisely set these
bits.  For reference, the the Java code (below) that I used to prototype
and test the algorithms before converting to C.  The code computes the
starting day of year and ending day of year and the idea is to use these
two DOY values to control to setting bits 57 and 58.  But, it's unclear to
me if I should set starting and ending state (code 2 and 1, respectively)
on the Sunday of the change or on the days before and after this date?  Or,
 are the starting and ending states set on Sunday and the other states on
the following Mondays?  I'm confused.

Wayne

public class DaylightSavings {
  private static final int daysToMonth[][] = {
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 },
  };
  private static final int MARCH = 3;
  private static final int NOVEMBER = 11;

  /*
   *  Compute state and end of Daylight Savings time for specified 4 digit year
   *    DST Starts the 2nd Sunday of March at 2:00 AM
   *    DST Ends the first Sunday of November at 1:00 AM
   */

  public static void main (String[] args) {
    int year = 2018;
    int start = getNthSundayOfMonth(2, MARCH, year);              //
Get 2nd Sunday of March
    int end = getNthSundayOfMonth(1, NOVEMBER, year);             //
Get 1st Sunday of November
    int startDoy = getDayOfYear(start, MARCH, isLeapYear(year));
    int endDoy = getDayOfYear(end, NOVEMBER, isLeapYear(year));
    System.out.println("DST starts: " + MARCH + "/" + start + "/" +
year + " (Day of Year: " + startDoy + ") ");
    System.out.println("DST ends:   " + NOVEMBER +  "/" + end + "/" +
year + " (Day of Year: " + endDoy + ")");
  }

  private static int getNthSundayOfMonth (int sundays, int month, int year) {
    for (int day = 1; day < 15; day++) {
      if (getDayOfWeek(month, day, year) == 2) {
        if (--sundays == 0) {
          return day;
        }
      }
    }
    // Should never get here
    return 0;
  }

  private static int getDayOfYear (int day, int month, boolean leapYear) {
    return daysToMonth[leapYear ? 1 : 0][month - 1] + day;
  }

  private static boolean isLeapYear (int year) {
    return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
  }

  /**
   * Compute Day Of Week (1-7) using Zeller's Method
   * @param month (1-12)
   * @param day (1-n)
   * @param year 4 digit year
   * @return day of week (1 = Sat, 2 = Su, 3 = Mon, 4 = Tue, 5 = Wed,
6 = Thr, 7 = Fri
   */
  private static int getDayOfWeek ( int month, int day, int year) {
    int cen = year / 100;
    year = year % 100;
    if (month == 1) {
      month = 13;
      year--;
    } else if (month == 2) {
      month = 14;
      year--;
    }
    return (day + 13 * (month + 1) / 5 + year + year / 4 + cen / 4 + 5
* cen) % 7 + 1;
  }
}


On Sat, Sep 1, 2018 at 5:14 PM Wayne Holder <[email protected]> wrote:

> My 15.36 MHz crystals arrived and using one to replace the 8 MHz crystal
> seems to, again, improve the ability of the BALDR clock to lock onto and
> decode the signal.  It now reliably syncs if the clock is within 6-7 inches
> of the tuned, ferrite rod antenna (still just wrapping the wire around the
> rod.)  With just a loose, wire antenna, the clock syncs if the antenna is
> with 2-3 inches of the clock, which is also an improvement.
>
> I'm also starting to work on moving the code over to an Arduino using an
> old Duemilanove board I had lying around, as ithe crystal it uses is a
> standard, HC-49 package, which makes it easer to replace than the surface
> mount crystals used on modern Arduino boards (you can also get a cheap clone
> of the Duemilanove on eBay
> <https://www.ebay.com/itm/Duemilanove-USB-Board-2009-ATMega328P-PU-Microcontroller-Compatible-With-Arduino/182256700970>
>  for
> about $6 + shipping.)  And, as near as I can tell, 15.36 MHz is close
> enough to 16 MHz that I didn't have to modify the boot loader and uploads
> seem to work ok.  Just swap the crystal and go.  So, that's one less thing
> to worry about.
>
> Wayne
>
> On Thu, Aug 30, 2018 at 8:04 PM paul swed <[email protected]> wrote:
>
>> Wayne very good progress. You can actually feed the loop coild that exists
>> with the cap it should resonate.
>> Thats my plan at least.
>> Regards
>> Paul
>> WB8TSL
>>
>> On Thu, Aug 30, 2018 at 9:44 PM, Wayne Holder <[email protected]>
>> wrote:
>>
>> > I've had some luck improving things with my ATTiny85-based WWVB
>> Simulator
>> > design by replacing the crappy, 8 MHz internal oscillator with an 8 MHz
>> > crystal and removing the tweaked timer values I had previously used.  In
>> > addition, based on a suggestion from Paul Swed, I tried looping the
>> antenna
>> > wire a few times around the ferrite rod of a WWVB receiver module I
>> > happened to have lying around and this also greatly improved things (see
>> > photo on web page at
>> > https://sites.google.com/site/wayneholder/controlling-time).  In fact,
>> > with
>> > the ferrite rod in place, the BALDR clock now syncs even when completely
>> > disconnected from being grounded to the ATTiny85 and the scope.
>> >
>> > I've updated my web page, and the source code at the bottom of the page,
>> > accordingly.  BTW, the SYNC output is now moved to pin 7 and the PPS
>> output
>> > is currently disabled in the code. In addition, I've added some
>> additional
>> > info on my web page about how to compile and download the program to an
>> > ATTiny85 using ATTinyCore by Spence Konde.
>> >
>> > I've ordered a 15.36 MHz crystal to try, as that should let the ATTiny85
>> > generate a true, 60,000 Hz output but, so far, the 8 MHz crystal has
>> helped
>> > improve things quite a bit.  In addition, I plan to do more tests on
>> > different types of antennas in order to see if I can make things even
>> more
>> > reliable and stable.
>> >
>> > I still plan on reworking the code so it can also run on a 328-based
>> > Arduino board but, currently, the Arduino IDE has no easy way to work
>> with
>> > boards that don't use a standard, 16 MHz crystal, as this frequency is
>> used
>> > by the serial port and, in turn, by the boot loader, so altering it can
>> > break the ability to upload code.  This has actually caused some issues
>> for
>> > some of my other projects, so I'm investigating how this issue might be
>> > handled.
>> >
>> > Also, if anyone is interested in trying out other modulation schemes, I
>> can
>> > easily add a compile option t the code that will let it output a binary
>> > low/high modulation signal instead of the PWM signal.
>> >
>> > Wayne
>> >
>> > On Thu, Aug 30, 2018 at 8:53 AM paul swed <[email protected]> wrote:
>> >
>> > > Wayne as I work through the chronverter I do know the good phase
>> tracking
>> > > clocks really demand on frequency behavior. As I measured its +/- .6
>> Hz
>> > at
>> > > 60 KHz. I believe the cheapy wall clocks are a bit wider, but not
>> sure as
>> > > they are hard to actually measure. They do use a small tuning fork
>> > crystal
>> > > and from experience these are sharp. When I experimented with them
>> they
>> > > were maybe 5 Hz. Indeed the Chinese website had 25 X 60 KHz crystals
>> for
>> > > maybe $2.
>> > > With respect to the antenna. My thinking is a loopstick resonated on
>> 60
>> > KHz
>> > > and most likely driving it push pull or single ended. Thats 1
>> transistor
>> > if
>> > > single ended as common collector if I had to guess. The reason is the
>> > > micros put out a fair level of signal so its a case of upping current
>> > into
>> > > the antenna. But it really will be a bit of experimenting.
>> > > I did look at your code and that was so nice it opened up straight
>> into
>> > the
>> > > arduino IDE.
>> > > Regards
>> > > Paul
>> > > WB8TSL
>> > >
>> > > On Thu, Aug 30, 2018 at 5:12 AM, Wayne Holder <[email protected]
>> >
>> > > wrote:
>> > >
>> > > > For anyone trying out my ATTiny85 code, I've done some additional
>> tests
>> > > and
>> > > > find that placement of the antenna near the clock is very finicky
>> and,
>> > so
>> > > > far, the only way to get a reliable decode of the time in the clock
>> is
>> > by
>> > > > using a scope to monitor the demodulated output and then moving the
>> > > antenna
>> > > > around until the demodulated signal lines up cleanly with modulated
>> > > carrier
>> > > > and there are no intra bit glitches.  This can take a bit of
>> patience,
>> > so
>> > > > clearly a better solution needs to be found.  I've found that any
>> type
>> > of
>> > > > glitch in the demodulated signal seems to prevent the clock chip
>> from
>> > > > decoding the time.
>> > > >
>> > > > It's possible the difficultly with locking onto my simulated WWVB
>> > signal
>> > > > may be partially due to the design of the clock (from my location
>> it's
>> > > > never been able to to lock onto the real WWVB signal), but I have no
>> > > > reference to compare it against so, for now, I have conclude that
>> the
>> > > > PWM-based modulation scheme my code uses may also be suboptimal for
>> > this
>> > > > application.  To make testing even more frustrating, the BALDR clock
>> > I'm
>> > > > using will only look for a signal for about 6 minutes before it
>> goes to
>> > > > sleep and I have to then power cycle the clock to get it to listen
>> > again.
>> > > >
>> > > > So, keep this in mind if you're going to try and replicate my
>> results.
>> > > >
>> > > > Wayne
>> > > >
>> > > > On Wed, Aug 29, 2018 at 6:03 PM Wayne Holder <
>> [email protected]>
>> > > > wrote:
>> > > >
>> > > > > For those that have asked for my to publish the source code for my
>> > > > > ATTiny85-based WWVB simulator, I have put up a somewhat hurriedly
>> > > written
>> > > > > page on my google site at:
>> > > > >
>> > > > >   https://sites.google.com/site/wayneholder/controlling-time
>> > > > >
>> > > > > that describes a bit about how the code works, how to compile it
>> > using
>> > > > the
>> > > > > Arduino IDE, how I tested it, some issues I have observed in
>> testing
>> > it
>> > > > > and, at the bottom of the page, a downloadable zip file that
>> contains
>> > > the
>> > > > > complete source code.
>> > > > >
>> > > > > Note: as mentioned at the top of this page, this is currently a
>> work
>> > in
>> > > > > process, so I'm not yet going to link the article to my main
>> website
>> > > > page,
>> > > > > so you'll need to link in this post to find it.  Also, as draft,
>> I'm
>> > > > going
>> > > > > to continue to revise the page until I feel the project is
>> complete
>> > > > enough
>> > > > > to publish.  That means the source code zip file is going to
>> > > potentially
>> > > > > change from time to time, too.
>> > > > >
>> > > > > Wayne
>> > > > >
>> > > > > On Wed, Aug 29, 2018 at 1:35 AM Wayne Holder <
>> [email protected]
>> > >
>> > > > > wrote:
>> > > > >
>> > > > >> As a follow up, I now have a simple WWVB simulator written in C
>> > that's
>> > > > >> now running an an ATTiny85 using nothing more than the internal,
>> 8
>> > > > >> mHz oscillator and about a 6 inch length of wire connected to
>> one of
>> > > the
>> > > > >> pins as an antenna.  It generates an approximate 60 kHz signal
>> using
>> > > > PWM on
>> > > > >> timer 1.  I tweaked the timer value a bit to correct for some
>> > variance
>> > > > in
>> > > > >> the internal oscillator, but I' not even sure that was
>> necessary, as
>> > > my
>> > > > >> target is just a  BALDR Model B0114ST, consumer grade "Atomic"
>> > clock.
>> > > > >> Modulation is done by varying the duty cycle of the PWM to
>> > approximate
>> > > > the
>> > > > >> -17 dBr drop on the carrier.  But, again, I don't think this
>> value
>> > is
>> > > > >> critical with a consumer clock chip.  I tapped the demodulated
>> > output
>> > > > >> inside the clock and displayed it on my scope along with the
>> > generated
>> > > > >> signal and I got good, steady demodulation with the wire antenna
>> > just
>> > > > >> placed near clock.  The next step is to connect up a GPS module
>> and
>> > > add
>> > > > >> code to use it to set the time.  I'm also going to change the
>> code
>> > to
>> > > > use
>> > > > >> the PPS signal from the GPS to drive the output timing rather
>> than
>> > the
>> > > > test
>> > > > >> code I have now that uses timer 0 to generate the PPS interrupt.
>> > I'm
>> > > > happy
>> > > > >> to share details if anyone is interested.
>> > > > >>
>> > > > >> Wayne
>> > > > >>
>> > > > >>
>> > > > >>
>> > > > >> On Sun, Aug 26, 2018 at 2:51 PM, paul swed <[email protected]>
>> > > wrote:
>> > > > >>
>> > > > >>> That would be a great neighbor to have but I can tell you around
>> > here
>> > > > its
>> > > > >>> the phone. Not to concerned about someone putting up a wwvb
>> > > > replacement.
>> > > > >>> And I can always up the power. Chickle.
>> > > > >>> Regards
>> > > > >>> Paul
>> > > > >>>
>> > > > >>> On Sun, Aug 26, 2018 at 2:34 PM, Bob kb8tq <[email protected]>
>> wrote:
>> > > > >>>
>> > > > >>> > Hi
>> > > > >>> >
>> > > > >>> > The gotcha is if you have neighbors two or three doors away
>> that
>> > > > *also*
>> > > > >>> > put up one of
>> > > > >>> > these devices. You then have a real problem with the
>> neighbor(s)
>> > in
>> > > > the
>> > > > >>> > middle. The
>> > > > >>> > wavelength is long enough that Raleigh issues won’t get you.
>> You
>> > > > still
>> > > > >>> > have the two
>> > > > >>> > signals ( at slightly different frequencies) beating against
>> each
>> > > > >>> other.
>> > > > >>> > The result is
>> > > > >>> > going to show up as who knows what to this or that receiver.
>> > With a
>> > > > >>> > precision receiver,
>> > > > >>> > you might even have issues from the guy two houses away …...
>> > > > >>> >
>> > > > >>> > Bob
>> > > > >>> >
>> > > > >>> > > On Aug 26, 2018, at 1:08 PM, paul swed <[email protected]
>> >
>> > > > wrote:
>> > > > >>> > >
>> > > > >>> > > Agree with the conversation. With respect to neighbors when
>> the
>> > > day
>> > > > >>> comes
>> > > > >>> > > they may ask you to boost your signal. :-)
>> > > > >>> > > Granted maybe the day won't come but at least having your
>> local
>> > > > >>> clocks
>> > > > >>> > work
>> > > > >>> > > is nice.
>> > > > >>> > > Regards
>> > > > >>> > > Paul
>> > > > >>> > > WB8TSL
>> > > > >>> > >
>> > > > >>> > > On Sat, Aug 25, 2018 at 10:29 PM, Dana Whitlow <
>> > > > >>> [email protected]>
>> > > > >>> > > wrote:
>> > > > >>> > >
>> > > > >>> > >> With the watch being physically close to the faux WWVB
>> > > > >>> "transmitter",
>> > > > >>> > one
>> > > > >>> > >> is in
>> > > > >>> > >> the so-called "near field" regime, where the field strength
>> > > (V/m)
>> > > > >>> falls
>> > > > >>> > as
>> > > > >>> > >> the inverse
>> > > > >>> > >> cube of the distance.  If one is putting the watch, say,
>> > within
>> > > a
>> > > > >>> few
>> > > > >>> > >> inches of the
>> > > > >>> > >> transmitter, reliable reception should be available yet the
>> > > signal
>> > > > >>> > should
>> > > > >>> > >> be literally
>> > > > >>> > >> undetectable by any practical receiving device more than a
>> few
>> > > > feet
>> > > > >>> > away.
>> > > > >>> > >> Hence,
>> > > > >>> > >> meeting the FCC field strength limit should be trivial.if
>> the
>> > > > >>> device is
>> > > > >>> > >> used as pictured.
>> > > > >>> > >> However, if one cranks up the power enough to reliably
>> cover
>> > > one's
>> > > > >>> > entire
>> > > > >>> > >> house,
>> > > > >>> > >> then there might be a problem depending how close the
>> nearest
>> > > > >>> neighbor
>> > > > >>> > >> lives,
>> > > > >>> > >> even at levels well within the FCC limit he quotes.
>> > > > >>> > >>
>> > > > >>> > >> Taking the near field relationship in hand, 40 uV/m at 300m
>> > > would
>> > > > >>> > translate
>> > > > >>> > >> into
>> > > > >>> > >> a whopping 0.135 V/m at 20 meters range, more than enough
>> to
>> > > feed
>> > > > >>> most
>> > > > >>> > >> peoples'
>> > > > >>> > >> entire house.  So the pragmatic issue would again be-
>> > neighbors.
>> > > > >>> On the
>> > > > >>> > >> other
>> > > > >>> > >> hand, most of them would never be aware of the local
>> signal as
>> > > > long
>> > > > >>> as
>> > > > >>> > they
>> > > > >>> > >> get good
>> > > > >>> > >> time settings, unless they live close enough to Ft. Collins
>> > for
>> > > > the
>> > > > >>> two
>> > > > >>> > >> signals to
>> > > > >>> > >> contend with each other.
>> > > > >>> > >>
>> > > > >>> > >> It looks to me like the ferrite rod antenna is considerable
>> > > > >>> overkill.
>> > > > >>> > Even
>> > > > >>> > >> with no
>> > > > >>> > >> purposeful antenna I'd expect leakage to yield sufficient
>> > signal
>> > > > >>> for at
>> > > > >>> > >> least a few
>> > > > >>> > >> inches.
>> > > > >>> > >>
>> > > > >>> > >> Dana
>> > > > >>> > >>
>> > > > >>> > >>
>> > > > >>> > >> On Sat, Aug 25, 2018 at 8:11 PM Wayne Holder <
>> > > > >>> [email protected]>
>> > > > >>> > >> wrote:
>> > > > >>> > >>
>> > > > >>> > >>> This guy has what looks like a well thought out design
>> using
>> > a
>> > > > >>> > Sirf-Based
>> > > > >>> > >>> GPS and ATTiny44A chip to generate a signal to update his
>> > > watch:
>> > > > >>> > >>>
>> > > > >>> > >>>  https://www.anishathalye.com/2016/12/26/micro-wwvb/
>> > > > >>> > >>>
>> > > > >>> > >>> Unfortunately, he doesn't seem to have published a
>> schematic
>> > or
>> > > > his
>> > > > >>> > >> source
>> > > > >>> > >>> code.  But, he covers enough detail that I think it
>> wouldn't
>> > be
>> > > > too
>> > > > >>> > hard
>> > > > >>> > >> to
>> > > > >>> > >>> replicate what he's done.  Or, perhaps he would disclose
>> > these
>> > > > >>> details
>> > > > >>> > if
>> > > > >>> > >>> contacted.
>> > > > >>> > >>>
>> > > > >>> > >>> Wayne
>> > > > >>> > >>>
>> > > > >>> > >>> On Sat, Aug 25, 2018 at 4:33 AM, D. Resor <
>> > > > [email protected]>
>> > > > >>> > >> wrote:
>> > > > >>> > >>>
>> > > > >>> > >>>> I thought I would search in a different way for a WWVB
>> > signal
>> > > > >>> > generator
>> > > > >>> > >>>> design.  I found this item.  While the designer explains
>> it
>> > > > isn't
>> > > > >>> as
>> > > > >>> > >>>> accurate as WWVB it may be another starting point.
>> > > > >>> > >>>>
>> > > > >>> > >>>> http://www.tauntek.com/wwvbgen-low-cost-wwvb-time-
>> > > > >>> > signal-generator.htm
>> > > > >>> > >>>>
>> > > > >>> > >>>>
>> > > > >>> > >>>>
>> > > > >>> > >>>> Donald R. Resor Jr. T. W. & T. C. Svc. Co.
>> > > > >>> > >>>> http://hammondorganservice.com
>> > > > >>> > >>>> Hammond USA warranty service
>> > > > >>> > >>>> "Most people don't have a sense of humor. They think they
>> > do,
>> > > > but
>> > > > >>> they
>> > > > >>> > >>>> don't." --Jonathan Winters
>> > > > >>> > >>>>
>> > > > >>> > >>>> _______________________________________________
>> > > > >>> > >>>> time-nuts mailing list -- [email protected]
>> > > > >>> > >>>> To unsubscribe, go to http://lists.febo.com/mailman/
>> > > > >>> > >>>> listinfo/time-nuts_lists.febo.com
>> > > > >>> > >>>> and follow the instructions there.
>> > > > >>> > >>>>
>> > > > >>> > >>> _______________________________________________
>> > > > >>> > >>> time-nuts mailing list -- [email protected]
>> > > > >>> > >>> To unsubscribe, go to
>> > > > >>> > >>>
>> > > http://lists.febo.com/mailman/listinfo/time-nuts_lists.febo.com
>> > > > >>> > >>> and follow the instructions there.
>> > > > >>> > >>>
>> > > > >>> > >> _______________________________________________
>> > > > >>> > >> time-nuts mailing list -- [email protected]
>> > > > >>> > >> To unsubscribe, go to http://lists.febo.com/mailman/
>> > > > >>> > >> listinfo/time-nuts_lists.febo.com
>> > > > >>> > >> and follow the instructions there.
>> > > > >>> > >>
>> > > > >>> > > _______________________________________________
>> > > > >>> > > time-nuts mailing list -- [email protected]
>> > > > >>> > > To unsubscribe, go to http://lists.febo.com/mailman/
>> > > > >>> > listinfo/time-nuts_lists.febo.com
>> > > > >>> > > and follow the instructions there.
>> > > > >>> >
>> > > > >>> >
>> > > > >>> > _______________________________________________
>> > > > >>> > time-nuts mailing list -- [email protected]
>> > > > >>> > To unsubscribe, go to http://lists.febo.com/mailman/
>> > > > >>> > listinfo/time-nuts_lists.febo.com
>> > > > >>> > and follow the instructions there.
>> > > > >>> >
>> > > > >>> _______________________________________________
>> > > > >>> time-nuts mailing list -- [email protected]
>> > > > >>> To unsubscribe, go to
>> > > > >>> http://lists.febo.com/mailman/listinfo/time-nuts_lists.febo.com
>> > > > >>> and follow the instructions there.
>> > > > >>>
>> > > > >>
>> > > > >>
>> > > > _______________________________________________
>> > > > time-nuts mailing list -- [email protected]
>> > > > To unsubscribe, go to http://lists.febo.com/mailman/
>> > > > listinfo/time-nuts_lists.febo.com
>> > > > and follow the instructions there.
>> > > >
>> > > _______________________________________________
>> > > time-nuts mailing list -- [email protected]
>> > > To unsubscribe, go to
>> > > http://lists.febo.com/mailman/listinfo/time-nuts_lists.febo.com
>> > > and follow the instructions there.
>> > >
>> > _______________________________________________
>> > time-nuts mailing list -- [email protected]
>> > To unsubscribe, go to http://lists.febo.com/mailman/
>> > listinfo/time-nuts_lists.febo.com
>> > and follow the instructions there.
>> >
>> _______________________________________________
>> time-nuts mailing list -- [email protected]
>> To unsubscribe, go to
>> http://lists.febo.com/mailman/listinfo/time-nuts_lists.febo.com
>> and follow the instructions there.
>>
>
_______________________________________________
time-nuts mailing list -- [email protected]
To unsubscribe, go to 
http://lists.febo.com/mailman/listinfo/time-nuts_lists.febo.com
and follow the instructions there.
/*                                  WWVB Translator
                                    A C Backus 8/18

To provide a digital signal to key a low power 60 kHz transmitter
to simulate the WWVB time code signal, taking the time and date from the GPS 
system.
Code is for Adafruit GPS modules using MTK3329/MTK3339 driver.
*/

#include <Adafruit_GPS.h>              // must have downloaded this library
#include <SoftwareSerial.h>

//   Connect the GPS TX pad to Digital 8
//   Connect the GPS RX pad to Digital 7

SoftwareSerial mySerial(8, 7);         // start soft serial and set pins for I/O

// set data rates:
#define PMTK_SET_NMEA_UPDATE_01HZ "$PMTK220,10000*2F"  // 0.1 Hz, 10 sec wait
#define PMTK_SET_NMEA_UPDATE_02HZ "$PMTK220,5000*1B"   // 0.2 Hz, 5 sec wait
#define PMTK_SET_NMEA_UPDATE_1HZ  "$PMTK220,1000*1F"
#define PMTK_SET_NMEA_UPDATE_5HZ  "$PMTK220,200*2C"
#define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F"

// set baud rates for GPS:
#define PMTK_SET_NMEA_BAUDRATE_38400 "PMTK251,38400*27"
#define PMTK_SET_NMEA_BAUDRATE_9600 "$PMTK251,9600*17"
#define PMTK_SET_NMEA_BAUDRATE_4800 "PMTK251,4800*14"

// turn on only RMC sentence:
#define PMTK_SET_NMEA_OUTPUT_RMCONLY 
"$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"
// turn on RMC and GGA sentences:
#define PMTK_SET_NMEA_OUTPUT_RMCGGA 
"$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
// turn on ALL THE DATA:
#define PMTK_SET_NMEA_OUTPUT_ALLDATA 
"$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
// turn off output:
#define PMTK_SET_NMEA_OUTPUT_OFF 
"$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
// get hardware version:
#define PMTK_Q_RELEASE "$PMTK605*31"
// turn off the default antenna report:
#define PGCMD_SET_NMEA_ANTRPTOFF "$PGCMD,33,0*6D"

char c = 'A';                    // dummy character variable
int n = 0;                       // dummy counting variable
int x = 0;                       // dummy counting variable
unsigned long timestamp = 0;     // time stamp for data
char sentence[75];               // data sentence

int dst = 0;                     // 1 for DST; 0 for standard
int dstwarn = 0;                 // changes one day before DST change
int dayDSTon = 0;                // day number(of the year) when DST goes on
int dayDSToff = 0;               // day number(of the year) when DST goes off

int year = 0;                    // two-digit year, beginning 2000
int month = 0;                   // 1-12
int dayyear = 0;                 // day number within the year
int daytemp = 0;                 // temp storage of day number
int daymonth = 0;                // day number within the month
int hour = 0;                    // 0-23
int minute = 0;                  // 0-59
int second = 0;                  // 0-59
int msecond = 0;                 // milliseconds

void setup()
{  
  pinMode(2,OUTPUT);             // HIGH turns on 60 kHz carrier
  digitalWrite(2, HIGH);         // default for signal is ON
  
  Serial.begin(115200);          // start serial connection over USB
  mySerial.begin(9600);          // start soft serial connection at default GPS 
rate

  mySerial.println(PMTK_SET_NMEA_BAUDRATE_9600);    // set GPS baud rate to 9600
  mySerial.println(PMTK_SET_NMEA_UPDATE_10HZ);      // set data rate at 10 Hz
  mySerial.println(PGCMD_SET_NMEA_ANTRPTOFF);       // turn off antenna report
  mySerial.println(PMTK_SET_NMEA_OUTPUT_RMCONLY);   // turn output on only for 
RMC
  
  delay(2000);                   // wait for GPS to send ack sentences
}

void loop()
{
  getdata();                     // get 74 data characters starting after '$' 
(final is a null)
  getdata();                     // do again to be sure everything was synced up
  while(int(sentence[10])==53 || timestamp > 4294907296)
  {                              // check if sentence too close to the end of 
the minute (at 50+ sec)
    getdata();                   // or the millis() max bits rollover (within 
60 seconds)
  }                              // if either, get another sentence
  parsedata();                   // read charater data into variables
  dayyearcalc();                 // calculate day number of the year from date 
and month
  minuteincrement();             // set variables for the next minute
  dstset();                      // calculate and set DST flags
  
// calulate delay before starting transmit on the next minute (with final 
correction):
  delay(60000 - (1000*second + msecond) - (millis() - timestamp) - 300);

  Serial.print("Year: ");
  Serial.println(year);
  Serial.print("Day: ");
  Serial.println(dayyear);
  Serial.print("Hour: ");
  Serial.println(hour);
  Serial.print("Minute: ");
  Serial.println(minute);
  Serial.println();
  
// run five minutes before refreshing data:
  for(n=0;n>=5;n++)
  {
    transmit();
    minuteincrement();
  }
}

// getdata() reads the serial output from the GPS, first clearing out the 
receive buffer,
// then looking for a '$', which marks the beginnning of the RMC sentence -- 
then
// reading the next 74 characters into the sentence[75] character array:
void getdata()
{
  while(mySerial.available())      // clear out receive buffer
  {
    c = mySerial.read();
  }
  
  c = 'x';
  
  while(c != '$')                  // look for '$'
  {
    if(mySerial.available())
    {
      c = mySerial.read();
      timestamp = millis();        // time stamp the sentence
    }
  }
  
  n = 0;
  
  while(n < 74)                    // collect the next 74 characters
  {
    if(mySerial.available())
    {
      sentence[n] = mySerial.read();
      n = n + 1;
    }
  }
}

// parsedata() reads the character data from the sentence[75] character array 
and
// populates the appropriate variables with corresponding integer data, 
converting
// from ASCII character numbers for the digits to the digits themselves by
// subtracting 48, the ASCII decimal code for zero:
void parsedata()
{
// the time data appear first in the RMC sentence, six characters beyond the 
start:
  hour = 10*(int(sentence[6])-48) + int(sentence[7])-48;
  minute = 10*(int(sentence[8])-48) + int(sentence[9])-48;
  second = 10*(int(sentence[10])-48) + int(sentence[11])-48;
  msecond = 100*(int(sentence[13])-48) + 10*(int(sentence[14])-48) + 
int(sentence[15])-48;

// the date data appear near the end, just before the * signifying that the 
error
// correction bytes follow -- whether or not there is a GPS fix:
  for (n=15;sentence[n]!='*';n++)  // find '*', looking beyond the time data
  {
    x = n - 9;
  }
  daymonth = 10*(int(sentence[x])-48) + int(sentence[x+1])-48;
  month = 10*(int(sentence[x+2])-48) + int(sentence[x+3])-48;
  year = 10*(int(sentence[x+4])-48) + int(sentence[x+5]-48);
}

void dayyearcalc()                 // determine the dayyear from the month and 
daymonth
{
  x = 0;
  if(year%4 == 0)                  // set x to 1 if leap year
  {
    x = 1;
  }
  if(month == 1)
  {
    dayyear = daymonth;
  }
  if(month == 2)
  {
    dayyear = daymonth + 31;
  }
  if(month == 3)
  {
      dayyear = daymonth + 59 + x;
  }
  if(month == 4)
  {
      dayyear = daymonth + 90 + x;
  }
  if(month == 5)
  {
      dayyear = daymonth + 120 + x;
  }
  if(month == 6)
  {
      dayyear = daymonth + 151 + x;
  }
  if(month == 7)
  {
      dayyear = daymonth + 181 + x;
  }
  if(month == 8)
  {
      dayyear = daymonth + 212 + x;
  }
  if(month == 9)
  {
      dayyear = daymonth + 243 + x;
  }
  if(month == 10)
  {
      dayyear = daymonth + 273 + x;
  }
  if(month == 11)
  {
      dayyear = daymonth + 304 + x;
  }
  if(month == 12)
  {
      dayyear = daymonth + 334 + x;
  }
}

// dstset() determines the dayyear for the start and end of DST then sets or 
unsets
// the flags for the 57th and 58th seconds in the WWVB time code:
void dstset()
{
  daytemp = dayyear;          // store current dayyear

  month = 3;                  // determine dayyear of 2nd Sunday in March
  daymonth = 1;
  dayyearcalc();
  while((5 + dayyear + year*365 + (year+3)/4)%7 != 0)  // Sunday == 0; find the 
first
  {
    dayyear++;
  }
  dayDSTon = dayyear + 7;     // set DST-on variable to second Sunday

  month = 11;                 // determine dayyear of 1st Sunday in November
  daymonth = 1;
  dayyearcalc();
  while((5 + dayyear + year*365 + (year+3)/4)%7 != 0)  // a Sunday == 0; find 
the first
  {
    dayyear++;
  }
  dayDSToff = dayyear;        // set DST-off variable to 1st Sunday in November
  
  dayyear = daytemp;          // return dayyear to current value

  dstwarn = 0;                // set warn flag to zero for beginning of the year
  dst = 0;                    // set change flag to zero for the beginning of 
the year

  if(dayyear >= (dayDSTon - 1))     // set warning flag if it is the day before 
change or later
  {
    dstwarn = 1;
  }
  if(dayyear >= dayDSTon)           // set change flag if it is the change day 
or later
  {
    dst = 1;
  }
  if(dayyear >= (dayDSToff - 1))    // unset warning flag if it is the day 
before change or later
  {
    dstwarn = 0;
  }
  if(dayyear >= dayDSToff)          // unset change flag if it is the change 
day or later
  {
    dst = 0;
  }
}

// minuteincrement() increases the minute variable by one and then makes the
// subsequent resulting changes in other time variables:
void minuteincrement()
{
  minute = minute + 1;

  if(minute == 60)
  {
    minute = 0;
    hour++;
    if(hour == 24)
    {
      hour = 0;
      dayyear++;
      if(year%4 == 0)
      {
        if(dayyear == 367)
        {
          year++;
        }
      }
      else
      {
        if(dayyear == 366)
        {
        year++;
        }
      }
    }
  }
}

// transmit() determines from the time and date variables the appropriate BCD 
code elements
// to be sent at each second of the minute of code transmission; in addition to 
zeros and ones,
// there is a mark element sent every ten seconds:
void transmit()
{
  mark();              //second 00 -- marks the beginning of the minute

  x = minute;          //second 01 -- beginning of MINUTE code
  if(x/40 == 1)
  {
    one();
    x = x-40;
  }
  else
  {
    zero();
  }

  if(x/20 == 1)        //second 02
  {
    one();
    x = x-20;
  }
  else
  {
    zero();
  }

  if(x/10 == 1)        //second 03
  {
    one();
    x = x-10;
  }
  else
  {
    zero();
  }

  zero();              //second 04
  
  if(x/8 == 1)         //second 05
  {
    one();
    x = x-8;
  }
  else
  {
    zero();
  }

  if(x/4 == 1)         //second 06
  {
    one();
    x = x-4;
  }
  else
  {
    zero();
  }

  if(x/2 == 1)         //second 07
  {
    one();
    x = x-2;
  }
  else
  {
    zero();
  }

  if(x/1 == 1)         //second 08
  {
    one();
    x = x-1;
  }
  else
  {
    zero();
  }

  mark();              //second 09

  zero();              //second 10
  
  zero();              //second 11
  
  x = hour;            //second 12 -- beginning of HOUR code
  if(x/20 == 1)
  {
    one();
    x = x-20;
  }
  else
  {
    zero();
  }

  if(x/10 == 1)        //second 13
  {
    one();
    x = x-10;
  }
  else
  {
    zero();
  }

  zero();              //second 14

  if(x/8 == 1)         //second 15
  {
    one();
    x = x-8;
  }
  else
  {
    zero();
  }

  if(x/4 == 1)         //second 16
  {
    one();
    x = x-4;
  }
  else
  {
    zero();
  }

  if(x/2 == 1)         //second 17
  { 
    one();
    x = x-2;
  }
  else
  {
    zero();
  }

  if(x/1 == 1)         //second 18
  {
    one();
    x = x-1;
  }
  else
  {
    zero();
  }

  mark();              //second 19

  zero();              //second 20

  zero();              //second 21

  x = dayyear;         //second 22 -- beginning of DAY code
  if(x/200 == 1)
  {
    one();
    x = x-200;
  }
  else
  {
    zero();
  }

  if(x/100 == 1)       //second 23
  {
    one();
    x = x-100;
  }
  else
  {
    zero();
  }

  zero();              //second 24

  if(x/80 == 1)        //second 25
  {
    one();
    x = x-80;
  }
  else
  {
    zero();
  }

  if(x/40 == 1)        //second 26
  {
    one();
    x = x-40;
  }
  else
  {
    zero();
  }

  if(x/20 == 1)        //second 27
  {
    one();
    x = x-20;
  }
  else
  {
    zero();
  }

  if(x/10 == 1)        //second 28
  {
    one();
    x = x-10;
  }
  else
  {
    zero();
  }

  mark();              //second 29

  if(x/8 == 1)         //second 30
  {
    one();
    x = x-8;
  }
  else
  {
    zero();
  }

  if(x/4 == 1)         //second 31
  {
    one();
    x = x-4;
  }
  else
  {
    zero();
  }

  if(x/2 == 1)         //second 32
  {
    one();
    x = x-2;
  }
  else
  {
    zero();
  }

  if(x/1 == 1)         //second 33
  {
    one();
    x = x-1;
  }
  else
  {
    zero();
  }

  zero();              //second 34

  zero();              //second 35

  one();               //second 36 -- UT1 correction positive

  zero();              //second 37 -- UT1 correction positive 

  one();               //second 38 -- UT1 correction positive

  mark();              //second 39
  
  zero();              //second 40 -- zero UT1 correction
  
  zero();              //second 41 -- zero UT1 correction
  
  zero();              //second 42 -- zero UT1 correction
  
  zero();              //second 43 -- zero UT1 correction
  
  zero();              //second 44

  x = year;            //second 45 -- beginning of YEAR code
  if(x/80 == 1)
  {
    one();
    x = x-80;
  }
  else
  {
    zero();
  }

  if(x/40 == 1)        //second 46
  {
    one();
    x = x-40;
  }
  else
  {
    zero();
  }

  if(x/20 == 1)        //second 47
  {
    one();
    x = x-20;
  }
  else
  {
    zero();
  }

  if(x/10 == 1)        //second 48
  {
    one();
    x = x-10;
  }
  else
  {
    zero();
  }

  mark();              //second 49

  if(x/8 == 1)         //second 50
  {
    one();
    x = x-8;
  }
  else
  {
    zero();
  }

  if(x/4 == 1)         //second 51
  {
    one();
    x = x-4;
  }
  else
  {
    zero();
  }

  if(x/2 == 1)         //second 52
  {
    one();
    x = x-2;
  }
  else
  {
    zero();
  }

  if(x/1 == 1)         //second 53
  {
    one();
    x = x-1;
  }
  else
  {
    zero();
  }

  zero();              //second 54

  if(year%4 == 0)      //second 55 --  one if leap year, zero if not
  {
    one();
  }
  else
  {
    zero();
  }

  zero();              //second 56 -- no leap second warning

  if( dstwarn == 1)
  {                    //second 57 -- DST change warning
    one();
  }
  else
  {
    zero();
  }
  
  if( dst == 1)
  {                    //second 58 -- make DST change
    one();
  }
  else
  {
    zero();
  }

  mark();              //second 59
}

void mark()            // constructs and sends the BCD code element MARK
{
  digitalWrite(2,LOW);
  delay(800);
  digitalWrite(2,HIGH);
  delay(200);
}

void zero()            // constructs and sends the BCD code element ZERO
{
  digitalWrite(2,LOW);
  delay(200);
  digitalWrite(2,HIGH);
  delay(800);
}
    
void one()             // constructs and sends the BCD code element ONE
{
  digitalWrite(2,LOW);
  delay(500);
  digitalWrite(2,HIGH);
  delay(500);
}
_______________________________________________
time-nuts mailing list -- [email protected]
To unsubscribe, go to 
http://lists.febo.com/mailman/listinfo/time-nuts_lists.febo.com
and follow the instructions there.

Reply via email to