comp.lang.ada
 help / color / mirror / Atom feed
* Using "delay until" in real-time
@ 2000-12-12 16:27 Ted Dennison
  2000-12-12 18:01 ` Mike Silva
                   ` (9 more replies)
  0 siblings, 10 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-12 16:27 UTC (permalink / raw)


I came across an interesting problem the other day that I'd like to
share, in the hopes that someone here has another solution that the ones
we thought up.

We have a real-time scheduler that uses the "delay until" statement to
perform its scheduling. So far, this shouldn't shock anyone, as
real-time scheduling is supposedly what "delay until" was put in the
language for.

Our scheduling code looks something like this (very simplified, and not
compiled. My apologies ahead of time if Deja screws up the formatting):

---
Iteration_Hz : constant Float := 60.0;

task Executive is
   Iteration : constant Ada.Real_Time.Time_Span
      := Ada.Real_Time.To_Time_Span (Duration(1.0/Iteration_Hz));

   Next_Time : Ada.Real_Time.Time := Ada.Real_Time.Clock + Iteration;
begin
   loop
      -- Perform scheduling tasks
      ...

      Next_Time := Next_Time + Iteration;
      delay until Next_Time;
   end loop;
end Executive;

---

That looks pretty straightforward, right? Well, unfortunately, its *WRONG*.

