修复非固定周期采样数据放大后波形出现断开的问题
问题:加载 Problem_Data.csv(约 27µs 且不固定的采样周期、含 100–277µs 空档),勾选 .UD\SLC_1ms\rtb_BodyIAC_K40+0x2,放大到主时间轴一大格 10ms 时波形大量断开。 根因:该缩放级别下波形走「像素列上下包络」渲染,而包络是按像素列聚合的, 每列只统计「落在本列内的采样点」。本文件的采样间隔(约 27µs)与一个像素列的宽度 (约 60µs)是同量级,因此一次跃变的两个端点常常分别落在相邻两列中:对每一列而言 列内采样点都在同一边,于是这段竖直折线在两侧的包络里都不出现,被整段丢掉, 看上去就是波形断开(与逐点描边渲染对照,1224 列中有 32 列完全没有波形像素)。 修复:在列包络中补上「跨越列边界的两段折线在边界处的取值」。新增 PolylineValueAt() 取折线在列左/右边界处的线性插值并计入该列包络。由于折线在列内是线性的,其极值必然 出现在采样点或列边界上,因此「列内采样点 + 两侧边界取值」就是该列包络的精确解; 相邻两列在公共边界取到同一个值,包络带因而严丝合缝。该处理同时覆盖了「整列没有采样点」 的情况(此时两侧边界取自同一条线段),因此也一并消除了采样稀疏处原有的断口。 验证: - 用临时开关强制走逐点描边路径作为对照基准,按像素列比对覆盖率: 修复前 32 列缺失,修复后 0 列缺失(1224/1224 全覆盖),像素总量与对照基准相当; - 同一区域的 12 倍放大截图显示波形已恢复为连续的方波,与逐点描边结果一致; - 回归 VERY_BIG_DATA.CSV(稠密包络)与 BIG_DATA.csv(稀疏描边 + 包络)渲染正常, make clean && make all 无警告。
This commit is contained in:
1 parent
79c84edea8
commit
18b8f3e38e
1 file changed
+88
-11
@@ -59,6 +59,51 @@ namespace jdi
|
||||
const wchar_t* const kMeanFilterBuildLabel = L"构建为新信号"; /**< 均值滤波面板底部按钮文案。 */
|
||||
const std::size_t kEdgeCountCacheLimit = 48; /**< 边沿统计缓存条目上限(超出时淘汰最早的一条)。 */
|
||||
|
||||
/**
|
||||
* @brief 取折线(相邻采样点之间线性连接)在给定时刻的取值。
|
||||
*
|
||||
* 包络是按像素列聚合的,因此某一段折线可能**跨越列边界**:跨越边界的那一小段
|
||||
* 在两个列里都不属于"落在本列内的采样点",只看列内采样点就会把它整段丢掉。
|
||||
* 该函数用于在列边界处取回这段折线的取值,使相邻两列的包络严丝合缝。
|
||||
*
|
||||
* @param [IN] time 时间轴。
|
||||
* @param [IN] values 采样值。
|
||||
* @param [IN] count 可用采样点数量。
|
||||
* @param [IN] firstIndex 第一个时间不小于查询时刻的采样下标(相交线段为 firstIndex-1 → firstIndex)。
|
||||
* @param [IN] t 查询时刻。
|
||||
* @param [OUT] out 输出取值。
|
||||
* @return 折线覆盖该时刻且取到有效值返回 true。
|
||||
*/
|
||||
bool PolylineValueAt(const std::vector<double>& time, const std::vector<double>& values,
|
||||
std::size_t count, std::size_t firstIndex, double t, double& out)
|
||||
{
|
||||
if (firstIndex == 0 || firstIndex >= count)
|
||||
{
|
||||
// 该时刻之前(或之后)没有采样点,折线并未延伸到这里。
|
||||
return false;
|
||||
}
|
||||
const double t0 = time[firstIndex - 1];
|
||||
const double t1 = time[firstIndex];
|
||||
const double v0 = values[firstIndex - 1];
|
||||
const double v1 = values[firstIndex];
|
||||
if (!std::isfinite(t0) || !std::isfinite(t1) || !std::isfinite(v0) || !std::isfinite(v1) ||
|
||||
!(t1 > t0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
double ratio = (t - t0) / (t1 - t0);
|
||||
if (ratio < 0.0)
|
||||
{
|
||||
ratio = 0.0;
|
||||
}
|
||||
else if (ratio > 1.0)
|
||||
{
|
||||
ratio = 1.0;
|
||||
}
|
||||
out = v0 + (v1 - v0) * ratio;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 统计相邻采样对 (k-1, k) 中穿越判定基准的边沿数量。
|
||||
*
|
||||
@@ -3929,13 +3974,6 @@ namespace jdi
|
||||
std::lower_bound(time.begin() + static_cast<std::ptrdiff_t>(i0),
|
||||
rangeEnd, t1) -
|
||||
time.begin());
|
||||
if (i1 <= i0)
|
||||
{
|
||||
// 该列没有采样点:说明数据在此处存在时间间隙,断开当前折线。
|
||||
inSegment = false;
|
||||
flushBandSegment();
|
||||
continue;
|
||||
}
|
||||
|
||||
double low = std::numeric_limits<double>::infinity();
|
||||
double high = -std::numeric_limits<double>::infinity();
|
||||
@@ -3943,8 +3981,8 @@ namespace jdi
|
||||
std::size_t highOrder = 0;
|
||||
bool broken = false;
|
||||
|
||||
// 一列至少覆盖两个分组时才用分组层(否则直接扫原始采样更准且更省)。
|
||||
if (hasCoarse && (i1 - i0) >= 2 * coarseStride)
|
||||
// (一)落在本列区间内的采样点。
|
||||
if (i1 > i0 && hasCoarse && (i1 - i0) >= 2 * coarseStride)
|
||||
{
|
||||
const std::size_t firstBucket = i0 / coarseStride;
|
||||
const std::size_t lastBucket =
|
||||
@@ -3971,7 +4009,7 @@ namespace jdi
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (i1 > i0)
|
||||
{
|
||||
for (std::size_t index = i0; index < i1; ++index)
|
||||
{
|
||||
@@ -3995,9 +4033,48 @@ namespace jdi
|
||||
}
|
||||
}
|
||||
|
||||
// (二)跨越列边界的两段折线在边界处的取值。
|
||||
//
|
||||
// 波形按相邻采样点之间的**直线**绘制,而包络是按像素列聚合的。采样间隔
|
||||
// 可能大于一个像素列(本仓库的 Problem_Data.csv 相邻采样间隔约 27µs 且不固定,
|
||||
// 放大到 10ms/大格时一个像素列约 60µs),此时一次跃变的两个端点会分别落在
|
||||
// 相邻两列里:对每一列来说"落在本列内的采样点"都在同一边,于是这段竖向折线
|
||||
// 在两侧的包络里都不出现 → 波形在这些跃变处被整段丢掉,看上去就是"断开"。
|
||||
//
|
||||
// 把列边界处的折线取值计入本列包络即可消除该现象:折线在列内是线性的,
|
||||
// 其极值必然出现在采样点或列边界上,因此「列内采样点 + 两侧边界取值」就是
|
||||
// 该列包络的精确解,且相邻两列在边界处取到同一个值,包络带严丝合缝。
|
||||
double boundary = 0.0;
|
||||
if (PolylineValueAt(time, values, count, i0, t0, boundary))
|
||||
{
|
||||
if (boundary < low)
|
||||
{
|
||||
low = boundary;
|
||||
lowOrder = (i0 > 0) ? (i0 - 1) : 0;
|
||||
}
|
||||
if (boundary > high)
|
||||
{
|
||||
high = boundary;
|
||||
highOrder = (i0 > 0) ? (i0 - 1) : 0;
|
||||
}
|
||||
}
|
||||
if (PolylineValueAt(time, values, count, i1, t1, boundary))
|
||||
{
|
||||
if (boundary < low)
|
||||
{
|
||||
low = boundary;
|
||||
lowOrder = i1;
|
||||
}
|
||||
if (boundary > high)
|
||||
{
|
||||
high = boundary;
|
||||
highOrder = i1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(low <= high))
|
||||
{
|
||||
// 本列全是无效值。
|
||||
// 本列没有任何有效取值:折线尚未开始、已经结束,或该处数据缺失。
|
||||
inSegment = false;
|
||||
flushBandSegment();
|
||||
continue;
|
||||
|
||||
Reference in new issue
Block a user