PyPNM / DOCSIS-3.1 / DS-OFDM-ChannelStats
Source Files
- HTML/script:
visual/PyPNM/DOCSIS-3.1/DS-OFDM-ChannelStats.html - JSON sample:
visual/PyPNM/DOCSIS-3.1/DS-OFDM-ChannelStats.json
Preview
Preview is best-effort. Some templates may rely on Postman-specific APIs that are not yet shimmed.
Visualizer HTML/script source
// Postman Visualizer: DOCSIS-3.1/DS-OFDM-ChannelStats
// Last Update: 2026-02-25 06:01:33 MST
// Postman Visualizer - DS OFDM Channel Statistics (Dark Mode)
// Useful visuals (separate graphs per channel; no overlap):
// 1) Per-channel: Active spectrum window (start/end freq) as a horizontal "range bar" on a shared MHz axis
// 2) Per-channel: PLC vs Zero frequency (two vertical bars)
// 3) Per-channel: Reliability counters (PLC Total CW vs PLC Unreliable CW; NCP Total Fields vs NCP CRC Failures) (two small bar charts)
// 4) Summary KPIs: channel count, total active subcarriers, estimated occupied bandwidth, worst CRC failures (PLC/NCP)
const template = `
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #0f1220;
color: #eaeaea;
}
.header {
background-color: #151a2e;
border: 1px solid rgba(255,255,255,0.08);
padding: 16px 16px 12px 16px;
border-radius: 10px;
margin-bottom: 18px;
box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}
.title {
font-size: 18px;
font-weight: bold;
margin: 0 0 10px 0;
color: #ff4d6d;
}
.meta {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 10px;
}
.pill {
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 999px;
padding: 6px 10px;
font-size: 12px;
color: #eaeaea;
}
.pill b { color: #9fb4ff; font-weight: 700; }
.sub {
margin: 0;
font-size: 12px;
color: rgba(234,234,234,0.85);
}
.kpi {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
margin-top: 12px;
}
.kpiItem {
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 10px;
padding: 10px;
}
.kpiItem .k { font-size: 11px; color: rgba(234,234,234,0.8); margin-bottom: 4px; }
.kpiItem .v { font-size: 16px; font-weight: 700; color: #eaeaea; }
.kpiItem .h { margin-top: 4px; font-size: 11px; color: rgba(159,180,255,0.95); }
.deviceInfoCard {
background-color: #151a2e;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 10px;
padding: 14px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}
.deviceInfoCard h3 { margin: 0 0 8px 0; font-size: 14px; color: #9fb4ff; }
.deviceInfoTable { width: 100%; border-collapse: collapse; font-size: 12px; }
.deviceInfoTable th, .deviceInfoTable td { border-bottom: 1px solid rgba(255,255,255,0.08); padding: 8px 6px; text-align: left; white-space: nowrap; }
.deviceInfoTable th { color: #9fb4ff; font-weight: 700; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
.card {
background-color: #151a2e;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 10px;
padding: 14px;
box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}
.card h3 {
margin: 0 0 6px 0;
font-size: 14px;
color: #9fb4ff;
}
.mini {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
margin-top: 10px;
}
.divider {
height: 1px;
background: rgba(255,255,255,0.08);
margin: 14px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
font-size: 12px;
}
th, td {
border-bottom: 1px solid rgba(255,255,255,0.08);
padding: 8px 6px;
text-align: left;
white-space: nowrap;
}
th { color: #9fb4ff; font-weight: 700; }
.warn { color: #ffcc66; }
.bad { color: #ff6b6b; }
</style>
<div class="deviceInfoCard">
<h3>Device Info</h3>
<table class="deviceInfoTable">
<thead>
<tr>
<th>MacAddress</th><th>Model</th><th>Vendor</th><th>SW Version</th><th>HW Version</th><th>Boot ROM</th>
</tr>
</thead>
<tbody>
<tr>
<td class="mono">{{deviceInfo.macAddress}}</td>
<td>{{deviceInfo.MODEL}}</td>
<td>{{deviceInfo.VENDOR}}</td>
<td class="mono">{{deviceInfo.SW_REV}}</td>
<td class="mono">{{deviceInfo.HW_REV}}</td>
<td class="mono">{{deviceInfo.BOOTR}}</td>
</tr>
</tbody>
</table>
</div>
<div class="header">
<div class="title">DS OFDM Channel Statistics</div>
<div class="meta">
<div class="pill"><b>MAC</b> {{mac}}</div>
<div class="pill"><b>Status</b> {{statusText}}</div>
<div class="pill"><b>Channels</b> {{channelCount}}</div>
<div class="pill"><b>Total Active Subcarriers</b> {{kpi.totalActiveSubcarriers}}</div>
</div>
<p class="sub">{{message}}</p>
<div class="kpi">
<div class="kpiItem">
<div class="k">Est. Occupied Bandwidth (per channel)</div>
<div class="v">{{kpi.bandwidthPerChannelMHz}}</div>
<div class="h">(last - first + 1) · spacing</div>
</div>
<div class="kpiItem">
<div class="k">Worst NCP CRC Failures</div>
<div class="v">{{kpi.worstNcpCrc}}</div>
<div class="h">Ch {{kpi.worstNcpCrcCh}}</div>
</div>
<div class="kpiItem">
<div class="k">Worst PLC Unreliable CW</div>
<div class="v">{{kpi.worstPlcUnrel}}</div>
<div class="h">Ch {{kpi.worstPlcUnrelCh}}</div>
</div>
<div class="kpiItem">
<div class="k">Global Active Spectrum Span</div>
<div class="v">{{kpi.globalSpanMHz}}</div>
<div class="h">{{kpi.globalSpanRange}}</div>
</div>
</div>
<div class="divider"></div>
<div class="sub"><b>Global View · Active Spectrum Windows (All Channels)</b></div>
<div class="sub">Each row is one channel. Bar shows active start/end; marker shows PLC frequency.</div>
<canvas id="globalRangeChart" height="120"></canvas>
<div class="divider"></div>
<div class="sub"><b>Quick Table</b></div>
<table>
<thead>
<tr>
<th>Channel</th>
<th>Indicator</th>
<th>Zero Freq (MHz)</th>
<th>Active Start (MHz)</th>
<th>Active End (MHz)</th>
<th>PLC (MHz)</th>
<th>BW (MHz)</th>
</tr>
</thead>
<tbody>
{{#each tableRows}}
<tr>
<td>{{channel}}</td>
<td>{{indicator}}</td>
<td>{{zero}}</td>
<td>{{start}}</td>
<td>{{end}}</td>
<td>{{plc}}</td>
<td>{{bw}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class="grid">
{{#each channels}}
<div class="card">
<h3>{{label}} · Active Spectrum Window</h3>
<p class="sub">Horizontal range bar (start/end) with PLC marker. Uses shared MHz axis for this channel.</p>
<canvas id="range_{{canvas_id}}" height="90"></canvas>
<div class="divider"></div>
<div class="mini">
<div>
<h3>{{label}} · PLC vs Subcarrier Zero Freq</h3>
<p class="sub">Two bars in MHz.</p>
<canvas id="freqs_{{canvas_id}}" height="120"></canvas>
</div>
<div>
<h3>{{label}} · Reliability Counters</h3>
<p class="sub">PLC / NCP errors vs totals.</p>
<canvas id="errs_{{canvas_id}}" height="120"></canvas>
</div>
</div>
</div>
{{/each}}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script>
pm.getData(function (err, value) {
if (err) {
console.error(err);
return;
}
var gridColor = 'rgba(255,255,255,0.12)';
var tickColor = 'rgba(234,234,234,0.85)';
var labelColor = '#eaeaea';
var cRange = 'rgb(54, 162, 235)';
var cPlc = 'rgb(255, 206, 86)';
var cZero = 'rgb(75, 192, 192)';
var cErr = 'rgb(255, 99, 132)';
var rgbaFill = function(rgb, a) {
return rgb.replace('rgb', 'rgba').replace(')', ', ' + a + ')');
};
// Global range chart (one dataset for ranges, one dataset for PLC markers)
(function renderGlobalRanges() {
var canvas = document.getElementById('globalRangeChart');
if (!canvas) return;
var labels = (value.global || {}).labels || [];
var ranges = (value.global || {}).ranges || []; // {x:[start,end], y: index}
var plcPts = (value.global || {}).plcPts || []; // {x: plc, y: index}
new Chart(canvas.getContext('2d'), {
type: 'horizontalBar',
data: {
labels: labels,
datasets: [
{
label: 'Active Window (MHz)',
data: ranges,
backgroundColor: rgbaFill(cRange, 0.45),
borderColor: cRange,
borderWidth: 1
},
{
label: 'PLC (MHz)',
data: plcPts,
backgroundColor: rgbaFill(cPlc, 0.9),
borderColor: cPlc,
borderWidth: 1,
barThickness: 2
}
]
},
options: {
responsive: true,
maintainAspectRatio: true,
legend: { display: true, labels: { fontColor: labelColor } },
tooltips: {
mode: 'nearest',
intersect: false,
callbacks: {
label: function(tooltipItem, data) {
var ds = data.datasets[tooltipItem.datasetIndex];
var v = ds.data[tooltipItem.index];
if (v && v.x && Array.isArray(v.x)) {
return ds.label + ': ' + v.x[0].toFixed(2) + ' - ' + v.x[1].toFixed(2) + ' MHz';
}
if (v && typeof v.x === 'number') {
return ds.label + ': ' + v.x.toFixed(2) + ' MHz';
}
return ds.label + ': ' + tooltipItem.xLabel;
}
}
},
scales: {
xAxes: [{
ticks: { fontColor: tickColor },
gridLines: { color: gridColor },
scaleLabel: { display: true, labelString: 'Frequency (MHz)', fontColor: labelColor }
}],
yAxes: [{
ticks: { fontColor: tickColor },
gridLines: { color: gridColor }
}]
}
}
});
})();
// Per-channel charts
(value.channels || []).forEach(function(ch) {
// Range chart
(function renderRange() {
var canvas = document.getElementById('range_' + ch.canvas_id);
if (!canvas) return;
// Single-row horizontal range bar with PLC marker overlay
var labels = ['Active'];
var ranges = [{ x: [ch.activeStartMHz, ch.activeEndMHz], y: 0 }];
var plcPts = [{ x: ch.plcMHz, y: 0 }];
new Chart(canvas.getContext('2d'), {
type: 'horizontalBar',
data: {
labels: labels,
datasets: [
{
label: 'Active Window (MHz)',
data: ranges,
backgroundColor: rgbaFill(cRange, 0.45),
borderColor: cRange,
borderWidth: 1
},
{
label: 'PLC (MHz)',
data: plcPts,
backgroundColor: rgbaFill(cPlc, 0.9),
borderColor: cPlc,
borderWidth: 1,
barThickness: 2
}
]
},
options: {
responsive: true,
maintainAspectRatio: true,
legend: { display: true, labels: { fontColor: labelColor } },
scales: {
xAxes: [{
ticks: { fontColor: tickColor },
gridLines: { color: gridColor },
scaleLabel: { display: true, labelString: 'Frequency (MHz)', fontColor: labelColor }
}],
yAxes: [{
ticks: { fontColor: tickColor },
gridLines: { color: gridColor }
}]
},
tooltips: {
mode: 'nearest',
intersect: false,
callbacks: {
label: function(tooltipItem, data) {
var ds = data.datasets[tooltipItem.datasetIndex];
var v = ds.data[tooltipItem.index];
if (v && v.x && Array.isArray(v.x)) {
return ds.label + ': ' + v.x[0].toFixed(2) + ' - ' + v.x[1].toFixed(2) + ' MHz';
}
if (v && typeof v.x === 'number') {
return ds.label + ': ' + v.x.toFixed(2) + ' MHz';
}
return ds.label + ': ' + tooltipItem.xLabel;
}
}
}
}
});
})();
// PLC vs Zero frequency (vertical bar)
(function renderFreqs() {
var canvas = document.getElementById('freqs_' + ch.canvas_id);
if (!canvas) return;
new Chart(canvas.getContext('2d'), {
type: 'bar',
data: {
labels: ['Zero Freq', 'PLC Freq'],
datasets: [{
label: 'MHz',
data: [ch.zeroMHz, ch.plcMHz],
backgroundColor: [rgbaFill(cZero, 0.55), rgbaFill(cPlc, 0.55)],
borderColor: [cZero, cPlc],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
legend: { display: false },
scales: {
xAxes: [{
ticks: { fontColor: tickColor },
gridLines: { color: gridColor }
}],
yAxes: [{
ticks: { fontColor: tickColor, beginAtZero: true },
gridLines: { color: gridColor },
scaleLabel: { display: true, labelString: 'Frequency (MHz)', fontColor: labelColor }
}]
}
}
});
})();
// Reliability counters (two grouped bars)
(function renderErrs() {
var canvas = document.getElementById('errs_' + ch.canvas_id);
if (!canvas) return;
new Chart(canvas.getContext('2d'), {
type: 'bar',
data: {
labels: ['PLC Codewords', 'NCP Fields'],
datasets: [
{
label: 'Total',
data: [ch.plcTotalCw, ch.ncpTotalFields],
backgroundColor: rgbaFill(cRange, 0.35),
borderColor: cRange,
borderWidth: 1
},
{
label: 'Errors',
data: [ch.plcUnreliableCw, ch.ncpCrcFailures],
backgroundColor: rgbaFill(cErr, 0.65),
borderColor: cErr,
borderWidth: 1
}
]
},
options: {
responsive: true,
maintainAspectRatio: true,
legend: { display: true, labels: { fontColor: labelColor } },
tooltips: { mode: 'index', intersect: false },
scales: {
xAxes: [{
ticks: { fontColor: tickColor },
gridLines: { color: gridColor }
}],
yAxes: [{
ticks: { fontColor: tickColor, beginAtZero: true },
gridLines: { color: gridColor }
}]
}
}
});
})();
});
});
</script>
`;
function constructVisualizerPayload() {
const r = pm.response.json();
const device = (r.device && typeof r.device === "object") ? r.device : {};
const sys = (device.system_description && typeof device.system_description === "object") ? device.system_description : {};
const mac = ((r.device || {}).mac_address) || 'N/A';
const status = (r.status !== undefined && r.status !== null) ? r.status : 'N/A';
const statusText = status === 0 ? 'Success' : String(status);
const message = r.message ? r.message : '';
const results = Array.isArray(r.results) ? r.results : [];
// Build channels with derived frequency window:
// activeStart = zeroFreq + firstActive * spacing
// activeEnd = zeroFreq + lastActive * spacing
// bandwidth = (last-first+1) * spacing
// All converted to MHz for visuals.
let totalActiveSubcarriers = 0;
let worstNcpCrc = -1;
let worstNcpCrcCh = 'N/A';
let worstPlcUnrel = -1;
let worstPlcUnrelCh = 'N/A';
let globalMinMHz = null;
let globalMaxMHz = null;
const tableRows = [];
const channels = results.map((item, idx) => {
const channelId = (item && item.channel_id !== undefined && item.channel_id !== null) ? item.channel_id : ('idx-' + (idx + 1));
const e = item && item.entry ? item.entry : {};
const zeroHz = Number(e.docsIf31CmDsOfdmChanSubcarrierZeroFreq || 0);
const spacingHz = Number(e.docsIf31CmDsOfdmChanSubcarrierSpacing || 0);
const first = Number(e.docsIf31CmDsOfdmChanFirstActiveSubcarrierNum || 0);
const last = Number(e.docsIf31CmDsOfdmChanLastActiveSubcarrierNum || 0);
const numActive = Number(e.docsIf31CmDsOfdmChanNumActiveSubcarriers || 0);
const plcHz = Number(e.docsIf31CmDsOfdmChanPlcFreq || 0);
const indicator = (e.docsIf31CmDsOfdmChanChanIndicator !== undefined && e.docsIf31CmDsOfdmChanChanIndicator !== null)
? e.docsIf31CmDsOfdmChanChanIndicator
: 'N/A';
const activeStartHz = zeroHz + (first * spacingHz);
const activeEndHz = zeroHz + (last * spacingHz);
const bwHz = (last >= first) ? ((last - first + 1) * spacingHz) : 0;
const zeroMHz = zeroHz / 1e6;
const plcMHz = plcHz / 1e6;
const activeStartMHz = activeStartHz / 1e6;
const activeEndMHz = activeEndHz / 1e6;
const bwMHz = bwHz / 1e6;
const plcTotalCw = Number(e.docsIf31CmDsOfdmChanPlcTotalCodewords || 0);
const plcUnreliableCw = Number(e.docsIf31CmDsOfdmChanPlcUnreliableCodewords || 0);
const ncpTotalFields = Number(e.docsIf31CmDsOfdmChanNcpTotalFields || 0);
const ncpCrcFailures = Number(e.docsIf31CmDsOfdmChanNcpFieldCrcFailures || 0);
totalActiveSubcarriers += numActive;
if (ncpCrcFailures > worstNcpCrc) {
worstNcpCrc = ncpCrcFailures;
worstNcpCrcCh = String(channelId);
}
if (plcUnreliableCw > worstPlcUnrel) {
worstPlcUnrel = plcUnreliableCw;
worstPlcUnrelCh = String(channelId);
}
if (Number.isFinite(activeStartMHz) && Number.isFinite(activeEndMHz)) {
if (globalMinMHz === null || activeStartMHz < globalMinMHz) globalMinMHz = activeStartMHz;
if (globalMaxMHz === null || activeEndMHz > globalMaxMHz) globalMaxMHz = activeEndMHz;
}
tableRows.push({
channel: String(channelId),
indicator: String(indicator),
zero: zeroMHz.toFixed(2),
start: activeStartMHz.toFixed(2),
end: activeEndMHz.toFixed(2),
plc: plcMHz.toFixed(2),
bw: bwMHz.toFixed(2)
});
return {
label: 'OFDM Channel ' + String(channelId),
canvas_id: 'c' + String(idx + 1),
channel_id: String(channelId),
indicator: String(indicator),
zeroMHz,
plcMHz,
activeStartMHz,
activeEndMHz,
bwMHz,
plcTotalCw,
plcUnreliableCw,
ncpTotalFields,
ncpCrcFailures
};
});
// Global chart payload (horizontal range bars)
// Chart.js 2 horizontalBar can accept "floating bars" if values are [start,end] in data,
// but we pass as {x:[start,end], y:index} and labels on y-axis for clarity.
// We keep y labels aligned with array order.
const labels = channels.map(ch => 'Ch ' + ch.channel_id);
const ranges = channels.map((ch, i) => ({ x: [ch.activeStartMHz, ch.activeEndMHz], y: i }));
const plcPts = channels.map((ch, i) => ({ x: ch.plcMHz, y: i }));
const globalSpanMHz = (globalMinMHz !== null && globalMaxMHz !== null) ? (globalMaxMHz - globalMinMHz) : 0;
const globalSpanRange = (globalMinMHz !== null && globalMaxMHz !== null)
? (globalMinMHz.toFixed(2) + ' - ' + globalMaxMHz.toFixed(2) + ' MHz')
: 'N/A';
const kpi = {
totalActiveSubcarriers: String(totalActiveSubcarriers),
bandwidthPerChannelMHz: (channels.length > 0 ? channels[0].bwMHz.toFixed(2) : '0.00') + ' MHz',
worstNcpCrc: String(Math.max(0, worstNcpCrc)),
worstNcpCrcCh: worstNcpCrcCh,
worstPlcUnrel: String(Math.max(0, worstPlcUnrel)),
worstPlcUnrelCh: worstPlcUnrelCh,
globalSpanMHz: globalSpanMHz.toFixed(2) + ' MHz',
globalSpanRange: globalSpanRange
};
return {
deviceInfo: {
macAddress: device.mac_address || "N/A",
MODEL: sys.MODEL || "N/A",
VENDOR: sys.VENDOR || "N/A",
SW_REV: sys.SW_REV || "N/A",
HW_REV: sys.HW_REV || "N/A",
BOOTR: sys.BOOTR || "N/A"
},
mac,
statusText,
message,
channelCount: channels.length,
kpi,
tableRows,
global: { labels, ranges, plcPts },
channels
};
}
pm.visualizer.set(template, constructVisualizerPayload());
Sample JSON payload
{
"system_description": {
"HW_REV": "1.0",
"VENDOR": "LANCity",
"BOOTR": "NONE",
"SW_REV": "1.0.0",
"MODEL": "LCPET-3"
},
"status": 0,
"message": "Successfully retrieved downstream OFDM channel statistics",
"device": {
"mac_address": "aa:bb:cc:dd:ee:ff",
"system_description": {
"HW_REV": "1.0",
"VENDOR": "LANCity",
"BOOTR": "NONE",
"SW_REV": "1.0.0",
"MODEL": "LCPET-3"
}
},
"results": [
{
"index": 48,
"channel_id": 194,
"entry": {
"docsIf31CmDsOfdmChanChannelId": 194,
"docsIf31CmDsOfdmChanChanIndicator": "backupPrimary",
"docsIf31CmDsOfdmChanSubcarrierZeroFreq": 1019600000,
"docsIf31CmDsOfdmChanFirstActiveSubcarrierNum": 296,
"docsIf31CmDsOfdmChanLastActiveSubcarrierNum": 7895,
"docsIf31CmDsOfdmChanNumActiveSubcarriers": 7528,
"docsIf31CmDsOfdmChanSubcarrierSpacing": 25000,
"docsIf31CmDsOfdmChanCyclicPrefix": 256,
"docsIf31CmDsOfdmChanRollOffPeriod": 128,
"docsIf31CmDsOfdmChanPlcFreq": 1150000000,
"docsIf31CmDsOfdmChanNumPilots": 56,
"docsIf31CmDsOfdmChanTimeInterleaverDepth": 16,
"docsIf31CmDsOfdmChanPlcTotalCodewords": 32837417,
"docsIf31CmDsOfdmChanPlcUnreliableCodewords": 0,
"docsIf31CmDsOfdmChanNcpTotalFields": 210146720,
"docsIf31CmDsOfdmChanNcpFieldCrcFailures": 0
}
},
{
"index": 49,
"channel_id": 193,
"entry": {
"docsIf31CmDsOfdmChanChannelId": 193,
"docsIf31CmDsOfdmChanChanIndicator": "backupPrimary",
"docsIf31CmDsOfdmChanSubcarrierZeroFreq": 827600000,
"docsIf31CmDsOfdmChanFirstActiveSubcarrierNum": 296,
"docsIf31CmDsOfdmChanLastActiveSubcarrierNum": 7895,
"docsIf31CmDsOfdmChanNumActiveSubcarriers": 7528,
"docsIf31CmDsOfdmChanSubcarrierSpacing": 25000,
"docsIf31CmDsOfdmChanCyclicPrefix": 256,
"docsIf31CmDsOfdmChanRollOffPeriod": 128,
"docsIf31CmDsOfdmChanPlcFreq": 930000000,
"docsIf31CmDsOfdmChanNumPilots": 56,
"docsIf31CmDsOfdmChanTimeInterleaverDepth": 16,
"docsIf31CmDsOfdmChanPlcTotalCodewords": 32838607,
"docsIf31CmDsOfdmChanPlcUnreliableCodewords": 0,
"docsIf31CmDsOfdmChanNcpTotalFields": 210153952,
"docsIf31CmDsOfdmChanNcpFieldCrcFailures": 0
}
}
]
}