【问题标题】:Get PVT packet from "USB GPS" in Android App从 Android App 中的“USB GPS”获取 PVT 数据包
【发布时间】:2016-08-29 10:37:39
【问题描述】:

我尝试从 Etrex 10 Garmin GPS 读取其 USB 端口连接到 Android 平板电脑(三星 Galaxy Tab S,型号 SM-T705 支持 USB 主机功能)的 PVT(位置、速度、时间)记录。首先我使用一个模拟这种通信的 Windows 应用程序,我监控了 USB 发送/接收的数据包。根据 Garmin 文档(Garmin GPS 接口规范,USB 附录),这些数据包已成功发送和接收(所有数字均为十六进制):

(PC到GPS,开始会话数据包)00 00 00 00 05 00 00 00 00 00 00 00

(GPS到PC,确认开始会话)00 00 00 00 06 00 00 00 04 00 00 00 0b ba aa e7

这是本文档第 6 节的一部分,展示了主机和设备 (GPS) 如何进行通信: …………

6-示例通信会话

本部分提供 USB 主机和 Garmin USB GPS 之间的示例通信会话。以下消息按时间顺序显示。所有数据均以 HEX 格式显示。 批量输出(USB 传输层 - 开始会话) 主机首先将其发送到 Garmin USB 设备,告诉它准备传输。 00 00 00 00 05 00 00 00 00 00 00 00 中断输入(USB 传输层 - 会话已启动) 设备响应主机,让主机知道设备已准备好进行对话。 00 00 00 00 06 00 00 00 04 00 00 00 37 C4 E4 AB

.......................

我尝试在 Android 应用程序中编写此通信。在 Android 代码中,当连接到平板电脑时,我可以识别 GPS 设备。有 3 个端点。一个 Bulk_Out,另一个 Bulk-in,第三个 Interrupt-in。当我发送开始会话 通过批量输出端点的数据包,我没有通过中断(甚至 Bulk_in)端点收到确认数据包。这是 Xamarin Studio 中使用 MONO 库的代码:

public class MainActivity : Activity
{
private UsbManager m_USBManager = null;
private UsbDevice m_GPS = null;
private UsbInterface m_USBInterface = null;
private UsbEndpoint m_BulkOut = null;
private UsbEndpoint m_BulkIn = null;
private UsbEndpoint m_InterruptIn = null;
private UsbDeviceConnection m_USBConnection = null;

public static string ACTION_USB_PERMISSION = "GarminEtrex10.USB_PERMISSION";

private TextView m_TxMsg;
private ListView m_LvTxRxRows;

private USBReceiver m_USBRx;
PendingIntent m_PermissionIntent;

public List<TxRxRow> m_TxRxRows = new List<TxRxRow> ();

protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);

    SetContentView (Resource.Layout.Main);

    this.m_PermissionIntent = PendingIntent.GetBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);

    Button button = FindViewById<Button> (Resource.Id.myButton);
    this.m_TxMsg = FindViewById<TextView> (Resource.Id.TxMsg);
    this.m_LvTxRxRows = FindViewById<ListView> (Resource.Id.LvTxRx);

    this.m_USBManager = (UsbManager) this.GetSystemService (Context.UsbService);
    this.m_USBRx = new USBReceiver (this, this.m_USBManager);
    IntentFilter filter = new IntentFilter (ACTION_USB_PERMISSION);
    RegisterReceiver (this.m_USBRx, filter);

    button.Click += new EventHandler (this.ButtonClicked);
}

