【无人机学习之Mission Planner】RTK/GPS Inject 学习

news2024/11/26 15:49:33

█ 【无人机学习之Mission Planner】RTK/GPS Inject 学习


█ 系列文章目录

提示:这里是收集了无人机的相关文章

  • 【无人机学习】无人机基础知识
  • 【无人机学习】Mission Planner(pc端)和QGroundControl(android端)
  • 【无人机学习之DroidPlanner】FlightActivity的启动过程
  • 【无人机学习之DroidPlanner】msg_heartbeat心跳处理(含MAVLink协议)
  • 【无人机学习之DroidPlanner】msg_sys_status系统状态
  • 【无人机学习之QGroundControl】android端App初解1
  • 【无人机学习之QGroundControl】android端App初解2-APMPowerComponent(含QML的介绍)
  • 【无人机学习之QGroundControl】android端App初解3-ParameterEditorController
  • 【无人机学习之Mission Planner】RTK/GPS Inject 学习

█ 文章目录

  • █ 【无人机学习之Mission Planner】RTK/GPS Inject 学习
  • █ 系列文章目录
  • █ 文章目录
  • █ 读前说明
  • █ RTK/GPS Inject
  • █ u-blox GNSS Receiver 是什么?
  • █ MissionPlanner 中的代码
  • █ 关于Utilities
  • █ 关于串口
  • █ 相关资料
  • █ 免责声明


█ 读前说明

  • 本文通过学习别人写demo,学习一些课件,参考一些博客,学习相关知识,如有涉及侵权请告知
  • 本文可能只简单罗列了一些相关的代码实现过程,复制了一些大神的高论,如内容有误请自行辨别
  • 涉及到的逻辑以及说明可能只做了简单的介绍,主要当做笔记,了解过程而已,如有不同看法,欢迎下方评论
  • 本文源码:https://github.com/ArduPilot/MissionPlanner

█ RTK/GPS Inject

  • 寻找u-blox GNSS Receiver 连接方式
  • >>>>外接USB时,显示 u-blox GNSS Receiver ,有时候会出现无法识别,可以重启设备
    在这里插入图片描述

在这里插入图片描述

  • 读取到的usb设备信息
