使用场景

定位模块上电后,每一秒从串口发送一套GNSS数据。模块坏了以后,用软件模拟定位模块的行为,按相同频率传输数据给上位机,数据来源于硬件测试时保存的日志。最终可以实现模拟一个串口GNSS信号源(使用任意数据)。

日志示例

硬件环境

利用两个串口对接(地对地,Rx Tx交叉),使用其中一个串口作为输出口,上位机可以使用另一个串口接收模拟软件的数据。

模拟程序

  1.  设定使用串口(从成对的串口中挑取一个供模拟程序使用,另一个供上位机使用);
  2. 读取并解析日志数据,按块保存(一个块包含多条,多个类型的GNSS数据);
  3. 按一秒一次的频率发送数据,发送前,替换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");
}
分类: articles