User avatar
joan
Posts: 14935
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Free wheeling clock versus real time

Sun Mar 16, 2014 1:34 pm

Does anyone have a feel for how much drift to expect between the Pi's internal microsecond clock (SYST_CLO) and actual time?

i.e. in 1000 seconds how close to 1,000,000,000 ticks should I expect to see?

User avatar
GTR2Fan
Posts: 1601
Joined: Sun Feb 23, 2014 9:20 pm
Location: South East UK

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 1:44 pm

This discussion from early last year may shed some light on the subject for you...

http://www.raspberrypi.org/phpBB3/viewt ... 63&t=29255
Pi2B Mini-PC/Media Centre: ARM=1GHz (+3), Core=500MHz, v3d=500MHz, h264=333MHz, RAM=DDR2-1200 (+6/+4/+4+schmoo). Sandisk Ultra HC-I 32GB microSD card on '50=100' OCed slot (42MB/s read) running Raspbian/KODI16, Seagate 3.5" 1.5TB HDD mass storage.

User avatar
joan
Posts: 14935
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 2:23 pm

Thanks. That seems to tie in with a 1/2 second discrepancy after an hour which is what I just measured.

User avatar
chrisryall
Posts: 155
Joined: Wed Nov 27, 2013 11:45 am
Location: Wirral UK
Contact: Website

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 9:05 pm

if one wants a clock one should buy a clock; I think the issue here may be how to train the little Pi to update its clock regularly from a readily available Internet connection?

User avatar
joan
Posts: 14935
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 9:17 pm

chrisryall wrote:if one wants a clock one should buy a clock; I think the issue here may be how to train the little Pi to update its clock regularly from a readily available Internet connection?
The issue had more to do with me trying to pulse a gpio once each (NTP) second boundary - and wondering why it happened every 1,000,025 Pi ticks.

techpaul
Posts: 1512
Joined: Sat Jul 14, 2012 6:40 pm
Location: Reading, UK
Contact: Website

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 9:51 pm

joan wrote:The issue had more to do with me trying to pulse a gpio once each (NTP) second boundary - and wondering why it happened every 1,000,025 Pi ticks.
Maybe your time pulse is being started every 1,000,000 ticks (+/- tolerance) and 25 ticks to toggle GPIO and schedule again via instructions.

Random thought if always asking for 1,000,000 and you get 1,000,025 what inaccuracy do you get when asking for 999,975 ticks?
Just another techie on the net - For GPIO boards see http:///www.facebook.com/pcservicesreading
or http://www.pcserviceselectronics.co.uk/pi/

User avatar
joan
Posts: 14935
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 10:25 pm

techpaul wrote:
joan wrote:The issue had more to do with me trying to pulse a gpio once each (NTP) second boundary - and wondering why it happened every 1,000,025 Pi ticks.
Maybe your time pulse is being started every 1,000,000 ticks (+/- tolerance) and 25 ticks to toggle GPIO and schedule again via instructions.

Random thought if always asking for 1,000,000 and you get 1,000,025 what inaccuracy do you get when asking for 999,975 ticks?
I'm trying to send a pulse on every real second boundary. It seems to work but every time I try to work out why I confuse myself. I did notice that each pulse was happening 25 microseconds later than I expected. But it turns out that the Pi I'm using runs at 1,000,025 ticks in an actual second so it seems to be correct. I'll sleep on it.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>

#include <pigpio.h>

#define GPIO  4
#define LEVEL 1
#define PULSE 200
#define VARY  200

static int gpio = GPIO;
static int plev = LEVEL;
static int plen = PULSE;
static int vary = VARY;

static unsigned long *varyA, *pulseA, *offA;

gpioPulse_t wave[3];

void fatal(char *fmt, ...)
{
   char buf[256];
   va_list ap;

   va_start(ap, fmt);
   vsnprintf(buf, sizeof(buf), fmt, ap);
   va_end(ap);

   fprintf(stderr, "%s\n", buf);

   exit(EXIT_FAILURE);
}

void usage()
{
   fprintf(stderr, "\n" \
      "Usage: sudo ./pps [OPTION] ...\n"\
      "   -g 0-31     gpio         (%d)\n"\
      "   -l 0,1      pulse level  (%d)\n"\
      "   -p 1-5000   pulse micros (%d)\n"\
      "   -v 100-5000 vary micros  (%d)\n"\
      "EXAMPLE\n"\
      "sudo ./square -g 23\n"\
      "  Generate pulse per second on gpio 23.\n"\
   "\n", GPIO, LEVEL, PULSE, VARY);
}

