Java正弦波

3

我正在尝试使用Java在并口上创建一个方波。到目前为止,我有以下实现:

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();
}

在这个实现中,使用JNI(一个共享的C库控制并口)来实现high()和low()。它工作得非常好;它生成一个周期约为40ms的方形波。使用示波器观察时,当计算机处于空闲状态时,标准差大约为10微秒。当计算机不空闲时,标准差会变得更大。我认为这是因为发生了更多的上下文切换,线程在等待状态下停留时间过长,无法准确达到指定的20毫秒。
有没有办法使我的实现更精确?我知道我可以使用硬件来实现,但我想知道是否也可以使用软件来实现。
可能的一个选项是“听取”时钟并按毫秒执行操作吗?

2
什么操作系统?我认为这在很大程度上取决于您系统的调度程序。 - andrew cooke
嗨,我正在使用Linux内核3.2。 - meijuh
3个回答

1

使用System.nanoTime()和一些模数(每20毫秒)可能会被紧密循环所错过。我认为,假设我创建一个IF语句,它可能每次都会错过第20毫秒。但是你的解决方案似乎有点太复杂了。如果抖动变得极端,我想我会选择一些FTDI芯片。 - meijuh

1

我认为会有两个抖动源。

首先,Java中的垃圾回收(可能还包括JIT等其他后台进程)。对于您提供的代码,不应该有任何垃圾回收。但如果这是一个更大系统的一部分,那么您可能会发现需要进行垃圾回收,并且它可能会在运行时改变时间。您可以尝试通过调整JVM设置(java -X)来改善这种情况。

其次,是系统调度器。除了aix的建议外,您可以提高进程的优先级并进行一些Linux特定的调整。这篇文章解释了一些关于Linux的问题。Ubuntu有一个低延迟内核,您可以安装它,但我找不到有关它实际包含什么的信息,因此您可以在其他系统上执行相同的操作(更新:我认为它可能包含这个补丁)。如果您想寻找更多信息,“低延迟”是搜索的关键词,而在Linux上进行音频处理的人往往是最关心这个问题的人。

0
如果您的上下文切换不会造成太多延迟,您可以尝试将线程停放到给定时间,而不是给定时间间隔:
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();
}

由于这依赖于墙钟时间的ms,而不是更精确的纳秒时间,因此对于更高的频率可能效果不佳。但这也可能行不通,因为GC(和其他进程)可能会在“不幸”的时间内中断此线程,导致相同的抖动。

当我在我的Windows 7四核JDK 6上测试时,每秒大约有一些非常可观的抖动,因此aix's solution可能更好。


它应该确实是一个无限循环。我将尝试您的parkUntil解决方案,并查看是否获得更好的结果。 - meijuh
@meijuh:这个程序的优点在于,在抖动之后,循环会自我修正,以达到正确的频率“平均值”。也就是说,大多数情况下,high()low()方法都会在20ms的倍数时被调用。 - Lukas Eder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接