On Wed, Nov 23, 2016 at 02:34:03PM +0100, Andrei Pistirica wrote:
> From what I understand, your suggestion is:
> (ns | frac) * ppb = (total_ns | total_frac)
> (total_ns | total_frac) / 10^9 = (adj_ns | adj_frac)
> This is correct iff total_ns/10^9 >= 1, but the problem is that there are
> missed fractions due to the following approximation:
> frac*ppb =~ (ns*ppb+frac*ppb*2^16)*2^16-10^9*2^16*flor(ns*ppb+frac*ppb*2^16,
> 10^9).

-ENOPARSE;
 
> An example which uses values from a real test:
> let ppb=4891, ns=12 and frac=3158

That is a very strange example for nominal frequency.  The clock
period is 12.048187255859375 nanoseconds, and so the frequency is
83000037.99 Hz.

But hey, let's go with it...

> - using suggested algorithm, yields: adj_ns = 0 and adj_frac = 0
> - using in-place algorithm, yields: adj_ns = 0, adj_frac = 4
> You can check the calculus.

The test program, below, shows you what I meant.  (Of course, you
should adjust this to fit the adjfine() method.)

Unfortunately, this device has a very coarse frequency resolution.
Using a nominal period of ns=12 as an example, the resolution is
2^-16 / 12 or 1.27 ppm.  The 24 bit device is much better in this
repect.

The output using your example numbers is:

   $ ./a.out 12 3158 4891
   ns=12 frac=3158
   ns=12 frac=3162

   $ ./a.out 12 3158 -4891
   ns=12 frac=3158
   ns=12 frac=3154

See how you get a result of +/- 4 with just one division?

Thanks,
Richard

---
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

static void adjfreq(uint32_t ns, uint32_t frac, int32_t ppb)
{
        uint64_t adj;
        uint32_t diff, word;
        int neg_adj = 0;

        printf("ns=%u frac=%u\n", ns, frac);

        if (ppb < 0) {
                neg_adj = 1;
                ppb = -ppb;
        }
        word = (ns << 16) + frac;
        adj = word;
        adj *= ppb;
        adj += 500000000UL;
        diff = adj / 1000000000UL;

        word = neg_adj ? word - diff : word + diff;
        printf("ns=%u frac=%u\n", word >> 16, word & 0xffff);
}

int main(int argc, char *argv[])
{
        uint32_t ns, frac;
        int32_t ppb;

        if (argc != 4) {
                puts("need ns, frac, and ppb");
                return -1;
        }
        ns = atoi(argv[1]);
        frac = atoi(argv[2]);
        ppb = atoi(argv[3]);
        adjfreq(ns, frac, ppb);
        return 0;
}

Reply via email to