this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add TDR plot

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+176 -2
+5 -1
app.js
··· 5 5 (() => { 6 6 const { clamp, fmt, resizeCanvasToCSS } = TLUtils; 7 7 const { computeWaveParams, buildBounceSeries, computeDynamicState } = TLPhysics; 8 - const { getTheme, drawCircuit, drawPlot, ensurePlotCanvasHeight } = TLRender; 8 + const { getTheme, drawCircuit, drawPlot, drawTDR, ensurePlotCanvasHeight } = TLRender; 9 9 10 10 // ---- DOM references ---- 11 11 const el = { 12 + tdr: document.getElementById("tdr"), 12 13 circuit: document.getElementById("circuit"), 13 14 plot: document.getElementById("plot"), 14 15 startBtn: document.getElementById("startBtn"), ··· 170 171 mathjaxTypesetDone = true; 171 172 window.MathJax.typesetPromise(); 172 173 } 174 + 175 + const d = resizeCanvasToCSS(el.tdr); 176 + drawTDR(d.ctx, d.w, d.h, tNorm, bounce, model.riseTimeTau, model.riseTimeTr, timeHorizon, theme); 173 177 174 178 const c = resizeCanvasToCSS(el.circuit); 175 179 drawCircuit(c.ctx, c.w, c.h, tNorm, dyn, theme, model.segments, model.RL);
+6 -1
index.html
··· 74 74 <button id="resetBtn">Reset</button> 75 75 </div> 76 76 77 - <!-- Circuit canvas (top) --> 77 + <!-- TDR canvas (above circuit) --> 78 + <canvas id="tdr" width="600" height="300" style="width:600px;margin:0 auto;" aria-label="TDR waveform — voltage at detection point vs time"></canvas> 79 + 80 + <div class="divider"></div> 81 + 82 + <!-- Circuit canvas --> 78 83 <canvas id="circuit" width="1100" height="260" aria-label="Circuit with wavefront highlight"></canvas> 79 84 80 85 <div class="divider"></div>
+164
render.js
··· 29 29 accent2: get("--accent2"), 30 30 warn: get("--warn"), 31 31 ok: get("--ok"), 32 + tdr: get("--tdr"), 32 33 }; 33 34 } 34 35 ··· 223 224 circleFill(ctx, xRight, yTop, 3.5, theme.ink); 224 225 circleFill(ctx, xRight, yBot, 3.5, theme.ink); 225 226 } 227 + 228 + // TDR detection point — green dot on top wire between switch and T-line input 229 + const xDetect = Math.round((xSwitch + 18 + xTL0) / 2); 230 + circleFill(ctx, xDetect, yTop, 5.5, theme.tdr); 231 + ctx.fillStyle = theme.tdr; 232 + ctx.font = "11px ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial"; 233 + ctx.textAlign = "left"; 226 234 227 235 // Wavefront markers (dashed vertical lines inside T-line boxes) 228 236 const wfY0 = yTop - 16; ··· 491 499 ctx.fillText("\u2113", xPlot1 - 4, h - 6); 492 500 } 493 501 502 + // ---- TDR canvas ---- 503 + // Draws VS(t) — voltage at the detection point (z=0, between Rg and T-line) versus time. 504 + // The trace grows up to the current tNorm; a cursor line shows current time. 505 + function drawTDR(ctx, w, h, tn, bounce, riseTimeTau, riseTimeTr, timeHorizon, theme) { 506 + ctx.clearRect(0, 0, w, h); 507 + ctx.fillStyle = theme.panel; 508 + ctx.fillRect(0, 0, w, h); 509 + 510 + const padL = 55, padR = 30, padT = 20, padB = 30; 511 + const plotW = w - padL - padR; 512 + const plotH = h - padT - padB; 513 + 514 + const tMax = timeHorizon; 515 + const xOfT = (t) => padL + (t / tMax) * plotW; 516 + 517 + const { sumEventsAtTime, sumEventsWithRise, sumEventsWithLinearRamp } = TLPhysics; 518 + const vsAtTime = (t) => { 519 + if (riseTimeTr > 0) return sumEventsWithLinearRamp(bounce.srcEvents, t, riseTimeTr); 520 + if (riseTimeTau > 0) return sumEventsWithRise(bounce.srcEvents, t, riseTimeTau); 521 + return sumEventsAtTime(bounce.srcEvents, t); 522 + }; 523 + 524 + // y range from all srcEvents (cumulative sum) 525 + const events = [...bounce.srcEvents].sort((a, b) => a.t - b.t); 526 + let cumV = 0, vMax = 0, vMin = 0; 527 + for (const ev of events) { 528 + cumV += ev.dV; 529 + if (cumV > vMax) vMax = cumV; 530 + if (cumV < vMin) vMin = cumV; 531 + } 532 + vMax = Math.max(vMax, 0.01); 533 + vMin = Math.min(vMin, 0); 534 + const span = Math.max(vMax - vMin, 0.05); 535 + const vHi = vMax + 0.2 * span; 536 + const vLo = vMin - 0.15 * span; 537 + const yOfV = (v) => padT + (vHi - v) / (vHi - vLo) * plotH; 538 + 539 + // Title 540 + ctx.fillStyle = theme.muted; 541 + ctx.font = "12px ui-sans-serif, system-ui"; 542 + ctx.fillText("TDR \u2014 V at detection point vs time", padL + 8, padT + 14); 543 + 544 + // Grid lines 545 + const vStep = niceStep(vHi - vLo, 4); 546 + ctx.strokeStyle = theme.grid; 547 + ctx.lineWidth = 1; 548 + for (let i = Math.ceil(vLo / vStep); ; i++) { 549 + const v = i * vStep; 550 + if (v > vHi + vStep * 0.01) break; 551 + const yv = yOfV(v); 552 + if (yv < padT - 1 || yv > padT + plotH + 1) continue; 553 + line(ctx, padL, yv, padL + plotW, yv); 554 + } 555 + 556 + // Border 557 + ctx.strokeStyle = theme.grid; 558 + ctx.lineWidth = 1; 559 + line(ctx, padL, padT, padL + plotW, padT); 560 + line(ctx, padL, padT + plotH, padL + plotW, padT + plotH); 561 + line(ctx, padL, padT, padL, padT + plotH); 562 + line(ctx, padL + plotW, padT, padL + plotW, padT + plotH); 563 + 564 + // Zero line 565 + const yZero = yOfV(0); 566 + if (yZero >= padT && yZero <= padT + plotH) { 567 + ctx.strokeStyle = theme.muted; 568 + ctx.lineWidth = 1.2; 569 + line(ctx, padL, yZero, padL + plotW, yZero); 570 + } 571 + 572 + // Y-axis ticks + labels 573 + const vDec = vStep >= 1 ? 0 : vStep >= 0.1 ? 1 : 2; 574 + ctx.textAlign = "right"; 575 + ctx.font = "11px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"; 576 + for (let i = Math.ceil(vLo / vStep); ; i++) { 577 + const v = i * vStep; 578 + if (v > vHi + vStep * 0.01) break; 579 + const yv = yOfV(v); 580 + if (yv < padT - 1 || yv > padT + plotH + 1) continue; 581 + ctx.strokeStyle = theme.muted; 582 + ctx.lineWidth = 1; 583 + line(ctx, padL - 4, yv, padL, yv); 584 + ctx.fillStyle = theme.muted; 585 + ctx.fillText(v.toFixed(vDec), padL - 7, yv + 4); 586 + } 587 + ctx.textAlign = "left"; 588 + 589 + // X-axis ticks + labels 590 + const tStep = niceStep(tMax, 6); 591 + const tDec = tStep >= 1 ? 0 : 1; 592 + ctx.textAlign = "center"; 593 + ctx.font = "11px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"; 594 + for (let t = 0; t <= tMax + tStep * 0.01; t += tStep) { 595 + const xt = xOfT(t); 596 + if (xt < padL - 1 || xt > padL + plotW + 1) continue; 597 + ctx.strokeStyle = theme.muted; 598 + ctx.lineWidth = 1; 599 + line(ctx, xt, padT + plotH, xt, padT + plotH + 4); 600 + ctx.fillStyle = theme.muted; 601 + ctx.fillText(t.toFixed(tDec), xt, padT + plotH + 16); 602 + } 603 + ctx.textAlign = "left"; 604 + 605 + // X-axis label 606 + ctx.fillStyle = theme.muted; 607 + ctx.font = "12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"; 608 + ctx.fillText("t / \u03C4d", padL + plotW + 6, padT + plotH + 4); 609 + 610 + // VS(t) trace up to tn 611 + const tnClamped = Math.min(tn, tMax); 612 + const smooth = riseTimeTau > 0 || riseTimeTr > 0; 613 + ctx.strokeStyle = theme.tdr; 614 + ctx.lineWidth = 2.2; 615 + ctx.beginPath(); 616 + 617 + if (smooth) { 618 + const SAMPLES = 500; 619 + for (let i = 0; i <= SAMPLES; i++) { 620 + const t = (i / SAMPLES) * tnClamped; 621 + const v = vsAtTime(t); 622 + if (i === 0) ctx.moveTo(xOfT(t), yOfV(v)); 623 + else ctx.lineTo(xOfT(t), yOfV(v)); 624 + } 625 + } else { 626 + // Staircase: horizontal segment then vertical jump at each srcEvent 627 + let prevV = 0; 628 + ctx.moveTo(xOfT(0), yOfV(0)); 629 + for (const ev of events) { 630 + if (ev.t > tnClamped) break; 631 + ctx.lineTo(xOfT(ev.t), yOfV(prevV)); 632 + prevV += ev.dV; 633 + ctx.lineTo(xOfT(ev.t), yOfV(prevV)); 634 + } 635 + ctx.lineTo(xOfT(tnClamped), yOfV(prevV)); 636 + } 637 + ctx.stroke(); 638 + 639 + // Current-time cursor (vertical dashed line) 640 + if (tn > 0 && tn <= tMax) { 641 + ctx.strokeStyle = theme.warn; 642 + ctx.lineWidth = 1; 643 + ctx.setLineDash([4, 5]); 644 + line(ctx, xOfT(tn), padT, xOfT(tn), padT + plotH); 645 + ctx.setLineDash([]); 646 + } 647 + 648 + // Current voltage readout 649 + const vNow = vsAtTime(tnClamped); 650 + ctx.fillStyle = theme.tdr; 651 + ctx.font = "13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"; 652 + ctx.textAlign = "right"; 653 + ctx.fillText("V(det) = " + vNow.toFixed(4) + " V", padL + plotW - 6, padT + 18); 654 + ctx.textAlign = "left"; 655 + } 656 + 494 657 // Resize the plot canvas height to fit the given number of panels. 495 658 function ensurePlotCanvasHeight(plotEl, nPanels) { 496 659 const targetH = Math.round( ··· 507 670 getTheme, 508 671 drawCircuit, 509 672 drawPlot, 673 + drawTDR, 510 674 ensurePlotCanvasHeight, 511 675 }; 512 676 })();
+1
style.css
··· 8 8 --accent2: #ff6ad5; 9 9 --warn: #ffd166; 10 10 --ok: #7CFF9B; 11 + --tdr: #eb5e34; 11 12 } 12 13 html, body { height: 100%; background: var(--bg); color: var(--ink); margin: 0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; } 13 14 .wrap { max-width: 1100px; margin: 0 auto; padding: 18px 14px 28px; }