用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Spectrum3847/2026-Spectrum --skill analyze-wpilog命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Use for AdvantageScope visualization automation: building/running our AdvantageScope fork (Spectrum 3847) or vanilla AdvantageScope, opening WPILOGs/DataLogs with layouts, using opt-in agent control/export hooks, capturing 2D/3D field screenshots or frame sequences, and falling back to vanilla AdvantageScope/manual inspection when hooks are unavailable.
Review an open PR with CodeRabbit — triage its comments, fix valid issues, push, and loop until the review approves. Use when a PR on this repo has a CodeRabbit review pending or needs one.
Spectrum 3847 FRC vision work in Java — Limelight MegaTag AprilTag pose estimation fused into the swerve pose estimator. Use when implementing or reviewing AprilTag detection, pose estimation, vision-aided odometry, or vision rejection/std-dev tuning. Triggers on: Limelight, MegaTag, AprilTags, pose estimation, Vision subsystem, addVisionMeasurement, SwerveDrivePoseEstimator, or any vision-related robot code task.
基于 SOC 职业分类
正在显示 SKILL.md
| name | analyze-wpilog |
| description | Analyze WPILOG log files from FRC robots using the wpilog-parser library with TypeScript. |
| license | MIT |
WPILOG is the standard format used by FRC loggers — the 2026-Spectrum robot logs via DogLog (frc.spectrumLib.telemetry.Telemetry extends DogLog), so robots publish topics like Swerve/State/Pose, Swerve/SystemState, Sim/RobotPose3d, Sim/Fuel. The wpilog-parser library makes it easy to read and analyze the log files using TS/JS.
Upstream repo: https://github.com/jonahsnider/wpilog-parser/. Logs saved as .wpilog (see simulation/ and robot/ output dirs).
Init a project using bun init or similar
Add wpilog-parser as a dependency
Create a .ts file for cataloging the records in the WPILOG file
import { readFile } from 'node:fs/promises';
import { readRecords, catalogEntries } from 'wpilog-parser';
const bytes = await readFile('./example.wpilog');
for (const catalogEntry of catalogEntries(readRecords(bytes))) {
console.log(catalogEntry);
// {
// entryId: 3,
// name: "/Robot/DogLog/Options",
// type: "string",
// metadata: "{\"source\":\"DogLog\"}",
// }
}
Use decodeRecords to parse record contents. Filter by name and narrow on type:
import { readFile } from 'node:fs/promises';
import { readRecords, decodeRecords, isDataRecord, RecordType } from 'wpilog-parser';
const bytes = await readFile('./example.wpilog');
for (const record of decodeRecords(readRecords(bytes))) {
if (!isDataRecord(record)) continue;
if (record.name === '/Robot/Intake/Voltage' && record.type === RecordType.Double) {
console.log(record.timestamp, record.payload);
}
}
import { structPayloadToJson, RecordType } from 'wpilog-parser';
for (const record of decodeRecords(readRecords(bytes))) {
if (record.type === RecordType.Struct && record.name === '/Robot/Localization/EstimatedPose') {
console.log(record.timestamp, structPayloadToJson(record.payload));
// 18688018n {
// translation: { x: 10.289, y: 0.47 },
// rotation: { value: 1.5707961072652017 },
// }
}
}
for (const record of decodeRecords(readRecords(bytes), { strict: true })) {
// throws on orphan data records instead of skipping
}
When DriverStation logging is on, every WPILOG has these boolean entries:
| Entry | Meaning |
|---|---|
/DS:enabled | true while the robot is enabled (any mode). |
/DS:autonomous | true during autonomous. false means teleop (unless /DS:test). |
/DS:test | true during test mode. |
/DS:estop | true if e-stopped. |
The mode flags (autonomous, test) reflect what the DS would run if enabled, so check /DS:enabled together with them. Timestamps are microseconds (bigint).
import { readRecords, decodeRecords, isDataRecord, RecordType } from 'wpilog-parser';
type Mode = 'auto' | 'teleop' | 'test';
function modeOf(auto: boolean, test: boolean): Mode {
if (test) return 'test';
if (auto) return 'auto';
return 'teleop';
}
let enabled = false;
let auto = false;
let test = false;
let enabledSince: bigint | null = null;
let lastTimestamp: bigint = 0n;
const totals = { auto: 0n, teleop: 0n, test: 0n };
for (const r of decodeRecords(readRecords(bytes))) {
lastTimestamp = r.timestamp;
if (!isDataRecord(r) || r. !== .) ;
wasEnabled = enabled;
prevMode = (auto, test);
(r. === ) enabled = r.;
(r. === ) auto = r.;
(r. === ) test = r.;
;
(wasEnabled && (!enabled || (auto, test) !== prevMode)) {
totals[prevMode] += r. - (enabledSince ?? r.);
enabledSince = enabled ? r. : ;
} (!wasEnabled && enabled) {
enabledSince = r.;
}
}
(enabled && enabledSince !== ) {
totals[(auto, test)] += lastTimestamp - enabledSince;
}
.(totals);