动画导出
--animate把一次运行的时间序列变成会动的内容:一个动画可缩放矢量图形(SVG)文件、一个自包含的超文本标记语言(HTML)播放器,或一组带编号的SVG帧序列,供视频编码器使用。测评活动按其各阶段(压制干扰、欺骗干扰、守时(holdover)、完好性告警)播放,并标出其告警;频谱运行逐行播放其瀑布图;低地球轨道(LEO)过境则播放其各频段链路,若有欺骗源,还逐历元播放监测统计量。
kshana scenarios/campaign-jam-spoof-holdover-integrity.toml --animate all
kshana scenarios/l-band-waterfall-jamming.toml --animate html
kshana scenarios/clock-holdover.toml --animate frames --animate-fps 24 --animate-duration 10
代码:src/animation.rs。测试:tests/animation.rs。
它是什么,它不是什么#
导出器读取该次运行已写出的结果文档。它不重新运行物理计算,不在样本之间重采样、平滑或插值(只有在光标处结束轨迹的那一段直线除外),也不自行添加任何数字。因此,动画的质量与其背后的运行完全一致。其等级为MODELLED,内部自洽:它是一种渲染,绝不是证据,也没有自己的验证矩阵行。
输出#
每种格式都写在场景旁边,与常规的result.json、chart.svg和report.html放在一起:
| 格式 | 写成 | 包含内容 |
|---|---|---|
svg |
<scenario>.animation.svg |
一个用层叠样式表(CSS)关键帧制作动画、不含脚本的SVG:轨迹在移动的时间光标后依次绘出,事件在其发生时刻出现,瀑布图各行按时间顺序显现。它循环播放,并在完成的画面上停留1.5 s。 |
html |
<scenario>.animation.html |
一个文件,内含内联脚本和样式,没有外部资源:播放和暂停、拖动条、速度(0.25x至4x)、所有面板共用同步光标、每条轨迹的实时数值、阶段条,以及点击即可跳转的事件列表。空格键播放或暂停,方向键单步,Home和End键跳转。 |
frames |
<scenario>.frames/ |
frame_0000.svg、frame_0001.svg、……以及manifest.json。 |
--animate可重复使用,接受逗号分隔的列表(--animate svg,html)或all。--animate-fps(1至60,默认12)和--animate-duration(0.5 s至600 s,默认8 s)设定播放;帧序列上限为7,200帧。
当--export在同一条命令中运行时,HTML播放器会把写在它旁边的导出文件(CZML、KML、GeoJSON、STK、SigMF)列为指向这些同级文件的相对链接;仅为纯文件名写入链接,因此没有链接可以离开该文件夹或访问网络。只要结果含有时间序列,高级报告(report.html)就会嵌入这幅动画图,无论是否运行过--animate。
当--animate运行时,result.json会增加一个animation块:格式、每秒帧数(fps)、时长、帧数、时间跨度、面板标题、读取每条轨迹所用的JSON路径(sources),以及找到但未绘制的任何序列(omitted)。不带--animate时,结果与普通运行逐字节相同。
帧序列#
序列恰好有round(duration × fps)帧。第i帧显示截至t_start + (t_end − t_start) × i / (n − 1)的时间线,因此第一帧是起点,最后一帧是完成的画面。manifest.json记录fps、duration_s、frame_count、frame_pattern(frame_%04d.svg)、像素尺寸、t_start、t_end、每一帧的任务时间(frame_times)、各阶段、事件和来源。重新导出到同一目录时,会先删除其中已有的每个frame_NNNN.svg(且不删除其他任何文件),因此较短的序列绝不会留下较长序列的帧让编码器读取。
这些帧是SVG,不是便携式网络图形(PNG)。用librsvg库构建的ffmpeg可直接读取它们,帧率与写出序列时一致(上例中为24;即清单中的fps):
ffmpeg -framerate 24 -i clock-holdover.frames/frame_%04d.svg -pix_fmt yuv420p clock-holdover.mp4
没有librsvg的ffmpeg会以“no decoder found for: svg”停止。请先把帧栅格化,例如用rsvg-convert,然后对PNG进行编码:
for f in clock-holdover.frames/frame_*.svg; do rsvg-convert "$f" -o "${f%.svg}.png"; done
ffmpeg -framerate 24 -i clock-holdover.frames/frame_%04d.png -pix_fmt yuv420p clock-holdover.mp4
无障碍与主题#
- 配色方案。播放器遵循
prefers-color-scheme,并在其变化时重绘;动画SVG在同一媒体查询下带有其调色板的浅色变体。帧使用静态图表的深色调色板,因为视频没有配色方案。 - 减弱动态效果。在
prefers-reduced-motion: reduce下,动画SVG会关闭所有动画并显示完成的画面(其基础样式即为终态,因此没有任何内容被隐藏),播放器则以暂停状态停在完成的画面上打开,而不是开始播放;用户仍可拖动或按“播放”。 - SVG带有
<title>和<desc>;播放器的控件是带标签的按钮、带语音时间值的范围输入,以及一个下拉选择框。
什么算作时间序列#
各类结果文档彼此不同,因此导出器识别的是引擎所产生的形状,而不是一份类型清单:
- 带有时间字段的记录数组(
t、t_s、t_hours、t_min、t_days,或epochs数组上的k),时间字段沿数组严格递增。其他每个数值字段成为一条轨迹;同一字段位于两个根之下(quantum.series[].error_ns和classical.series[].error_ns)时共用一个面板。 - 带有严格递增时间数组的对象(
t_s、t、times_s、……),旁边是等长的数组:同级的数值数组、由{unit, values}记录构成的channels映射(campaign时间线)、其记录中含有此类数组的数组(spectrum频段、星座的卫星轨迹),以及其各行与时间轴对应、并带有同级*_hz频率轴的二维数组(spectrum瀑布图)。 - 除该时间数组外,
phases(name、t0_s、t1_s)成为阶段条,events(t_s、label、alarm)成为标记。频段的*_t_s标量(其首次失锁、其最低载噪密度比)同样会被标出。
带单位的轨迹,在其量级相差不超过100倍时共用一个面板,因此50 ns的保护值不会把2 ns的误差压平。最多绘制6个面板,每个面板6条轨迹;其余列在omitted之下。超过2,400个样本的轨迹按均匀步长抽稀,并保留最后一个样本;超过96 × 120个单元的瀑布图则被平均降采样(对分贝单元按功率平均,这样窄带干扰源不会被稀释)。
结果中没有上述任何形状的类型(链路预算、单历元几何、仅含统计量的蒙特卡罗汇总,或其结果只公布记录的哈希而不公布记录本身的电信守时)会被拒绝,提示no time series to animate,且不写入任何内容。对于kshana 0.40.0,在193个可运行的场景文件(除研究套件外的每个文件)中,有94个可制作动画,99个被拒绝。cargo test --test animation -- --nocapture会打印这份普查。
在LEO各类型中,每个内置的leo-pass、leo-pnt-chain和leo-ppp场景都可制作动画。leo-pvt在其多普勒、联合定位和授时模式下可制作动画(授时运行需要trace = true,它使每一行在每个历元都报告其钟差和预测的sigma),在极区覆盖模式下被拒绝。leo-navmsg可对其通过中途的更新制作动画,对编码、拟合区间和模型比较分析则被拒绝。leo-signal和ntn-positioning的结果不含时间轴,被拒绝。
确定性#
输出是结果文档和选项的纯函数:库中没有时钟、没有随机数、没有哈希表迭代、没有文件或网络访问,任何地方也没有时间戳,因此相同的场景、随机种子(seed)和选项会给出逐字节相同的文件。该模块无需改动即可构建为WebAssembly;只有命令行界面会写文件。
库的用法#
use kshana::animation::{AnimationFormat, AnimationOptions};
let anim = kshana::api::animate_toml(src, AnimationFormat::Html, &AnimationOptions::default())?;
std::fs::write("run.html", &anim.files[0].content)?;
kshana::animation::animate_result为你已有的结果文档制作动画,extract_timeline返回将会绘制的内容,而不进行渲染。
限制#
- Python wheel和WebAssembly模块目前尚未提供导出器;可通过命令行、Rust库以及模型上下文协议(MCP)服务器的
animate_scenario工具使用(该工具一次回复最多返回120帧)。 - 坐标轴是线性的。跨越多个数量级的序列(时钟误差从0.1 ns增长到一微秒)在运行的较晚阶段之前看起来是平的。
- 动画SVG依赖对裁剪矩形的CSS动画;当前的Chromium、Firefox和Safari引擎可以播放它,而不支持CSS动画的查看器则显示完成的画面。
- 扫描、蒙特卡罗和组合测评活动不含时间轴,被拒绝;扫描或集合的汇总统计量不是时间序列。链式测评活动可制作动画。