█ 【无人机学习之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-IF | usb厂商列表 PDF | USB-IF是一家非营利性公司,USB的官网,通过官网查询最为准确,官网每个季度都会进行更新。 |
Linux USB Project | List 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=">>$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 UBLOX | ubx_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) | 发送和接收数据 |
接收Can1062 | can.MessageReceived | 如果我们收到can消息,则将rtcm数据输入rtcm解析器(这边测试应该是没执行到) |
接收Can1062 | can.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);
}
。。。。。。
}
█ 相关资料
提示:这里是参考的相关文章
- 雷迅创新 C-RTK系列定位系统 · C-RTK
- RTK GPS - CUAV C-RTK - 《PX4 用户手册》 - 书栈网 · BookStack
- geeksville/arduleader: An android ground controller (and other things) for Mavlink/Arduplane
- mik3y/usb-serial-for-android: Android USB host serial driver library for CDC, FTDI, Arduino and other devices.
- mikey0000/Ublox-GPS-receiver-android: Provide mock location data from serial to usb ublox 7020 GPS
- 2019-12-08 安卓APP:利用AndroidStudio开发usb串口通信软件【第2步】_崭蓝码农的博客-CSDN博客_wchusbdriver
- CH34xUARTDriver.jar 驱动的官方下载地址:
- 2021-12-03 qxcors账号怎么连接?华测、中海达、南方等品牌RTK连接qxcors方法
- 中海达 - 聚焦北斗卫星导航产业,提供北斗+ 精准位置应用解决方案
- 2021-05-27 android 申请usb权限,USB 权限申请流程_Everglow577的博客-CSDN博客
- 2021-12-31 USB协议分析_llljjlj的博客-CSDN博客_usb协议
- 一篇读懂:Android手机如何通过USB接口与外设通信(附原理分析及方案选型) - 腾讯云开发者社区-腾讯云
- 2022-11-02 Android 插入USB摄像头生成两个video节点_祥子Gyx的博客-CSDN博客_android video节点
- 2022-10-28 UBLOX配置/GPS配置设置/u-center使用_ba_wang_mao的博客-CSDN博客_ubx协议
- 厘米级高精度差分GPS(C-RTK)使用教程 - 多旋翼综合技术讨论专栏-5iMX.com 我爱模型 玩家论坛 ——专业遥控模型和无人机玩家论坛(玩模型就上我爱模型,创始于2003年)-[成都艾麦克斯科技有限公司]
- 飞控使用PX4固件,支持使用Here3 CAN RTK功能吗? 产品使用有疑问?官方解答来啦! - 赫星技术支持 - DroneChina
- 2021-06-06【USB笔记】查询VID对应的USB设备厂商_dadalaohua的博客-CSDN博客_usb vid查询
- 2022-05-20 浅谈USB设备的VID和PID_PrinciplesMan的博客-CSDN博客_usb设备的vid与pid
█ 免责声明
博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持! |
---|
转载请注明出处:
https://blog.csdn.net/ljb568838953/article/details/128614252