dataforth(datasheet): DSCA33/45 accuracy-data reverse-engineering — 54/56 validated
Cracked the DSCA33/45 accuracy-block numeric formatting against the Hoffman originals
(formatAccuracyLineDSCA3345):
- mA-output models store calc (and, for DSCA45, meas) in AMPS -> x1000 to display mA;
DSCA33 stores meas already in display unit (NOT scaled), DSCA45 scales both.
- DSCA33 (AC-RMS): stim/calc/meas UNSIGNED, error signed; stim is AC input to 3 dp.
- DSCA45 (frequency): stim is an UNSIGNED integer Hz; calc/meas/error SIGNED.
- Math.fround on accuracy values (QB single-precision rounding), matching the Final-Test fix.
Final-Test fixes too: leading-zero drop only when the value overflows QB's 6-char field
("-0.0005"->"-.0005", but "-0.750" keeps its zero); spec-less section sub-heads
(Zero-Crossing Input / TTL Input) render with NO status (only Withstand/Hi-Pot get PASS);
DSCA33 prints a "Check List" header after the underline.
slotmap-from-hoffman.js (new): derive slotMaps for the models the staged multi-unit
derivation couldn't (vintage-heavy) by matching the Hoffman _srcSerial original's
Final-Test measured values (at display precision) to the DB STATUS entries. Recovered
all 13 remaining DSCA33 models.
Validation (validate-dsca3345.js, content-normalized byte-compare vs live Hoffman
originals): 54 of 56 models PASS and are marked validated:true (the render gate).
2 holdouts (DSCA33-04A, DSCA33-1891) each have ONE accuracy cert at a rounding boundary
where fround rounds opposite to the original; left UNvalidated -> still render null
(safe). DSCA33-1948 + DSCA45-1746 (24 units) have no Hoffman original.
Gate now OPEN for the 54 validated models (render live); 2 holdouts + the no-template
pair stay null. Publishing the api_uploaded_at IS NULL gap next (never re-pushes the
~7,157 pristine originals).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -522,6 +522,36 @@ function formatAccuracyLine(point, sensorNum, maxIn) {
|
||||
return ' ' + stimStr + ' ' + calcStr + ' ' + measStr + ' ' + errorStr + ' ' + point.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accuracy row for the Hoffman-mined DSCA33/DSCA45 families, whose original software
|
||||
* used different column conventions than the voltage/temp models (verified against the
|
||||
* Hoffman originals):
|
||||
* - mA-output models store calc (and, for DSCA45, meas) in AMPS -> x1000 to display mA.
|
||||
* DSCA33 stores meas already in the display unit (NOT scaled); DSCA45 scales both.
|
||||
* - DSCA33 (AC-RMS) prints stim/calc/meas UNSIGNED (error signed); stim is the AC input
|
||||
* to 3 decimals.
|
||||
* - DSCA45 (frequency input) prints stim as an UNSIGNED integer Hz; calc/meas/error SIGNED.
|
||||
*/
|
||||
function formatAccuracyLineDSCA3345(point, model, accOut) {
|
||||
const scale = /mA/.test(accOut || '') ? 1000 : 1;
|
||||
const isDSCA45 = /^DSCA45/i.test((model || '').trim());
|
||||
// values were computed in QB single precision; recover the single before formatting
|
||||
// so last-digit rounding at the .5 boundary matches the original (Math.fround).
|
||||
const num = (val, decimals, signed) => ((signed && val >= 0) ? '+' : '') + Math.fround(val).toFixed(decimals);
|
||||
let stimStr, calcStr, measStr;
|
||||
if (isDSCA45) {
|
||||
stimStr = num(point.stim, 0, false).padStart(8);
|
||||
calcStr = num(point.calc * scale, 3, true).padStart(7);
|
||||
measStr = num(point.meas * scale, 3, true).padStart(7);
|
||||
} else {
|
||||
stimStr = num(point.stim, 3, false).padStart(8);
|
||||
calcStr = num(point.calc * scale, 3, false).padStart(7);
|
||||
measStr = num(point.meas, 3, false).padStart(7);
|
||||
}
|
||||
const errorStr = num(point.error, 3, true).padStart(8);
|
||||
return ' ' + stimStr + ' ' + calcStr + ' ' + measStr + ' ' + errorStr + ' ' + point.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text at a specific column position (0-indexed) in a string.
|
||||
* Pads with spaces if the string is shorter than the target column.
|
||||
@@ -665,6 +695,10 @@ function generateExactDatasheet(record, specs) {
|
||||
}
|
||||
|
||||
for (const point of parsed.accuracy) {
|
||||
if (dscaTpl && Array.isArray(dscaTpl.accHeader)) {
|
||||
lines.push(formatAccuracyLineDSCA3345(point, record.model_number, dscaTpl.accOut));
|
||||
continue;
|
||||
}
|
||||
lines.push(formatAccuracyLine(point, sensorNum, maxIn));
|
||||
}
|
||||
lines.push('');
|
||||
@@ -709,6 +743,7 @@ function generateExactDatasheet(record, specs) {
|
||||
h2 = setCol(h2, 69, '='.repeat(6));
|
||||
lines.push(h2);
|
||||
|
||||
const is3345 = Array.isArray(dscaTpl.accHeader); // Hoffman-mined DSCA33/45
|
||||
let mi = 0, si = 0;
|
||||
for (const row of dscaTpl.rows) {
|
||||
const spec = (row.spec || '').trim();
|
||||
@@ -719,8 +754,12 @@ function generateExactDatasheet(record, specs) {
|
||||
? formatMeasuredExact(parsed.statusEntries[dscaTpl.slotMap[si++]])
|
||||
: measurements[mi++];
|
||||
if (m) {
|
||||
// measured value right-justified ending at col 38, unit at col 40
|
||||
const v = String(m.valStr);
|
||||
// measured value right-justified ending at col 38, unit at col 40.
|
||||
// DSCA33/45 follow QB's fixed 6-char number field: a value that would
|
||||
// overflow drops its leading zero to fit ("-0.0005" (7) -> "-.0005" (6));
|
||||
// values that already fit (e.g. "-0.750", "0.0000") keep it.
|
||||
let v = String(m.valStr);
|
||||
if (is3345 && v.length > 6) v = v.replace(/^-0\./, '-.');
|
||||
line = setCol(line, 39 - v.length, v);
|
||||
if (su.unit) line = setCol(line, 40, su.unit);
|
||||
}
|
||||
@@ -728,8 +767,12 @@ function generateExactDatasheet(record, specs) {
|
||||
line = setCol(line, 59 - su.valuePart.length, su.valuePart);
|
||||
if (su.unit) line = setCol(line, 60, su.unit);
|
||||
line = setCol(line, 70, m ? m.passFail : 'PASS');
|
||||
} else if (is3345 && !/withstand|hi-?pot/i.test(row.name)) {
|
||||
// DSCA33/45 spec-less rows that are NOT a pass/fail test (e.g. the
|
||||
// "Zero-Crossing Input" / "TTL Input" section sub-heads) carry no status.
|
||||
// (Leave the row as just the name.)
|
||||
} else {
|
||||
// no spec => 240VAC Withstand / Hi-Pot style row: blank measured + PASS
|
||||
// spec-less pass/fail row (240VAC Withstand / Hi-Pot): blank measured + PASS
|
||||
line = setCol(line, 70, 'PASS');
|
||||
}
|
||||
lines.push(line);
|
||||
@@ -830,6 +873,9 @@ function generateExactDatasheet(record, specs) {
|
||||
lines.push(setCol(TAB5 + 'Pins Straight: _____', 44, 'Module Header: _____'));
|
||||
lines.push('');
|
||||
lines.push(setCol(TAB5 + 'Tested by: _____________', 44, 'QC: _______________'));
|
||||
} else if (/^DSCA33/i.test((record.model_number || '').trim())) {
|
||||
// DSCA33 originals print just the centered "Check List" header (no items).
|
||||
lines.push(' Check List');
|
||||
} else if (family !== 'DSCA') {
|
||||
lines.push(' Check List');
|
||||
lines.push('');
|
||||
|
||||
Reference in New Issue
Block a user