Java开发的散点时间戳序列数据播放工具can-do-play

CAN-do-player

项目地址

https://github.com/Jarrettluo/can-do-play

背景

1 功能概述

用于对散点的一系列数据进行播放,支持按照确定的时间间隔进行播放数据。

2 功能用途

使用采集工具采集一系列时间序列数据,数据需要按照真实的时间间隔进行回放。例如ROS系统采集的ROSbag数据包,例如CANoe采集的blf等数据。
配合可视化展示,数据流处理等工具,可以对数据进行展示或者处理。

使用方法

// 使用示例
public static void main(String[] args) throws Exception {
    // 创建测试数据源(内存映射文件)
    // 注意时间戳都是us
    List<TimestampedData<String>> data = Arrays.asList(
            new TimestampedData<>(1_000_000L, "Event1"),
            new TimestampedData<>(3_000_000L, "Event2"),
            new TimestampedData<>(6_000_000L, "Event3"),
            new TimestampedData<>(9_000_000L, "Event4"),
            new TimestampedData<>(11_000_000L, "Event5"),
            new TimestampedData<>(20_000_000L, "Event6")
    );
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
    // 创建播放器
    EnhancedTimestampPlayer<String> player = new EnhancedTimestampPlayer<>(
            data,
            event -> {
                System.out.printf("[%dµs] %s%n", System.nanoTime() / 1000, event);
                System.out.println("当前时间:" + sdf.format(new Date(System.currentTimeMillis())));
            },
            1024
    );

    player.setSpeed(0.5);
    player.play();

    // 运行演示
    TimeUnit.SECONDS.sleep(25);
    player.close();
}

输出结果

[77359625162µs] Event1
当前时间:2025-04-06 11:27:20.000223
[77363622187µs] Event2
当前时间:2025-04-06 11:27:24.000218
[77369624022µs] Event3
当前时间:2025-04-06 11:27:30.000220
[77375623595µs] Event4
当前时间:2025-04-06 11:27:36.000219
[77379623361µs] Event5
当前时间:2025-04-06 11:27:40.000219

方案设计

在系统中最关键的部分是对时间进行判断。

    // 核心调度逻辑
    private void scheduleNext() {
        if (!isPlaying) return;

        long currentMicros = nanoTime() / 1000;
        TimestampedData<T> next = playQueue.peek();

        if (next != null) {
            long expectedTime = baseTimestamp + (long) ((next.getTimestamp()) / speedFactor);
            long delayMicros = expectedTime - currentMicros - timeDrift.get();
            // 动态时间补偿
            // 统一处理正负偏差,确保补偿步长不超过预设的 COMPENSATION_STEP 绝对值
            if (Math.abs(delayMicros) > COMPENSATION_STEP) {
                long compensation = Math.min(Math.max(-COMPENSATION_STEP, delayMicros), COMPENSATION_STEP);
                timeDrift.addAndGet(compensation);
                delayMicros -= compensation;
            }
            if (delayMicros <= 0) {
                // 立即触发
                handleData(playQueue.poll());
                scheduleNext();
            } else {
                // 高精度调度
                long delayNanos = delayMicros * 1000;
                lastScheduledTime.set(nanoTime());
                scheduler.schedule(this::handleAndSchedule, delayNanos, TimeUnit.NANOSECONDS);
            }
        }
    }

对每一个散点时间戳进行判断,判断到与程序的时间差异,进行时间偏移。

通过递归的方式将所有散点按照时间间隔输出出来。
示意图

项目参考

参考自apollo对record数据的播放

https://github.com/moyituo/apollo/blob/master/cyber/tools/cyber_recorder/player/play_task_producer.cc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值