Show code
colors = ({
navy: "#2C3E50",
teal: "#16A085",
orange: "#E67E22",
blue: "#3498DB",
gray: "#7F8C8D",
purple: "#9B59B6",
red: "#E74C3C"
})
// Input controls
viewof headerBytes = Inputs.range([5, 50], {
value: 15,
step: 1,
label: "Header + Trailer (bytes)"
})
viewof payloadBytes = Inputs.range([1, 100], {
value: 2,
step: 1,
label: "Payload (bytes)"
})
viewof messagesPerDay = Inputs.range([1, 288], {
value: 96,
step: 1,
label: "Messages per day"
})
viewof numSensors = Inputs.range([1, 10000], {
value: 1000,
step: 100,
label: "Number of sensors"
})
// Calculations
totalPacketSize = headerBytes + payloadBytes
overheadRatio = (headerBytes / totalPacketSize * 100).toFixed(1)
dataRatio = (payloadBytes / totalPacketSize * 100).toFixed(1)
dailyBytes = messagesPerDay * totalPacketSize
annualBytes = dailyBytes * 365
networkAnnualBytes = annualBytes * numSensors
networkAnnualMB = (networkAnnualBytes / (1024 * 1024)).toFixed(2)
// Display results
html`<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid ${colors.teal}; margin: 20px 0;">
<h3 style="color: ${colors.navy}; margin-top: 0;">Packet Breakdown</h3>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
<div>
<strong style="color: ${colors.navy};">Total Packet Size:</strong><br>
<span style="font-size: 1.8em; color: ${colors.teal}; font-weight: bold;">${totalPacketSize} bytes</span>
</div>
<div>
<strong style="color: ${colors.navy};">Overhead Ratio:</strong><br>
<span style="font-size: 1.8em; color: ${colors.orange}; font-weight: bold;">${overheadRatio}%</span>
</div>
</div>
<div style="margin: 20px 0;">
<div style="display: flex; height: 40px; border-radius: 4px; overflow: hidden;">
<div style="background: ${colors.orange}; width: ${overheadRatio}%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold;">
Header: ${headerBytes}B
</div>
<div style="background: ${colors.teal}; width: ${dataRatio}%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold;">
Data: ${payloadBytes}B
</div>
</div>
</div>
<h3 style="color: ${colors.navy}; margin-top: 20px;">Network Impact</h3>
<ul style="list-style: none; padding: 0;">
<li style="padding: 8px 0; border-bottom: 1px solid #ddd;">
<strong style="color: ${colors.navy};">Per Sensor:</strong>
${dailyBytes.toLocaleString()} bytes/day |
${(annualBytes / 1024).toFixed(0)} KB/year
</li>
<li style="padding: 8px 0; border-bottom: 1px solid #ddd;">
<strong style="color: ${colors.navy};">Network Total (${numSensors.toLocaleString()} sensors):</strong>
${networkAnnualMB} MB/year
</li>
<li style="padding: 8px 0;">
<strong style="color: ${colors.navy};">Cost Estimate:</strong>
$${(networkAnnualMB * 0.001).toFixed(2)}/year
<span style="color: ${colors.gray};">(at $0.001/KB for commercial LoRaWAN networks)</span>
</li>
</ul>
</div>`Show code
{
const width = 600;
const height = 300;
const margin = {top: 20, right: 30, bottom: 50, left: 60};
// Generate data points for different payload sizes
const data = Array.from({length: 50}, (_, i) => {
const payload = i + 1;
const total = headerBytes + payload;
const overhead = (headerBytes / total) * 100;
return {payload, overhead};
});
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.style("background", "#f8f9fa");
const x = d3.scaleLinear()
.domain([0, 50])
.range([margin.left, width - margin.right]);
const y = d3.scaleLinear()
.domain([0, 100])
.range([height - margin.bottom, margin.top]);
// Grid lines
svg.append("g")
.attr("stroke", "#ddd")
.attr("stroke-dasharray", "2,2")
.call(g => g.selectAll("line")
.data(y.ticks(5))
.join("line")
.attr("x1", margin.left)
.attr("x2", width - margin.right)
.attr("y1", d => y(d))
.attr("y2", d => y(d)));
// Line
const line = d3.line()
.x(d => x(d.payload))
.y(d => y(d.overhead))
.curve(d3.curveMonotoneX);
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", colors.teal)
.attr("stroke-width", 3)
.attr("d", line);
// Current point
svg.append("circle")
.attr("cx", x(payloadBytes))
.attr("cy", y(overheadRatio))
.attr("r", 6)
.attr("fill", colors.orange)
.attr("stroke", "white")
.attr("stroke-width", 2);
// Axes
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(10))
.call(g => g.append("text")
.attr("x", width / 2)
.attr("y", 40)
.attr("fill", colors.navy)
.attr("font-weight", "bold")
.attr("text-anchor", "middle")
.text("Payload Size (bytes)"));
svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(5).tickFormat(d => d + "%"))
.call(g => g.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("y", -45)
.attr("fill", colors.navy)
.attr("font-weight", "bold")
.attr("text-anchor", "middle")
.text("Overhead Ratio"));
// Title
svg.append("text")
.attr("x", width / 2)
.attr("y", 15)
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.attr("fill", colors.navy)
.text(`Efficiency vs Payload Size (${headerBytes}B header fixed)`);
return svg.node();
}