Correct downsampling
This commit is contained in:
@@ -19,8 +19,9 @@ let activeConfig = {
|
|||||||
let lowRateState = {
|
let lowRateState = {
|
||||||
accMin: 4096,
|
accMin: 4096,
|
||||||
accMax: 0,
|
accMax: 0,
|
||||||
count: 0,
|
accMax: 0,
|
||||||
targetCount: 1
|
progress: 0.0,
|
||||||
|
targetCount: 1.0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Approximate full-scale voltages for ESP32C6/ESP32 ADC attenuations
|
// Approximate full-scale voltages for ESP32C6/ESP32 ADC attenuations
|
||||||
@@ -74,7 +75,8 @@ function calculateTimeOffset(offsetX, effectiveSampleRate) {
|
|||||||
function resetLowRateState() {
|
function resetLowRateState() {
|
||||||
lowRateState.accMin = 4096;
|
lowRateState.accMin = 4096;
|
||||||
lowRateState.accMax = 0;
|
lowRateState.accMax = 0;
|
||||||
lowRateState.count = 0;
|
lowRateState.accMax = 0;
|
||||||
|
// NOTE: We do NOT reset progress to maintain fractional phase alignment across packets
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to schedule WebSocket reconnection
|
// Helper function to schedule WebSocket reconnection
|
||||||
@@ -227,21 +229,31 @@ function processData(/** @type Uint16Array */newData) {
|
|||||||
// Passthrough mode
|
// Passthrough mode
|
||||||
pushToBuffer(newData);
|
pushToBuffer(newData);
|
||||||
} else {
|
} else {
|
||||||
// Accumulation mode (Peak Detect)
|
// Accumulation mode (Peak Detect) with Fractional Resampling
|
||||||
let pointsToPush = new Uint16Array(newData.length * 2); // Worst case
|
let pointsToPush = new Uint16Array(newData.length * 2); // Worst case
|
||||||
let idx = 0;
|
let idx = 0;
|
||||||
|
|
||||||
|
// We add 1.0 "samples worth" of progress for each input sample.
|
||||||
|
// When progress >= targetCount, we have enough input density to emit an output point.
|
||||||
|
// We subtract targetCount (rather than reset to 0) to preserve fractional error (dither/phase).
|
||||||
|
|
||||||
for (const val of newData) {
|
for (const val of newData) {
|
||||||
if (val < lowRateState.accMin) lowRateState.accMin = val;
|
if (val < lowRateState.accMin) lowRateState.accMin = val;
|
||||||
if (val > lowRateState.accMax) lowRateState.accMax = val;
|
if (val > lowRateState.accMax) lowRateState.accMax = val;
|
||||||
lowRateState.count++;
|
|
||||||
|
|
||||||
if (lowRateState.count >= lowRateState.targetCount) {
|
lowRateState.progress += 1.0;
|
||||||
|
|
||||||
|
if (lowRateState.progress >= lowRateState.targetCount) {
|
||||||
// Push min and max to draw a vertical line
|
// Push min and max to draw a vertical line
|
||||||
pointsToPush[idx++] = lowRateState.accMin;
|
pointsToPush[idx++] = lowRateState.accMin;
|
||||||
pointsToPush[idx++] = lowRateState.accMax;
|
pointsToPush[idx++] = lowRateState.accMax;
|
||||||
|
|
||||||
// Reset
|
// Reset Min/Max for next window
|
||||||
resetLowRateState();
|
lowRateState.accMin = 4096;
|
||||||
|
lowRateState.accMax = 0;
|
||||||
|
|
||||||
|
// Subtract one full window's worth of progress
|
||||||
|
lowRateState.progress -= lowRateState.targetCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (idx > 0) {
|
if (idx > 0) {
|
||||||
@@ -361,7 +373,6 @@ function draw() {
|
|||||||
ctx.strokeStyle = '#4ade80';
|
ctx.strokeStyle = '#4ade80';
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
|
|
||||||
const step = w / (maxPoints - 1);
|
|
||||||
const maxAdcVal = 4096; // 12-bit fixed scale
|
const maxAdcVal = 4096; // 12-bit fixed scale
|
||||||
|
|
||||||
// Trigger values
|
// Trigger values
|
||||||
@@ -374,6 +385,7 @@ function draw() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let step = w / (maxPoints - 1);
|
||||||
drawData.forEach((val, i) => {
|
drawData.forEach((val, i) => {
|
||||||
const x = i * step;
|
const x = i * step;
|
||||||
const y = h - (val / maxAdcVal * h);
|
const y = h - (val / maxAdcVal * h);
|
||||||
|
|||||||
Reference in New Issue
Block a user