{"spec_id":"eye-diagram-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# eye-diagram-basic: Signal Integrity Eye Diagram\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 90/100 | Created: 2026-06-18\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# --- Theme tokens ------------------------------------------------------------\nconst THEME       = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG     = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst ELEVATED_BG = THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"\nconst INK         = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT    = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst INK_MUTED   = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\n\n# Imprint sequential colormap: brand green → blue (density)\nconst ANYPLOT_SEQ = cgrad([colorant\"#009E73\", colorant\"#4467A3\"])\n\n# --- Simulation parameters --------------------------------------------------\nconst N_TRACES       = 500    # overlaid NRZ bit periods (spec: 200–500)\nconst SAMPLES_PER_UI = 150    # time samples per unit interval\nconst JITTER_SIGMA   = 0.03   # transition timing jitter (fraction of UI)\nconst NOISE_SIGMA    = 0.05   # additive Gaussian noise amplitude (V)\nconst N_BINS_T       = 300    # histogram bins along time axis\nconst N_BINS_V       = 200    # histogram bins along voltage axis\nconst V_MIN          = -0.30\nconst V_MAX          =  1.30\nconst T_STEP         = 2.0 / N_BINS_T\nconst V_STEP         = (V_MAX - V_MIN) / N_BINS_V\n\n# --- Data: generate NRZ eye diagram traces ----------------------------------\nt_base = collect(range(0.0, 2.0, length = 2 * SAMPLES_PER_UI))\nn_t    = length(t_base)\n\nall_t = Vector{Float64}(undef, N_TRACES * n_t)\nall_v = Vector{Float64}(undef, N_TRACES * n_t)\n\nfor trace_idx in 1:N_TRACES\n    offset = (trace_idx - 1) * n_t\n\n    # 4 random NRZ bits: [pre-window, bit0, bit1, post-window]\n    bits = rand(Bool, 4)\n\n    # Build waveform: start from bits[1] level\n    v = fill(Float64(bits[1]), n_t)\n\n    # Accumulate bandwidth-limited sigmoid transitions at t = 0, 1, 2 UI\n    for b in 2:4\n        if bits[b] != bits[b - 1]\n            delta   = Float64(bits[b]) - Float64(bits[b - 1])\n            t_trans = Float64(b - 2) + randn() * JITTER_SIGMA\n            @. v += delta / (1.0 + exp(-25.0 * (t_base - t_trans)))\n        end\n    end\n\n    # Additive Gaussian noise\n    v .+= randn(n_t) .* NOISE_SIGMA\n\n    all_t[(offset + 1):(offset + n_t)] .= t_base\n    all_v[(offset + 1):(offset + n_t)]  = v\nend\n\n# --- 2D density histogram ---------------------------------------------------\nt_centers = collect(range(T_STEP / 2, 2.0 - T_STEP / 2, length = N_BINS_T))\nv_centers = collect(range(V_MIN + V_STEP / 2, V_MAX - V_STEP / 2, length = N_BINS_V))\n\ndensity = zeros(Int, N_BINS_T, N_BINS_V)\nfor i in eachindex(all_t)\n    ti = clamp(floor(Int, all_t[i] / T_STEP) + 1, 1, N_BINS_T)\n    vi = clamp(floor(Int, (all_v[i] - V_MIN) / V_STEP) + 1, 1, N_BINS_V)\n    density[ti, vi] += 1\nend\n\n# Log-scale density; empty cells → NaN (renders as background in heatmap)\ndensity_log = [density[i, j] > 0 ? log1p(Float64(density[i, j])) : NaN\n               for i in 1:N_BINS_T, j in 1:N_BINS_V]\n\n# --- Plot -------------------------------------------------------------------\nconst TITLE_STR  = \"eye-diagram-basic · julia · makie · anyplot.ai\"\nconst TITLE_SIZE = max(16, round(Int, 20 * 67 / length(TITLE_STR)))\n\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title             = TITLE_STR,\n    titlesize         = TITLE_SIZE,\n    titlecolor        = INK,\n    xlabel            = \"Time (UI)\",\n    ylabel            = \"Voltage (V)\",\n    xlabelsize        = 14,\n    ylabelsize        = 14,\n    xlabelcolor       = INK,\n    ylabelcolor       = INK,\n    xticklabelsize    = 12,\n    yticklabelsize    = 12,\n    xticklabelcolor   = INK_SOFT,\n    yticklabelcolor   = INK_SOFT,\n    xtickcolor        = INK_SOFT,\n    ytickcolor        = INK_SOFT,\n    backgroundcolor   = PAGE_BG,\n    topspinevisible   = false,\n    rightspinevisible = false,\n    leftspinecolor    = INK_SOFT,\n    bottomspinecolor  = INK_SOFT,\n    xgridcolor        = RGBAf(INK.r, INK.g, INK.b, 0.12),\n    ygridcolor        = RGBAf(INK.r, INK.g, INK.b, 0.12),\n    xminorgridvisible = false,\n    yminorgridvisible = false,\n    xticks            = [0.0, 0.5, 1.0, 1.5, 2.0],\n    yticks            = [0.0, 0.5, 1.0],\n)\n\nhm = heatmap!(ax, t_centers, v_centers, density_log;\n    colormap  = ANYPLOT_SEQ,\n    nan_color = PAGE_BG,\n)\n\n# Eye opening reference lines (spec: \"Optionally annotate eye height and eye width\")\nconst ANNOT_COLOR = RGBAf(INK_SOFT.r, INK_SOFT.g, INK_SOFT.b, 0.65)\n# Eye height: 20% and 80% voltage boundaries (NRZ: 0–1 V amplitude)\nhlines!(ax, [0.20, 0.80];\n    color     = ANNOT_COLOR,\n    linewidth = 1.5,\n    linestyle = :dash,\n)\n# Eye width: center of each eye opening at 0.5 and 1.5 UI\nvlines!(ax, [0.5, 1.5];\n    color     = ANNOT_COLOR,\n    linewidth = 1.5,\n    linestyle = :dash,\n)\ntext!(ax, 0.52, 0.82; text = \"Eye height: 0.60 V\", color = INK_MUTED,\n    fontsize = 10, align = (:left, :bottom))\ntext!(ax, 1.52, -0.24; text = \"Eye width\", color = INK_MUTED,\n    fontsize = 10, align = (:left, :bottom))\n\nColorbar(fig[1, 2], hm;\n    label          = \"Trace Density (log scale)\",\n    labelsize      = 12,\n    ticklabelsize  = 10,\n    labelcolor     = INK,\n    ticklabelcolor = INK_SOFT,\n    tickcolor      = INK_SOFT,\n    width          = 18,\n)\n\nxlims!(ax, 0.0, 2.0)\nylims!(ax, V_MIN, V_MAX)\ncolsize!(fig.layout, 1, Relative(0.90))\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}