使用Mathematica连接到UDP数据流

8
我在iPhone上有一个名为iSeismometer的应用程序,它读取iPhone的加速度计并充当服务器,通过UDP流式传输此数据(我可以设置IP地址和端口号)。问题是如何使用Mathematica读取此数据流?显然,Dreeves已经在12年前研究了这个问题,因此我想在此期间一定发生了一些事情。 更新
到目前为止,我得到了两个很好的答案;一个来自WReach,另一个来自Mark McClure。他们都使用JLink来获取数据。这似乎是一个不错的方法。但是,我想起我在WII平衡板上做过一些工作。我使用一些免费程序(GlovePIE和PPJoy)使这个蓝牙外设出现为Windows的操纵杆,因此也可以通过ControllerState将其连接到Mathematica。当然,蓝牙和UDP是非常不同的,但是是否可以通过类似的方式使其工作呢?

2
@Mr.Wizard 没问题。顺便说一下,今天很安静,你觉得呢? - Sjoerd C. de Vries
2
我认为您可以设置一个类似于http://www.iseismometer.com/wp-content/uploads/2010/05/receiver.zip的服务器,从UDP端口读取数据并将其写入命名管道。然后在Mma中作为流读取命名管道。 - Dr. belisarius
1
也许你可以写一些使用JLink的东西? - Brett Champion
2
他是如何在12年前拥有一部iPhone的? - 681234
1
@TomD 显然,dreeves在12年前的Mathgroup线程中只是处理了读取套接字,而不是在iPhone环境下。 - Sjoerd C. de Vries
显示剩余9条评论
2个回答

4
假设在iSeismometer网站的一篇博客文章中所讨论的设置下,有几个选项可供选择。 导入 第一个选项是使用外部程序来捕获数据包,然后使用导入将结果带入,例如:
Import["!someexternalprog", "Lines"]

哎呀,博客文章中提到的Python程序在这里无法很好地工作,因为它运行在必须手动终止的无限循环中。如果该程序被修改为在固定数量的数据包或时间限制后停止,则Import方法才能起作用。

JLink

可以通过使用JLink在舒适的Mathematica环境中实现另一种方法。也许说我们仍然在Mathematica中有点牵强,因为相当数量的奇怪的Java代码与Mathematica代码混合在一起。尽管如此,它确实展示了随每个Mathematica副本一起附带的内置Java分发的实用性:

Needs["JLink`"]
LoadJavaClass["java.util.Arrays"];

ClearAll@ListenToISeismometer
ListenToISeismometer[port_] :=
  JavaBlock@Module[{socket, packet, listen, record = Null, listening = True}
  , packet = JavaNew["java.net.DatagramPacket", JavaNew["[B", 1024], 1024]
  ; listen[] :=
      If[$Failed =!= Quiet[socket@receive[packet], Java::excptn]
      , record =
          JavaNew[
            "java.lang.String"
          , java`util`Arrays`copyOfRange @@ packet /@ {getData[], getOffset[], getLength[]}
          ]@toString[] // Sow
      ]
  ; Row[{Button["Stop", listening = False], Dynamic[record]}, "  "] // PrintTemporary
  ; AbortProtect[
      socket = JavaNew["java.net.DatagramSocket", port]
    ; socket@setSoTimeout[1000]
    ; Reap[While[listening, listen[]]; socket@close[]][[2, 1]]
    ]
  ]

为了使这个示例长度可控,有关异常处理、数据包解码等方面已经采取了一些捷径。

ListenToISeismometer需要指定要监听的UDP端口号。让我们使用博客文章中相同的端口号10552:

In[33]:= data = ListenToISeismometer[10552];

停止按钮

该函数将监听该端口上的所有UDP事件,直到被告知停止。为此提供了一个按钮,每个数据包都会在旁边闪烁以表示接收到。 当按下按钮时,该函数将返回接收到的数据包列表:

In[34]:= data // Column
Out[34]= 1,83575.099,0.029,0.044,0.094
         1,83575.781,0.056,0.033,0.099
         1,83575.924,0.047,0.054,0.094
         1,83575.613,0.096,0.092,0.057
         1,83575.748,0.073,0.049,0.061
         1,83575.577,0.008,0.089,0.020
         ...

JLink使这成为可能,但无法逃脱使用JLink需要掌握Java的事实。


谢谢!这很容易适应显示三个加速度计通道的实时图形。使用一个具有400个数据点的数组和一些RotateLeft,最近的数据在最后一个位置获得了一个漂亮的浮动图形。我注意到了一点延迟,似乎随着时间的推移而增加。一个写入比读取更快的读/写缓冲区?你认为可以运行多个“ListenToISeismometer”的实例吗?如果可能的话,我可以使用我的妻子的iPhone和我们的iPad来看看附近铁路建设的波浪如何穿过我们的房子。 - Sjoerd C. de Vries
@Sjoerd ListenToISeismometer不支持多个实例。博客文章中有一张截图显示了ISeismometer中的“广播”按钮,也许它支持UDP组播?如果是这样,ListenToISeismometer就必须改用多播套接字。使用不同的Java API可以更直接地加速数据包吞吐量,但我不确定是否会通过JLink尝试。我可能会将大部分逻辑推入常规Java类中,就像@Mark的解决方案一样。 - WReach

4
JLink绝对是一个好选择。我更喜欢通过编译Java程序并从Mathematica中调用来保持我的Java代码和Mathematica代码分开。我设置了一个笔记本和伴随的Java程序,你可以在这里获取:http://facstaff.unca.edu/mcmcclur/UDPFiles.tar.gz 以下是必要的Mathematica代码:
Needs["JLink`"];
InstallJava[];
AddToClassPath[NotebookDirectory[]];
udpReader = JavaNew["myClient"];
i = 0;
While[True && i++ < 100,
  Print[udpReader@udpReadOne[10552]]]

updReader类由以下Java代码定义。

// A simple UDP client to read from iseismometer:
// http://www.iseismometer.com/
// You can run this from the command line via "java myClient"
// to check that your iseismometer setup is correct or you can
// call the the udpReadOne method from another program.

import java.io.*;
import java.net.*;
import java.util.*;

public class myClient {
    public static void main() throws IOException {
        DatagramSocket socket = new DatagramSocket(10552);
        byte[] buffer = new byte[500];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
        while(true) {
            socket.receive(packet);
            String received = new String(packet.getData(), 0, packet.getLength());
            System.out.println(received);
        }
    }

    public static String udpReadOne(int port) throws IOException {
        DatagramSocket socket = new DatagramSocket(port);
        byte[] buffer = new byte[100];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
        socket.receive(packet);
        String received = new String(packet.getData(), 0, packet.getLength());
        socket.close();
        return received;
    }
}

请注意,您可以使用myClient类的主方法来检查您的设置是否正常工作,而无需Mathematica,从而将一个潜在问题排除在外。

谢谢Mark!请参考我的评论@WReach。很难确定哪种方法是最好的解决方案。两种方法都非常适合实时绘制数据图表。看起来你的方法更容易使用,如果你想同时读取更多的iPhone,而WReach的方法则是自包含在mma中,不需要与外部Java程序交互。 - Sjoerd C. de Vries

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