protected override void OnResume ()
{
    base.OnResume ();

    bool bHasPermission = false;

    if (this.m_USBManager != null)
    {
        foreach (string strKey in this.m_USBManager.DeviceList.Keys)
        {
            UsbDevice usbd = this.m_USBManager.DeviceList [strKey];
            if (usbd.VendorId == 2334)//Garmin product Id
            {
                this.m_GPS = usbd;
                break;
            }
        }
    }
    else
        this.m_TxMsg.Text = "USB Manager is null";
    if (this.m_GPS != null) 
    {
        this.m_USBManager.RequestPermission (this.m_GPS, this.m_PermissionIntent);
        bHasPermission = this.m_USBManager.HasPermission (this.m_GPS);
        this.m_TxMsg.Text = "Garmin GPS found.";
        if (bHasPermission)
            this.m_TxMsg.Text += " and have permission to use it.";
        else
            this.m_TxMsg.Text += " but not have permission to use it.";
    }
    else
        this.m_TxMsg.Text = "No GPS found.";
    if (bHasPermission) 
    {
        this.m_USBInterface = this.m_GPS.GetInterface (0);
        if (this.m_USBInterface != null) 
        {

            for (int i = 0; i < this.m_USBInterface.EndpointCount; i++)
            {
                UsbEndpoint ep = this.m_USBInterface.GetEndpoint (i);
                if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.Out && this.m_BulkOut == null)
                    this.m_BulkOut = ep;
                if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.In && this.m_BulkIn == null)
                    this.m_BulkIn = ep;
                if (ep.Type == UsbAddressing.XferInterrupt && ep.Direction == UsbAddressing.DirMask && this.m_InterruptIn == null) 
                    this.m_InterruptIn = ep;
            }
        }
    }
}

public void PopulateListView(byte[] bytes, byte port)
{
    TxRxRow row = new TxRxRow (port, bytes);
    this.m_TxRxRows.Add (row);
    TxRxAdapter adp = new TxRxAdapter (this, this.m_TxRxRows);
    this.m_LvTxRxRows.Adapter = adp;
}

public void SetStatus(string strMsg)
{
    this.m_TxMsg.Text = strMsg;
}

public void SetDevice(UsbDevice gps, UsbInterface gpsInterface, UsbDeviceConnection gpsConnection, UsbEndpoint gpsBulkout,UsbEndpoint gpsBultIn,UsbEndpoint gpsInterruptIn)
{
    if (this.m_BulkIn != null)
        return;
    this.m_GPS = gps;
    this.m_USBConnection = gpsConnection;
    this.m_USBInterface = gpsInterface;
    this.m_InterruptIn = gpsInterruptIn;
    this.m_BulkIn = gpsBultIn;
    this.m_BulkOut = gpsBulkout;
}
private void ButtonClicked(object sender, EventArgs e)
{
    if (this.m_BulkOut == null || (this.m_BulkIn == null && this.m_InterruptIn == null)) 
    {
        Toast.MakeText (this, "Could not find right endpoints", ToastLength.Long).Show ();
        return;
    }
    if(this.m_USBConnection ==null)
        this.m_USBConnection = this.m_USBManager.OpenDevice (this.m_GPS);
    if (this.m_USBConnection == null) 
    {
        Toast.MakeText (this, "Can not open USB device", ToastLength.Long).Show ();
        return;
    }

    if (!this.m_USBConnection.ClaimInterface (this.m_USBInterface, true)) 
    {
        Toast.MakeText (this, "Can not claim interface", ToastLength.Long).Show ();
        return;
    }

    int sentBytesCount = 0;

    #region Send Start_Session Packet
    byte[] obuffer = new byte[12];
    for(int i = 0; i < 12; i++)
        obuffer[i] = 0;
    obuffer[4]=5;

    this.m_TxMsg.Text = "Sending Start session packet....";
    sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, 12, 100);

    if (sentBytesCount >= 0) 
    {
        this.m_TxMsg.Text = "Sent bytes = "+sentBytesCount.ToString();
        this.PopulateListView(obuffer,1);
        this.m_USBRx.ReceiveFlag = true;
        this.m_USBConnection.RequestWaitAsync();
    }
    else
    {
        Toast.MakeText (this, "Sent bytes for start session packet = 0.operation failed", ToastLength.Long).Show ();
    }
    #endregion
}
}

这是广播接收器类