[mName=/dev/bus/usb/001/021,mVendorId=5446,mProductId=425,mClass=2,mSubclass=0,mProtocol=0,mManufacturerName=u-blox AG - www.u-blox.com,mProductName=u-blox GNSS receiver,mVersion=1.16,mSerialNumber=null,mConfigurations=[
UsbConfiguration[mId=1,mName=null,mAttributes=192,mMaxPower=0,mInterfaces=[
UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=2,mSubclass=2,mProtocol=1,mEndpoints=[
UsbEndpoint[mAddress=131,mAttributes=3,mMaxPacketSize=64,mInterval=255]]
UsbInterface[mId=1,mAlternateSetting=0,mName=null,mClass=10,mSubclass=0,mProtocol=255,mEndpoints=[
UsbEndpoint[mAddress=1,mAttributes=2,mMaxPacketSize=64,mInterval=0]
UsbEndpoint[mAddress=130,mAttributes=2,mMaxPacketSize=64,mInterval=0]]]]
  • 5446是什么

USB设备的厂商ID。产品名为 u-blox GNSS receiver。

  • ubloxusb.inf

此文件在Drivers\ubloxusb.inf

;------------------------------------------------------------------------------
;  String Definitions
;------------------------------------------------------------------------------

[Strings]
PROVNAME="u-blox AG"
MFGNAME="u-blox AG" // 生产厂家/制造商 名称
INSTDISK="Installation Disc"
DESCRIPTIONU8="u-blox GNSS Receiver"
DESCRIPTIONU7="u-blox 7 GPS/GNSS Receiver"
DESCRIPTIONU6="u-blox 6 GPS Receiver"
DESCRIPTIONU5="u-blox 5 GPS and GALILEO Receiver"
DESCRIPTIONA4="u-blox ANTARIS 4 GPS Receiver"

█ u-blox GNSS Receiver 是什么?

  • Vendor ID 和 Product ID

USB设备的VID(Vendor ID)是指厂商ID、供应商ID。每一个USB设备都具有VID,通过VID可以获取到该USB设备对应的USB设备厂商。
USB设备的PID(Product ID)是指产品ID、产品识别码。
USB设备的VID、PID可能是主控芯片的生产商信息,也有可能是USB设备生产商信息。
当USB设备连接主机时,如果固件中有设备生产商的VID和PID,会将该VID和PID报告给主机,而忽略主控芯片生产商的VID和PID。
很多USB设备生产商(山寨厂居多)为了方便,并不会向USB执行论坛申请自己的VID,而是依然沿用主控生产商的VID或随便向产品写入VID和PID;

    <!-- 0x1546 / 0x01a7: ublox 7020-->
    <usb-device vendor-id="5446" product-id="423" />

    <!-- 0x1546 / 0x01a6: ublox NEO-6P -->
    <usb-device vendor-id="5446" product-id="422" />

    <!-- 0x1546 / 0x01a9: u-blox GNSS receiver -->
    <usb-device vendor-id="5446" product-id="425" />
    
    <!-- 0x0403 / 0x6015: FTDI FT231X -->
    <usb-device vendor-id="1027" product-id="24597" />

    <!-- 0x2341 / Arduino -->
    <usb-device vendor-id="9025" />

    <!-- 0x16C0 / 0x0483: Teensyduino  -->
    <usb-device vendor-id="5824" product-id="1155" />

    <!-- 0x10C4 / 0xEA60: CP210x UART Bridge -->
    <usb-device vendor-id="4292" product-id="60000" />
    
    <!-- 0x067B / 0x2303: Prolific PL2303 -->
    <usb-device vendor-id="1659" product-id="8963" />
  • 查询方式
查询方式网站说明
USB-IFusb厂商列表 PDFUSB-IF是一家非营利性公司,USB的官网,通过官网查询最为准确,官网每个季度都会进行更新。
Linux USB ProjectList of USB ID’s这个查询比较方便
Device Hunt通过Vendor ID和Device ID查询一个PCI和USB设备数据库,用于帮助任何寻找PCIdatabase.com替代 PCI 或 USB 设备的人。
  • u-blox 查询结果

u-blox芯片是瑞士优北罗股份有限公司(u-blox AG) (SIX:UBXN)所研发的芯片。u-blox公司除芯片产品之外,自主研发的解决方案能准确进行人员、车辆和机器的定位。
u-blox C099-F9P应用套件主要用于u-blox ZED-F9P多频RTK解决方案的评估与测试,ZED-F9P是一款多星座、多频段GNSS接收机,可同时接收来自多达四种GNSS卫星导航系统(GPS、GLONASS、Galileo、北斗)的信号,能在数秒内提供厘米级定位精度,适用于大众应用市场。
C-RTK 9Ps是由CUAV推出的一款四星多频RTK定位系统(Real Time Kinematics) ,您可以使用2个C-RTK 9Ps模块(一个做飞机端、一个做基站端)组成完整的多星多频RTK定位系统。

>>>>大概推测下,C-RTK 9Ps 使用了 u-blox 9 (即 u-blox GNSS receiver),在使用u-center 配置C-RTK9P时,选择u-blox Generation 9 和 ucenter_config_f9p_gvins.txl 配置,所以称之为 u-blox 9 也没错, 因此读取到 mVendorId=5446,mProductId=425,但是他们没去论坛申请PID,因此查询不到

1546  U-Blox AG
	01a4  Antaris 4
	01a5  [u-blox 5]
	01a6  [u-blox 6]
	01a7  [u-blox 7]
	01a8  [u-blox 8]
	1102  LISA-U2

在这里插入图片描述

█ MissionPlanner 中的代码

  • UI:ConfigurationView.ConfigSerialInjectGPS.xam

此文件在ExtLibs\uno\UnoUI\UnoUI.Shared\test\MissionPlanner.GCSViews.ConfigurationView.ConfigSerialInjectGPS.xaml

<UserControl xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
    x:Class="MissionPlanner.GCSViews.ConfigurationView.ConfigSerialInjectGPS">
    <!-- 右上 链路状态 和 定位类型-->
    <Grid>
        <!-- 右上1 链路状态 link status  -->
        <Grid Name="groupBox3">
            <TextBlock Name="label3">Input data rate</TextBlock>
            <TextBlock Name="lbl_status1">300 bps</TextBlock>
            <TextBlock Name="label4">Output data rate</TextBlock>
            <TextBlock Name="label6">Messages Seen</TextBlock>
            <TextBlock Name="lbl_status2">2  bps sent</TextBlock>
            <TextBlock Name="labelmsgseen">rtcm0000</TextBlock>
        </Grid>
        <!-- 右上2 RTCM 四大卫星导航与定位系统:美国GPS、俄罗斯GLONASS、北斗(BDS)、欧盟GALILEO -->
        <Grid Name="groupBox2">
            <TextBlock Name="labelGall"></TextBlock>
            <TextBlock Name="label16">Galileo</TextBlock>
            <TextBlock Name="label14BDS"></TextBlock>
            <TextBlock Name="label15">Beidou</TextBlock>
            <TextBlock Name="labelglonass"></TextBlock>
            <TextBlock Name="labelgps"></TextBlock>
            <TextBlock Name="labelbase"></TextBlock>
            <TextBlock Name="label13">Glonass</TextBlock>
            <TextBlock Name="label12">Gps</TextBlock>
            <TextBlock Name="label5">RTCM Base</TextBlock>
            <TextBlock Name="label11">Base</TextBlock>
            <TextBlock Name="lbl_status3">-00.000000000 000.000000000 0000.000000000</TextBlock>
        </Grid>
        <!-- 左上 -->
        <ComboBox Name="CMB_serialport"></ComboBox>
        <Button Name="BUT_connect">Connect</Button>
        <ComboBox Name="CMB_baudrate"></ComboBox>
        <CheckBox Name="chk_rtcmmsg">Inject MSG Type</CheckBox>
        <CheckBox Name="chk_ubloxautoconfig">M8P/F9P autoconfig</CheckBox>
        <!-- 底部  M8P/F9P -->
        <!-- SplitContainer 类 表示一个由可移动条组成的控件,该可移动条将容器的显示区域分成两个大小可调的面板。 -->
        <!-- splitContainer控件本身就分为panel1和panel2两部分 -->
        <Grid Name="splitContainer1">
            <Grid>
                <!-- this.groupBoxm8p.Controls.Add(this.groupBox1); -->
                <!-- this.groupBoxm8p.Controls.Add(this.panel2); -->
                <!-- this.splitContainer1.Panel1.Controls.Add(this.groupBoxm8p); -->
                <Grid Name="groupBoxm8p">
                    <!-- groupBox1 即 ubxsvin 标签-->
                    <Grid Name="groupBox1">
                        <TextBlock Name="lbl_svin">Postion is valid/invalid</TextBlock>
                        <TextBlock Name="label7">Lat/X / In Progress / Complete</TextBlock>
                        <TextBlock Name="label8">Lng/Y / Duration </TextBlock>
                        <TextBlock Name="label9">Alt/Z / Observations</TextBlock>
                        <TextBlock Name="label10">Current Acc: </TextBlock>
                    </Grid>

                    <Grid Name="panel2"">
                        <CheckBox Name="chk_movingbase">Moving Base</CheckBox>
                        <Button Name="but_restartsvin">Restart</Button>
                        <CheckBox Name="chk_m8p_130p">M8P fw 130+/F9P</CheckBox>
                        <Button Name="but_save_basepos">Save Current Position</Button>
                        <controls:DataGrid Name="dg_basepos"></controls:DataGrid>
                        <TextBlock Name="label2">Time(s)</TextBlock>
                        <TextBox Name="txt_surveyinAcc" Text="2.00"></TextBox>
                        <TextBlock Name="label1">SurveyIn Acc(m)</TextBlock>
                        <TextBox Name="txt_surveyinDur" Text="60"></TextBox>
                    </Grid>
                </Grid>
            </Grid>
            <!-- this.splitContainer1.Panel2.Controls.Add(this.panel1); -->
            <Grid>
                <Grid Name="panel1"></Grid>
            </Grid>
        </Grid>
    </Grid>
</UserControl>
  • ConfigSerialInjectGPS.Designer.cs 组件初始化

此文件在GCSViews\ConfigurationView\ConfigSerialInjectGPS.Designer.cs

namespace MissionPlanner.GCSViews.ConfigurationView
{
    partial class ConfigSerialInjectGPS
    {
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ConfigSerialInjectGPS));
            // 左上
            this.CMB_serialport = new System.Windows.Forms.ComboBox();
            this.CMB_baudrate = new System.Windows.Forms.ComboBox();
            this.chk_rtcmmsg = new System.Windows.Forms.CheckBox();
            this.chk_ubloxautoconfig = new System.Windows.Forms.CheckBox();
            this.BUT_connect = new MissionPlanner.Controls.MyButton();
             this.chk_sendgga = new System.Windows.Forms.CheckBox();
            // 底部  M8P/F9P
            this.groupBoxm8p = new System.Windows.Forms.GroupBox();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.lbl_svin = new System.Windows.Forms.Label();
            this.label10 = new System.Windows.Forms.Label();
            this.label7 = new System.Windows.Forms.Label();
            this.label9 = new System.Windows.Forms.Label();
            this.label8 = new System.Windows.Forms.Label();
            
            this.panel2 = new System.Windows.Forms.Panel();
            this.but_restartsvin = new MissionPlanner.Controls.MyButton();
            this.chk_m8p_130p = new System.Windows.Forms.CheckBox();
            this.but_save_basepos = new MissionPlanner.Controls.MyButton();
            this.dg_basepos = new MissionPlanner.Controls.MyDataGridView();
            
            this.Lat = new System.Windows.Forms.DataGridViewTextBoxColumn();
            this.Long = new System.Windows.Forms.DataGridViewTextBoxColumn();
            this.Alt = new System.Windows.Forms.DataGridViewTextBoxColumn();
            this.BaseName1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
            this.Use = new System.Windows.Forms.DataGridViewButtonColumn();
            this.Delete = new System.Windows.Forms.DataGridViewButtonColumn();
            
            this.label2 = new System.Windows.Forms.Label();
            this.txt_surveyinAcc = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.txt_surveyinDur = new System.Windows.Forms.TextBox();
            this.panel1 = new System.Windows.Forms.Panel();
            
            this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
           
			// 右上 链路状态 link status  RTCM 四大卫星导航与定位系统
            this.lbl_status2 = new System.Windows.Forms.Label();
            this.lbl_status3 = new System.Windows.Forms.Label();
            this.label3 = new System.Windows.Forms.Label();
            this.label4 = new System.Windows.Forms.Label();
            。。。。。。
            
            this.myGMAP1 = new MissionPlanner.Controls.myGMAP();
            
            // 
            // CMB_serialport
            // 
            resources.ApplyResources(this.CMB_serialport, "CMB_serialport");
            this.CMB_serialport.Name = "CMB_serialport";
            this.CMB_serialport.SelectedIndexChanged += new System.EventHandler(this.CMB_serialport_SelectedIndexChanged);
            // 
            // CMB_baudrate
            // 
            this.CMB_baudrate.Items.AddRange(new object[] {
            resources.GetString("CMB_baudrate.Items"),
            resources.GetString("CMB_baudrate.Items1"),
            resources.GetString("CMB_baudrate.Items2"),
            resources.GetString("CMB_baudrate.Items3"),
            resources.GetString("CMB_baudrate.Items4"),
            resources.GetString("CMB_baudrate.Items5"),
            resources.GetString("CMB_baudrate.Items6"),
            resources.GetString("CMB_baudrate.Items7"),
            resources.GetString("CMB_baudrate.Items8"),
            resources.GetString("CMB_baudrate.Items9"),
            resources.GetString("CMB_baudrate.Items10"),
            resources.GetString("CMB_baudrate.Items11"),
            resources.GetString("CMB_baudrate.Items12")});
            resources.ApplyResources(this.CMB_baudrate, "CMB_baudrate");
            this.CMB_baudrate.Name = "CMB_baudrate";
            // 
            // lbl_status1
            // 
            resources.ApplyResources(this.lbl_status1, "lbl_status1");
            this.lbl_status1.Name = "lbl_status1";
            // 
            // chk_ubloxautoconfig
            // 
            resources.ApplyResources(this.chk_ubloxautoconfig, "chk_ubloxautoconfig");
            this.chk_ubloxautoconfig.Name = "chk_ubloxautoconfig";
            this.toolTip1.SetToolTip(this.chk_ubloxautoconfig, resources.GetString("chk_ubloxautoconfig.ToolTip"));
            this.chk_ubloxautoconfig.UseVisualStyleBackColor = true;
            this.chk_ubloxautoconfig.CheckedChanged += new System.EventHandler(this.chk_m8pautoconfig_CheckedChanged);
            // 
            // panel2
            // 
            resources.ApplyResources(this.panel2, "panel2");
            this.panel2.Controls.Add(this.but_restartsvin);
            this.panel2.Controls.Add(this.chk_m8p_130p);
            this.panel2.Controls.Add(this.but_save_basepos);
            this.panel2.Controls.Add(this.dg_basepos);
            this.panel2.Controls.Add(this.label2);
            this.panel2.Controls.Add(this.txt_surveyinAcc);
            this.panel2.Controls.Add(this.label1);
            this.panel2.Controls.Add(this.txt_surveyinDur);
            this.panel2.Name = "panel2";
            // 
            // but_restartsvin
            // 
            resources.ApplyResources(this.but_restartsvin, "but_restartsvin");
            this.but_restartsvin.Name = "but_restartsvin";
            this.but_restartsvin.Click += new System.EventHandler(this.but_restartsvin_Click);
            // 
            // chk_m8p_130p
            // 
            resources.ApplyResources(this.chk_m8p_130p, "chk_m8p_130p");
            this.chk_m8p_130p.Name = "chk_m8p_130p";
            this.chk_m8p_130p.CheckedChanged += new System.EventHandler(this.chk_m8p_130p_CheckedChanged);
			。。。。。。
            this.but_save_basepos.Click += new System.EventHandler(this.but_save_basepos_Click);
            this.txt_surveyinAcc.TextChanged += new System.EventHandler(this.txt_surveyinAcc_TextChanged);
            this.txt_surveyinDur.TextChanged += new System.EventHandler(this.txt_surveyinDur_TextChanged);
            this.BUT_connect.Click += new System.EventHandler(this.BUT_connect_Click);
            。。。。。。
            // 
            // ConfigSerialInjectGPS
            // 
            this.Controls.Add(this.chk_rtcmmsg);
            this.Controls.Add(this.CMB_baudrate);
            this.Controls.Add(this.BUT_connect);
            this.Controls.Add(this.CMB_serialport);
            this.Name = "ConfigSerialInjectGPS";
            resources.ApplyResources(this, "$this");
            。。。。。。
            this.ResumeLayout(false);
            this.PerformLayout();

        }
    }
}
  • ConfigSerialInjectGPS.resx 翻译

此文件在GCSViews\ConfigurationView\ConfigSerialInjectGPS.resx

  <data name="&gt;&gt;$this.Name" xml:space="preserve">
    <value>ConfigSerialInjectGPS</value>
  </data>
  <data name="groupBox3.Text" xml:space="preserve">
    <value>Link Status</value>
  </data>
  <data name="groupBox2.Text" xml:space="preserve">
    <value>RTCM</value>
  </data>
  <data name="chk_ubloxautoconfig.Text" xml:space="preserve">
    <value>UBlox M8P/F9P autoconfig</value>
  </data>
   <data name="groupBoxm8p.Text" xml:space="preserve">
    <value>UBlox M8P/F9P</value>
  </data>
 
  <data name="groupBoxm8p.Text" xml:space="preserve">
    <value>UBlox M8P/F9P</value>
  </data> <data name="CMB_baudrate.Items" xml:space="preserve">
    <value>2400</value>
  </data>

  <data name="CMB_baudrate.Items1" xml:space="preserve">
    <value>4800</value>
  </data>
  <data name="CMB_baudrate.Items2" xml:space="preserve">
    <value>9600</value>
  </data>
  <data name="CMB_baudrate.Items3" xml:space="preserve">
    <value>14400</value>
  </data>
  <data name="CMB_baudrate.Items4" xml:space="preserve">
    <value>19200</value>
  </data>
  <data name="CMB_baudrate.Items5" xml:space="preserve">
    <value>28800</value>
  </data>
  <data name="CMB_baudrate.Items6" xml:space="preserve">
    <value>38400</value>
  </data>
  <data name="CMB_baudrate.Items7" xml:space="preserve">
    <value>57600</value>
  </data>
  <data name="CMB_baudrate.Items8" xml:space="preserve">
    <value>115200</value>
  </data>
  <data name="CMB_baudrate.Items9" xml:space="preserve">
    <value>230400</value>
  </data>
  <data name="CMB_baudrate.Items10" xml:space="preserve">
    <value>460800</value>
  </data>
  <data name="CMB_baudrate.Items11" xml:space="preserve">
    <value>500000</value>
  </data>
  <data name="CMB_baudrate.Items12" xml:space="preserve">
    <value>1000000</value>
  </data>

  <data name="lbl_svin.ToolTip" xml:space="preserve">
    <value>Survey in information
