{"spec_id":"scatter-constellation-diagram","library":"echarts","language":"javascript","code":"// anyplot.ai\n// scatter-constellation-diagram: Digital Modulation Constellation Diagram\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 90/100 | Created: 2026-06-18\n//# anyplot-orientation: square\n// anyplot.ai\n// scatter-constellation-diagram: Digital Modulation Constellation Diagram\n// Library: echarts 5.5.1 | JavaScript 22\n// Quality: pending | Created: 2026-06-18\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Seeded LCG PRNG — reproducible data without Math.random()\nfunction mkRng(seed) {\n  let s = seed >>> 0;\n  return function () {\n    s = (Math.imul(s, 1664525) + 1013904223) >>> 0;\n    return s / 4294967296;\n  };\n}\n\n// Box-Muller: uniform → Gaussian\nfunction gaussian(rng, sigma) {\n  const u1 = rng() + 1e-10;\n  const u2 = rng();\n  return sigma * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\n// 16-QAM ideal constellation: 16 points on {-3, -1, +1, +3}² grid\nconst ampLevels = [-3, -1, 1, 3];\nconst idealPoints = [];\nfor (const q of ampLevels) {\n  for (const i of ampLevels) {\n    idealPoints.push([i, q]);\n  }\n}\n\n// Received symbols: 32 per ideal point (512 total) with AWGN noise (noiseStd = 0.12)\nconst rng = mkRng(42);\nconst noiseStd = 0.12;\nconst symbolsPerPoint = 32;\nconst received = [];\nlet totalErrorPower = 0;\n\nfor (const [i0, q0] of idealPoints) {\n  for (let k = 0; k < symbolsPerPoint; k++) {\n    const ni = gaussian(rng, noiseStd);\n    const nq = gaussian(rng, noiseStd);\n    received.push([i0 + ni, q0 + nq]);\n    totalErrorPower += ni * ni + nq * nq;\n  }\n}\n\n// EVM = sqrt(mean error power / mean symbol power) × 100%\n// 16-QAM mean symbol power: mean(I²+Q²) for I,Q ∈ {±1,±3} = 2×(1+9)/2 = 10\nconst evm = (Math.sqrt(totalErrorPower / received.length / 10) * 100).toFixed(1);\n\n// Decision boundary lines at ±2 and 0 (midpoints between adjacent symbol levels)\nconst boundaryLines = [\n  { xAxis: -2 }, { xAxis: 0 }, { xAxis: 2 },\n  { yAxis: -2 }, { yAxis: 0 }, { yAxis: 2 },\n];\n\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n\n  title: {\n    text: \"scatter-constellation-diagram · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 22,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: \"bold\" },\n  },\n\n  legend: {\n    data: [\"Received Symbols\", \"Ideal Points\"],\n    top: 58,\n    left: \"center\",\n    orient: \"horizontal\",\n    textStyle: { color: t.inkSoft, fontSize: 14 },\n    itemGap: 32,\n  },\n\n  // Square grid: left+right = top+bottom = 200 → 1000×1000 CSS plot area\n  // Equal I/Q range [-4.5, 4.5] on a 1000×1000 area ensures equal aspect ratio\n  grid: { left: 120, right: 80, top: 100, bottom: 100 },\n\n  xAxis: {\n    type: \"value\",\n    name: \"In-Phase (I)\",\n    nameLocation: \"middle\",\n    nameGap: 50,\n    nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n    min: -4.5,\n    max: 4.5,\n    interval: 1,\n    axisLabel: { color: t.inkSoft, fontSize: 13 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: false },\n  },\n\n  yAxis: {\n    type: \"value\",\n    name: \"Quadrature (Q)\",\n    nameLocation: \"middle\",\n    nameGap: 60,\n    nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n    min: -4.5,\n    max: 4.5,\n    interval: 1,\n    axisLabel: { color: t.inkSoft, fontSize: 13 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: false },\n  },\n\n  series: [\n    {\n      name: \"Received Symbols\",\n      type: \"scatter\",\n      data: received,\n      symbolSize: 7,\n      itemStyle: {\n        color: t.palette[0],  // #009E73 brand green — always first series (Imprint palette)\n        opacity: 0.55,\n      },\n      // Decision boundary grid overlaid on the received-symbols series\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        label: { show: false },\n        lineStyle: { type: \"dashed\", color: t.grid, width: 1.5 },\n        data: boundaryLines,\n      },\n    },\n    {\n      name: \"Ideal Points\",\n      type: \"scatter\",\n      data: idealPoints,\n      symbol: \"rect\",\n      symbolSize: [18, 18],\n      itemStyle: {\n        color: t.palette[4],  // #AE3030 matte red — engineering convention for ideal I/Q reference points\n        opacity: 1,\n      },\n    },\n  ],\n\n  // EVM annotation placed in the upper-right quadrant of the constellation\n  graphic: [\n    {\n      type: \"text\",\n      left: \"72%\",\n      top: \"11%\",\n      style: {\n        text: `EVM = ${evm}%`,\n        fill: t.ink,\n        font: \"bold 16px sans-serif\",\n      },\n    },\n  ],\n});\n"}