public class USBReceiver : BroadcastReceiver
{
private UsbManager m_USBMngr = null;
private Context m_Activity = null;
private UsbEndpoint m_BulkIN = null;
private UsbEndpoint m_InterruptIN = null;
private UsbDeviceConnection m_USBConnection=null;

public bool ReceiveFlag = false;

public USBReceiver(Context activity, UsbManager mngr) 
{
    this.m_Activity = activity;
    this.m_USBMngr = mngr;
}
public override void OnReceive (Context context, Intent intent)
{
    String action = intent.Action;
    (this.m_Activity as MainActivity).SetStatus ("Receiving activity=" + intent.Action + "...");
    if (action.Equals (MainActivity.ACTION_USB_PERMISSION)) 
    {
        lock (this) 
        {
            UsbDevice device = (UsbDevice)intent.GetParcelableExtra (UsbManager.ExtraDevice);
            bool bHasPermission = intent.GetBooleanExtra (UsbManager.ExtraPermissionGranted, false);
            //For the first time initialize MainActivity local parameters
            if (this.m_USBConnection == null) 
            {
                if (device != null && bHasPermission) 
                {
                    if (device.VendorId != 2334)
                        return;
                    this.m_USBConnection = this.m_USBMngr.OpenDevice (device);
                    UsbInterface ui = device.GetInterface (0);
                    UsbEndpoint epBulkOut = null;
                    for (int i = 0; i < ui.EndpointCount; i++) 
                    {
                        UsbEndpoint ep = ui.GetEndpoint (i);
                        if (ep.Type == UsbAddressing.XferInterrupt && ep.Direction == UsbAddressing.In)
                            this.m_InterruptIN = ep;
                        if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.In)
                            this.m_BulkIN = ep;
                        if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.Out)
                            epBulkOut = ep;
                    }
                    (this.m_Activity as MainActivity).SetDevice (device, ui, this.m_USBConnection, epBulkOut, this.m_BulkIN, this.m_InterruptIN);
                }
            }
            if (this.m_BulkIN != null) 
            {
                int maxPacketSize = this.m_BulkIN.MaxPacketSize;
                byte[] buff = new byte[maxPacketSize];
                int bytesRead = this.m_USBConnection.BulkTransfer (this.m_BulkIN, buff, maxPacketSize, 4000);
                if (bytesRead > 0) 
                {
                    (this.m_Activity as MainActivity).PopulateListView (buff, 2);
                    ReceiveFlag = false;
                }
            }
            if (this.m_InterruptIN != null) 
            {
                UsbRequest req = new UsbRequest ();
                if (req.Initialize (this.m_USBConnection, this.m_InterruptIN)) 
                {

                    int maxsize = this.m_InterruptIN.MaxPacketSize;
                    Java.Nio.ByteBuffer buff = Java.Nio.ByteBuffer.Allocate (maxsize);
                    if (req.Queue (buff, maxsize)) 
                    {
                        if (this.m_USBConnection.RequestWait () == req)
                        {
                            byte[] bb = new byte[maxsize];
                            buff.Get (bb, 0, buff.Remaining ());
                            if (bb.Length > 0) 
                            {
                                (this.m_Activity as MainActivity).PopulateListView (bb, 3);
                            }
                        }
                    }
                }
            }
        }
    }
}
}

当我将 GPS 连接到平板电脑并运行应用程序时,我收到一些内容为 00 的数据包,然后我点击命令按钮和应用程序 发送 start-session-packet,BulkTransfer 函数返回 12 表示成功发送了 12 个字节但之后在广播类中没有收到任何字节。应用程序开始时收到的零字节表明广播类以正确的方式工作,但我找不到为什么不承认 来自 GPS。似乎 GPS 没有收到命令字节,即使函数返回一个大于零的数字。我应该通过控制端点向 GPS 发送任何命令吗?Garmin 没有关于此的任何文档。你能告诉我出了什么问题使用此代码或程序? 任何帮助或指导线都非常感谢

这是 android 应用程序的快照,正如我在 2016/9/4 的评论中所述