Valid: is the survey in valid
InProgress: are we currently surveying in our position
Duration: the number of seconds we have surveyed in
Obs: number of obsivation used in our survey in
Acc: current accuracy of our survey in point
</value>
  </data>


  • ConfigSerialInjectGPS.cs 主要功能类

此文件在GCSViews\ConfigurationView\ConfigSerialInjectGPS.cs"

点击连接public void BUT_connect_Click(object sender, EventArgs e)若已连接,则直接关闭连接,关闭文件流
开始连接public void DoConnect()根据选项,创建对应的 ICommsSerial
初始化comPort.Write(‘’\r\r\r’‘}, 0, 3);
comPort.Write(’‘S8\r’‘}, 0, 3);
comPort.Write(’‘O\r’'}, 0, 2);
this is for a CAN adapter
创建文件流new BinaryWriter(
new BufferedStream( File.Open()))
文件流将串口接收的数据保存下来
Setup UBLOXubx_m8p.SetupM8P(comPort, chk_m8p_130p)
ubx_m8p.SetupBasePos(comPort, basepos, 0, 0, true);
CMB_baudrate.Text = “460800”;
inject init strings - m8p
创建线程new System.Threading.ThreadStart(mainloop)发送和接收数据
接收Can1062can.MessageReceived如果我们收到can消息,则将rtcm数据输入rtcm解析器(这边测试应该是没执行到)
接收Can1062can.MessageReceived如果我们收到can消息,则将rtcm数据输入rtcm解析器(这边测试应该是没执行到)
转发到飞控sendData(buffer, (ushort)read);上传飞控
    public partial class ConfigSerialInjectGPS : UserControl, IActivate, IDeactivate  {
        internal static ICommsSerial comPort;// serialport
        private static Utilities.rtcm3 rtcm3 = new Utilities.rtcm3();// rtcm detection
        private static Utilities.sbp sbp = new Utilities.sbp();// sbp detection
        private static Utilities.Ubx ubx_m8p = new Utilities.Ubx();// ubx detection
        static nmea nmea = new nmea();
        static DroneCAN.DroneCAN can = new DroneCAN.DroneCAN();
        private static System.Threading.Thread t12;// background thread 
        private static bool threadrun = false;
        private static ConcurrentDictionary<string,int> msgseen = new ConcurrentDictionary<string, int>();// track rtcm msg's seen,保存 如 Ubx0671=2 Ubx0107=43 的键值对,方便显示
        private static int bytes = 0;// track bytes seen
        private static int bps = 0;// 每秒接收的字节数
        private static int bpsusefull = 0;// 每秒发送的字节数
        private static bool rtcm_msg = true;// chk_rtcmmsg.Checked (Inject MSG Type)
        private static ConfigSerialInjectGPS Instance;
        static private BinaryWriter basedata;// 文件写入流,连接一次创建一个,文件路径为:Android\data\com.michaeloborne.MissionPlanner\files\Mission Planner\logs\2023-01-10 14-30-38.gpsbase
        private static string status_line3;// RTCM Base 值,如 "24.9 118.6 17.8 - 14:32:30"

        static ConfigSerialInjectGPS()  {
            try  {
                comPort = new SerialPort();
            }  catch  {
                comPort = new TcpSerial();
            }
        }
        
        public ConfigSerialInjectGPS() {
            InitializeComponent();
            Instance = this;
            status_line3 = null;
            CMB_serialport.Items.AddRange(SerialPort.GetPortNames());
            CMB_serialport.Items.Add("UDP Host");
            CMB_serialport.Items.Add("UDP Client");
            CMB_serialport.Items.Add("TCP Client");
            CMB_serialport.Items.Add("NTRIP");
            if (threadrun)  {  BUT_connect.Text = Strings.Stop; }
            splitContainer1.Panel1Collapsed = true;
            // restore last port and baud - its the simple things that make life better
            。。。。。。
            // restore current static state
            chk_rtcmmsg.Checked = rtcm_msg;// true
            rtcm3.ObsMessage += Rtcm3_ObsMessage;

            MissionPlanner.Utilities.Tracking.AddPage(this.GetType().ToString(), this.Text);
        }

        private void Rtcm3_ObsMessage(object sender, EventArgs e)     {
            if (MainV2.instance.IsDisposed)
                threadrun = false;

            MainV2.instance.BeginInvoke((MethodInvoker)delegate {
               List<rtcm3.ob> obs = sender as List<rtcm3.ob>;
               if (obs.Count == 0) return;

               panel1.SuspendLayout();
                // get system controls
                Func<char, List<VerticalProgressBar2>> ctls = delegate (char sys) {
                   return panel1.Controls.OfType<VerticalProgressBar2>()
                       .Where(ctl => { return ctl.Label.StartsWith(sys + ""); }).ToList();
               };
                // we need more ctls for this system
                while (ctls.Invoke(obs[0].sys).Count() < obs.Count)
                   panel1.Controls.Add(new VerticalProgressBar2()  {
                       Height = panel1.Height - 30,
                       Label = obs[0].sys + ""
                   });

               ctls.Invoke(obs[0].sys).ForEach((vp) => vp.Value = 0);
               int width = panel1.Width / panel1.Controls.OfType<VerticalProgressBar2>().Count();
               var tmp = ctls('G'); var tmp2 = ctls('R');var tmp3 = ctls('B');var tmp4 = ctls('E'); var tmp5 = ctls('Q');
               var start = 0;

               if (obs[0].sys == 'G') start = 0;
               if (obs[0].sys == 'R') start = tmp.Count;
               if (obs[0].sys == 'B') start = tmp.Count + tmp2.Count;
               if (obs[0].sys == 'E') start = tmp.Count + tmp2.Count + tmp3.Count;
               if (obs[0].sys == 'Q') start = tmp.Count + tmp2.Count + tmp3.Count + tmp4.Count;

               // if G 0, if R = G.count (2 system support)
               var a = start;

               var sysctls = ctls.Invoke(obs[0].sys);
               var cnt = 0;
               foreach (var ob in obs) {
                   var vpb = sysctls[cnt];
                   vpb.Value = (int)ob.snr;
                    //vpb.Text = ob.snr.ToString();
                    vpb.Label = ob.sys + ob.prn.ToString();
                   vpb.Location = new Point(width * (a + cnt), 0);
                   vpb.DrawLabel = true;
                   vpb.Width = width;
                   vpb.Height = panel1.Height - 30;
                   vpb.Minimum = 25; vpb.Maximum = 55; vpb.minline = 40; vpb.maxline = 99;
                   cnt++;
               }
               ThemeManager.ApplyThemeTo(panel1);
               panel1.ResumeLayout();
           });
        }
		// 连接按钮
        public void BUT_connect_Click(object sender, EventArgs e) {
            threadrun = false;
            if (comPort.IsOpen) {// 停止
                threadrun = false;
                comPort.Close();
                BUT_connect.Text = Strings.Connect;
                chk_sendgga.Enabled = true;
                try {
                    basedata.Close();
                    basedata = null;
                }  catch  { }
            } else {// 连接
                DoConnect();
            }
        }

        public void DoConnect()  {
            status_line3 = null;
            try {
                if (!comPort.IsOpen) {
                    switch (CMB_serialport.Text) {
                        case "NTRIP":
                            comPort = new CommsNTRIP();
                            CMB_baudrate.SelectedIndex = 0;
                            if (chk_sendgga.Checked) {
                                ((CommsNTRIP) comPort).lat = MainV2.comPort.MAV.cs.PlannedHomeLocation.Lat;
                                ((CommsNTRIP) comPort).lng = MainV2.comPort.MAV.cs.PlannedHomeLocation.Lng;
                                ((CommsNTRIP) comPort).alt = MainV2.comPort.MAV.cs.PlannedHomeLocation.Alt;
                            }
                            chk_sendgga.Enabled = false;
                            chk_ubloxautoconfig.Checked = false;
                            break;
                        case "TCP Client":
                            comPort = new TcpSerial();
                            CMB_baudrate.SelectedIndex = 0;
                            break;
                        case "UDP Host":
                            comPort = new UdpSerial();
                            CMB_baudrate.SelectedIndex = 0;
                            break;
                        case "UDP Client":
                            comPort = new UdpSerialConnect();
                            CMB_baudrate.SelectedIndex = 0;
                            break;
                        default:
                            comPort = new SerialPort();//  如:usb设备 u-blox GNSS Receiver
                            comPort.PortName = CMB_serialport.Text;
                            break;
                    }
                    Settings.Instance["SerialInjectGPS_port"] = CMB_serialport.Text;
                    Settings.Instance["SerialInjectGPS_baud"] = CMB_baudrate.Text;
                }
                // ArgumentException
                comPort.BaudRate = int.Parse(CMB_baudrate.Text);
                comPort.ReadBufferSize = 1024 * 64;
                if (!comPort.IsOpen)
                   comPort.Open();
                if (comPort is SerialPort) {
                    // this is for a CAN adapter
                    comPort.Write(new byte[] {(byte) '\r', (byte) '\r', (byte) '\r'}, 0, 3);
                    Thread.Sleep(50);
                    comPort.Write(new byte[] {(byte) 'S', (byte) '8', (byte) '\r'}, 0, 3);
                    Thread.Sleep(50);
                    comPort.Write(new byte[] {(byte) 'O', (byte) '\r'}, 0, 2);
                }
                // Exception 
                basedata = new BinaryWriter(new BufferedStream(
                File.Open(// 文件路径为:Android\data\com.michaeloborne.MissionPlanner\files\Mission Planner\logs\2023-01-10 14-30-38.gpsbase
                    Settings.Instance.LogDir + Path.DirectorySeparatorChar +
                    DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".gpsbase", FileMode.CreateNew,
                    FileAccess.ReadWrite, FileShare.None)));
            } catch (ArgumentException ex) {
                log.Error(ex);
                // try pipe method
                comPort = new CommsSerialPipe();
                comPort.PortName = CMB_serialport.Text;
                comPort.BaudRate = int.Parse(CMB_baudrate.Text);
				try {
                     comPort.Open();
                 } catch {
                     comPort.Close();
                     throw;
                 }
            }  catch (Exception ex2) {
                CustomMessageBox.Show("Error creating file to save base data into " + ex2.ToString());
            }  catch (Exception ex) {
                CustomMessageBox.Show("Error Connecting\nif using com0com please rename the ports to COM??\n" + ex.ToString());
            } catch  {
                CustomMessageBox.Show(Strings.InvalidPortName);
                return;
            }
            // inject init strings - m8p
            if (chk_ubloxautoconfig.Checked) {
                this.LogInfo("Setup UBLOX");
                try {
                    ubx_m8p.SetupM8P(comPort, chk_m8p_130p.Checked);
                } catch (Exception ex) {
                    log.Error(ex);
                    CustomMessageBox.Show("Error configuring\n" + ex.ToString());
                    return;
                }
                if (basepos != PointLatLngAlt.Zero) {
                    ubx_m8p.SetupBasePos(comPort, basepos, 0, 0, true);
                    ubx_m8p.SetupBasePos(comPort, basepos, 0, 0, false);
                }
                CMB_baudrate.Text = "460800";
                this.LogInfo("Setup UBLOX done");
            }

            t12 = new System.Threading.Thread(new System.Threading.ThreadStart(mainloop)) {IsBackground = true, Name = "injectgps"};
            t12.Start();
            BUT_connect.Text = Strings.Stop;
            msgseen.Clear();
            bytes = 0;
            invalidateRTCMStatus();
            panel1.Controls.Clear();
        }

        private static void mainloop() {
            threadrun = true;
            bool isrtcm = false;
            bool issbp = false;
            bool iscan = false;
            // feed the rtcm data into the rtcm parser if we get a can message
            can.MessageReceived += (frame, msg, id) => {
                string msgname = "Can" + frame.MsgTypeID;// 没执行到
                msgseen[msgname] = (int) msgseen[msgname] + 1;
				// public const int UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_DT_ID = 1062;
                if (frame.MsgTypeID == (ushort)DroneCAN.DroneCAN.uavcan_equipment_gnss_RTCMStream.UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_DT_ID)  {
                    var rtcm = (DroneCAN.DroneCAN.uavcan_equipment_gnss_RTCMStream) msg;
                    。。。。。。
                }
            };
            while (threadrun) {
                try {
                	{
                	 // reconnect logic - 10 seconds with no data, or comport is closed  10s超时重连
                	}
                    byte[] buffer = new byte[110]; // limit to 110 byte packets
                    if (rtcm_msg)// limit to 180 byte packet if using new packet
                        buffer = new byte[180];

                    while (comPort.BytesToRead > 0) {
                        int read = comPort.Read(buffer, 0, Math.Min(buffer.Length, comPort.BytesToRead));
                        bytes += read;
                        bps += read;// 每秒接收的字节数
                        if (basedata != null)// 保存到文件中
                          basedata.Write(buffer, 0, read);
                        // if this is raw data transport of unknown packet types
                        if (!(isrtcm || issbp || iscan))// 其实issbp isrtcm 也要上传给飞控
                            sendData(buffer, (ushort)read);// 上传给飞控
                        // check for valid rtcm/sbp/ubx packets
                        for (int a = 0; a < read; a++)  {
                            int seenmsg = -1;
                            // rtcm and not can
                            if (!iscan && (seenmsg = rtcm3.Read(buffer[a])) > 0)  {
                                isrtcm = true;
                                sendData(rtcm3.packet, (ushort)rtcm3.length);// 上传给飞控
                                bpsusefull += rtcm3.length;
                                string msgname = "Rtcm" + seenmsg;
                                if (!msgseen.ContainsKey(msgname))
                                    msgseen[msgname] = 0;
                                msgseen[msgname] = (int)msgseen[msgname] + 1;
                                ExtractBasePos(seenmsg);
                                seenRTCM(seenmsg);
                            }
                            // sbp : string msgname = "Sbp" + seenmsg.ToString("X4");
                            if ((seenmsg = sbp.read(buffer[a])) > 0)  { sendData(sbp.packet, (ushort)sbp.length); 。。。。。。}
                            // ubx
                            if ((seenmsg = ubx_m8p.Read(buffer[a])) > 0) {
                                ProcessUBXMessage();
                                string msgname = "Ubx" + seenmsg.ToString("X4");
                                if (!msgseen.ContainsKey(msgname))
                                    msgseen[msgname] = 0;
                                msgseen[msgname] = (int)msgseen[msgname] + 1;
                            }
                            // nmea : string msgname = "NMEA";
                            if ((seenmsg = nmea.Read(buffer[a])) > 0)  { 。。。。。。 }
                            // can_rtcm : string msgname = "CAN";
                            if ((seenmsg = can.ReadSLCAN(buffer[a])) > 0) { 。。。。。。 }
                            rtcm3.resetParser();
                            sbp.resetParser();
                            ubx_m8p.resetParser();
                            nmea.resetParser();
                        }
                    }
                    System.Threading.Thread.Sleep(10);
                } catch (Exception ex) {
                    log.Error(ex);
                }
            }
        }

        private static void seenRTCM(int seenmsg)  {
            if (Instance.IsDisposed || !Instance.IsHandleCreated)
                return;

            Instance.BeginInvoke((Action)delegate () {
               switch (seenmsg) {
                   case 1001:
                   case 1002:
                   case 1003:
                   case 1004:
                   case 1071:
                   case 1072:
                   case 1073:
                   case 1074:
                   case 1075:
                   case 1076:
                   case 1077:
                       Instance.labelgps.BackColor = Color.Green;
                       ExpireType.Set(Instance.labelgps, 5);
                       break;
                   case 1005:
                   case 1006:
                   case 4072: // ublox moving base
                        Instance.labelbase.BackColor = Color.Green;
                       ExpireType.Set(Instance.labelbase, 20);
                       break;
                   case 1009:
                   case 1010:
                   case 1011:
                   case 1012:
                   case 1081:
                   case 1082:
                   case 1083:
                   case 1084:
                   case 1085:
                   case 1086:
                   case 1087:
                       Instance.labelglonass.BackColor = Color.Green;
                       ExpireType.Set(Instance.labelglonass, 5);
                       break;
                   case 1094:
                   case 1095:
                   case 1096:
                   case 1097:
                       Instance.labelGall.BackColor = Color.Green;
                       ExpireType.Set(Instance.labelGall, 5);
                       break;
                   case 1121:
                   case 1122:
                   case 1123:
                   case 1124:
                   case 1125:
                   case 1126:
                   case 1127:
                       Instance.label14BDS.BackColor = Color.Green;
                       ExpireType.Set(Instance.label14BDS, 5);
                       break;
                   default:
                       break;
               }
           } );
        }

        private static void ProcessUBXMessage() {
            try  { // survey in
                if (ubx_m8p.@class == 0x1 && ubx_m8p.subclass == 0x3b)  {
                    var svin = ubx_m8p.packet.ByteArrayToStructure<Utilities.Ubx.ubx_nav_svin>(6);
                    ubxsvin = svin;
                    updateSVINLabel((svin.valid == 1), (svin.active == 1), svin.dur, svin.obs, svin.meanAcc / 10000.0);
                    var pos = svin.getECEF();
                    double[] baseposllh = new double[3];
                    Utilities.rtcm3.ecef2pos(pos, ref baseposllh);
                } else if (ubx_m8p.@class == 0x1 && ubx_m8p.subclass == 0x7)  {
                    var pvt = ubx_m8p.packet.ByteArrayToStructure<Utilities.Ubx.ubx_nav_pvt>(6);
                    if (pvt.fix_type >= 0x3 && (pvt.flags & 1) > 0) {
                        MainV2.comPort.MAV.cs.MovingBase = new Utilities.PointLatLngAlt(pvt.lat / 1e7, pvt.lon / 1e7, pvt.height / 1000.0);
                    }
                    ubxpvt = pvt;
                }  else if (ubx_m8p.@class == 0x5 && ubx_m8p.subclass == 0x1) {
                    log.InfoFormat("ubx ack {0} {1}", ubx_m8p.packet[6], ubx_m8p.packet[7]);
                } else if (ubx_m8p.@class == 0x5 && ubx_m8p.subclass == 0x0) {
                    log.InfoFormat("ubx Nack {0} {1}", ubx_m8p.packet[6], ubx_m8p.packet[7]);
                } else if (ubx_m8p.@class == 0xa && ubx_m8p.subclass == 0x4)  {
                    var ver = ubx_m8p.packet.ByteArrayToStructure<Utilities.Ubx.ubx_mon_ver>(6);//, ubx_m8p.length - 8);
                    Console.WriteLine("ubx mon-ver {0} {1}", ASCIIEncoding.ASCII.GetString(ver.hwVersion),
                        ASCIIEncoding.ASCII.GetString(ver.swVersion));
                    for (int a = 40 + 6; a < ubx_m8p.length - 2; a += 30)  {
                        var extension = ASCIIEncoding.ASCII.GetString(ubx_m8p.buffer, a, 30);
                        Console.WriteLine("ubx mon-ver {0}", extension);
                    }
                }  else if (ubx_m8p.@class == 0xa && ubx_m8p.subclass == 0x9)  {
                    var hw = ubx_m8p.packet.ByteArrayToStructure<Utilities.Ubx.ubx_mon_hw>(6);
                    Console.WriteLine("ubx mon-hw noise {0} agc% {1} jam% {2} jamstate {3}", hw.noisePerMS, (hw.agcCnt / 8191.0) * 100.0, (hw.jamInd / 256.0) * 100, hw.flags & 0xc);
                } else if (ubx_m8p.@class == 0x1 && ubx_m8p.subclass == 0x12) {
                    var velned = ubx_m8p.packet.ByteArrayToStructure<Utilities.Ubx.ubx_nav_velned>(6);

                    var time = (velned.iTOW - ubxvelned.iTOW) / 1000.0;

                    ubxvelned = velned;
                }   else if (ubx_m8p.@class == 0xf5)  {
                    // rtcm
                }  else if (ubx_m8p.@class == 0x02) {
                    // rxm-raw
                } else if (ubx_m8p.@class == 0x06 && ubx_m8p.subclass == 0x71)  {
                    // TMODE3
                    var tmode = ubx_m8p.packet.ByteArrayToStructure<Utilities.Ubx.ubx_cfg_tmode3>(6);
                    ubxmode = tmode;
                    log.InfoFormat("ubx TMODE3 {0} {1}", (Ubx.ubx_cfg_tmode3.modeflags)tmode.flags, "");
                }  else {
                    ubx_m8p.turnon_off(comPort, ubx_m8p.@class, ubx_m8p.subclass, 0);
                }

                if (pollTMODE < DateTime.Now)  {
                    ubx_m8p.poll_msg(comPort, 0x06, 0x71);
                    pollTMODE = DateTime.Now.AddSeconds(30);

                    ubx_m8p.poll_msg(comPort, 0x0a, 0x4);
                }
            } catch (Exception ex) {
                log.Error(ex);
            }
        }

        static DateTime pollTMODE = DateTime.MinValue;
        static Ubx.ubx_cfg_tmode3 ubxmode;
        static Ubx.ubx_nav_svin ubxsvin;
        internal static Ubx.ubx_nav_velned ubxvelned;
        internal static Ubx.ubx_nav_pvt ubxpvt;

        private static void updateSVINLabel(bool valid, bool active, uint dur, uint obs, double acc) {
        	// 更新 Survey In 数据
            Instance.lbl_svin.Text = valid ? "Postion is valid" : "Position is invalid";
            Instance.label7.Text = "Lat/X: " + posllh[0] * MathHelper.rad2deg;
            Instance.label8.Text = "Lng/Y: " + posllh[1] * MathHelper.rad2deg;
            Instance.label9.Text = "Alt/Z: " + posllh[2];
            Instance.label10.Text = "Current Acc: " + acc;
            。。。。。。
        }

        private static void ExtractBasePos(int seen) {
            try {
                if (seen == 1005) {
                    var basepos = new Utilities.rtcm3.type1005();
                    basepos.Read(rtcm3.packet);
                    var pos = basepos.ecefposition;
                    double[] baseposllh = new double[3];
                    Utilities.rtcm3.ecef2pos(pos, ref baseposllh);
                    MainV2.comPort.MAV.cs.MovingBase = new Utilities.PointLatLngAlt(baseposllh[0] * Utilities.rtcm3.R2D,
                        baseposllh[1] * Utilities.rtcm3.R2D, baseposllh[2]);

                    status_line3 =
                        (String.Format("{0} {1} {2} - {3}", baseposllh[0] * Utilities.rtcm3.R2D,
                            baseposllh[1] * Utilities.rtcm3.R2D, baseposllh[2], DateTime.Now.ToString("HH:mm:ss")));

                    if (!Instance.IsDisposed && Instance.but_save_basepos.Enabled == false)
                        Instance.but_save_basepos.Enabled = true;
                } else if (seen == 1006)  {
                    var basepos = new Utilities.rtcm3.type1006();
                    basepos.Read(rtcm3.packet);

                    var pos = basepos.ecefposition;

                    double[] baseposllh = new double[3];

                    Utilities.rtcm3.ecef2pos(pos, ref baseposllh);

                    MainV2.comPort.MAV.cs.MovingBase = new Utilities.PointLatLngAlt(baseposllh[0] * Utilities.rtcm3.R2D, baseposllh[1] * Utilities.rtcm3.R2D,
                        baseposllh[2]);

                    **status_line3** =
                       (String.Format("{0} {1} {2} - {3}", baseposllh[0] * Utilities.rtcm3.R2D,
                           baseposllh[1] * Utilities.rtcm3.R2D, baseposllh[2], DateTime.Now.ToString("HH:mm:ss")));

                    if (!Instance.IsDisposed && Instance.but_save_basepos.Enabled == false)
                        Instance.but_save_basepos.Enabled = true;
                }
            } catch (Exception ex)  {
                log.Error(ex);
            }
        }

        private static void sendData(byte[] data, ushort length) {
            foreach (var port in MainV2.Comports)  {
                bool CanJustSendOnce = GetCanJustSendOnce(port.MAVlist);
                foreach (var MAV in port.MAVlist) {
                    port.InjectGpsData(MAV.sysid, MAV.compid, data, length, rtcm_msg);
                    if (CanJustSendOnce) { break; }
                }
            }
        }

        private void CMB_serialport_SelectedIndexChanged(object sender, EventArgs e) {
            if (!CMB_serialport.Text.ToLower().Contains("com"))
                CMB_baudrate.Enabled = false;
            else
                CMB_baudrate.Enabled = true;
        }
		// 定时更新链路状态和地图显示
        private void timer1_Tick(object sender, EventArgs e) {
		   // sb = "Ubx0671=2 Ubx0107=43 Rtcm1230=9 Rtcm1084=42 Ubx0215=37 Rtcm1124=43 Ubx0500=2 Ubx0213=339 CAN=15 Ubx013B=44 Ubx0A04=3 Ubx0501=42 Ubx0112=44 Rtcm1005=7 Rtcm1094=42 Rtcm1074=42 Ubx0A09=23 NMEA=14"
		   // status_line3 = "24.9 118.6 17.8 - 14:32:30"
           updateLabel(String.Format("{0,10} bps", bps),// lbl_status1 输入速率
                String.Format("{0,10} bps sent", bpsusefull),// lbl_status2 输出速率
                status_line3,// lbl_status3 RTCM Base
                sb.ToString());// labelmsgseen Messages Seen
           。。。。。。
           myGMAP1.Overlays[0].Markers.Add(new GMarkerGoogle(MainV2.comPort.MAV.cs.MovingBase, GMarkerGoogleType.yellow_dot));
           myGMAP1.ZoomAndCenterMarkers("base");
           。。。。。。
        }

        private void chk_rtcmmsg_CheckedChanged(object sender, EventArgs e) {
            rtcm_msg = chk_rtcmmsg.Checked;
        }
		// Restart
        private void but_restartsvin_Click(object sender, EventArgs e) {
            basepos = PointLatLngAlt.Zero;
            invalidateRTCMStatus();
            updateSVINLabel(false,false,0,0,0);
            msgseen.Clear();

            if (comPort.IsOpen) {
                try {
                    ubx_m8p.SetupBasePos(comPort, basepos, 0, 0, true);
                    ubx_m8p.SetupM8P(comPort, chk_m8p_130p.Checked);
                    ubx_m8p.SetupBasePos(comPort, basepos, int.Parse(txt_surveyinDur.Text, CultureInfo.InvariantCulture),
                    double.Parse(txt_surveyinAcc.Text, CultureInfo.InvariantCulture), false);
                } catch (Exception ex) {
                    log.Error(ex);
                    CustomMessageBox.Show("Error configuring\n" + ex.ToString());
                    return;
                }
            }
        }
    }
  • DroneCAN.DroneCAN.uavcan_equipment_gnss_RTCMStream

此文件在ExtLibs\DroneCAN\out\include\uavcan.equipment.gnss.RTCMStream.cs

namespace DroneCAN
{
    public partial class DroneCAN 
    {
        public partial class uavcan_equipment_gnss_RTCMStream: IDroneCANSerialize 
        {
            public const int UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_MAX_PACK_SIZE = 130;
            public const ulong UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_DT_SIG = 0x1F56030ECB171501;
            public const int UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_DT_ID = 1062;

            public const double UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_PROTOCOL_ID_UNKNOWN = 0; // saturated uint8
            public const double UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_PROTOCOL_ID_RTCM2 = 2; // saturated uint8
            public const double UAVCAN_EQUIPMENT_GNSS_RTCMSTREAM_PROTOCOL_ID_RTCM3 = 3; // saturated uint8
	        。。。。。。
        }
    }
}

█ 关于Utilities

  • ubx_m8p.cs (public class Ubx : ICorrections,其实就是对串口发送数据的二次封装)

此文件在ExtLibs\Utilities\ubx_m8p.cs

  public class Ubx : ICorrections {
        int step = 0;
        public byte[] buffer = new byte[1024 * 8];
        int payloadlen = 0;
        int msglencount = 0;

        public byte @class {  get { return buffer[2]; } }

        public byte subclass {  get { return buffer[3]; } }

        public int length {
            get  { return 2 + 2 + 2 + 2 + payloadlen; // header2, class,subclass,length2,data,crc2 }
        }

        public byte[] packet {
            get { return buffer; }
        }

        public bool resetParser() {
            step = 0;
            return true;
        }
        
        public int Read(byte data) { 。。。。。。 }
        public static byte[] generate(byte cl, byte subclass, byte[] payload) { 。。。。。。 }
		// u-blox
        public void SetupM8P(ICommsSerial port, bool m8p_130plus = false, bool movingbase = false) {
            port.BaseStream.Flush();
            var bauds = new[] {port.BaudRate, 9600, 38400, 57600, 115200, 230400, 460800};
            foreach (var baud in bauds)  {// change the baudrate
                port.BaudRate = baud;
                System.Threading.Thread.Sleep(50);
                // U = bit  01010101  - often used for autobaud
                port.Write("UU");
                // port config - 460800 - uart1
                var packet = generate(0x6, 0x00, new byte[] {
                    0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x08,
                    0x07, 0x00, 0x23, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00
                });
                port.Write(packet, 0, packet.Length);
                port.BaseStream.Flush();
                System.Threading.Thread.Sleep(100);
            }
            { // port config - usb
                var packet = generate(0x6, 0x00, new byte[]  {
                    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x23, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00
                });
                port.Write(packet, 0, packet.Length);
                System.Threading.Thread.Sleep(300);
                port.BaseStream.Flush();
                port.BaudRate = 460800;
            }
            { // set rate to 1hz
                var packet = generate(0x6, 0x8, new byte[] {0xE8, 0x03, 0x01, 0x00, 0x01, 0x00});
                port.Write(packet, 0, packet.Length);
                System.Threading.Thread.Sleep(200);
                { // set navmode to stationary
                    packet = generate(0x6, 0x24,
                        new byte[] { 0xFF, 0xFF, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x0F, 0x00, 0xFA, 0x00,
                            0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x23, 0x10, 0x27, 0x00, 0x00, 0x00,  0x00,
                            0x00, 0x00, 0x00, 0x00 });
                    port.Write(packet, 0, packet.Length);
                    System.Threading.Thread.Sleep(200);
                }
            }
            // turn off all nmea
            for (int a = 0; a <= 0xf; a++) {
                if (a == 0xb || a == 0xc || a == 0xe)
                    continue;
                turnon_off(port, 0xf0, (byte) a, 0);
            }
            poll_msg(port, 0xa, 0x4);// mon-ver
            turnon_off(port, 0x01, 0x3b, 1);// surveyin msg - for feedback
            turnon_off(port, 0x01, 0x07, 1);// pvt msg - for feedback
            turnon_off(port, 0xf5, 0x05, 5);// 1005 - 5s

            byte rate1 = 1;
            byte rate2 = 0;
            if (m8p_130plus) {
                rate1 = 0;
                rate2 = 1;
            }
            turnon_off(port, 0xf5, 0x4a, rate2);// 1074 - 1s
            turnon_off(port, 0xf5, 0x4d, rate1);// 1077 - 1s
            turnon_off(port, 0xf5, 0x54, rate2);// 1084 - 1s
            turnon_off(port, 0xf5, 0x57, rate1);// 1087 - 1s
            turnon_off(port, 0xf5, 0x5e, rate2);// 1094 - 1s
            turnon_off(port, 0xf5, 0x61, rate1); // 1097 - 1s
            turnon_off(port, 0xf5, 0x7c, rate2);// 1124 - 1s
            turnon_off(port, 0xf5, 0x7f, rate1);// 1127 - 1s
            turnon_off(port, 0xf5, 0xFE, 0);// 4072
            turnon_off(port, 0xf5, 0xE6, 5);// 1230 - 5s
            turnon_off(port, 0x01, 0x12, 1);// NAV-VELNED - 1s
            turnon_off(port, 0x02, 0x15, 1);// rxm-raw/rawx - 1s
            turnon_off(port, 0x02, 0x10, 1);
            turnon_off(port, 0x02, 0x13, 2);// rxm-sfrb/sfrb - 2s
            turnon_off(port, 0x02, 0x11, 2);
            turnon_off(port, 0x0a, 0x09, 2);// mon-hw - 2s
            System.Threading.Thread.Sleep(100);
        }
        
        public void turnon_off(ICommsSerial port, byte clas, byte subclass, byte every_xsamples) {
            byte[] datastruct1 = {clas, subclass, 0, every_xsamples, 0, every_xsamples, 0, 0};
            var packet = generate(0x6, 0x1, datastruct1);
            port.Write(packet, 0, packet.Length);
            System.Threading.Thread.Sleep(10);
        }
    }
  • rtcm3.cs

此文件在ExtLibs\Utilities\rtcm3.cs

    public class rtcm3 : ICorrections
    {
        private const byte RTCM3PREAMB = 0xD3;
        private const double PRUNIT_GPS = 299792.458; /* rtcm ver.3 unit of gps pseudorange (m) */
        private const double PRUNIT_GLO = 599584.916; /* rtcm 3 unit of glo pseudorange (m) */
        private const double CLIGHT = 299792458.0; /* speed of light (m/s) */
        private const double SC2RAD = 3.1415926535898; /* semi-circle to radian (IS-GPS) */
        private const double FREQ1 = 1.57542E9; /* L1/E1  frequency (Hz) */
        private const double FREQ2 = 1.22760E9; /* L2     frequency (Hz) */
        private const double RANGE_MS = CLIGHT*0.001; /* range in 1 ms */
        private const double P2_5 = 0.03125; /* 2^-5 */
        private const double P2_6 = 0.015625; /* 2^-6 */
        private const double P2_10 = 0.0009765625; /* 2^-10 */
        。。。。。。
        private const double P2_55 = 2.775557561562891E-17; /* 2^-55 */
        private const double RE_WGS84 = 6378137.0; /* earth semimajor axis (WGS84) (m) */
        private const double FE_WGS84 = (1.0/298.257223563); /* earth flattening (WGS84) */
        private const double PI = Math.PI; /* pi */
        public const double D2R = (PI/180.0); /* deg to rad */
        public const double R2D = (180.0/PI); /* rad to deg */
        。。。。。。
        public bool resetParser() {
            step = 0;
            return true;
        }
        。。。。。。
        public class ob {
            public double cp;
            public double cp2;
            public double pr;
            public double pr2;
            public byte prn;
            public rawrtcm raw = new rawrtcm();
            public byte snr;
            public double tow;
            public int week;
            public char sys;

            public class rawrtcm {
                public byte amb;
                public byte cnr1;
                public byte cnr2;
                public byte code1;
                public byte code2;
                public byte lock1;
                public byte lock2;
                public int ppr1;
                public int ppr2;
                public uint pr1;
                public int pr21;
                public byte prn;
                public byte fcn;
            }
        }
   }
  • sbp.cs

此文件在ExtLibs\Utilities\sbp.cs

    public class sbp : ICorrections {
        int state = 0;
        piksimsg msg = new piksimsg();
        int lengthcount = 0;
        Crc16Ccitt crc;
        ushort crcpacket = 0;

        public class piksimsg {
            public byte preamble; // 0x55
            public UInt16 msg_type;
            public UInt16 sender;
            public byte length; // payload length
            [MarshalAs(UnmanagedType.ByValArray)]
            public byte[] payload;
            public UInt16 crc; // - preamble

            public byte[] buffer = new byte[4096];
        }

        public bool resetParser()  {
            state = 0;
            return true;
        }

        public int read(byte data)  { 。。。。。。 }

        public enum InitialCrcValue { Zeros, NonZero1 = 0xffff, NonZero2 = 0x1D0F }

        public class Crc16Ccitt {
            const ushort poly = 4129;
            static ushort[] table;
            ushort initialValue = 0;
			。。。。。。
        }

        public s32 length  {
            get { return msg.length + 8; }
        }

        public u8[] packet  {
            get { return msg.buffer; }
        }
    }

█ 关于串口

  • 串口
外部使用CommsSerialPort.cs中的 SerialPort: ICommsSerial
实际调用CommsSerialPort.cs中的 WinSerialPort: System.IO.Ports.SerialPort, ICommsSerial
父类系统类 System.IO.Ports.SerialPort.cs中的 SerialPort: System.ComponentModel.Component
接口ICommsSerial.cs中的 ICommsSerial: IDisposable
  • 封装类 SerialPort
    此文件在ExtLibs\Comms\CommsSerialPort.cs
    public class SerialPort : ICommsSerial {// 实际为 WinSerialPort
        public static Func<SerialPort, string, int, ICommsSerial> DefaultType { get; set; } = (self, portname, baudrate) => { return new WinSerialPort(); };

        public ICommsSerial _baseport;
        public Stream BaseStream => _baseport.BaseStream;
        private static readonly object locker = new object();
        private static readonly Dictionary<string, string> comportnamecache = new Dictionary<string, string>();
        private static string portnamenice = "";
 public static Func<List<string>> GetCustomPorts;
 
        public SerialPort() {
            _baseport = DefaultType.Invoke(this, null, 0);
        }

        public SerialPort(string portname, int baudrate) {
            _baseport = DefaultType.Invoke(this, portname, baudrate);
            PortName = portname;
            BaudRate = baudrate;
        }

        public SerialPort(string portname) {
            _baseport = DefaultType.Invoke(this, portname, 0);
            PortName = portname;
        }
        
        public void Close() {  _baseport.Close(); }
        public void Open() {  _baseport.Open(); }
        public int Read(byte[] buffer, int offset, int count) {  return _baseport.Read(buffer, offset, count);}
        。。。。。。 // 以下方法都直接用 _baseport 实现


        public new static string[] GetPortNames() {
            // prevent hammering
            lock (locker) {
                var allPorts = new List<string>();
                if (Directory.Exists("/dev/")) {
                    // cleanup now
                    GC.Collect();
                    // mono is failing in here on linux "too many open files"
                    try {
                        if (Directory.Exists("/dev/serial/by-id/"))
                            allPorts.AddRange(Directory.GetFiles("/dev/serial/by-id/", "*"));
                        allPorts.AddRange(Directory.GetFiles("/dev/", "ttyACM*"));
                        allPorts.AddRange(Directory.GetFiles("/dev/", "ttyUSB*"));
                        allPorts.AddRange(Directory.GetFiles("/dev/", "rfcomm*"));
                        allPorts.AddRange(Directory.GetFiles("/dev/", "*usb*"));
                        allPorts.AddRange(Directory.GetFiles("/dev/", "tty.*"));
                        allPorts.AddRange(Directory.GetFiles("/dev/", "cu.*"));
                    } catch {}
                }
                string[] ports = null;
                try {
                    ports = System.IO.Ports.SerialPort.GetPortNames();
                    // any exceptions will still result in a list
                    ports = ports.Select(p => p?.TrimEnd()).ToArray();
                    ports = ports.Select(FixBlueToothPortNameBug).ToArray();
                    if (ports != null)
                      allPorts.AddRange(ports);
                    if (GetCustomPorts != null)
                      allPorts.AddRange(GetCustomPorts.Invoke());
                } catch {}
                return allPorts.Distinct().ToArray();
            }
        }

        public static string GetNiceName(string port)  {
            // make sure we are exclusive
            lock (locker) {
                portnamenice = "";

                if (port == "AUTO" || port == "UDP" || port == "UDPCl" || port == "TCP" || port == "WS")
                    return "";

                if (comportnamecache.ContainsKey(port))  {
                    log.Info("done GetNiceName cache " + port + " " + comportnamecache[port]);
                    return comportnamecache[port];
                }

                try {
                    log.Info("start GetNiceName " + port);
                    CallWithTimeout(GetName, 1000, port);
                }  catch {  }

                log.Info("done GetNiceName " + port + " = " + portnamenice);

                comportnamecache[port] = portnamenice;

                return (string) portnamenice.Clone();
            }
        }
    }
  • 实现类 WinSerialPort
    此文件在ExtLibs\Comms\CommsSerialPort.cs
    public class WinSerialPort : System.IO.Ports.SerialPort, ICommsSerial {
        public WinSerialPort() { }
        
        public new bool DtrEnable {
            get => base.DtrEnable;
            set {
                log.Info(base.PortName + " DtrEnable " + value);
                if (base.DtrEnable == value) return;
                base.DtrEnable = value;
            }
        }

        public new bool RtsEnable {
            get => base.RtsEnable;
            set {
                log.Info(PortName + " RtsEnable " + value);
                if (base.RtsEnable == value) return;
                base.RtsEnable = value;
            }
        }

        public new void Open() {
            // 500ms write timeout - win32 api default
            base.WriteTimeout = 500;
            if (base.IsOpen)
                return;

            if (PortName.StartsWith("/"))
                if (!File.Exists(PortName))
                    throw new Exception("No such device");

            try  {
                base.Open();
            } catch {
                try { Close(); } catch { }
                throw;
            }
        }

        public new void Close() { base.Close();  }

        public void toggleDTR() {
            var open = base.IsOpen;
            Console.WriteLine("toggleDTR " + IsOpen);
            try {
                if (!open)
                    Open();
            } catch { }
            base.DtrEnable = false;
            base.RtsEnable = false;
            Thread.Sleep(50);
            base.DtrEnable = true;
            base.RtsEnable = true;
            Thread.Sleep(50);
            try {
                if (!open)
                    Close();
            } catch { }
            Console.WriteLine("toggleDTR done " + IsOpen);
        }
    }
  • 父类 System.IO.Ports.SerialPort(系统类)
    此文件在SerialPort 类 (System.IO.Ports) | Microsoft Learn
public class SerialPort : System.ComponentModel.Component
方法说明
BaseStream获取 Stream 对象的基础 SerialPort 对象。
BaudRate获取或设置串行波特率。
BreakState获取或设置中断信号状态。
BytesToRead获取接收缓冲区中数据的字节数。
BytesToWrite获取发送缓冲区中数据的字节数。
CanRaiseEvents获取一个指示组件是否可以引发事件的值。(继承自 Component)
CDHolding获取端口的载波检测行的状态。
Container获取包含 IContainer 的 Component。(继承自 Component)
CtsHolding获取“可以发送”行的状态。
DataBits获取或设置每个字节的标准数据位长度。
DesignMode获取一个值,用以指示 Component 当前是否处于设计模式。(继承自 Component)
DiscardNull获取或设置一个值,该值指示 null 字节在端口和接收缓冲区之间传输时是否被忽略。
DsrHolding获取数据设置就绪 (DSR) 信号的状态。
DtrEnable获取或设置一个值,该值在串行通信过程中启用数据终端就绪 (DTR) 信号。
Encoding获取或设置传输前后文本转换的字节编码。
Events获取附加到此 Component 的事件处理程序的列表。(继承自 Component)
Handshake使用 Handshake 中的值获取或设置串行端口数据传输的握手协议。
IsOpen获取一个值,该值指示 SerialPort 对象的打开或关闭状态。
NewLine获取或设置用于解释 ReadLine() 和 WriteLine(String) 方法调用结束的值。
Parity获取或设置奇偶校验检查协议。
ParityReplace获取或设置一个字节,该字节在发生奇偶校验错误时替换数据流中的无效字节。
PortName获取或设置通信端口,包括但不限于所有可用的 COM 端口。
ReadBufferSize获取或设置 SerialPort 输入缓冲区的大小。
ReadTimeout获取或设置读取操作未完成时发生超时之前的毫秒数。
ReceivedBytesThreshold获取或设置 DataReceived 事件发生前内部输入缓冲区中的字节数。
RtsEnable获取或设置一个值,该值指示在串行通信中是否启用请求发送 (RTS) 信号。
Site获取或设置 Component 的 ISite。(继承自 Component)
StopBits获取或设置每个字节的标准停止位数。
WriteBufferSize获取或设置串行端口输出缓冲区的大小。
WriteTimeout获取或设置写入操作未完成时发生超时之前的毫秒数。
  • 接口 ICommsSerial
    此文件在ExtLibs\Interfaces\ICommsSerial.cs
public interface ICommsSerial : IDisposable
    {
        // Properties
        Stream BaseStream { get; }

        int BaudRate { get; set; }

        //bool BreakState { get; set; }
        int BytesToRead { get; }

        int BytesToWrite { get; }

        //bool CDHolding { get; }
        //bool CtsHolding { get; }
        int DataBits { get; set; }

        //bool DiscardNull { get; set; }
        //bool DsrHolding { get; }
        bool DtrEnable { get; set; }

        //Encoding Encoding { get; set; }
        //Handshake Handshake { get; set; }
        bool IsOpen { get; }

        //string NewLine { get; set; }
        //Parity Parity { get; set; }
        //byte ParityReplace { get; set; }
        string PortName { get; set; }

        int ReadBufferSize { get; set; }
        int ReadTimeout { get; set; }

        bool RtsEnable { get; set; }

        //StopBits StopBits { get; set; }
        int WriteBufferSize { get; set; }

        int WriteTimeout { get; set; }

        // from serialport class
        // Methods
        void Close();

        void DiscardInBuffer();

        //void DiscardOutBuffer();
        void Open();

        int Read(byte[] buffer, int offset, int count);

        //int Read(char[] buffer, int offset, int count);
        int ReadByte();

        int ReadChar();

        string ReadExisting();

        string ReadLine();

        //string ReadTo(string value);
        void Write(string text);

        void Write(byte[] buffer, int offset, int count);

        //void Write(char[] buffer, int offset, int count);
        void WriteLine(string text);

        void toggleDTR();
    }
  • 其他 MonoSerialPort(应该没用,参考,Device.RuntimePlatform == Device.macOS时使用,原以为是 SerialPort 初类)
    此文件在ExtLibs\Comms\System.IO.Ports\SerialPort.cs
	public class MonoSerialPort : Component {
		// 默认配置 9600 一个停止位 无奇偶校验
		public const int InfiniteTimeout = -1;// 读写超时值
		const int DefaultReadBufferSize = 4096;// 缓存
		const int DefaultWriteBufferSize = 2048;// 缓存
		const int DefaultBaudRate = 9600;// 波特率
		const int DefaultDataBits = 8;// 数据位数
		const Parity DefaultParity = Parity.None;// 奇偶校验
		const StopBits DefaultStopBits = StopBits.One;// 停止位个数

		bool is_open;// 是否打开端口
		int baud_rate;
		Parity parity;
		StopBits stop_bits;
		Handshake handshake;
		int data_bits;
		bool break_state = false;// 当前BREAK状态
		bool dtr_enable = false;// 启用硬件(DSR/DTR)流量控制
		bool rts_enable = false;// 启用硬件(RTS/CTS)流量控制
		ISerialStream stream;
		Encoding encoding = Encoding.ASCII;// Unicode字符串必须编码(例如: “hello”.encode(“utf - 8”)
		string new_line = Environment.NewLine;
		string port_name;// 设备名称
		int read_timeout = InfiniteTimeout;
		int write_timeout = InfiniteTimeout;
		int readBufferSize = DefaultReadBufferSize;// 输入缓冲区
		int writeBufferSize = DefaultWriteBufferSize;// 输出缓冲区
		object error_received = new object ();
		object data_received = new object ();
		object pin_changed = new object ();

		public MonoSerialPort () : 
			this (GetDefaultPortName (), DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits){ }

		public MonoSerialPort (IContainer container) : this () {}

		public MonoSerialPort (string portName) :
			this (portName, DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits){}

		public MonoSerialPort (string portName, int baudRate) :
			this (portName, baudRate, DefaultParity, DefaultDataBits, DefaultStopBits){}

		public MonoSerialPort (string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits) {
			port_name = portName;
			baud_rate = baudRate;
			data_bits = dataBits;
			stop_bits = stopBits;
			this.parity = parity;
		}
		// 以下都是 set  get 方法
		static string GetDefaultPortName () {
			string[] ports = GetPortNames();
			if (ports.Length > 0) {
				return ports[0];
			} else {
				int p = (int)Environment.OSVersion.Platform;
				if (p == 4 || p == 128 || p == 6)
					return "ttyS0"; // Default for Unix
				else
					return "COM1"; // Default for Windows
			}
		}

		public int BaudRate {
			get {return baud_rate;}
			set {baud_rate = value;}
		}
		。。。。。。

		// methods
		public void Close (){ Dispose (true);}

		protected override void Dispose (bool disposing) {
			if (!is_open)
				return;
			
			is_open = false;
			// Do not close the base stream when the finalizer is run; the managed code can still hold a reference to it.
			if (disposing)
				stream.Close ();
			stream = null;
		}

		static bool IsWindows {
			get {
				PlatformID id =  Environment.OSVersion.Platform;
				return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
			}
		}

		public void Open ()	{
			if (is_open)
				throw new InvalidOperationException ("Port is already open");
			
			if (IsWindows) // Use windows kernel32 backend
				stream = new WinSerialStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
					rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
			else // Use standard unix backend
				stream = new SerialPortStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
					rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
			
			is_open = true;
		}
		
        // 以下都是 Read  方法
		public int Read (byte[] buffer, int offset, int count){
            try {
                return stream.Read(buffer, offset, count);
            } catch (IOException)  {
                is_open = false;
                throw;
            }
        }
		。。。。。。
	    // 以下都是 Write 方法
		public void Write (string text) {
			byte [] buffer = encoding.GetBytes (text);
			Write (buffer, 0, buffer.Length);
		}

		public void Write (byte [] buffer, int offset, int count) {
			stream.Write (buffer, offset, count);
		}
		。。。。。。
	}

█ 相关资料

提示:这里是参考的相关文章

  1. 雷迅创新 C-RTK系列定位系统 · C-RTK
  2. RTK GPS - CUAV C-RTK - 《PX4 用户手册》 - 书栈网 · BookStack
  3. geeksville/arduleader: An android ground controller (and other things) for Mavlink/Arduplane
  4. mik3y/usb-serial-for-android: Android USB host serial driver library for CDC, FTDI, Arduino and other devices.
  5. mikey0000/Ublox-GPS-receiver-android: Provide mock location data from serial to usb ublox 7020 GPS
  6. 2019-12-08 安卓APP:利用AndroidStudio开发usb串口通信软件【第2步】_崭蓝码农的博客-CSDN博客_wchusbdriver
  7. CH34xUARTDriver.jar 驱动的官方下载地址:
  8. 2021-12-03 qxcors账号怎么连接?华测、中海达、南方等品牌RTK连接qxcors方法
  9. 中海达 - 聚焦北斗卫星导航产业,提供北斗+ 精准位置应用解决方案
  10. 2021-05-27 android 申请usb权限,USB 权限申请流程_Everglow577的博客-CSDN博客
  11. 2021-12-31 USB协议分析_llljjlj的博客-CSDN博客_usb协议
  12. 一篇读懂:Android手机如何通过USB接口与外设通信(附原理分析及方案选型) - 腾讯云开发者社区-腾讯云
  13. 2022-11-02 Android 插入USB摄像头生成两个video节点_祥子Gyx的博客-CSDN博客_android video节点
  14. 2022-10-28 UBLOX配置/GPS配置设置/u-center使用_ba_wang_mao的博客-CSDN博客_ubx协议
  15. 厘米级高精度差分GPS(C-RTK)使用教程 - 多旋翼综合技术讨论专栏-5iMX.com 我爱模型 玩家论坛 ——专业遥控模型和无人机玩家论坛(玩模型就上我爱模型,创始于2003年)-[成都艾麦克斯科技有限公司]
  16. 飞控使用PX4固件,支持使用Here3 CAN RTK功能吗? 产品使用有疑问?官方解答来啦! - 赫星技术支持 - DroneChina
  17. 2021-06-06【USB笔记】查询VID对应的USB设备厂商_dadalaohua的博客-CSDN博客_usb vid查询
  18. 2022-05-20 浅谈USB设备的VID和PID_PrinciplesMan的博客-CSDN博客_usb设备的vid与pid

█ 免责声明

博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持!

转载请注明出处:
https://blog.csdn.net/ljb568838953/article/details/128614252

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/153798.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

P1102 A-B 数对

题目背景 出题是一件痛苦的事情&#xff01; 相同的题目看多了也会有审美疲劳&#xff0c;于是我舍弃了大家所熟悉的 AB Problem&#xff0c;改用 A-B 了哈哈&#xff01; 题目描述 给出一串正整数数列以及一个正整数 CC&#xff0c;要求计算出所有满足 A - B CA−BC 的数对的…

vb.net多功能白板(集成:绘图,编辑,批注,橡皮,图片处理,拍摄,裁剪,旋转等功能

根据上一次的自定义白板&#xff0c;我已经更新了很多内容了 这一次打算再细一点 初始化程序&#xff1a;所有的整体变量&#xff08;作者提醒&#xff0c;请不要直接照抄代码&#xff0c;可以和作者发的文件进行学习和参考 Public ListOfPen As New List(Of Bitmap)Public L…

ArcGIS如何进行自动矢量化操作

这里我们在网络上找一幅高中地理课本上看的等高线图给大家能进行操作演示。 等高线图 01 地理配准 1、定义投影 给数据框定义一个投影&#xff0c;右键Layers>Properties>Coordinate System>Projected Coordinate Systems>Gauss Kruger>Beijing1954> Be…

雅思经验总结(1)

听力技巧&#xff1a;听sections 3就是看你何时进入状态&#xff0c;还有审题&#xff0c;之后就是听but&#xff0c;其他的转折词什么yet because however什么都非常的少&#xff0c;最主要的还是but&#xff0c;注意bus之后的话&#xff0c;其余的什么细节题就是说还要听懂文…

Biome-BGC生态系统模型区域模拟

Biome-BGC是利用站点描述数据、气象数据和植被生理生态参数&#xff0c;模拟日尺度碳、水和氮通量的有效模型&#xff0c;其研究的空间尺度可以从点尺度扩展到陆地生态系统。在Biome-BGC模型中&#xff0c;对于碳的生物量积累&#xff0c;采用光合酶促反应机理模型计算出每天的…

Java面向对象进阶之static

目录static静态关键字static&#xff1a;修饰成员变量&#xff0c;内存机制static是什么、修饰成员变量的方法总结static修饰成员变量的内存原理static&#xff1a;修饰成员方法、内存机制static修饰成员方法的基本用法总结static修饰成员方法的内存原理static的注意事项static…

计算机组成原理习题二

计算机组成原理习题二 文章目录计算机组成原理习题二1、某指令系统的指令格式如下&#xff1a;答案&#xff1a;(1)152301Q1101 010011 000 001I10&#xff0c;I21&#xff0c;Z/C0&#xff0c;D/I0&#xff0c;故为变址寄存器2寻址&#xff0c;EA(I2)A063215301063516Q。 (4)…

大咖年终“讲” 维视教育李明睿——制造业转型升级下需要重新定义人才培养

数字化转型迫在眉睫建设应用型大学风潮正涌制造企业在推进智能制造和数字化转型进程中&#xff0c;衍生出大量人才需求。据人社部、工信部发布的《制造业人才发展规划指南》显示&#xff0c;中国制造业10大重点领域人才缺口2025年将接近3000万人&#xff0c;缺口率高达48&#…

流媒体基础-RTCP

1、RTCP的封装 RTP需要RTCP为其服务器质量提供保证&#xff0c;周期性发送 RTCP的主要功能是&#xff1a;服务质量的监视、反馈&#xff08;QoS&#xff09;、媒体间的同步&#xff08;Sync&#xff09;&#xff0c;以及多播组中成员的标识。在RTP会话期间&#xff0c;各参与者…

Lichee_RV学习系列---认识Lichee_RV、环境搭建和编译第一个程序

系列文章目录 文章目录系列文章目录前言一、认识Lichee RV1、D1-H 芯片2、Lichee RV开发板3、系统镜像二、Lichee RV 固件烧录1、要求基本硬件2、基本资料下载3、固件烧录在这里插入图片描述三、连接上开发板1、ADB方式连接a&#xff1a;ADB下载b&#xff1a;ADB连接c&#xff…

孙溟㠭篆刻《无有中无尽藏》

《无有中无尽藏》孙溟㠭篆刻 无一物中无尽藏&#xff0c;是说当“我执”袪除&#xff0c;仅余“真如”时&#xff0c;便可以理解“无尽藏”。虽然身上没有东西&#xff0c;但是其实世人身上藏了所有的东西。“无心”亦是有心&#xff0c;心中富足。所以当人祛除心中的偏执&…

自动语音识别(ASR)研究综述

自动语音识别ASR研究综述 一、语言识别基础知识 从语音系统识别构成来讲&#xff0c;一套完整的语音识别系统包括&#xff1a;预处理、特征提取、声学模型、语言模型、以及搜索算法等模块&#xff0c;具体结构示意图如下所示: 特征提取&#xff08;MFCC声学特征&#xff09…

Error handling response: TypeError: self.processResponse is not a function

问题背景 &#xff1a; 自己在搭建 Vue 初始模板架子的时候 &#xff0c; 解决完 router 路由的报错问题后 &#xff0c; 控制台还剩下一个显眼的 Error 红色 Bug &#xff0c; 不解决的话看着难受 &#xff0c; 盘它 &#xff01; 点击报错内容后进入 &#xff1a; Error h…

redis应用笔记

1.登录服务 在登陆服务中,如果将数据全部存储到tomcat中,当存在多个tomcat的时候,数据是无法同步的,这就导致了数据的共享问题: 1、每台服务器中都有完整的一份session数据&#xff0c;服务器压力过大。 2、session拷贝数据时&#xff0c;可能会出现延迟 解决办法就是采用redi…

SpringBoot整合Redis实现优惠券秒杀服务(笔记+优化思路版)

本文属于看黑马的redis的学习笔记&#xff0c;记录了思路和优化流程&#xff0c;精简版最终版请点击这里查看。 文章目录一、全局ID生成器1.1 理论1.1.1 全局唯一ID生成策略1.2 代码(Redis自增)二、实现优惠券秒杀下单2.1 SQL2.2 SQL对应实体类2.2.1 普通券实体类2.2.2 秒杀券实…

声纹识别之说话人验证speaker verification

目录 一、speaker verification简介 二、主流方案和模型 1、Ecapa_TDNN模型 2、WavLm 三、代码实践 1、Ecapa_TDNN方案 a、模型结构 b、loss c、数据处理 d、模型训练和评估 e、说话人验证推理 2、WavLm预训练方案 a、模型结构和loss b、数据处理 c、模型训练 …

html5支持的几种音频格式介绍

关于音频的格式 ogg音频 Ogg全称应该是OGGVobis(oggVorbis)是一种新的音频压缩格式&#xff0c;类似于MP3等的音乐格式。Ogg是完全免费、开放和没 有专利限制的。OggVorbis文件的扩展名是.OGG。Ogg文件格式可以不断地进行大小和音质的改良&#xff0c;而不影响旧有的编码器或…

合合信息扫描全能王“照片高清修复”功能上线,3秒还原老照片

穿越时光的“美颜”!合合信息智能图像处理技术让老照片“焕新”“春运”已经开始&#xff0c;团聚时刻即将到来。和亲人们一起围炉话家常&#xff0c;翻开旧日的相册&#xff0c;品读一张张泛黄的照片背后最牵动人心的情感&#xff0c;也是“年味”所在。时光会在照片上留下斑驳…

巨量引擎·2023教育Future大会:扎根内容生态,做好经营提效

求知方寸间&#xff0c;如风过千川。当知识创作成为新的潮流&#xff0c;当教育数字化迈入直播与短视频新时代&#xff0c;当图书电商红红火火&#xff0c;如何做好教育全产业链升级与创新&#xff1f;新年伊始&#xff0c;巨量引擎举办“行知.行为.行万里 2023教育Future大会”…

嵌入式实时操作系统的设计与开发(六)

中断系统结构 在RTOS中&#xff0c;中断是与具体硬件平台关联度最大的部分&#xff0c;为了实现高可移植性、可配置性&#xff0c;中断子系统依照aCoral的整体结构来设计&#xff0c;划分为HAL&#xff08;硬件抽象层&#xff09;和内核层。 在HAL层先将各种中断汇拢&#xff…