From 6eaa1985aa080feaebb33306cd590577ec26cecb Mon Sep 17 00:00:00 2001 From: Jan Hoffmann Date: Fri, 27 Mar 2026 18:53:32 +0100 Subject: [PATCH] Fix SFP diagnostic monitoring data in web interface If the module is not internally calibrated, it is necessary to apply calibration values to get meaningful results. --- html/main.js | 82 ++++++++++++++++++++++++++++++++++++++++++++--- httpd/page_impl.c | 13 ++++++++ 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/html/main.js b/html/main.js index d9ed749..8eefa01 100644 --- a/html/main.js +++ b/html/main.js @@ -40,6 +40,78 @@ function drawPorts() { } } +function parseUint16(val) { + return parseInt(val, 16) & 0xffff; +} + +function parseInt16(val) { + let valInt = parseInt(val, 16); + let num = valInt & 0x7fff; + if (valInt & 0x8000) { + return num - 0x8000; + } + return num; +} + +function applyCalibrationSlopeOffset(val, cal) { + if (typeof cal !== 'string') { + return val; + } + if (cal.startsWith("0x")) { + cal = cal.substring(2); + } + if (cal.length != 8) { + return val; + } + let slope = parseUint16(cal.substring(0, 4)) / 256; + let offset = parseInt16(cal.substring(4, 8)); + return slope * val + offset; +} + +function applyRxPowerCalibration(val, cal) { + if (typeof cal !== 'string') { + return val; + } + if (cal.startsWith("0x")) { + cal = cal.substring(2); + } + if (cal.length != 40) { + return val; + } + let bytes = cal.match(/.{1,2}/g).map(function (x) { return parseInt(x, 16); }); + let view = new DataView(new Uint8Array(bytes).buffer); + return view.getFloat32(0) * Math.pow(val, 4) + + view.getFloat32(4) * Math.pow(val, 3) + + view.getFloat32(8) * Math.pow(val, 2) + + view.getFloat32(12) * val + + view.getFloat32(16); +} + +function decodeSfpTemp(val, cal) { + let temp = parseInt16(val); + return applyCalibrationSlopeOffset(temp, cal) / 256; +} + +function decodeSfpVcc(val, cal) { + let vcc = parseUint16(val); + return applyCalibrationSlopeOffset(vcc, cal) / 10000; +} + +function decodeSfpTxBias(val, cal) { + let bias = parseUint16(val); + return applyCalibrationSlopeOffset(bias, cal) / 500; +} + +function decodeSfpTxPower(val, cal) { + let txPower = parseUint16(val); + return applyCalibrationSlopeOffset(txPower, cal) / 10000; +} + +function decodeSfpRxPower(val, cal) { + let rxPower = parseUint16(val); + return applyRxPowerCalibration(rxPower, cal) / 10000; +} + function update(callback) { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { @@ -98,13 +170,13 @@ function update(callback) { iHTML += "Model:" + p.sfp_model + ""; iHTML += "Serial:" + p.sfp_serial + ""; if (hasExtendedStatus) { - iHTML += "Temp:" + (Number(p.sfp_temp) >> 8) + "." + ((Number(p.sfp_temp) & 0xff)/256.0 * 100).toFixed(0) + " ℃"; - iHTML += "Vcc:" + (Number(p.sfp_vcc) / 10000.0).toFixed(2) + " V"; + iHTML += "Temp:" + decodeSfpTemp(p.sfp_temp, p.sfp_temp_cal).toFixed(2) + " ℃"; + iHTML += "Vcc:" + decodeSfpVcc(p.sfp_vcc, p.sfp_vcc_cal).toFixed(2) + " V"; iHTML += "TX-Fault:" + (Boolean(Number(p.sfp_state) & 0x4)) + ""; iHTML += "TX-Disabled:" + (Boolean(Number(p.sfp_state) & 0x80)) + ""; - iHTML += "TX-Bias:" + (Number(p.sfp_txbias) / 500.0).toFixed(1) + " mA"; - iHTML += "TX-Power:" + (Number(p.sfp_txpower) / 10000.0).toFixed(3) + " mW"; - iHTML += "RX-Power:" + (Number(p.sfp_rxpower) / 10000.0).toFixed(3) + " mW"; + iHTML += "TX-Bias:" + decodeSfpTxBias(p.sfp_txbias, p.sfp_txbias_cal).toFixed(1) + " mA"; + iHTML += "TX-Power:" + decodeSfpTxPower(p.sfp_txpower, p.sfp_txpower_cal).toFixed(3) + " mW"; + iHTML += "RX-Power:" + decodeSfpRxPower(p.sfp_rxpower, p.sfp_rxpower_cal).toFixed(3) + " mW"; } // Not all devices & modules have LOS pin... const rx_los_pin = p.sfp_los !== null ? Boolean(Number(p.sfp_los)) : null; diff --git a/httpd/page_impl.c b/httpd/page_impl.c index c40dca3..b49f356 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -651,6 +651,19 @@ void send_status(void) sfp_send_data(machine.is_sfp[i] - 1, 230, 2); slen += strtox(outbuf + slen,"\",\"sfp_rxpower\":\"0x"); sfp_send_data(machine.is_sfp[i] - 1, 232, 2); + if (sfp_options[machine.is_sfp[i]-1] & 0x10) { + slen += strtox(outbuf + slen,"\",\"sfp_temp_cal\":\"0x"); + sfp_send_data(machine.is_sfp[i] - 1, 212, 4); + slen += strtox(outbuf + slen,"\",\"sfp_vcc_cal\":\"0x"); + sfp_send_data(machine.is_sfp[i] - 1, 216, 4); + slen += strtox(outbuf + slen,"\",\"sfp_txbias_cal\":\"0x"); + sfp_send_data(machine.is_sfp[i] - 1, 204, 4); + slen += strtox(outbuf + slen,"\",\"sfp_txpower_cal\":\"0x"); + sfp_send_data(machine.is_sfp[i] - 1, 208, 4); + slen += strtox(outbuf + slen,"\",\"sfp_rxpower_cal\":\"0x"); + sfp_send_data(machine.is_sfp[i] - 1, 184, 16); + sfp_send_data(machine.is_sfp[i] - 1, 200, 4); + } slen += strtox(outbuf + slen,"\",\"sfp_state\":\"0x"); sfp_send_data(machine.is_sfp[i] - 1, 238, 1); }