PyPNM / MultiCapture / ChannelEstimation / Ofdm-ChannelEstimation-Analysis-LTE-Detection-Phase-Slope
Source Files
- HTML/script:
visual/PyPNM/MultiCapture/ChannelEstimation/Ofdm-ChannelEstimation-Analysis-LTE-Detection-Phase-Slope.html - JSON sample:
visual/PyPNM/MultiCapture/ChannelEstimation/Ofdm-ChannelEstimation-Analysis-LTE-Detection-Phase-Slope.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: MultiCapture/ChannelEstimation/Ofdm-ChannelEstimation-Analysis-LTE-Detection-Phase-Slope
// Last Update: 2026-03-01 14:10:00 MST
const template = `
<style>
body { background:#0b0b0b; color:#e8e8e8; font-family:Arial,sans-serif; margin:0; padding:16px; }
.container { max-width: 1500px; margin: 0 auto; }
.header-row { display:grid; grid-template-columns: 1fr auto 1fr; align-items:center; gap:8px; margin-bottom:12px; }
.page-title { grid-column:2; text-align:center; margin:0; color:#f2f2f2; font-size:22px; font-weight:700; }
.capture-time { grid-column:3; justify-self:end; font-size:12px; color:#d7deec; background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.08); border-radius:999px; padding:6px 10px; white-space:nowrap; }
.device-info { background:#151515; padding:14px; margin-bottom:14px; border-radius:10px; border:1px solid #2a2a2a; }
.table-wrap { overflow-x:auto; border:1px solid rgba(255,255,255,0.08); border-radius:10px; }
.table-title { font-size:11px; text-transform:uppercase; letter-spacing:.7px; padding:10px 12px; background:rgba(255,255,255,0.03); border-bottom:1px solid rgba(255,255,255,0.08); color:#dbe3ff; }
.table { width:100%; min-width:720px; border-collapse:collapse; }
.table th { text-align:left; white-space:nowrap; padding:9px 12px; font-size:11px; text-transform:uppercase; letter-spacing:.45px; color:#dbe3ff; background:rgba(255,255,255,0.03); }
.table td { padding:10px 12px; font-size:12px; color:#fff; white-space:nowrap; border-top:1px solid rgba(255,255,255,0.08); }
.mono { font-family:Consolas,"Liberation Mono",Menlo,monospace; }
.chart-container { background:#202020; border:1px solid #303030; border-radius:10px; padding:14px; margin-bottom:14px; }
.chart-title { margin:0 0 10px 0; color:#5a6fd8; text-align:center; font-size:14px; font-weight:700; }
.chart-wrap { position:relative; height:320px; }
.chart-canvas { display:block; width:100% !important; height:100% !important; box-sizing:border-box; background:#1a1a1a; border:1px solid #4d4d4d; border-radius:6px; }
.summary-table { width:100%; border-collapse:collapse; margin-top:10px; }
.summary-table th, .summary-table td { border:1px solid rgba(255,255,255,0.08); padding:8px 10px; font-size:12px; }
.summary-table th { background:rgba(255,255,255,0.03); color:#dbe3ff; }
.chip { display:inline-block; padding:2px 8px; border-radius:999px; font-size:11px; border:1px solid rgba(255,255,255,0.15); }
.chip-high { color:#ff8f8f; border-color:rgba(255,107,107,0.45); background:rgba(255,107,107,0.08); }
.chip-low { color:#9be8c7; border-color:rgba(57,194,142,0.45); background:rgba(57,194,142,0.08); }
</style>
<div class="container">
<div class="header-row">
<div class="page-title">OFDM Channel Estimation LTE Detection (Phase Slope)</div>
{{#if captureTime}}<div class="capture-time">Capture Time: {{captureTime}}</div>{{/if}}
</div>
{{#if showDeviceInfo}}
<div class="device-info">
<div class="table-wrap">
<div class="table-title">Device Info</div>
<table class="table">
<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>
{{/if}}
<div class="chart-container">
<div class="chart-title">Anomaly Count By Channel</div>
<div class="chart-wrap"><canvas id="anomalyCountChart" class="chart-canvas"></canvas></div>
</div>
<div class="chart-container">
<div class="chart-title">Per-Channel Threshold And Bin-Width Summary</div>
<table class="summary-table">
<thead>
<tr><th>Channel</th><th>Anomaly Count</th><th>Threshold</th><th>Bin Widths (Hz)</th><th>Severity</th></tr>
</thead>
<tbody>
{{#each rows}}
<tr>
<td>{{channelId}}</td>
<td>{{anomalyCount}}</td>
<td>{{threshold}}</td>
<td class="mono">{{binWidths}}</td>
<td>{{{severityHtml}}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
<script>
pm.getData(function (err, value) {
if (err || !value) return;
const textColor = '#e0e0e0';
const gridColor = 'rgba(255,255,255,0.1)';
const labels = (value.rows || []).map(r => String(r.channelId));
const counts = (value.rows || []).map(r => Number(r.anomalyCount) || 0);
const ctx = document.getElementById('anomalyCountChart');
if (!ctx) return;
new Chart(ctx.getContext('2d'), {
type: 'bar',
data: {
labels,
datasets: [{
label: 'Anomaly Count',
data: counts,
backgroundColor: 'rgba(90,111,216,0.45)',
borderColor: '#5a6fd8',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { labels: { color: textColor } } },
scales: {
x: { ticks: { color: textColor }, grid: { color: gridColor }, title: { display: true, text: 'Channel', color: textColor } },
y: { ticks: { color: textColor, precision: 0 }, grid: { color: gridColor }, title: { display: true, text: 'Anomaly Count', color: textColor } }
}
}
});
});
</script>
`;
function sanitizeMac(value, fallback) {
var fb = (fallback === undefined || fallback === null) ? 'N/A' : fallback;
if (value === undefined || value === null) return fb;
var text = String(value).trim();
if (!text) return fb;
var compact = text.replace(/[^0-9a-f]/gi, '').toLowerCase();
if (compact.length !== 12) return text;
return compact.match(/.{1,2}/g).join(':');
}
function sanitizeSystemDescription(_sys) {
var sys = (_sys && typeof _sys === 'object') ? _sys : {};
function present(v) { return v !== undefined && v !== null && String(v).trim() !== ''; }
return {
MODEL: present(sys.MODEL) ? String(sys.MODEL).trim() : 'LCPET-3',
VENDOR: present(sys.VENDOR) ? String(sys.VENDOR).trim() : 'LANCity',
SW_REV: present(sys.SW_REV) ? String(sys.SW_REV).trim() : '1.0.0',
HW_REV: present(sys.HW_REV) ? String(sys.HW_REV).trim() : '1.0',
BOOTR: present(sys.BOOTR) ? String(sys.BOOTR).trim() : 'NONE'
};
}
function formatCaptureTime(raw) {
if (raw === undefined || raw === null || raw === '') return null;
if (typeof raw === 'number' && isFinite(raw)) {
var ms = raw > 1e12 ? raw : raw * 1000;
var d = new Date(ms);
if (isNaN(d.getTime())) return null;
return d.toISOString().slice(0, 19).replace('T', ' ') + ' UTC';
}
return String(raw);
}
function constructVisualizerPayload() {
const response = pm.response.json();
const rawData = (response && response.data && typeof response.data === 'object') ? response.data : {};
const results = Array.isArray(rawData.results) ? rawData.results : [];
const rows = results.map((r, idx) => {
const anomalies = Array.isArray(r.anomalies) ? r.anomalies : [];
const threshold = (typeof r.threshold === 'number' && isFinite(r.threshold)) ? r.threshold : null;
const binWidths = Array.isArray(r.bin_widths) ? r.bin_widths : [];
const anomalyCount = anomalies.filter(v => typeof v === 'number' && isFinite(v)).length;
const severityHigh = anomalyCount > 5;
return {
channelId: (r.channel_id !== undefined && r.channel_id !== null) ? r.channel_id : (idx + 1),
anomalyCount: anomalyCount,
threshold: threshold === null ? 'N/A' : threshold.toExponential(3),
binWidths: binWidths.length ? binWidths.join(', ') : 'N/A',
severityHtml: severityHigh
? '<span class="chip chip-high">High</span>'
: '<span class="chip chip-low">Low</span>'
};
}).sort((a, b) => Number(a.channelId) - Number(b.channelId));
const deviceInfo = sanitizeSystemDescription(response.system_description || {});
return {
rows,
captureTime: formatCaptureTime(((rawData.pnm_header || {}).capture_time) || response.capture_time),
showDeviceInfo: true,
deviceInfo: {
macAddress: sanitizeMac(response.mac_address, 'N/A'),
MODEL: deviceInfo.MODEL,
VENDOR: deviceInfo.VENDOR,
SW_REV: deviceInfo.SW_REV,
HW_REV: deviceInfo.HW_REV,
BOOTR: deviceInfo.BOOTR
}
};
}
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,
"mac_address": "aa:bb:cc:dd:ee:ff",
"message": "Analysis LTE_DETECTION_PHASE_SLOPE completed",
"data": {
"analysis_type": "LTE_DETECTION_PHASE_SLOPE",
"results": [
{
"channel_id": 193,
"anomalies": [
1.2e-09,
1.8e-09,
1.1e-09
],
"threshold": 1e-09,
"bin_widths": [
1000000,
500000,
100000
]
},
{
"channel_id": 194,
"anomalies": [
9.8e-10
],
"threshold": 1e-09,
"bin_widths": [
1000000,
500000,
100000
]
}
]
}
}