这是我在这一步中实现的代码:

        private void ButtonClicked(object sender, EventArgs e)
    {
        if (this.m_BulkOut == null || (this.m_BulkIn == null && this.m_InterruptIn == null)) 
        {
            Toast.MakeText (this, "Could not find right endpoints", ToastLength.Long).Show ();
            return;
        }

        if (this.m_USBConnection == null) {
            try {
                this.m_USBConnection = this.m_USBManager.OpenDevice (this.m_GPS);
            } catch {
            }
        }

        if (this.m_USBConnection == null) 
        {
            Toast.MakeText (this, "Can not open USB device", ToastLength.Long).Show ();
            return;
        }

        if (!this.m_USBConnection.ClaimInterface (this.m_USBInterface, true)) 
        {
            Toast.MakeText (this, "Can not claim interface", ToastLength.Long).Show ();
            return;
        } 
        else
            this.PopulateListView (null, (byte)USBOpCode.ClaimInterface);

        int sentBytesCount = 0;

        #region Send Start_Session Packet
        byte[] obuffer = new byte[12];
        byte[] ibuffer = new byte[12];
        for(int i = 0; i < 12; i++)
        {
            obuffer[i] = 0;
            ibuffer[i] = 0;
        }
        obuffer[4]=5;

        UsbRequest req=new UsbRequest();
        if(req.Initialize(this.m_USBConnection,this.m_InterruptIn))
        {
            this.PopulateListView(null,(byte)USBOpCode.InitializeRequest);
            ByteBuffer buff=ByteBuffer.Wrap(ibuffer,0,12);
                //.Allocate(12);
            if(req.Queue(buff, 12))
            {
                this.PopulateListView(null,(byte)USBOpCode.QueueRequest);

                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);
                sentBytesCount=0;
                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);
                sentBytesCount=0;
                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);

                if(this.m_USBConnection.RequestWait() == req)
                    this.PopulateListView(null,(byte)USBOpCode.RequestWait);
                if(true)//buff.Position() > 0)
                {

                    //buff.Get(ibuffer,0,12);
                    this.PopulateListView(ibuffer,(byte)USBOpCode.InterruptIn_Received);
                }
            }
            req.Close();
        }
        this.m_USBConnection.ReleaseInterface(this.m_USBInterface);
        this.PopulateListView(null,(byte)USBOpCode.ReleaseInterface);
        return;

}

【问题讨论】:

  • @Reza,请要求合并您的帐户,以便您可以编辑自己的帖子。您可以通过页面底部的contact us 链接执行此操作。

标签: android gps usb garmin


【解决方案1】:

Garmin 设备在 linux 上有点棘手,在 android 上肯定是这样。这是(美国)商业战略的另一个案例(封闭源代码、丑陋的文档……)

看到这个:http://wiki.openstreetmap.org/wiki/USB_Garmin_on_GNU/Linux

在 linux 上,您需要一个名为 'garmin' lsmod | grep garmin http://northwestspatial.com/wp/?p=162 的内核驱动程序

这里有一个类似问题的app,可以联系作者https://play.google.com/store/apps/details?id=com.carlopescio.sportablet

【讨论】:

  • ralf 我对 linux 了解不多,我发现这些软件(如 gpsBabel)使用 garmin_drivers 作为它们在其下运行的操作系统。但我找不到任何适用于 Android 的 garmin gps 驱动程序,我认为我的问题就在这里。据我了解,“grmnusb.sys”(Windows 的 garmin 驱动程序)做了一些初始化,使 gps 准备好与最终用户程序通信。没有文件说明这些初始化是什么。我使用 USBLyzer 监控发送/在笔记本电脑-gps 之间收到数据包,但没有成功找到这些初始化数据是什么。还有其他想法吗?
  • 我已经编辑了问题并附上了一些屏幕截图,这些屏幕截图显示了当我的 Windows 应用程序成功运行时发送/接收的数据包。如您所见,关于那些 pnp 类型的行(一些它们由 grmnusb.sys 生成)。
  • 这里是linux的garmin_gps驱动源代码:searchcode.com/codesearch/view/2063852 android OS是linux。如果你想添加像 garmin_gps 这样的新内核模块,你必须编译你自己的 android,这真的很复杂。如果您决定编译自己的 android,您可以在诸如 hcitool 之类的 busybox 的东西旁边添加其他有用的实用程序......
  • 还有其他应用程序,例如 Garmin 的 Uploader,我认为它们使用了 garmin 的海量存储能力,因此可以访问 garmin 的内部存储器。我认为您不需要新的内核模块,您必须正确初始化与 garmin 的通信。也许关键在于 garmin_gps 驱动程序源代码 (searchcode.com/codesearch/view/2063852)。如果他愿意分享信息,请联系 Garmin 应用程序上传者的作者play.google.com/store/apps/… 电子邮件在本页底部
  • ralf,多亏了你和你放在这里的 linux garmin gps drvier 的链接,我终于可以与 GPS 通信了,但我的缓冲区中只收到 0。如果我发送正确的确认包 gps 回答我interrupt_in 端点,但我从中读取的都是零字节。如果我发送错误的数据包,gps 没有任何答案。我确定我非常接近最终答案,并且我的代码有问题,我必须找到它。我编辑了第一个问题并添加了一个显示此通信的屏幕截图。我在列表视图行中包含了我在代码中传递的步骤。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多