1/60 works out to about 0.016(6-repeating). That can't be accurately
represented in an IEEE floating-point register. So what we get is
something like 0.016667 (forgive me if I'm rounding the wrong digit).
That rouding error slowly accumulates over time, until after about 52
seconds our poor Executive task skips a tick. On our system ticks come
at 240Hz, so that means that once every 52 seconds the executive
scheduler waits about 20ms instead of 16.6(6-repeating)ms. Of course
this wreaks havoc in our hard-realtime system.

So the question is, what's the best way to solve this problem? Here's
what we came up with so far:

  o  Use the RTOS' "taskDelay" function to delay the appropriate amount
of ticks (4).

I don't like this one, as its a non-Ada solution, and thus renders our
scheduler non-portable. Its also succeptable to drift if something
causes an extra tick to pass before we get to the taskDelay.

  o  Check the Ada.Real_Time.Clock every cycle when we awaken, and add
the Iteration to that for the "delay until".

I don't like this because the clock check is going to be fairly
expensive, and this solution is also succeptable to drift.

  o  Figure out what the denominator is (60 in this case), and redo the
calculation every time that number of runs have happened.

For instance, in the above code, I'd add something like:
      Runs := Runs + 1;
      if Runs >= Natural(Iteration_Hz) then
         -- Adjust for drift due to rounding errors.
         Runs := 0;
         Run_Time := Run_Time + Ada.Real_Time.To_Time_Span (1.0);
         Next_Time := Run_Time;
      else
         Next_Time := Next_Time + Iteration;
      end if;

I kind of like this option, as it is a mathematical solution to what is,
in essence, a mathematical problem. This ought to work as long as the
denominator can be counted on to be a whole (natural) number. In my
case, I think it can.

Since this problem should happen to anyone using "delay until" with an
iteration rate divisible by 3, and 60, 30, and 15Hz are common rates in
the industry, I can't be the first person to stumble on this problem.
How does everyone else handle it?


--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
@ 2000-12-12 18:01 ` Mike Silva
  2000-12-12 19:57   ` Ted Dennison
  2000-12-12 20:00 ` Ken Garlington
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Mike Silva @ 2000-12-12 18:01 UTC (permalink / raw)


It's Son of Patriot bug...

I'm as far from an expert as you can get, but what if you defined your
Iteration in terms of Tick (4*Tick in your case)?  Seems like that
should provide an exact representation (why do I feel like that comment
will get me nailed?).  Otherwise, keep your own Iteration count in a
scalar and add it (first converted to a Time_Span) to a Start_time for
each delay until.

It's a very interesting question, and if I'm wrong I look forward to
learning the right answer.

Mike

In article <915jl7$jt5$1@nnrp1.deja.com>,
  Ted Dennison <dennison@telepath.com> wrote:
> I came across an interesting problem the other day that I'd like to
> share, in the hopes that someone here has another solution that the
ones
> we thought up.
>
> We have a real-time scheduler that uses the "delay until" statement to
> perform its scheduling. So far, this shouldn't shock anyone, as
> real-time scheduling is supposedly what "delay until" was put in the
> language for.
>
> Our scheduling code looks something like this (very simplified, and
not
> compiled. My apologies ahead of time if Deja screws up the
formatting):
>
> ---
> Iteration_Hz : constant Float := 60.0;
>
> task Executive is
>    Iteration : constant Ada.Real_Time.Time_Span
>       := Ada.Real_Time.To_Time_Span (Duration(1.0/Iteration_Hz));
>
>    Next_Time : Ada.Real_Time.Time := Ada.Real_Time.Clock + Iteration;
> begin
>    loop
>       -- Perform scheduling tasks
>       ...
>
>       Next_Time := Next_Time + Iteration;
>       delay until Next_Time;
>    end loop;
> end Executive;
>
> ---
>
> That looks pretty straightforward, right? Well, unfortunately, its
*WRONG*.
>
> 1/60 works out to about 0.016(6-repeating). That can't be accurately
> represented in an IEEE floating-point register. So what we get is
> something like 0.016667 (forgive me if I'm rounding the wrong digit).
> That rouding error slowly accumulates over time, until after about 52
> seconds our poor Executive task skips a tick. On our system ticks come
> at 240Hz, so that means that once every 52 seconds the executive
> scheduler waits about 20ms instead of 16.6(6-repeating)ms. Of course
> this wreaks havoc in our hard-realtime system.
>
> So the question is, what's the best way to solve this problem? Here's
> what we came up with so far:
>
>   o  Use the RTOS' "taskDelay" function to delay the appropriate
amount
> of ticks (4).
>
> I don't like this one, as its a non-Ada solution, and thus renders our
> scheduler non-portable. Its also succeptable to drift if something
> causes an extra tick to pass before we get to the taskDelay.
>
>   o  Check the Ada.Real_Time.Clock every cycle when we awaken, and add
> the Iteration to that for the "delay until".
>
> I don't like this because the clock check is going to be fairly
> expensive, and this solution is also succeptable to drift.
>
>   o  Figure out what the denominator is (60 in this case), and redo
the
> calculation every time that number of runs have happened.
>
> For instance, in the above code, I'd add something like:
>       Runs := Runs + 1;
>       if Runs >= Natural(Iteration_Hz) then
>          -- Adjust for drift due to rounding errors.
>          Runs := 0;
>          Run_Time := Run_Time + Ada.Real_Time.To_Time_Span (1.0);
>          Next_Time := Run_Time;
>       else
>          Next_Time := Next_Time + Iteration;
>       end if;
>
> I kind of like this option, as it is a mathematical solution to what
is,
> in essence, a mathematical problem. This ought to work as long as the
> denominator can be counted on to be a whole (natural) number. In my
> case, I think it can.
>
> Since this problem should happen to anyone using "delay until" with an
> iteration rate divisible by 3, and 60, 30, and 15Hz are common rates
in
> the industry, I can't be the first person to stumble on this problem.
> How does everyone else handle it?
>
> --
> T.E.D.
>
> http://www.telepath.com/~dennison/Ted/TED.html
>
> Sent via Deja.com
> http://www.deja.com/
>


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 18:01 ` Mike Silva
@ 2000-12-12 19:57   ` Ted Dennison
  2000-12-12 23:02     ` Mike Silva
  2000-12-18  6:26     ` Ray Blaak
  0 siblings, 2 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-12 19:57 UTC (permalink / raw)


In article <915p5n$p2j$1@nnrp1.deja.com>,
  Mike Silva <mjsilva@my-deja.com> wrote:
> It's Son of Patriot bug...

That's similar to what I called it. :-)

> I'm as far from an expert as you can get, but what if you defined your
> Iteration in terms of Tick (4*Tick in your case)?  Seems like that
> should provide an exact representation (why do I feel like that
> comment will get me nailed?). Otherwise, keep your own Iteration
> count in a scalar and add it (first converted to a Time_Span) to a
> Start_time for each delay until.

I'm still feeling too dumb for not seeing this before to "nail" anyone
about it. Since Iteration_Hz in my old example was 4*tick, it sounds
like you are saying something like:

   task Executive is
      Iteration_Count : Natural := 0;
      Start_Time      : Ada.Real_Time.Time := Ada.Real_Time.Clock;
   begin
      loop
         -- do work
         Iteration_Count := Iteration_Count + 1;
         delay until Start_Time + (Iteration_Count * Iteration_Hz);
      end loop;
   end Executive;

I see a couple of problems with this.
   o  This would only work for Natural'last iterations. That could be
accounted for though.
   o  Iteration_Count * Iteration_Hz is no different mathematicly from
adding Iteration_Hz to Start_Time Iteration_Count times, which is what I
was doing. You'd still get the same compounding round-off error.

> It's a very interesting question, and if I'm wrong I look forward to
> learning the right answer.

Very. I had a lot of enthusiasticly offered ideas here at work when I
asked about it yesterday.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
  2000-12-12 18:01 ` Mike Silva
@ 2000-12-12 20:00 ` Ken Garlington
  2000-12-12 20:40   ` Ted Dennison
  2000-12-12 20:22 ` Keith Thompson
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Ken Garlington @ 2000-12-12 20:00 UTC (permalink / raw)


"Ted Dennison" <dennison@telepath.com> wrote in message
news:915jl7$jt5$1@nnrp1.deja.com...

: Since this problem should happen to anyone using "delay until" with an
: iteration rate divisible by 3, and 60, 30, and 15Hz are common rates in
: the industry, I can't be the first person to stumble on this problem.
: How does everyone else handle it?

For our systems, we usually pick a base iteration rate that can be
accurately represented (64Hz  is popular, although I've also seen 50Hz and
80Hz).





^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
  2000-12-12 18:01 ` Mike Silva
  2000-12-12 20:00 ` Ken Garlington
@ 2000-12-12 20:22 ` Keith Thompson
  2000-12-12 20:54   ` Ted Dennison
  2000-12-13  5:35   ` tmoran
  2000-12-12 20:23 ` David C. Hoos, Sr.
                   ` (6 subsequent siblings)
  9 siblings, 2 replies; 33+ messages in thread
From: Keith Thompson @ 2000-12-12 20:22 UTC (permalink / raw)


Ted Dennison <dennison@telepath.com> writes:
[...]
> 1/60 works out to about 0.016(6-repeating). That can't be accurately
> represented in an IEEE floating-point register. So what we get is
> something like 0.016667 (forgive me if I'm rounding the wrong digit).

I think you're assuming that type Time is a floating-point type;
either that, or you're (inappropriately) using floating-point to store
time values.  It's more likely to be fixed-point.  If you're using
Gnat, Ada.Real_Time.Time and Ada.Real_Time.Time_Span are both derived
from Duration, which is a 64-bit fixed-point type with a Small of
exactly 1.0e-9 (one nanosecond).

The value 1.0/60.0 is still not exactly representable; it's going to
be either 0.166666666 or 0.166666667.

> That rouding error slowly accumulates over time, until after about 52
> seconds our poor Executive task skips a tick. On our system ticks come
> at 240Hz, so that means that once every 52 seconds the executive
> scheduler waits about 20ms instead of 16.6(6-repeating)ms. Of course
> this wreaks havoc in our hard-realtime system.
> 
> So the question is, what's the best way to solve this problem? Here's
> what we came up with so far:
> 
>   o  Use the RTOS' "taskDelay" function to delay the appropriate amount
> of ticks (4).
> 
> I don't like this one, as its a non-Ada solution, and thus renders our
> scheduler non-portable. Its also succeptable to drift if something
> causes an extra tick to pass before we get to the taskDelay.

Is the scheduler intended to be portable?  If the design of the
scheduler depends on the underlying 240Hz clock tick, perhaps you
should just bite the bullet and delay for 4 ticks.

> Since this problem should happen to anyone using "delay until" with an
> iteration rate divisible by 3, and 60, 30, and 15Hz are common rates in
> the industry, I can't be the first person to stumble on this problem.
> How does everyone else handle it?

If you're using Gnat, it will happen to anyone with an iteration rate
that's not a whole number of nanoseconds.  For other compilers, it may
happen with an iteration rate that's not a multiple of a power of 2.0,
depending on how the time types are implemented.  (In either case,
that includes iteration rates divisible by 3.)

-- 
Keith Thompson (The_Other_Keith) kst@cts.com  <http://www.ghoti.net/~kst>
San Diego Supercomputer Center           <*>  <http://www.sdsc.edu/~kst>
Welcome to the last year of the 20th century.



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
                   ` (2 preceding siblings ...)
  2000-12-12 20:22 ` Keith Thompson
@ 2000-12-12 20:23 ` David C. Hoos, Sr.
  2000-12-12 21:58   ` Ted Dennison
  2000-12-12 23:18   ` Jeff Carter
  2000-12-12 21:18 ` JP Thornley
                   ` (5 subsequent siblings)
  9 siblings, 2 replies; 33+ messages in thread
From: David C. Hoos, Sr. @ 2000-12-12 20:23 UTC (permalink / raw)


Well... the first thing I'd do is to use Long_Float for the frequency.
This alone will reduce the drift rate by 11 orders of magnitude, by
providing 11 more correct (decimal) digits in the computation of
the interval.


"Ted Dennison" <dennison@telepath.com> wrote in message
news:915jl7$jt5$1@nnrp1.deja.com...
> I came across an interesting problem the other day that I'd like to
> share, in the hopes that someone here has another solution that the ones
> we thought up.
>
> We have a real-time scheduler that uses the "delay until" statement to
> perform its scheduling. So far, this shouldn't shock anyone, as
> real-time scheduling is supposedly what "delay until" was put in the
> language for.
>
> Our scheduling code looks something like this (very simplified, and not
> compiled. My apologies ahead of time if Deja screws up the formatting):
>
> ---
> Iteration_Hz : constant Float := 60.0;
>
> task Executive is
>    Iteration : constant Ada.Real_Time.Time_Span
>       := Ada.Real_Time.To_Time_Span (Duration(1.0/Iteration_Hz));
>
>    Next_Time : Ada.Real_Time.Time := Ada.Real_Time.Clock + Iteration;
> begin
>    loop
>       -- Perform scheduling tasks
>       ...
>
>       Next_Time := Next_Time + Iteration;
>       delay until Next_Time;
>    end loop;
> end Executive;
>
> ---
>
> That looks pretty straightforward, right? Well, unfortunately, its
*WRONG*.
>
> 1/60 works out to about 0.016(6-repeating). That can't be accurately
> represented in an IEEE floating-point register. So what we get is
> something like 0.016667 (forgive me if I'm rounding the wrong digit).
> That rouding error slowly accumulates over time, until after about 52
> seconds our poor Executive task skips a tick. On our system ticks come
> at 240Hz, so that means that once every 52 seconds the executive
> scheduler waits about 20ms instead of 16.6(6-repeating)ms. Of course
> this wreaks havoc in our hard-realtime system.
>
> So the question is, what's the best way to solve this problem? Here's
> what we came up with so far:
>
>   o  Use the RTOS' "taskDelay" function to delay the appropriate amount
> of ticks (4).
>
> I don't like this one, as its a non-Ada solution, and thus renders our
> scheduler non-portable. Its also succeptable to drift if something
> causes an extra tick to pass before we get to the taskDelay.
>
>   o  Check the Ada.Real_Time.Clock every cycle when we awaken, and add
> the Iteration to that for the "delay until".
>
> I don't like this because the clock check is going to be fairly
> expensive, and this solution is also succeptable to drift.
>
>   o  Figure out what the denominator is (60 in this case), and redo the
> calculation every time that number of runs have happened.
>
> For instance, in the above code, I'd add something like:
>       Runs := Runs + 1;
>       if Runs >= Natural(Iteration_Hz) then
>          -- Adjust for drift due to rounding errors.
>          Runs := 0;
>          Run_Time := Run_Time + Ada.Real_Time.To_Time_Span (1.0);
>          Next_Time := Run_Time;
>       else
>          Next_Time := Next_Time + Iteration;
>       end if;
>
> I kind of like this option, as it is a mathematical solution to what is,
> in essence, a mathematical problem. This ought to work as long as the
> denominator can be counted on to be a whole (natural) number. In my
> case, I think it can.
>
> Since this problem should happen to anyone using "delay until" with an
> iteration rate divisible by 3, and 60, 30, and 15Hz are common rates in
> the industry, I can't be the first person to stumble on this problem.
> How does everyone else handle it?
>
>
> --
> T.E.D.
>
> http://www.telepath.com/~dennison/Ted/TED.html
>
>
> Sent via Deja.com
> http://www.deja.com/





^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 20:00 ` Ken Garlington
@ 2000-12-12 20:40   ` Ted Dennison
  2000-12-13  4:02     ` Ken Garlington
  2000-12-13 16:53     ` Larry Hazel
  0 siblings, 2 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-12 20:40 UTC (permalink / raw)


In article <ntvZ5.6802$bw.582745@news.flash.net>,
  "Ken Garlington" <Ken.Garlington@computer.org> wrote:
> For our systems, we usually pick a base iteration rate that can be
> accurately represented (64Hz  is popular, although I've also seen 50Hz
> and 80Hz).

That would indeed be the smart way to do it.

Unfortunately we have some algorithms that we have to match results with
that *have* to run at 60Hz. Its Legacy Fortran code, written and
controlled by developers at working for our customer who have little
patience with this newfangled "Ada" stuff.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 20:22 ` Keith Thompson
@ 2000-12-12 20:54   ` Ted Dennison
  2000-12-13  5:35   ` tmoran
  1 sibling, 0 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-12 20:54 UTC (permalink / raw)


In article <yec3dfttmnd.fsf@king.cts.com>,
  Keith Thompson <kst@cts.com> wrote:
> Ted Dennison <dennison@telepath.com> writes:
> The value 1.0/60.0 is still not exactly representable; it's going to
> be either 0.166666666 or 0.166666667.

Right, that's the issue. The resolution just dictates how often we get
hosed. We don't want to get hosed at all.

> Is the scheduler intended to be portable?  If the design of the
> scheduler depends on the underlying 240Hz clock tick, perhaps you
> should just bite the bullet and delay for 4 ticks.

It is portable right now. The only system dependancy is that the
executive's clock rate be achieveable exactly. On the OS we are using
(vxWorks) that can be set via a script or compiled into the kernel. I
see no good reason to make it non-portable if I don't have to.

I'm trying out the cycle-counting solution right now. If anyone has any
better ideas, I'd still like to hear them.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
                   ` (3 preceding siblings ...)
  2000-12-12 20:23 ` David C. Hoos, Sr.
@ 2000-12-12 21:18 ` JP Thornley
  2000-12-12 22:31   ` Ted Dennison
  2000-12-12 23:09 ` Ted Dennison
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: JP Thornley @ 2000-12-12 21:18 UTC (permalink / raw)


In article <915jl7$jt5$1@nnrp1.deja.com>, Ted Dennison
<dennison@telepath.com> writes
>1/60 works out to about 0.016(6-repeating). That can't be accurately
>represented in an IEEE floating-point register. So what we get is
>something like 0.016667 (forgive me if I'm rounding the wrong digit).

Why not use 3 'minor' cycles in a 'major' cycle.

The major cycle then has an exact delay of 0.05, and the minor cycles
are offset by 0.016667 and 0.033333 within the major cycle.

Cheers,

Phil Thornley

-- 
JP Thornley



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 20:23 ` David C. Hoos, Sr.
@ 2000-12-12 21:58   ` Ted Dennison
  2000-12-12 23:18   ` Jeff Carter
  1 sibling, 0 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-12 21:58 UTC (permalink / raw)


In article <9161h3$7n0$1@hobbes2.crc.com>,
  "David C. Hoos, Sr." <david.c.hoos.sr@ada95.com> wrote:
> Well... the first thing I'd do is to use Long_Float for the frequency.
> This alone will reduce the drift rate by 11 orders of magnitude, by
> providing 11 more correct (decimal) digits in the computation of
> the interval.

Again, getting hosed less often isn't the goal. I need to eliminate the
error entirely.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 21:18 ` JP Thornley
@ 2000-12-12 22:31   ` Ted Dennison
  2000-12-13  8:02     ` Brian Orpin
  2000-12-13 17:32     ` JP Thornley
  0 siblings, 2 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-12 22:31 UTC (permalink / raw)


In article <iA1duAAkYpN6Iw1g@diphi.demon.co.uk>,
  JP Thornley <jpt@diphi.demon.co.uk> wrote:
> In article <915jl7$jt5$1@nnrp1.deja.com>, Ted Dennison
> <dennison@telepath.com> writes
> >1/60 works out to about 0.016(6-repeating). That can't be accurately
> >represented in an IEEE floating-point register. So what we get is
> >something like 0.016667 (forgive me if I'm rounding the wrong digit).
>
> Why not use 3 'minor' cycles in a 'major' cycle.
>
> The major cycle then has an exact delay of 0.05, and the minor cycles
> are offset by 0.016667 and 0.033333 within the major cycle.

Ahhh, someone who speaks our language. :-)

It looks like you are essentially suggesting that the scheduler do
something like:
      Minor_Iteration_Rate : constant := 1.0/60.0;
      Major_Iteration_Rate : consant  := 0.05;
begin

   loop
      -- do work
      for Minor_Cycle in 1..2 loop
         delay until Next_Time + Iteration_Rate * Minor_Cycle;
         -- do work
      end loop;
      Next_Time := Next_Time + Major_Iteration_Rate;
      delay until Next_Time;
   end loop;

I think this is essentially the same as my iteration-counting scheme,
except that the "Major_Iteration_Rate" is set to 0.05 instead of 1.0,
and the number of minor cycles is hard-coded.

I did simplify things a bit for my question. We are actually using 4
minor cycles in our 60Hz major cycle. Under the scheme we use here
things can only run once a major cycle, so the fastest with the current
settings would be 60Hz. However, we can adjust the minor cycle used in
order to prevent jitter in those tasks that are expecially sensitive to
it (eg: Controls and the Visual system). So our executive is *actually*
running at 240Hz (but of course this doesn't affect the math problem.
1/240th still a repeating decimal).

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 19:57   ` Ted Dennison
@ 2000-12-12 23:02     ` Mike Silva
  2000-12-12 23:49       ` Ted Dennison
  2000-12-18  6:26     ` Ray Blaak
  1 sibling, 1 reply; 33+ messages in thread
From: Mike Silva @ 2000-12-12 23:02 UTC (permalink / raw)


In article <915vv7$vfo$1@nnrp1.deja.com>,
  Ted Dennison <dennison@telepath.com> wrote:
> In article <915p5n$p2j$1@nnrp1.deja.com>,
>   Mike Silva <mjsilva@my-deja.com> wrote:
> > It's Son of Patriot bug...
>
> That's similar to what I called it. :-)
>
> > I'm as far from an expert as you can get, but what if you defined
your
> > Iteration in terms of Tick (4*Tick in your case)?  Seems like that
> > should provide an exact representation (why do I feel like that
> > comment will get me nailed?). Otherwise, keep your own Iteration
> > count in a scalar and add it (first converted to a Time_Span) to a
> > Start_time for each delay until.
>
> I'm still feeling too dumb for not seeing this before to "nail" anyone
> about it. Since Iteration_Hz in my old example was 4*tick, it sounds
> like you are saying something like:
>
>    task Executive is
>       Iteration_Count : Natural := 0;
>       Start_Time      : Ada.Real_Time.Time := Ada.Real_Time.Clock;
>    begin
>       loop
>          -- do work
>          Iteration_Count := Iteration_Count + 1;
>          delay until Start_Time + (Iteration_Count * Iteration_Hz);
>       end loop;
>    end Executive;
>
> I see a couple of problems with this.
>    o  This would only work for Natural'last iterations. That could be
> accounted for though.

Yep.  You could, e.g. add 1.0 to Start_time and zero Iteration_count
every 60 iterations.

>    o  Iteration_Count * Iteration_Hz is no different mathematicly from
> adding Iteration_Hz to Start_Time Iteration_Count times, which is
what I
> was doing. You'd still get the same compounding round-off error.

I don't think so.  Adding an imprecise float value N times will give a
cumulative error that adding a scalar won't give.  The final I/N
division (really should be (I+epsilon)/N) won't have a cumulative error
as long as you don't run out of bits to do the math.

Still, I think it's better to try and represent your iteration in units
of Ada.Real_Time.Tick.  Then you are never dividing, only adding a
scalar.  If Ada.Real_Time.Tick can exactly represent your system tick
of 1/240 sec. then you should never have an error.  Then the question
becomes whether Tick is an exact representation of the system tick time
(seems like it would have to be!).

>
> > It's a very interesting question, and if I'm wrong I look forward to
> > learning the right answer.
>
> Very. I had a lot of enthusiasticly offered ideas here at work when I
> asked about it yesterday.

Mike


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
                   ` (4 preceding siblings ...)
  2000-12-12 21:18 ` JP Thornley
@ 2000-12-12 23:09 ` Ted Dennison
  2000-12-13  7:43 ` Brian Orpin
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-12 23:09 UTC (permalink / raw)


In article <915jl7$jt5$1@nnrp1.deja.com>,
  Ted Dennison <dennison@telepath.com> wrote:

>   o  Figure out what the denominator is (60 in this case), and redo
> the calculation every time that number of runs have happened.
>
> For instance, in the above code, I'd add something like:
>       Runs := Runs + 1;
>       if Runs >= Natural(Iteration_Hz) then
>          -- Adjust for drift due to rounding errors.
>          Runs := 0;
>          Run_Time := Run_Time + Ada.Real_Time.To_Time_Span (1.0);
>          Next_Time := Run_Time;
>       else
>          Next_Time := Next_Time + Iteration;
>       end if;

Arg! Slight problem with this. Since 1/60th rounds UP, that means the
first time I enter the else part, the next delay until delays *2* ticks
instead of one. Then when I do the adjustment, it asks to delay until a
time that has already occurred, which misses a tick (or double-ticks,
I'm not sure which). Ug.

Is there a way to specify that a floating-point calculation must round
down? The only other out I see would be to multiply by some largish
power of 10 then do a 'floor and divide that power of 10 back out. Yuck.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 20:23 ` David C. Hoos, Sr.
  2000-12-12 21:58   ` Ted Dennison
@ 2000-12-12 23:18   ` Jeff Carter
  1 sibling, 0 replies; 33+ messages in thread
From: Jeff Carter @ 2000-12-12 23:18 UTC (permalink / raw)


"David C. Hoos, Sr." wrote:
> 
> Well... the first thing I'd do is to use Long_Float for the frequency.
> This alone will reduce the drift rate by 11 orders of magnitude, by
> providing 11 more correct (decimal) digits in the computation of
> the interval.
> 
> "Ted Dennison" <dennison@telepath.com> wrote in message
> news:915jl7$jt5$1@nnrp1.deja.com...
> > Iteration_Hz : constant Float := 60.0;

That's incorrect. Not all compilers provide Long_Float, so the
suggestion is not portable. The increased precision from Long_Float, if
it exists, may not be 11 more digits. You could use

type Big is digits System.Max_Digits;

to obtain the maximum floating-point precision for the compiler.

It is the fact that this is a typed constant that is causing the loss of
precision. If it were a named number, the expression

1.0 / Iteration_Hz [I'll call this Increment]

would be a static universal real expression instead of a Float
expression, the conversion to Duration would be unnecessary, and the
result would have the full precision and accuracy of Duration.

However, that would still not be exact. You probably need to combine
various numbers of applications of the rounded-down and rounded-up
values of Increment so that after 30 of these you have delayed exactly
0.5 seconds.

-- 
Jeff Carter
"You brightly-colored, mealy-templed, cranberry-smelling, electric
donkey-bottom biters."
Monty Python & the Holy Grail



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 23:02     ` Mike Silva
@ 2000-12-12 23:49       ` Ted Dennison
  0 siblings, 0 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-12 23:49 UTC (permalink / raw)


In article <916aqq$9a7$1@nnrp1.deja.com>,
  Mike Silva <mjsilva@my-deja.com> wrote:

> Yep.  You could, e.g. add 1.0 to Start_time and zero Iteration_count
> every 60 iterations.
>

Ahhh. That's essentially what I was talking about doing.


> Still, I think it's better to try and represent your iteration in
> units of Ada.Real_Time.Tick.  Then you are never dividing, only adding
> a scalar.  If Ada.Real_Time.Tick can exactly represent your system
> tick of 1/240 sec. then you should never have an error.  Then the
> question becomes whether Tick is an exact representation of the system
> tick time(seems like it would have to be!).

I don't know about that. The system call on this OS to set the clock
rate takes in units of iterations per second. If that is the actual
units that the real-time clock hardware uses, then there's no possible
way for something expressed in units of seconds (like
Ada.Real_Time.Tick) to represent the *exact* same amount of time.

However, its quite possible that the real-time clock hardware on my
platform (PC) uses something like nannoseconds, and the OS call just
approximates that. In that case it indeed ought to be possible for
Ada.Real_Time.Tick to be an exact match for what the hardware clock
uses. However, since this compiler works with any PC-based version of
this OS, it could be that there was no good way of knowing what the
real-time clock's exact internal representation of time would be, so
they made Ada.Real_Time.Tick an approximation anyway. I'll have to look
into this issue...

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 20:40   ` Ted Dennison
@ 2000-12-13  4:02     ` Ken Garlington
  2000-12-13 14:29       ` Ted Dennison
  2000-12-13 16:53     ` Larry Hazel
  1 sibling, 1 reply; 33+ messages in thread
From: Ken Garlington @ 2000-12-13  4:02 UTC (permalink / raw)


"Ted Dennison" <dennison@telepath.com> wrote in message
news:9162gf$1or$1@nnrp1.deja.com...
: In article <ntvZ5.6802$bw.582745@news.flash.net>,
:   "Ken Garlington" <Ken.Garlington@computer.org> wrote:
: > For our systems, we usually pick a base iteration rate that can be
: > accurately represented (64Hz  is popular, although I've also seen 50Hz
: > and 80Hz).
:
: That would indeed be the smart way to do it.
:
: Unfortunately we have some algorithms that we have to match results with
: that *have* to run at 60Hz. Its Legacy Fortran code, written and
: controlled by developers at working for our customer who have little
: patience with this newfangled "Ada" stuff.

I'm not sure what the language has to do with it... this rule has been
around since I first started in the field - back when we used BDX930
assembly.





^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 20:22 ` Keith Thompson
  2000-12-12 20:54   ` Ted Dennison
@ 2000-12-13  5:35   ` tmoran
  1 sibling, 0 replies; 33+ messages in thread
From: tmoran @ 2000-12-13  5:35 UTC (permalink / raw)


>It's more likely to be fixed-point.  If you're using
>Gnat, Ada.Real_Time.Time and Ada.Real_Time.Time_Span are both derived
>from Duration, which is a 64-bit fixed-point type with a Small of
>exactly 1.0e-9 (one nanosecond).
  (A win for mathematical oversimplification over engineering, ;)

Clearly you need to do your arithmetic in a system that correctly
represents a single tick.  Integers come to mind.  How about

  Iteration_Count : Natural := 0;
  ...
  delay until Start_Time + Ada.Real_Time.Time_Span(Iteration_Count)/60;

If Ada.Real_Time.Time_Span(Iteration_Count) is going to get too large
before your program is done, then you need something that isn't so big
it overflows, but will count ticks without roundoff.  A fixed point
value with suitable range and a 'small of 1/60 will do that.  Then
you need a way to convert this accurate time to an Ada.Real_Time.Time
How about:

with Ada.Real_Time;
package Accurate_Time is

  type Time_Span is delta 1.0/60 range 0.0 .. long enough
  for Time_Span'small use 1.0/60;

  type Time is private;

  function Clock return Time;

  function "+"(Left : Time; Right : Time_Span) return Time;

  function To_Real_Time(T : in Time) return Ada.Real_Time.Time;

private

  type Time is record
    Start_SC : Ada.Real_Time.Seconds_Count;
    Start_TS : Ada.Real_Time.Time_Span;
    Since : Time_Span;
  end record;

end Accurate_Time;

package body Accurate_Time is
  use Ada.Real_Time;

  function Clock return Time is
    Result : Time;
    Now : Ada.Real_Time.Time := Ada.Real_Time.Clock;
  begin
    Ada.Real_Time.Split(Now, Result.Start_SC, Result.Start_TS);
    Result.Since := 0.0;
    return Result;
  end Clock;

  function "+"(Left : Time; Right : Time_Span) return Time is
    Result : Time := Left;
  begin
    Result.Since := Result.Since + Right;
    return Result;
  end "+";

  function To_Real_Time(T : in Time) return Ada.Real_Time.Time is
    Whole_Seconds_Since : Ada.Real_Time.Seconds_Count;
    Fractional_Seconds_Since : Time_Span;
    SC : Ada.Real_Time.Seconds_Count;
    TS : Ada.Real_Time.Time_Span;
  begin
    Whole_Seconds_Since := Ada.Real_Time.Seconds_Count(T.Since);
    if Time_Span(Whole_Seconds_Since) > T.Since then
      Whole_Seconds_Since := Whole_Seconds_Since-1;
    end if;
    Fractional_Seconds_Since := T.Since - Time_Span(Whole_Seconds_Since);
    SC := T.Start_SC + Whole_Seconds_Since;
    TS := T.Start_TS
          + Ada.Real_Time.To_Time_Span(Duration(Fractional_Seconds_Since));
    return Ada.Real_Time.Time_Of(SC, TS);
  end To_Real_Time;

end Accurate_Time;

>However, its quite possible that the real-time clock hardware on my
>platform (PC) uses something like nannoseconds, and the OS call just
>approximates that.
  If it uses the usual 8253 descendant, that clock hardware ticks
at 1,193,182 Hz, (1/3 of the NTSC TV color subcarrier crystal
frequency) which is 0.838 mics/tick.



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
                   ` (5 preceding siblings ...)
  2000-12-12 23:09 ` Ted Dennison
@ 2000-12-13  7:43 ` Brian Orpin
  2000-12-15  0:27 ` Frank
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Brian Orpin @ 2000-12-13  7:43 UTC (permalink / raw)


On Tue, 12 Dec 2000 16:27:31 GMT, Ted Dennison <dennison@telepath.com>
wrote:

>On our system ticks come
>at 240Hz, so that means that once every 52 seconds the executive
>scheduler waits about 20ms instead of 16.6(6-repeating)ms. Of course
>this wreaks havoc in our hard-realtime system.

What we do for varying iteration rates is have a scheduler that runs on
every tick.  This then decides what to fire off and initiates the
appropriate tasks.  We do not use the real time clock but rely on
external interrupts and a counter reset at the lowest rate.  We do a 'mod
= 0' to decide what needs to run.  Doing this we have 5 'working' tasks
running at different rates.  Of course your incoming interrupt must be of
an appropriate frequency!

-- 
Brian Orpin    BAE SYSTEMS, Edinburgh
"If you really know C++, there isn't much you can't do with it, though it may 
not always be what you intended!"  Tucker Taft 1998 



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 22:31   ` Ted Dennison
@ 2000-12-13  8:02     ` Brian Orpin
  2000-12-13 17:32     ` JP Thornley
  1 sibling, 0 replies; 33+ messages in thread
From: Brian Orpin @ 2000-12-13  8:02 UTC (permalink / raw)


On Tue, 12 Dec 2000 22:31:14 GMT, Ted Dennison <dennison@telepath.com>
wrote:

>I did simplify things a bit for my question. We are actually using 4
>minor cycles in our 60Hz major cycle. Under the scheme we use here
>things can only run once a major cycle, so the fastest with the current
>settings would be 60Hz. However, we can adjust the minor cycle used in
>order to prevent jitter in those tasks that are expecially sensitive to
>it (eg: Controls and the Visual system). So our executive is *actually*
>running at 240Hz (but of course this doesn't affect the math problem.
>1/240th still a repeating decimal).

Counter : Natural := 0
Freq1 : constant := 4;
Freq2 : constant := 2;
Min_Freq : constant := Freq1;

begin
  Counter := (Counter mod Min_Freq) +1;

  -- do it every other cycle.
  if Counter mod Freq2 = 0 then
	do Task1;
  end if;

  -- do it every 4 cycles
  if Counter mod Freq1 = 0 then
	do Task2;
  end if;

  -- do it every 4 cycles but on a different cycle to task2
  if Counter + 1 mod Freq1 = 0 then
	do Task3;
  end if;

end;

-- 
Brian Orpin    BAE SYSTEMS, Edinburgh
"If you really know C++, there isn't much you can't do with it, though it may 
not always be what you intended!"  Tucker Taft 1998 



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-13  4:02     ` Ken Garlington
@ 2000-12-13 14:29       ` Ted Dennison
  0 siblings, 0 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-13 14:29 UTC (permalink / raw)


In article <cxCZ5.7009$bw.627218@news.flash.net>,
  "Ken Garlington" <Ken.Garlington@computer.org> wrote:
> "Ted Dennison" <dennison@telepath.com> wrote in message

> : Unfortunately we have some algorithms that we have to match results with
> : that *have* to run at 60Hz. Its Legacy Fortran code, written and
> : controlled by developers at working for our customer who have little
> : patience with this newfangled "Ada" stuff.
>
> I'm not sure what the language has to do with it... this rule has been

Nothing really. But it would have *everything* to do with it in their
minds...

> around since I first started in the field - back when we used BDX930
> assembly.

I'm not sure why these folks like to use 60, but they do it for all
their sims as near as I can tell.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 20:40   ` Ted Dennison
  2000-12-13  4:02     ` Ken Garlington
@ 2000-12-13 16:53     ` Larry Hazel
  2000-12-13 17:41       ` Ted Dennison
  1 sibling, 1 reply; 33+ messages in thread
From: Larry Hazel @ 2000-12-13 16:53 UTC (permalink / raw)




Ted Dennison wrote:
> 
> In article <ntvZ5.6802$bw.582745@news.flash.net>,
>   "Ken Garlington" <Ken.Garlington@computer.org> wrote:
> > For our systems, we usually pick a base iteration rate that can be
> > accurately represented (64Hz  is popular, although I've also seen 50Hz
> > and 80Hz).
> 
> That would indeed be the smart way to do it.
> 
> Unfortunately we have some algorithms that we have to match results with
> that *have* to run at 60Hz. Its Legacy Fortran code, written and
> controlled by developers at working for our customer who have little
> patience with this newfangled "Ada" stuff.
> 
Why don't they have the same rounding problem in FORTRAN?  If 60 Hz can't be
done exactly in the hardware, language shouldn't matter.  Are they using
different hardware where 60 Hz is exact?

-- 
Larry Hazel



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 22:31   ` Ted Dennison
  2000-12-13  8:02     ` Brian Orpin
@ 2000-12-13 17:32     ` JP Thornley
  1 sibling, 0 replies; 33+ messages in thread
From: JP Thornley @ 2000-12-13 17:32 UTC (permalink / raw)


In article <9168vi$7mp$1@nnrp1.deja.com>, Ted Dennison
<dennison@telepath.com> writes
>I think this is essentially the same as my iteration-counting scheme,
>except that the "Major_Iteration_Rate" is set to 0.05 instead of 1.0,
>and the number of minor cycles is hard-coded.

Yes, I guess it is.

>
>I did simplify things a bit for my question. We are actually using 4
>minor cycles in our 60Hz major cycle.

Perhaps you can get a solution by avoiding the use of Duration. The
delay expression for a relative delay is only restricted to being "any
non-limited type" (9.6(5)), so why not declare your own:

   type My_Duration is delta (1/Iteration_Hz) range <whatever>;
   for My_Duration'Small use 1/Iteration_Hz;

(Although I would want to be very sure that my compiler was reliable
when dealing with a Small that was not a power of 2).

Cheers,

Phil

-- 
JP Thornley



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-13 16:53     ` Larry Hazel
@ 2000-12-13 17:41       ` Ted Dennison
  0 siblings, 0 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-13 17:41 UTC (permalink / raw)


In article <3A37A974.C85EB683@mindspring.com>,
  Larry Hazel <lhazel@mindspring.com> wrote:
>
>
> Why don't they have the same rounding problem in FORTRAN?  If 60 Hz
can't be
> done exactly in the hardware, language shouldn't matter.  Are they using
> different hardware where 60 Hz is exact?

Fortran has no such problem because Fortran has no tasking. It would be
forced to use the OS facilities, which would probably involve doing a
taskDelay X (where X is an intergral number of clock ticks). I could of
course do that in Ada too. But the issue I raised here deals with the
Ada mechanism, which is "delay until", and uses units of seconds. If the
OS's "ticks" can't be represented exactly in seconds, then the problem
arises.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
                   ` (6 preceding siblings ...)
  2000-12-13  7:43 ` Brian Orpin
@ 2000-12-15  0:27 ` Frank
  2000-12-19  7:50 ` Martin Gangkofer
  2000-12-20  3:17 ` Ted Dennison
  9 siblings, 0 replies; 33+ messages in thread
From: Frank @ 2000-12-15  0:27 UTC (permalink / raw)


Hi!

What about making several tasks each starting at a, by you, precomputed
exact time,
but from then on scheduled for each "nice" timedelay-
In your case I think of something like:

1/60= 1/(3*5*2*2), meaning that it is the 1/(2*2) part that is really
possible to put in binary figures,
and the 1/(3*5) part must be solved by other means.
Start the first task at 0.0 the second at 0.06666.....(good enough:-) the
third at 0.133333....(good enough) asf
until the 15.th task (if I'm not too tired) each of these task must then
reschedule at 1/(2*2) sek = 0.25 sec.


Frank
New to Ada world :-)


Ted Dennison <dennison@telepath.com> wrote in message
news:915jl7$jt5$1@nnrp1.deja.com...
> I came across an interesting problem the other day that I'd like to
> share, in the hopes that someone here has another solution that the ones
> we thought up.
>
> We have a real-time scheduler that uses the "delay until" statement to
> perform its scheduling. So far, this shouldn't shock anyone, as
> real-time scheduling is supposedly what "delay until" was put in the
> language for.
>
> Our scheduling code looks something like this (very simplified, and not
> compiled. My apologies ahead of time if Deja screws up the formatting):
>
> ---
> Iteration_Hz : constant Float := 60.0;
>
> task Executive is
>    Iteration : constant Ada.Real_Time.Time_Span
>       := Ada.Real_Time.To_Time_Span (Duration(1.0/Iteration_Hz));
>
>    Next_Time : Ada.Real_Time.Time := Ada.Real_Time.Clock + Iteration;
> begin
>    loop
>       -- Perform scheduling tasks
>       ...
>
>       Next_Time := Next_Time + Iteration;
>       delay until Next_Time;
>    end loop;
> end Executive;
>
> ---
>
> That looks pretty straightforward, right? Well, unfortunately, its
*WRONG*.
>
> 1/60 works out to about 0.016(6-repeating). That can't be accurately
> represented in an IEEE floating-point register. So what we get is
> something like 0.016667 (forgive me if I'm rounding the wrong digit).
> That rouding error slowly accumulates over time, until after about 52
> seconds our poor Executive task skips a tick. On our system ticks come
> at 240Hz, so that means that once every 52 seconds the executive
> scheduler waits about 20ms instead of 16.6(6-repeating)ms. Of course
> this wreaks havoc in our hard-realtime system.
>
> So the question is, what's the best way to solve this problem? Here's
> what we came up with so far:
>
>   o  Use the RTOS' "taskDelay" function to delay the appropriate amount
> of ticks (4).
>
> I don't like this one, as its a non-Ada solution, and thus renders our
> scheduler non-portable. Its also succeptable to drift if something
> causes an extra tick to pass before we get to the taskDelay.
>
>   o  Check the Ada.Real_Time.Clock every cycle when we awaken, and add
> the Iteration to that for the "delay until".
>
> I don't like this because the clock check is going to be fairly
> expensive, and this solution is also succeptable to drift.
>
>   o  Figure out what the denominator is (60 in this case), and redo the
> calculation every time that number of runs have happened.
>
> For instance, in the above code, I'd add something like:
>       Runs := Runs + 1;
>       if Runs >= Natural(Iteration_Hz) then
>          -- Adjust for drift due to rounding errors.
>          Runs := 0;
>          Run_Time := Run_Time + Ada.Real_Time.To_Time_Span (1.0);
>          Next_Time := Run_Time;
>       else
>          Next_Time := Next_Time + Iteration;
>       end if;
>
> I kind of like this option, as it is a mathematical solution to what is,
> in essence, a mathematical problem. This ought to work as long as the
> denominator can be counted on to be a whole (natural) number. In my
> case, I think it can.
>
> Since this problem should happen to anyone using "delay until" with an
> iteration rate divisible by 3, and 60, 30, and 15Hz are common rates in
> the industry, I can't be the first person to stumble on this problem.
> How does everyone else handle it?
>
>
> --
> T.E.D.
>
> http://www.telepath.com/~dennison/Ted/TED.html
>
>
> Sent via Deja.com
> http://www.deja.com/





^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 19:57   ` Ted Dennison
  2000-12-12 23:02     ` Mike Silva
@ 2000-12-18  6:26     ` Ray Blaak
  1 sibling, 0 replies; 33+ messages in thread
From: Ray Blaak @ 2000-12-18  6:26 UTC (permalink / raw)


Ted Dennison <dennison@telepath.com> writes:
> I see a couple of problems with this.
[...]
>    o  Iteration_Count * Iteration_Hz is no different mathematicly from
> adding Iteration_Hz to Start_Time Iteration_Count times, which is what I
> was doing. You'd still get the same compounding round-off error.

Would it? It might be the same mathematically, but computers don't do Real
arithmetic mathematically, only approximately.

Consider floating arithmetic accurate to 2 decimal places:

 Hz = 60, Inc = 1/Hz = 1.67

Adding incrementally twice:

 Inc + Inc = 3.34

Multiplying 2 iterations from a start time of zero:

 (1 + 1) / 60 = 3.33

The trick is to divide the total count so far by your Hz, not multiply by a
precomputed 1/Hz.
-- 
Cheers,                                        The Rhythm is around me,
                                               The Rhythm has control.
Ray Blaak                                      The Rhythm is inside me,
blaak@infomatch.com                            The Rhythm has my soul.



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
                   ` (7 preceding siblings ...)
  2000-12-15  0:27 ` Frank
@ 2000-12-19  7:50 ` Martin Gangkofer
  2000-12-20  3:32   ` Ted Dennison
  2000-12-20  3:17 ` Ted Dennison
  9 siblings, 1 reply; 33+ messages in thread
From: Martin Gangkofer @ 2000-12-19  7:50 UTC (permalink / raw)


Hello Ted,

I have seen your problem and I have seen a lot of answers, which seem to be
quite far away from the real problem. There is a german phrase, which would
translate into English like this: "Between the blind, the one-eyed is the
king". Well closing one eye I see a solution for your problem .. (Just some
motivation for all the greenhornes around),. in fact I know the problem from
another project and I also know the solution.
The problem you have is one of accuracy, you are getting a systematic error,
because your are using an inaccurate increment. The correct term for this kind
of problem, as I remember lessons on numerical mathmatics, is that the
algorithm you are using is badly conditioned and has to be turned into a well
conditioned algorithm:
Imagine the problem of drawing a straight line through two points, as closer
the points gets to each other the bigger is the deviation you get for a point
far  away. So thats exactly what you have done! You have chosen the smallest
possible separation of two points and expected accuracy forever, this kind of
approach only works in case of zero error and as computers usually use binary
arithmetics, this is only true if the frequency is can be expressed as
something like

    f = 1 / ( SIGMA(  a[i] * 2**i ) );

I have two solution solutions for you , both based on the idea to draw your
(imagined) straight line between two points which are separated as much as
possible, i.e. basically you take the start time of your task and the current
time. The solution is similar to the algorithm used to draw a straight line on
your screen.

1st solution:
=========
You have a defined start point (the startup time of your task) and a defined
end point, i.e. the current time.
Express the period in the form of a precise fixed point number:  1/f = dx / dy,
where dx and dy are integers.
Use a counter (n) to count cycles.

Use integer arithmetics, which is 100% accurate to calculate next time:

    NextTime  =  StartTime + ( 1/f ) * n  =  StartTime + ( n * dx ) / dy;

By calculating ( n * dx ) first and then divide you get a single error, which
do not accumulate.

2nd solution;
==========

(perhaps more fancy, but basically the same idea: use only a single unprecise
operation, it is just another way of using a counter)
Use precise fixed point arithmetics. Assuming the compiler does only use binary
deltas (I think the LRM does leave this open, but I am not sure), at least the
compiler I was using provided only binary deltas.

Then chose a fixed point type, which is able to accurately represent the period
you need:

    T = 1 / 60 = 1 / 2 / 2 / 3 / 5 ;

==> (only a choice)
type Precise_Time is delta 0.25;  -- Unit of time is 1/15 sec

Iteration : constant Precise_Time := 0.25;
Next_Time : Precise_Time := 0;
Start_Time : constant Ada.Real_Time.Time := Ada.Real_Time.Clock;
begin
   loop
         ....

        Next_Time := Next_Time + Iteration;
        delay until Start_Time + Ada.Real_Time.Time( Next_Time );
    end loop
end;

In this case the unprecise operation is the type cast.

I hope this helped you,
kind regards
Martin Gangkofer


Ted Dennison schrieb:

> I came across an interesting problem the other day that I'd like to
> share, in the hopes that someone here has another solution that the ones
> we thought up.
>
> We have a real-time scheduler that uses the "delay until" statement to
> perform its scheduling. So far, this shouldn't shock anyone, as
> real-time scheduling is supposedly what "delay until" was put in the
> language for.
>
> Our scheduling code looks something like this (very simplified, and not
> compiled. My apologies ahead of time if Deja screws up the formatting):
>
> ---
> Iteration_Hz : constant Float := 60.0;
>
> task Executive is
>    Iteration : constant Ada.Real_Time.Time_Span
>       := Ada.Real_Time.To_Time_Span (Duration(1.0/Iteration_Hz));
>
>    Next_Time : Ada.Real_Time.Time := Ada.Real_Time.Clock + Iteration;
> begin
>    loop
>       -- Perform scheduling tasks
>       ...
>
>       Next_Time := Next_Time + Iteration;
>       delay until Next_Time;
>    end loop;
> end Executive;
>
> ---
>
> That looks pretty straightforward, right? Well, unfortunately, its *WRONG*.
>
> 1/60 works out to about 0.016(6-repeating). That can't be accurately
> represented in an IEEE floating-point register. So what we get is
> something like 0.016667 (forgive me if I'm rounding the wrong digit).
> That rouding error slowly accumulates over time, until after about 52
> seconds our poor Executive task skips a tick. On our system ticks come
> at 240Hz, so that means that once every 52 seconds the executive
> scheduler waits about 20ms instead of 16.6(6-repeating)ms. Of course
> this wreaks havoc in our hard-realtime system.
>
> So the question is, what's the best way to solve this problem? Here's
> what we came up with so far:
>
>   o  Use the RTOS' "taskDelay" function to delay the appropriate amount
> of ticks (4).
>
> I don't like this one, as its a non-Ada solution, and thus renders our
> scheduler non-portable. Its also succeptable to drift if something
> causes an extra tick to pass before we get to the taskDelay.
>
>   o  Check the Ada.Real_Time.Clock every cycle when we awaken, and add
> the Iteration to that for the "delay until".
>
> I don't like this because the clock check is going to be fairly
> expensive, and this solution is also succeptable to drift.
>
>   o  Figure out what the denominator is (60 in this case), and redo the
> calculation every time that number of runs have happened.
>
> For instance, in the above code, I'd add something like:
>       Runs := Runs + 1;
>       if Runs >= Natural(Iteration_Hz) then
>          -- Adjust for drift due to rounding errors.
>          Runs := 0;
>          Run_Time := Run_Time + Ada.Real_Time.To_Time_Span (1.0);
>          Next_Time := Run_Time;
>       else
>          Next_Time := Next_Time + Iteration;
>       end if;
>
> I kind of like this option, as it is a mathematical solution to what is,
> in essence, a mathematical problem. This ought to work as long as the
> denominator can be counted on to be a whole (natural) number. In my
> case, I think it can.
>
> Since this problem should happen to anyone using "delay until" with an
> iteration rate divisible by 3, and 60, 30, and 15Hz are common rates in
> the industry, I can't be the first person to stumble on this problem.
> How does everyone else handle it?
>
> --
> T.E.D.
>
> http://www.telepath.com/~dennison/Ted/TED.html
>
> Sent via Deja.com
> http://www.deja.com/




^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
                   ` (8 preceding siblings ...)
  2000-12-19  7:50 ` Martin Gangkofer
@ 2000-12-20  3:17 ` Ted Dennison
  9 siblings, 0 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-20  3:17 UTC (permalink / raw)


I finally did get a solution to this that worked to my satisfaction.

In article <915jl7$jt5$1@nnrp1.deja.com>,
  Ted Dennison <dennison@telepath.com> wrote:

>   o  Figure out what the denominator is (60 in this case), and redo
> the calculation every time that number of runs have happened.
>
> For instance, in the above code, I'd add something like:
>       Runs := Runs + 1;
>       if Runs >= Natural(Iteration_Hz) then
>          -- Adjust for drift due to rounding errors.
>          Runs := 0;
>          Run_Time := Run_Time + Ada.Real_Time.To_Time_Span (1.0);
>          Next_Time := Run_Time;
>       else
>          Next_Time := Next_Time + Iteration;
>       end if;

This was close to my final solution, but not quite. Instead, it ended up
causing *more* problems with missed clocks. The solution was to use the
above code, except that all calls to "To_Time_Span" are replaced by
calls to "Ada.Real_Time.Nanoseconds", which gives better accuracy.
Real_Time.Microseconds would probably have been sufficient, but this
works too, and is a bit more portable.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-19  7:50 ` Martin Gangkofer
@ 2000-12-20  3:32   ` Ted Dennison
  2000-12-20  5:29     ` tmoran
                       ` (3 more replies)
  0 siblings, 4 replies; 33+ messages in thread
From: Ted Dennison @ 2000-12-20  3:32 UTC (permalink / raw)


In article <3A3F133E.4C13077C@esg-gmbh.de>,
  Martin Gangkofer <mgangkof@esg-gmbh.de> wrote:

> translate into English like this: "Between the blind, the one-eyed is
> the king". Well closing one eye I see a solution for your problem ..

I've heard it in English as "In the land of the blind, the one-eyed man
is king."

(
> Use integer arithmetics, which is 100% accurate to calculate next
> time:
>     NextTime  =  StartTime + ( 1/f ) * n  =  StartTime + ( n * dx ) /
>     dy;

Something pretty much like that has been suggested. My problem with it
is that "n" is a counter that has to be monotonically increasing. That
means that it will eventually hit an archetectural limit. Of course on
my 32-bit machine at 240Hz that limit won't happen until the machine has
been running for about 1/3 of a year, but the limit is there.

--
T.E.D.

http://www.telepath.com/~dennison/Ted/TED.html


Sent via Deja.com
http://www.deja.com/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-20  3:32   ` Ted Dennison
@ 2000-12-20  5:29     ` tmoran
  2000-12-20  7:59     ` Martin Gangkofer
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 33+ messages in thread
From: tmoran @ 2000-12-20  5:29 UTC (permalink / raw)


>is that "n" is a counter that has to be monotonically increasing. That
>means that it will eventually hit an archetectural limit. Of course on
>my 32-bit machine at 240Hz that limit won't happen until the machine has
>been running for about 1/3 of a year, but the limit is there.
  That suggests "n" is 32 bits.  Will your compiler support a larger
integer?  Or perhaps a "type counter is delta 1.0 range 0.0 .. 2.0**63;"
Anything that counts or sums must eventually overflow.  If
Real_Time.Time doesn't overflow during your program's lifespan then
it either increments less often or has more bits.



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-20  3:32   ` Ted Dennison
  2000-12-20  5:29     ` tmoran
@ 2000-12-20  7:59     ` Martin Gangkofer
  2000-12-20  9:15       ` java servlets JF Harrison
  2000-12-20 12:50     ` Using "delay until" in real-time Marin David Condic
  2000-12-21  0:08     ` Alejandro R. Mosteo
  3 siblings, 1 reply; 33+ messages in thread
From: Martin Gangkofer @ 2000-12-20  7:59 UTC (permalink / raw)




Ted Dennison schrieb:

> In article <3A3F133E.4C13077C@esg-gmbh.de>,
>   Martin Gangkofer <mgangkof@esg-gmbh.de> wrote:
>
> > translate into English like this: "Between the blind, the one-eyed is
> > the king". Well closing one eye I see a solution for your problem ..
>
> I've heard it in English as "In the land of the blind, the one-eyed man
> is king."
>
> (
> > Use integer arithmetics, which is 100% accurate to calculate next
> > time:
> >     NextTime  =  StartTime + ( 1/f ) * n  =  StartTime + ( n * dx ) /
> >     dy;
>
> Something pretty much like that has been suggested. My problem with it
> is that "n" is a counter that has to be monotonically increasing. That
> means that it will eventually hit an archetectural limit. Of course on
> my 32-bit machine at 240Hz that limit won't happen until the machine has
> been running for about 1/3 of a year, but the limit is there.

The solution is to increment the start time:

task body executive is
    function "+"( L : Ada.Real_Time.Time; R : Ada.Real_Time.Time_Span )
        return Ada.Real_Time.Time renames Ada.Real_Time."+";

    Period_Enumerator : constant Some_Integer_Type := 1;
    Period_Denominator : constant Some_Integer_Type := 60;
    Limit : constant Some_Integer_Type := Some_Integer_Type'Last;

    Start_Time : Ada.Real_Time.Time;
    Next_Time : Ada.Real_Time.Time;
    Cycles_Counter : Some_Integer_Type := 0;


    function Increment( Step : in Some_Integer_Type ) return
Ada.Real_Time.Time_Span is
        use Ada.Real_Time;

        -- use float type to avoid constraint error due to "*"
        -- single precision float should be sufficient, error is O( 1/10 )
usec
        -- if you need higher precision scheduling (???) take long float
        Val : constant Float := Float( Period_Enumerator * Step )
                / Float( Period_Denominator);
    begin
        return To_Time_Span( Duration( Val ) );
    end Increment;

begin

    -- startup initialisation, sync, etc.

    Start_Time := Ada.Real_Time.Clock;
    loop
        if ( Cycles_Counter < Limit ) then
            Cycles_Counter := Cycles_Counter + 1;
        else
            Cycles_Counter := 0;
            Start_Time := Start_Time + Increment( Limit );
        end if;


        -- do_your_job;


        Next_Time := Start_Time + Increment( Cycles_Counter );
        delay until Next_Time;
    end loop;

end Executive;

I did'nt compile it, but it should work.

Martin Gangkofer




^ permalink raw reply	[flat|nested] 33+ messages in thread

* java servlets
  2000-12-20  7:59     ` Martin Gangkofer
@ 2000-12-20  9:15       ` JF Harrison
  0 siblings, 0 replies; 33+ messages in thread
From: JF Harrison @ 2000-12-20  9:15 UTC (permalink / raw)
  To: comp.lang.ada

Is it possible to write servlets using JGNAT and Ada?
If not, is it a direction JGNAT is considering?

Regards,
JF Harrison





^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-20  3:32   ` Ted Dennison
  2000-12-20  5:29     ` tmoran
  2000-12-20  7:59     ` Martin Gangkofer
@ 2000-12-20 12:50     ` Marin David Condic
  2000-12-21  0:08     ` Alejandro R. Mosteo
  3 siblings, 0 replies; 33+ messages in thread
From: Marin David Condic @ 2000-12-20 12:50 UTC (permalink / raw)


Its not uncommon to have that sort of limitation, but depending on the
system, you can usually reason your way around it. The box gets turned on
and run for N hours at most, so we'll never overflow the counter." I had a
similar problem once with a control that used EEPROM and there was a major
concern about limiting the number of times it would be rewritten. The
estimate was that you had about 10,000 writes before the EEPROM would
potentially burn out. We were concerned with minimizing the writes to it
until I started investigating the probable number of updates. The best guess
was that a given control might need the EEPROM reloaded not more than 5..10
times in its production life. In the lab, it might experience 50..100
updates. That left plenty of margin, so we stopped worrying about it.

Of course we had to make sure we didn't accidentally get into some sort of
loop that might repetitively write a single address - it happened once on
another project so people got nervous about it.

You always need to look at the real world and sometimes say "Good Enough is
Good Enough!"

MDC

Ted Dennison wrote:

> Something pretty much like that has been suggested. My problem with it
> is that "n" is a counter that has to be monotonically increasing. That
> means that it will eventually hit an archetectural limit. Of course on
> my 32-bit machine at 240Hz that limit won't happen until the machine has
> been running for about 1/3 of a year, but the limit is there.

--
======================================================================
Marin David Condic - Quadrus Corporation - http://www.quadruscorp.com/
Send Replies To: m c o n d i c @ q u a d r u s c o r p . c o m
Visit my web site at:  http://www.mcondic.com/

    "Giving money and power to Government is like giving whiskey
    and car keys to teenage boys."

        --   P. J. O'Rourke
======================================================================





^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: Using "delay until" in real-time
  2000-12-20  3:32   ` Ted Dennison
                       ` (2 preceding siblings ...)
  2000-12-20 12:50     ` Using "delay until" in real-time Marin David Condic
@ 2000-12-21  0:08     ` Alejandro R. Mosteo
  3 siblings, 0 replies; 33+ messages in thread
From: Alejandro R. Mosteo @ 2000-12-21  0:08 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 318 bytes --]

En su mensaje Ted Dennison ha dicho...

> 
> I've heard it in English as "In the land of the blind, the one-eyed man
> is king."
> 

In spanish:

"En el pa�s de los ciegos, el tuerto es el rey".


-- 
------------------------------
Alejandro R. Mosteo
mailto: 402450@cepsz.unizar.es
------------------------------



^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2000-12-21  0:08 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2000-12-12 16:27 Using "delay until" in real-time Ted Dennison
2000-12-12 18:01 ` Mike Silva
2000-12-12 19:57   ` Ted Dennison
2000-12-12 23:02     ` Mike Silva
2000-12-12 23:49       ` Ted Dennison
2000-12-18  6:26     ` Ray Blaak
2000-12-12 20:00 ` Ken Garlington
2000-12-12 20:40   ` Ted Dennison
2000-12-13  4:02     ` Ken Garlington
2000-12-13 14:29       ` Ted Dennison
2000-12-13 16:53     ` Larry Hazel
2000-12-13 17:41       ` Ted Dennison
2000-12-12 20:22 ` Keith Thompson
2000-12-12 20:54   ` Ted Dennison
2000-12-13  5:35   ` tmoran
2000-12-12 20:23 ` David C. Hoos, Sr.
2000-12-12 21:58   ` Ted Dennison
2000-12-12 23:18   ` Jeff Carter
2000-12-12 21:18 ` JP Thornley
2000-12-12 22:31   ` Ted Dennison
2000-12-13  8:02     ` Brian Orpin
2000-12-13 17:32     ` JP Thornley
2000-12-12 23:09 ` Ted Dennison
2000-12-13  7:43 ` Brian Orpin
2000-12-15  0:27 ` Frank
2000-12-19  7:50 ` Martin Gangkofer
2000-12-20  3:32   ` Ted Dennison
2000-12-20  5:29     ` tmoran
2000-12-20  7:59     ` Martin Gangkofer
2000-12-20  9:15       ` java servlets JF Harrison
2000-12-20 12:50     ` Using "delay until" in real-time Marin David Condic
2000-12-21  0:08     ` Alejandro R. Mosteo
2000-12-20  3:17 ` Ted Dennison

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox