其实是接受长波电台的时钟,基站倒是Atomic时钟。其核心是美英60/中国68.5/德国77.5KHz的长波收音机,再数字译码。网上有人DIY。室内用比GPS方便一点。
我2006年左右买的圆形数显Skyscan,radio强度满格,现在还能用,但因DST改过几次因此它不适合现行标准。买的La Crosse其radio强度为0,退了。国产UMEXUS的样子还行,但液晶是第一代,可视角小。
https://en.wikipedia.org/wiki/WWVB
https://www.nist.gov/pml/time-and-frequency-division/radio-stations/wwvb
https://www.lloydm.net/Demos/wwvb.html https://www.youtube.com/watch?v=zD_INHy3BBI
https://www.youtube.com/watch?v=aUKLQaWvwXc src:https://github.com/KC7MMI/AVR-ASM-WWVB-Atomic-Clock
http://www.leapsecond.com/pages/sony-wwvb/
http://canaduino.ca/downloads/CANADUINO_Atomic_Clock_Receiver_Kit_SMD.pdf 这个用的是D10x60磁棒加固定电容调谐,MAS6180C芯片。https://www.sparkfun.com/products/retired/10060
1MHz以下通常用Mn-Zn Ferrite锰锌铁氧体,其指标包括电感系数Al, L=Al*N^2;与空心线圈相比的实用导磁率uapp=L/L0和实用Q值,Qapp=Q/Q0。
信号不强时更新可能出错,许多人发现会快/慢一个小时,有的产品允许只更新分/秒。
我觉得不如用无线网接受网上的network time protocol,如PC和手机那样。
// here's the Arduino code https://www.youtube.com/watch?v=OeZzNehKL_Y&t=3s
#include
#include
#include
#include
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
// DO NOT use all zeros!
byte mac[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
unsigned int localPort = 8888; // local port to listen for UDP packets
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov NTP server
// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov NTP server
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov NTP server
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
enum KEYS {
ALLUP,
SELECT,
LEFT,
DOWN,
UP,
RIGHT
};
char str[20];
int nStart,nEnd,nWid;
int nLastDecode;
int nBytes[6];
int nIndex, nMask;
int nMode;
int hour, minute, day, year, second;
int nPinState;
int keyVal, oldKey, curKey;
void setup()
{
lcd.begin(16, 2); // start the library
pinMode(3,INPUT); // clock receiver input
lcd.setCursor(0,0);
lcd.print("Hello ");
lcd.setCursor(0,0);
for(int i =0; i < 5; i++)
{
nBytes[i] = 0;
}
nIndex = 0;
nMode = 0;
nMask = 0x80;
nStart = nEnd = millis();
nPinState = digitalRead(3);
oldKey = analogRead(0);
hour = minute = second = day = year = 0;
// start Ethernet and UDP
if (Ethernet.begin(mac) == 0) {
lcd.print("Failed DHCP");
lcd.setCursor(0,0);
// no point in carrying on, so do nothing forevermore:
for(;;)
;
}
Udp.begin(localPort);
}
void loop()
{
sendNTPpacket(timeServer); // send an NTP packet to a time server
delay(1000);
if ( Udp.parsePacket() )
{
// We've received a packet, read the data from it
Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
lcd.clear();
lcd.setCursor(0,0);
lcd.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
lcd.print(':');
if ( ((epoch % 3600) / 60) < 10 ) {
// In the first 10 minutes of each hour, we'll want a leading '0'
lcd.print('0');
}
lcd.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute)
lcd.print(':');
if ( (epoch % 60) < 10 ) {
// In the first 10 seconds of each minute, we'll want a leading '0'
lcd.print('0');
}
lcd.print(epoch %60); // print the second
}
while(1)
{
readclock();
keyboard();
}
}
void readclock(void)
{
int nRead;
nRead = digitalRead(3);
if(nRead != nPinState)
{
nPinState = nRead;
// we get here on every transition of the input signal (edge)
if(nRead == HIGH)
{
// when sig goes high, just save the systenm clock
nStart = millis();
second++;
if(second == 60) second = 0;
lcd.setCursor(11,0);
if(second < 10) lcd.print('0');
lcd.print(second);
}
else if(nRead == LOW)
{
// when sig goes low, do all the work
// this means we have a new bit from the receiver
nEnd = millis();
// nWid is our measurement of the pulse width in ms.
// this can vary a bit, so we dice it up into three
// very generous windows.
nWid = nEnd - nStart;
if(nWid > 725) // > 725ms = sync pulse
{
if(nLastDecode == 2)
{
dsync();
nLastDecode = 3;
}
else
{
sync();
nLastDecode = 2;
}
}
else if(nWid < 275)
{
its0();
nLastDecode = 0;
}
else
{
its1();
nLastDecode = 1;
}
}
}
}
void keyboard(void)
{
keyVal = analogRead(0);
if(keyVal != oldKey)
{
if(keyVal < 70) curKey = RIGHT;
else if(keyVal < 225) curKey = UP;
else if(keyVal < 420) curKey = DOWN;
else if(keyVal < 620) curKey = LEFT;
else if(keyVal < 900) curKey = SELECT;
else curKey = ALLUP;
// lcd.setCursor(0,1);
// lcd.print(curKey);
// lcd.print(" ");
oldKey = keyVal;
}
}
void its1(void)
{
nBytes[nIndex] |= nMask;
nMask = nMask >> 1;
lcd.setCursor(14,0);
lcd.print("1");
}
void its0(void)
{
nMask = nMask >> 1;
lcd.setCursor(14,0);
lcd.print("0");
}
void sync(void)
{
nMask = 0x100;
nIndex++;
lcd.setCursor(14,0);
lcd.print("S");
}
void dsync(void)
{
second = 0;
nMask = 0x80;
nIndex = 0;
showit();
for(int i =0; i < 5; i++)
{
nBytes[i] = 0;
}
nMode = 1;
lcd.setCursor(14,0);
lcd.print("D");
}
void showit(void)
{
int i;
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,0);
if(nMode == 1)
{
Decode();
lcd.print("UTC: ");
lcd.print(hour);
lcd.print(":");
if(minute <= 9) lcd.print("0"); // do I have to do everything?
lcd.print(minute);
lcd.print(":");
lcd.setCursor(0,1);
lcd.print("DAY: ");
lcd.print(day);
lcd.print(" YR: ");
lcd.print(year);
}
else
{
lcd.print("In sync");
}
}
void Decode(void)
{
minute = nBytes[0] & 0x0f;
minute += ((nBytes[0] >> 5) & 0x07) * 10;
hour = nBytes[1] & 0x0f;
hour += ((nBytes[1] >> 5) & 0x07) * 10;
// WWVB sends the time AFTER the time mark, so we need to add a minute
// for the correct time.
minute++;
if(minute >= 60)
{
minute = 0;
hour++;
if(hour >= 24)
{
hour = 0;
}
}
day = (nBytes[3] >> 5) & 0x0f;
day += (nBytes[2] & 0x0f) * 10;
day += ((nBytes[2] >> 5) & 0x03) * 100;
year = (nBytes[5] >> 5) & 0x0f;
year += ((nBytes[4]) & 0x0f) * 10;
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer,NTP_PACKET_SIZE);
Udp.endPacket();
}
UMEXUS