| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- "use strict";
- /**
- * 传入 path_test.json 同类路径点数组,返回模拟真机上传的 IMU 采样数组(与小程序一致:仅最后一段;间隔约 60–70ms;各轴 float32 量化)。
- * 单参数、无文件读写。
- */
- module.exports = function generateGyrFromPath(pathRows) {
- const R0 = 6371000;
- const D2R = Math.PI / 180;
- function parseRow(row) {
- if (!row || typeof row.d !== "string") return null;
- const [ts] = row.d.trim().split(/\s+/);
- const t = parseInt(ts, 10);
- if (!Number.isFinite(t)) return null;
- const lat = row.a;
- const lng = row.o;
- if (typeof lat !== "number" || typeof lng !== "number") return null;
- const speed = typeof row.s === "number" ? row.s : 0;
- return { t, lat, lng, speed };
- }
- function sortPath(points) {
- const list = points.map(parseRow).filter(Boolean);
- list.sort((a, b) => a.t - b.t);
- if (list.length < 2) return list;
- const d = [list[0]];
- for (let i = 1; i < list.length; i++) {
- if (list[i].t !== list[i - 1].t) d.push(list[i]);
- }
- return d;
- }
- function hav(lat1, lng1, lat2, lng2) {
- const φ1 = lat1 * D2R;
- const φ2 = lat2 * D2R;
- const dφ = (lat2 - lat1) * D2R;
- const dλ = (lng2 - lng1) * D2R;
- const s =
- Math.sin(dφ / 2) ** 2 +
- Math.cos(φ1) * Math.cos(φ2) * Math.sin(dλ / 2) ** 2;
- return 2 * R0 * Math.asin(Math.min(1, Math.sqrt(s)));
- }
- function bear(lat1, lng1, lat2, lng2) {
- const φ1 = lat1 * D2R;
- const φ2 = lat2 * D2R;
- const dλ = (lng2 - lng1) * D2R;
- const y = Math.sin(dλ) * Math.cos(φ2);
- const x =
- Math.cos(φ1) * Math.sin(φ2) -
- Math.sin(φ1) * Math.cos(φ2) * Math.cos(dλ);
- return Math.atan2(y, x);
- }
- function unwrap(prev, next) {
- let d = next - prev;
- while (d > Math.PI) d -= 2 * Math.PI;
- while (d < -Math.PI) d += 2 * Math.PI;
- return prev + d;
- }
- function lerpPath(path, t, key) {
- if (t <= path[0].t) return path[0][key];
- const L = path[path.length - 1];
- if (t >= L.t) return L[key];
- let i = 0;
- while (i < path.length - 1 && path[i + 1].t < t) i++;
- const p0 = path[i];
- const p1 = path[i + 1];
- const f = (t - p0.t) / (p1.t - p0.t);
- return p0[key] + f * (p1[key] - p0[key]);
- }
- function ll(path, t) {
- return { lat: lerpPath(path, t, "lat"), lng: lerpPath(path, t, "lng") };
- }
- function segmentStartMs(t0, tEnd) {
- const sec = Math.max(0, tEnd - t0) / 1000;
- return t0 + Math.floor(sec / 60) * 60 * 1000;
- }
- /** 与真机常见的 60–72ms 间隔起伏一致(含 70→60 这类相邻差) */
- function gapMs(i) {
- const g = [70, 60, 65, 70, 60, 62, 68, 60, 72, 61];
- return g[i % g.length];
- }
- function f32(x) {
- return Math.fround(x);
- }
- function rngFactory(path) {
- let h = 2166136261 >>> 0;
- for (let i = 0; i < path.length; i++) {
- const p = path[i];
- h ^= p.t >>> 0;
- h = Math.imul(h, 16777619);
- const x = ((p.lat * 1e6) ^ (p.lng * 1e6)) | 0;
- h ^= x;
- h = Math.imul(h, 16777619);
- }
- let s = h || 0x9e3779b9;
- return function () {
- s = (s + 0x6d2b79f5) >>> 0;
- let t = Math.imul(s ^ (s >>> 15), 1 | s);
- t ^= t + Math.imul(t ^ (t >>> 7), 61 | t);
- return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
- };
- }
- if (!Array.isArray(pathRows) || pathRows.length < 2) return [];
- const path = sortPath(pathRows);
- if (path.length < 2) return [];
- const rand = rngFactory(path);
- const t0 = path[0].t;
- const t1 = path[path.length - 1].t;
- const tSeg = segmentStartMs(t0, t1);
- const rngHalf = 35;
- const pA0 = ll(path, tSeg);
- const pA1 = ll(path, Math.min(t1, tSeg + Math.max(rngHalf, 1)));
- let prevU = bear(pA0.lat, pA0.lng, pA1.lat, pA1.lng);
- let prevV = lerpPath(path, tSeg, "speed");
- const out = [];
- let tCur = tSeg;
- let idx = 0;
- let lastT = tCur - gapMs(0);
- /** 贴近真机样例的加计零偏量级(持机姿态下重力分摊 + 跑动微扰,非 9.8 单列) */
- const baseAx = -1.08;
- const baseAy = 0.9;
- const baseAz = -0.06;
- const amp = 0.36;
- // 一阶低通,避免数值跳变过于“脚本化”
- const lp = 0.82; // 越大越平滑
- let lpAx = baseAx;
- let lpAy = baseAy;
- let lpAz = baseAz;
- let lpGx = 0.43;
- let lpGy = 0.42;
- let lpGz = -1.78;
- function clamp(x, lo, hi) {
- return x < lo ? lo : x > hi ? hi : x;
- }
- while (tCur <= t1) {
- const dtMs = tCur - lastT;
- const dtSec = Math.max(dtMs, 1) / 1000;
- const pb = ll(path, Math.max(t0, tCur - rngHalf));
- const pf = ll(path, Math.min(t1, tCur + rngHalf));
- const vPath = lerpPath(path, tCur, "speed");
- const dist = hav(pb.lat, pb.lng, pf.lat, pf.lng);
- const vTrack =
- (2 * rngHalf) > 0 ? dist / ((2 * rngHalf) / 1000) : vPath;
- const v = 0.62 * vPath + 0.38 * Math.max(0, vTrack);
- const br = bear(pb.lat, pb.lng, pf.lat, pf.lng);
- const uW = unwrap(prevU, br);
- const yaw = (uW - prevU) / dtSec;
- const aT = (v - prevV) / dtSec;
- const aL = v * yaw;
- prevU = uW;
- prevV = v;
- lastT = tCur;
- const co = Math.cos(0.31);
- const si = Math.sin(0.31);
- const jx = aL * co - aT * si;
- const jy = aL * si + aT * co;
- const runSec = (tCur - t0) / 1000;
- const ph = 2 * Math.PI * 1.82 * runSec;
- let ax =
- baseAx +
- amp * jx +
- 0.2 * Math.sin(ph) +
- 0.1 * (rand() - 0.5);
- let ay =
- baseAy +
- amp * jy +
- 0.17 * Math.cos(ph * 0.97) +
- 0.1 * (rand() - 0.5);
- let az =
- baseAz +
- 0.28 * Math.sin(2 * ph) +
- 0.18 * Math.cos(ph) +
- 0.08 * (rand() - 0.5);
- let gx = 0.43 + 0.12 * Math.cos(ph) + 0.07 * (rand() - 0.5);
- let gy = 0.42 + 0.26 * Math.sin(ph * 1.07) + 0.1 * (rand() - 0.5);
- let gz = -1.78 + 0.8 * yaw + 0.14 * (rand() - 0.5);
- // 去除极端尖峰注入,改为限幅 + 平滑,更贴近正常跑步统计特征
- gx = clamp(gx, -2.2, 2.2);
- gy = clamp(gy, -2.2, 2.2);
- gz = clamp(gz, -3.2, 1.2);
- lpAx = lp * lpAx + (1 - lp) * ax;
- lpAy = lp * lpAy + (1 - lp) * ay;
- lpAz = lp * lpAz + (1 - lp) * az;
- lpGx = lp * lpGx + (1 - lp) * gx;
- lpGy = lp * lpGy + (1 - lp) * gy;
- lpGz = lp * lpGz + (1 - lp) * gz;
- out.push({
- t: tCur,
- ax: f32(lpAx),
- ay: f32(lpAy),
- az: f32(lpAz),
- gx: f32(lpGx),
- gy: f32(lpGy),
- gz: f32(lpGz),
- });
- tCur += gapMs(idx++);
- }
- return out;
- };
|