{"spec_id":"scatter-constellation-diagram","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// scatter-constellation-diagram: Digital Modulation Constellation Diagram\n// Library: highcharts 12.6.0 | JavaScript 22.22.3\n// Quality: 87/100 | Created: 2026-06-18\n//# anyplot-orientation: square\n// anyplot.ai\n// scatter-constellation-diagram: Digital Modulation Constellation Diagram\n// Library: Highcharts 12.6.0 | Node 22\n// License: Highcharts — commercial license, free for non-commercial use (highcharts.com/license)\n// Quality: pending | Created: 2026-06-18\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Deterministic LCG for reproducible Gaussian noise (Box-Muller)\nlet seed = 42195;\nfunction lcg() {\n    seed = (1664525 * seed + 1013904223) >>> 0;\n    return seed / 0x100000000;\n}\nfunction randn() {\n    const u1 = lcg() || 1e-10;\n    const u2 = lcg();\n    return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\n// 16-QAM ideal constellation points at {-3,-1,+1,+3} × {-3,-1,+1,+3}\nconst LEVELS = [-3, -1, 1, 3];\nconst idealPoints = [];\nfor (const iVal of LEVELS) {\n    for (const qVal of LEVELS) {\n        idealPoints.push({ x: iVal, y: qVal });\n    }\n}\n\n// Received symbols: 64 per ideal point with additive Gaussian noise (SNR ≈ 20 dB)\nconst NOISE_STD = 0.12;\nconst PER_POINT = 64;\nconst received = [];\nlet sumSqErr = 0;\nfor (const { x: ip, y: qp } of idealPoints) {\n    for (let k = 0; k < PER_POINT; k++) {\n        const iRx = ip + randn() * NOISE_STD;\n        const qRx = qp + randn() * NOISE_STD;\n        received.push({ x: iRx, y: qRx });\n        sumSqErr += (iRx - ip) ** 2 + (qRx - qp) ** 2;\n    }\n}\n\n// EVM = sqrt(mean squared error / mean signal power) × 100%\n// Mean signal power for 16-QAM at ±1,±3: mean(I²+Q²) = 10\nconst AVG_SIGNAL_POWER = 10;\nconst evmPct = (Math.sqrt(sumSqErr / received.length / AVG_SIGNAL_POWER) * 100).toFixed(1);\n\n// Decision boundary positions (midpoints between adjacent symbol levels)\nconst BOUNDARIES = [-2, 0, 2];\n\nconst chart = Highcharts.chart('container', {\n    chart: {\n        type: 'scatter',\n        backgroundColor: 'transparent',\n        plotBorderWidth: 0,\n        animation: false,\n        style: { fontFamily: 'inherit' },\n    },\n    credits: { enabled: false },\n    colors: t.palette,\n    title: {\n        text: 'scatter-constellation-diagram · javascript · highcharts · anyplot.ai',\n        style: { color: t.ink, fontSize: '21px', fontWeight: '600' },\n    },\n    subtitle: {\n        text: '16-QAM Constellation · SNR ≈ 20 dB',\n        style: { color: t.inkSoft, fontSize: '14px' },\n    },\n    xAxis: {\n        title: {\n            text: 'In-Phase (I)',\n            style: { color: t.inkSoft, fontSize: '16px' },\n        },\n        min: -4.5,\n        max: 4.5,\n        tickInterval: 1,\n        lineColor: t.inkSoft,\n        tickColor: t.inkSoft,\n        gridLineWidth: 0,\n        labels: { style: { color: t.inkSoft, fontSize: '14px' } },\n        plotLines: BOUNDARIES.map(val => ({\n            value: val,\n            color: t.inkSoft,\n            dashStyle: 'Dash',\n            width: 1.2,\n            zIndex: 2,\n        })),\n    },\n    yAxis: {\n        title: {\n            text: 'Quadrature (Q)',\n            style: { color: t.inkSoft, fontSize: '16px' },\n        },\n        min: -4.5,\n        max: 4.5,\n        tickInterval: 1,\n        lineColor: t.inkSoft,\n        tickColor: t.inkSoft,\n        gridLineWidth: 0,\n        labels: { style: { color: t.inkSoft, fontSize: '14px' } },\n        plotLines: BOUNDARIES.map(val => ({\n            value: val,\n            color: t.inkSoft,\n            dashStyle: 'Dash',\n            width: 1.2,\n            zIndex: 2,\n        })),\n    },\n    legend: {\n        itemStyle: { color: t.inkSoft, fontSize: '14px' },\n        itemHoverStyle: { color: t.ink },\n        backgroundColor: t.elevatedBg,\n        borderColor: t.grid,\n        borderRadius: 3,\n        borderWidth: 1,\n    },\n    plotOptions: {\n        series: { animation: false, enableMouseTracking: false },\n        scatter: { marker: { symbol: 'circle' } },\n    },\n    series: [\n        {\n            name: 'Received Symbols',\n            data: received,\n            color: t.palette[0],\n            marker: {\n                radius: 3,\n                fillColor: t.palette[0] + '66',\n                lineWidth: 0,\n            },\n        },\n        {\n            name: 'Ideal Points',\n            data: idealPoints,\n            color: t.palette[4],\n            marker: {\n                symbol: 'diamond',\n                radius: 11,\n                fillColor: t.palette[4],\n                lineWidth: 2,\n                lineColor: t.elevatedBg,\n            },\n        },\n    ],\n});\n\n// EVM annotation placed directly on the plot area via Highcharts SVG renderer\nchart.renderer.label(\n    `EVM = ${evmPct}%`,\n    chart.plotLeft + 8,\n    chart.plotTop + 8\n).css({\n    color: t.inkSoft,\n    fontSize: '13px',\n    fontWeight: '600',\n    fontFamily: 'inherit',\n}).attr({\n    fill: t.elevatedBg,\n    stroke: t.grid,\n    'stroke-width': 1,\n    padding: 6,\n    r: 3,\n    zIndex: 5,\n}).add();\n"}