使用场景
定位模块上电后,每一秒从串口发送一套GNSS数据。模块坏了以后,用软件模拟定位模块的行为,按相同频率传输数据给上位机,数据来源于硬件测试时保存的日志。最终可以实现模拟一个串口GNSS信号源(使用任意数据)。
日志示例
硬件环境
利用两个串口对接(地对地,Rx Tx交叉),使用其中一个串口作为输出口,上位机可以使用另一个串口接收模拟软件的数据。
模拟程序
- 设定使用串口(从成对的串口中挑取一个供模拟程序使用,另一个供上位机使用);
- 读取并解析日志数据,按块保存(一个块包含多条,多个类型的GNSS数据);
- 按一秒一次的频率发送数据,发送前,替换GNSS数据中的时间,并重新计算校验。
关于xor计算:输入内容为$到*之间的内容,不包含$,也不包含*
static void Main(string[] args)
{
Console.WriteLine("输入模拟端口号\r\n" + string.Join("\r\n", SerialPort.GetPortNames()));
string portname = Console.ReadLine() ?? "COM1";
SerialPort port = new(portname, 115200);
port.Open();
//把串口编号保存到标题栏,备忘
Console.Title = $"GNSS信号模拟器 - {portname}";
var lines = File.ReadAllText("RAW.txt");
//一个group就是一个多行区块,包含一套完整的GNSS信息(GNRMC,GNGGA,GNGSA,GNTXT……)
var groups = Regex.Matches(lines, "\\$GNRMC.*?\\$GNTXT\\S+", RegexOptions.Singleline);
List<List<string>> batches = new();
foreach (Match group in groups.Cast<Match>())
{
//一行一条,每行从$开始,截取到*结束,校验不需要保留,发送前会重新计算
var records = group.Value.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(rec => Regex.Match(rec, "(?<=\\$).*(?=\\*)"));
batches.Add(records.Where(match => match.Success).Select(match => match.Value).ToList());
}
System.Timers.Timer timer = new(1000);
timer.Elapsed += (s, e) =>
{
ConsoleColor color = (ConsoleColor)new Random().Next(1, 15);
Console.ForegroundColor = color;//显示不同颜色,方便区分
string ts = DateTime.Now.AddHours(-8).ToString("HHmmss.000");
var batch = batches[new Random().Next(0, batches.Count)].Select(row => Process(row, ts));
foreach (string record in batch) port.WriteLine(record);
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss}\t" + string.Join("\r\n\t", batch) + "\r\n");
};
timer.Start();
Console.ReadLine();
}
/// <summary>
/// 替换模板里的时间为当前时间(UTC时间),防止服务器因为时间差过大导致拒收;
/// 因为修改了报文内容,最后还需要重新校验
/// </summary>
/// <param name="input"></param>
/// <param name="ts"></param>
/// <returns></returns>
static string Process(string input, string ts)
{
//时间格式 HHmmss.fff,但是毫秒部分全为0
input = Regex.Replace(input, "\\d{6}\\.000", ts);
return input + Xor(input);
}
static string Xor(string input)
{
byte xor = 0;
foreach (char c in input.ToCharArray()) xor ^= (byte)c;
return "*" + xor.ToString("X2");
}