3

I am trying to create a square wave on the parallel port with java. So far I have this implementation.

public class Wave extends Thread {

    public Wave() {
        super();
        setPriority(MAX_PRIORITY);
    }

    @Override
    public void run() {
        Wave.high();
        LockSupport.parkNanos(20000000);
        Wave.low();
        LockSupport.parkNanos(20000000);
    }

    public static native void high();
    public static native void low();
}

In which high() and low() are implemented using JNI (a shared C library controls the parallel port). It works pretty well; it generates a square wave with a period of about 40ms. Using an oscilloscope it looks like the standard deviation is about 10 microseconds when the computer is idle. When the computer is not idle the standard deviation becomes much larger. I think this is because more context switches happen and Threads stay too long in the waiting state and the specified 20 ms is not achieved accurately.

Is there a way to make my implementation more accurate? I know I could use hardware for this but I want to know if I can do this with software too.

Would an option be to "listen" to a clock and perform an action timed to the millisecond?

skaffman
  • 398,947
  • 96
  • 818
  • 769
meijuh
  • 1,067
  • 1
  • 9
  • 23

3 Answers3

1

Just "listening" to the clock won't solve the problem of context switches causing jitter.

If you can dedicate a core to this:

This way you should be able to achieve very low jitter.

Of course, if the task is to simply produce a square wave, this is a pretty inefficient use of computing resources.

Community
  • 1
  • 1
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • Using System.nanoTime() and some modulo (every 20 ms) might be missed by a tight loop I think. Suppose I create an IF-statement it might just miss the 20th ms every time. But your solution seems a little too complex. I think I am going for some FTDI chip if the jitter becomes to extreme. – meijuh May 03 '12 at 12:56
1

i think there are going to be two sources of jitter.

first, garbage collection (and possibly other background processes, like the JIT) in java. for the code you gave, there should not be any gc. but if this is part of a larger system then you will likely find that garbage collection is required, and that it may alter the timings when it runs. you can try ameliorate this by playing with the jvm settings (java -X).

second, the system scheduler. in addition to the suggestions by aix, you can bump the priority of the process and do some linux-specific tweaks. this article explains some of the problems with linux. ubuntu has a low-latency kernel, which you can install, but i can't find info on what it actually contains so you can do the same on other systems (update: i think it may contain this patch). if you want to look for more info "low latency" is the key think to search for, and people doing audio processing on linux tend to be the ones who care most about this).

andrew cooke
  • 45,717
  • 10
  • 93
  • 143
0

If your context switching does not cause too much delay, you may try to park your thread until a given time, rather than for a given interval:

public class Wave extends Thread {
    private final Object BLOCKER = new Object();

    public Wave() {
        super();
        setPriority(MAX_PRIORITY);
    }

    @Override
    public void run() {
      // I suspect this should be running in an endless loop?
      for (;;) {
        Wave.high();
        long t1 = System.currentTimeMillis();

        // Round interval up to the next 20ms "deadline"
        LockSupport.parkUntil(BLOCKER, t1 + 20 - (t1 % 20));
        Wave.low();

        // Round interval up to the next 20ms "deadline"
        long t2 = System.currentTimeMillis();
        LockSupport.parkUntil(BLOCKER, t2 + 20 - (t2 % 20));
      }
    }

    public static native void high();
    public static native void low();
}

As this relies on the wall-clock time in ms, rather than a more precise nano-seconds time, this will not work well for much higher frequencies. But this may not work either, as GC (and other processes) may interrupt this thread for an "unfortunate" amount of time, resulting in the same jitter.

When I tested this on my Windows 7 quad-core with JDK 6, I had some non-negligible jitter about every second, so aix's solution is probably better

Community
  • 1
  • 1
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
  • It should indeed be an endless loop. I will try your solution with parkUntil and see if I get better results. – meijuh May 03 '12 at 13:02
  • @meijuh: What's definitely better about this is that after jittering, this loop corrects itself again to have the correct frequency "on average". I.e. most of the time, the `high()` and `low()` methods are invoked at a multiple of `20ms` – Lukas Eder May 03 '12 at 13:20