static void initOpts(int argc, char *argv[])
{
   int i, opt;

   while ((opt = getopt(argc, argv, "g:l:p:v:")) != -1)
   {
      i = -1;

      switch (opt)
      {
         case 'g':
            i = atoi(optarg);
            if ((i >= 0) && (i <= 31)) gpio = i;
            else fatal("invalid -g option (%d)", i);
            break;

         case 'l':
            i = atoi(optarg);
            if ((i == 0) || (i == 1)) plev = i;
            else fatal("invalid -l option (%d)", i);
            break;

         case 'p':
            i = atoi(optarg);
            if ((i > 0) && (i<=5000)) plen = i;
            else fatal("invalid -p option (%d)", i);
            break;

         case 'v':
            i = atoi(optarg);
            if ((i > 0) && (i<=5000)) vary = i;
            else fatal("invalid -v option (%d)", i);
            break;

         default: /* '?' */
            usage();
            exit(EXIT_FAILURE);
        }
    }
}

void callback(int gpio, int level, uint32_t tick)
{
   int i, optional;
   struct timespec tp;
   uint32_t micro, t1, t2, diff, mt1;
   uint32_t nextPulse, nextPulseTick, delay, fixed;

   if (level == plev)
   {
      diff = 1000;

      for (i=0; i<10; i++)
      {
         t1 = gpioTick();
         clock_gettime(CLOCK_REALTIME, &tp);
         t2 = gpioTick();
         if ((t2-t1) < diff)
         {
            diff = t2-t1;
            mt1 = t1;
            micro = (tp.tv_nsec+500) / 1000;
         }
      }

      if (diff < 6)
      {
         nextPulse = 1000000 - micro;
         nextPulseTick = mt1 + (diff/2) + nextPulse;
         delay = nextPulseTick - tick;
         fixed = 1000000 - vary;
         optional = delay - fixed;
         if (optional < 0) optional += 1000000;
         if (!optional) optional = 1;
         *varyA = (optional * 4);
         printf("diff=%d t=%u nt=%u micro=%d nextPulse=%d opt=%d\n",
            diff, tick, nextPulseTick, micro, nextPulse, optional);
      }
   }
}

int main(int argc, char *argv[])
{
   int off;
   int i;

   dmaCbs_t *p;

   initOpts(argc, argv);

   off = 1000000 - (plen+vary);

   printf("gpio=%d, level=%d vary=%dus, off=%dus\n", gpio, plev, vary, off);

   gpioCfgClock(1, 1, 1);

   if (gpioInitialise()<0) return -1;

   /* get pps callback */

   gpioSetAlertFunc(gpio, callback);

   varyA  = &(waveCbVOadr(2)->length);
   pulseA = &(waveCbVOadr(4)->length);
   offA   = &(waveCbVOadr(6)->length);

   gpioSetMode(gpio, PI_OUTPUT);

   if (plev) /* pulse is high */
   {
      wave[0].gpioOn  = 0;
      wave[0].gpioOff = (1<<gpio);
      wave[0].usDelay = vary;

      wave[1].gpioOn = (1<<gpio);
      wave[1].gpioOff = 0;
      wave[1].usDelay = plen;

      wave[2].gpioOn  = 0;
      wave[2].gpioOff = (1<<gpio);
      wave[2].usDelay = off;
   }
   else /* plen is low */
   {
      wave[0].gpioOn  = (1<<gpio);
      wave[0].gpioOff = 0;
      wave[0].usDelay = vary;

      wave[1].gpioOn  = 0;
      wave[1].gpioOff = (1<<gpio);
      wave[1].usDelay = plen;

      wave[2].gpioOn  = (1<<gpio);
      wave[2].gpioOff = 0;
      wave[2].usDelay = off;
   }

   gpioWaveClear();

   gpioWaveAddGeneric(3, wave);

   gpioWaveTxStart(PI_WAVE_MODE_REPEAT);

   printf("v=%d p=%d o=%d\n", *varyA, *pulseA, *offA);

   while (1) sleep(1);

   gpioTerminate();
}

User avatar
jojopi
Posts: 3270
Joined: Tue Oct 11, 2011 8:38 pm

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 10:57 pm

There are only two oscillators on board, and one of those is for the LAN. So I believe both the DMA clock and the kernel timer interrupt are ultimately derived from the same source and should be phase-locked.

The drift then is due to NTP correction. Rather than insert discontinuities for small adjustments, ntpd calls adjtimex(2) to tell the kernel to add slightly more or less than 1µs at each timer tick. You can see the difference by comparing elapsed times using clock_gettime() for both CLOCK_REALTIME and CLOCK_MONOTONIC_RAW:

Code: Select all

// link with gcc -lrt
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/mman.h>
#include <sched.h>

int main()
{
  struct sched_param param;
  param.sched_priority = sched_get_priority_max(SCHED_FIFO);
  if (sched_setscheduler(0, SCHED_FIFO, &param) < 0)
    perror("warning: sched_setscheduler");
  if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0)
    perror("warning: mlockall");
  struct timespec t1, t2, r1, r2;
  clock_gettime(CLOCK_MONOTONIC_RAW, &r1);
  clock_gettime(CLOCK_REALTIME, &t1);
  clock_gettime(CLOCK_MONOTONIC_RAW, &r1);
  clock_gettime(CLOCK_REALTIME, &t1);
  do {
    clock_gettime(CLOCK_MONOTONIC_RAW, &r2);
    clock_gettime(CLOCK_REALTIME, &t2);
  } while (r2.tv_sec - r1.tv_sec < 1 || r2.tv_nsec < r1.tv_nsec);
  long t = (t2.tv_sec-t1.tv_sec)*1000000000L + t2.tv_nsec-t1.tv_nsec;
  long r = (r2.tv_sec-r1.tv_sec)*1000000000L + r2.tv_nsec-r1.tv_nsec;
  printf("raw %ld.%09lds\n", r/1000000000, r%1000000000);
  printf("ntp %ld.%09lds\n", t/1000000000, t%1000000000);
  return 0;
}

Code: Select all

raw 1.000001000s
ntp 0.999981266s
You can read the current adjtimex() parameters:

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/timex.h>

int main()
{
  struct timex tx;
  tx.modes = 0;
  if (adjtimex(&tx) < 0) {
    perror("adjtimex");
    exit(1);
  }
  printf("current frequency: %ld\n", tx.freq);
  return 0;
}

Code: Select all

current frequency: -1235414
The interpretation of this parameter appears to be -1235414/65536 parts per million, giving 1000000-1235414/65536 = 999981 (NTP µs per raw tick), which agrees with the above result.

I think that reading once per second should allow you cancel most of the difference. But to stay locked in the long term may require feedback.

(I am assuming you want to synchronise with the NTP clock. Otherwise just use CLOCK_MONOTONIC_RAW.)

User avatar
mahjongg
Forum Moderator
Forum Moderator
Posts: 13099
Joined: Sun Mar 11, 2012 12:19 am
Location: South Holland, The Netherlands

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 11:09 pm

the ARM CPU of the PI is clocked with a 19.2 MegaHertz Quartz, which typically have 10 to 30 part per million precision, and a 10 ppm stability.

Quartz Crystals for real time clocks can have three times better precision, but a hundred times better stability, and can be calibrated to be even more precise.

You cannot expect a CPU to be clocked with the same kind of precision a real time clock does.

you should expect at least few dozen more or less clock cycles per million of them.

User avatar
joan
Posts: 14935
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Free wheeling clock versus real time

Sun Mar 16, 2014 11:15 pm

jojopi wrote:There are only two oscillators on board, and one of those is for the LAN. So I believe both the DMA clock and the kernel timer interrupt are ultimately derived from the same source and should be phase-locked.

The drift then is due to NTP correction. Rather than insert discontinuities for small adjustments, ntpd calls adjtimex(2) to tell the kernel to add slightly more or less than 1µs at each timer tick. You can see the difference by comparing elapsed times using clock_gettime() for both CLOCK_REALTIME and CLOCK_MONOTONIC_RAW:
...
(I am assuming you want to synchronise with the NTP clock. Otherwise just use CLOCK_MONOTONIC_RAW.)
Thanks. I think I understand that now. I'll have a look at what adjtime says on my Pi to confirm the result.

I don't need to sync with the NTP clock. I was just wondering how easy it would be to do so and what level of accuracy I could expect. The answer to that seems to be circa +/- 5μs of when the pulse ought to be.

Sillyname
Posts: 45
Joined: Mon Mar 03, 2014 2:22 pm

Re: Free wheeling clock versus real time

Mon Mar 17, 2014 3:12 pm

I think mine draws its clock from my wireless router.

Return to “Troubleshooting”