got new broadband. much the same as the old broadband.
This function generates time-stamps in the format used in leap-seconds.list (on e.g. NTP or PTP servers). The format is integer seconds since 1900-01-01T00:00+00.
import datetime import pytz def generate_ntp_timestamp(year,month,day,hour,minute,second): t = datetime.datetime(year,month,day,hour,minute,second,tzinfo=pytz.utc) #NTP leap-seconds list wants seconds since 1900 epoch_start = datetime.datetime(1900,1,1,0,0,0,tzinfo=pytz.utc) delta = t-epoch_start delta_s = delta.total_seconds() return int(delta_s) # test that this generates OK values. # from existing leap-seconds.list # 2918937600 27 # 1 Jul 1992 # 2950473600 28 # 1 Jul 1993 # 2982009600 29 # 1 Jul 1994 # 3029443200 30 # 1 Jan 1996 # 3076704000 31 # 1 Jul 1997 # 3124137600 32 # 1 Jan 1999 # 3345062400 33 # 1 Jan 2006 # 3439756800 34 # 1 Jan 2009 print "1 Jan 2009: ", generate_ntp_timestamp(2009,1,1,0,0,0) print "1 Jan 2006: ", generate_ntp_timestamp(2006,1,1,0,0,0) print "1 Jul 1997: ", generate_ntp_timestamp(1997,7,1,0,0,0) #output: #1 Jan 2009: 3439756800 #1 Jan 2006: 3345062400 #1 Jul 1997: 3076704000
I put together a PICDIV frequency divider for use with a Rubidium clock.
I used an LTC6957-3 to convert the 10 MHz sine-wave from the clock to a CMOS logic signal (square wave). The LTC6957-3 has two outputs, one is routed to a BNC connector output, the other is used as the clock for a PIC12F675. The PIC runs pd09.asm which outputs a 20 us long pulse every second - i.e. it divides the 10 MHz input frequency by 1e7. The PIC is programmed through a 5-pin 100 mil ICSP header.
Here are some test-signals with a SRS PRS-10 as the source, and recorded on a Rigol scope.
The outputs behave as expected, but the 1PPS from the PIC is only 700 mVpp into 50R - a bit low. When terminated to 1 MOhm the rise-time is much worse so this is best avoided. Perhaps a buffer or level-translator would be a good addition.
Finally phase-noise measurements on the 10 MHz CMOS output, performed with a 3120A phase-noise probe.
I tried shielding the circuit with aluminium foil and powering it from a +12 VDC lead-acid battery - however the three measurement runs look roughly similar. Perhaps the LM317 regulator is not a great choice here, and both the LTC sine-to-square chip and the PIC should have more bypass caps and decoupling (inductors, ferrites?). In any case the phase-noise is 10-20x better than the measurement noise from a typical counter (SR620 or 53230A), so any issues only show up with high-end phase-noise probes.
Update: now with the colours matching in all graphs:
The PSD lines and MDEV lines seem spot-on, but are the ADEV lines systematically a bit low?
Code here: example_noise_slopes.py
Here's a comarison against Stable32 and the NIST SP 1065 table values.
There's no bias-correction for now, and apparently it is customary not to use htotdev(m=1) at an averaging-factor of 1, but instead use ohdev(m=1). This is why the 'raw' value from allantools at tau=1 in the plot below is a factor of 2 too low.
We use cron-jobs to control lab instruments once a minute in the lab. I noticed that specifying 'once a minute' to cron produces a start-time for the job with quite a bit of variability. Usually the job starts at 1s past the minute, but sometimes 2s. There's also a random delay of 20 ms to 50 ms (but sometimes up to 300 ms). This probably depends a lot on the hardware. So if we assume the cron-job starts 1s past the minute the worst error I saw in the test above is 1.3 seconds.
Here's a very simple synchronization idea: first ask for a time-stamp, then wait until we are at an inter-second boundary, then continue. This makes the cron-job continue at the inter-second boundary with a maximum error of 6 ms, a fair improvement over 1.3 seconds without any sync code.
import datetime import time dt1 = datetime.datetime.now() # time-stamp when cron decides to run us usecs_to_go = 1e6-dt1.microsecond # we want to run at an inter-second boundary secs_target = 5 # we want to run at 5 seconds past minute secs_to_go = secs_target - dt1.second -1 # amount of waiting to do. time.sleep( secs_to_go+ usecs_to_go/1.0e6 ) dt2 = datetime.datetime.now() # now we are synced!? fn = '/home/user/my_cron_log.txt' # write to logfile for later analysis. with open(fn,'a+') as f: f.write(str(dt1) +" %02d.%06d" % (dt1.second, dt1.microsecond) + ' run!\n') f.write(str(dt2) +" %02d.%06d" % (dt2.second, dt2.microsecond) + ' sync!\n') f.write('\n')
The way to enable this to run from crontab is to add something like this to crontab (use crontab -e):
# m h dom mon dow command * * * * * /usr/bin/python /home/user/cron_sync.py
Where cron_sync.py is the name of the script above.
I'd be interested in hearing how this works on other hardware, or if there are some alternative solutions.