{"id":2375,"date":"2026-08-18T20:13:44","date_gmt":"2026-08-18T20:13:44","guid":{"rendered":"https:\/\/smartdata.ece.ufl.edu\/?p=2375"},"modified":"2026-08-20T16:04:21","modified_gmt":"2026-08-20T16:04:21","slug":"discrete-time-signal-explorer","status":"publish","type":"post","link":"https:\/\/smartdata.ece.ufl.edu\/index.php\/2026\/08\/18\/discrete-time-signal-explorer\/","title":{"rendered":"Discrete-Time Signal Explorer"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Disclaimer:<\/strong> This was built with the help of ClaudeAI as an educational tool for Foundations of Digital Signal Processing<\/p>\n\n\n\n<div id=\"dts-root\" style=\"font-family:Georgia,serif;max-width:920px;margin:0 auto;padding:20px;background:#f5f5f0;border:2px solid #a0a080;border-radius:10px;color:#1a1a1a;\">\n\n  <h2 style=\"text-align:center;margin:0 0 4px 0;font-size:1.45em;color:#2a2a0a;letter-spacing:1px;\">Discrete-Time Signal Explorer<\/h2>\n  <p style=\"text-align:center;margin:0 0 18px 0;font-size:0.92em;color:#666;\">Build a signal, apply transformations, and analyse its properties.<\/p>\n\n  <div style=\"background:#fff;border:1px solid #c8c8a8;border-radius:8px;padding:14px 18px;margin-bottom:12px;\">\n    <div style=\"font-size:0.88em;font-weight:bold;color:#3a3a0a;margin-bottom:10px;letter-spacing:0.5px;\">\u2460 SIGNAL PROTOTYPE<\/div>\n    <div style=\"display:flex;flex-wrap:wrap;gap:14px;align-items:flex-end;\">\n      <div>\n        <label style=\"font-size:0.82em;color:#555;display:block;margin-bottom:4px;\">Type<\/label>\n        <select id=\"dts-proto\" style=\"font-size:0.9em;padding:5px 8px;border:1px solid #bbb;border-radius:5px;background:#fff;min-width:170px;\">\n          <option value=\"delta\">Unit Impulse \u03b4[n]<\/option>\n          <option value=\"step\">Unit Step u[n]<\/option>\n          <option value=\"ramp\">Unit Ramp r[n]<\/option>\n          <option value=\"rect\">Rectangle (pulse)<\/option>\n          <option value=\"exp_real\">Real Exponential a^n\u00b7u[n]<\/option>\n          <option value=\"exp_decay\">Two-sided Exp |a|^|n|<\/option>\n          <option value=\"sinusoid\">Sinusoid cos(\u03c9\u2080n)<\/option>\n          <option value=\"tri\">Triangle<\/option>\n          <option value=\"rand\">Random (noise)<\/option>\n        <\/select>\n      <\/div>\n      <div id=\"dts-pA-wrap\">\n        <label style=\"font-size:0.82em;color:#555;display:block;margin-bottom:4px;\">\n          <span id=\"dts-pA-label\">Amplitude A<\/span>: <span id=\"dts-pA-val\" style=\"font-family:monospace;color:#b03a00;\">1.00<\/span>\n        <\/label>\n        <input id=\"dts-pA\" type=\"range\" min=\"-3\" max=\"3\" step=\"0.1\" value=\"1\" style=\"width:120px;accent-color:#b03a00;cursor:pointer;\">\n      <\/div>\n      <div id=\"dts-pB-wrap\">\n        <label style=\"font-size:0.82em;color:#555;display:block;margin-bottom:4px;\">\n          <span id=\"dts-pB-label\">Base a<\/span>: <span id=\"dts-pB-val\" style=\"font-family:monospace;color:#0a5a1a;\">0.80<\/span>\n        <\/label>\n        <input id=\"dts-pB\" type=\"range\" min=\"0.1\" max=\"0.99\" step=\"0.01\" value=\"0.8\" style=\"width:120px;accent-color:#0a5a1a;cursor:pointer;\">\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div style=\"background:#fff;border:1px solid #c8c8a8;border-radius:8px;padding:14px 18px;margin-bottom:12px;\">\n    <div style=\"font-size:0.88em;font-weight:bold;color:#0a2a4a;margin-bottom:10px;letter-spacing:0.5px;\">\n      \u2461 TRANSFORMATIONS <span style=\"font-weight:normal;color:#888;font-size:0.9em;\">(Forms x[\u00b1Mn \u2212 k])<\/span>\n    <\/div>\n    <div style=\"display:flex;flex-wrap:wrap;gap:16px;align-items:flex-end;\">\n      <div style=\"background:#f0f4ff;border:1px solid #b0c0e0;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.82em;font-weight:bold;color:#0a2a4a;margin-bottom:8px;\">Time Reversal<\/div>\n        <label style=\"font-size:0.85em;cursor:pointer;\">\n          <input id=\"dts-reverse\" type=\"checkbox\" style=\"accent-color:#2471a3;cursor:pointer;\"> x[\u2212n]\n        <\/label>\n      <\/div>\n      <div style=\"background:#f0f4ff;border:1px solid #b0c0e0;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.82em;font-weight:bold;color:#0a2a4a;margin-bottom:8px;\">Time Scaling x[Mn]<\/div>\n        <label style=\"font-size:0.82em;color:#555;display:block;margin-bottom:4px;\">M = <span id=\"dts-scale-val\" style=\"font-family:monospace;color:#7a3abf;\">1<\/span><\/label>\n        <input id=\"dts-scale\" type=\"range\" min=\"1\" max=\"4\" step=\"1\" value=\"1\" style=\"width:100px;accent-color:#7a3abf;cursor:pointer;\">\n        <div style=\"font-size:0.72em;color:#999;margin-top:2px;\">1 = none (downsampling)<\/div>\n      <\/div>\n      <div style=\"background:#f0f4ff;border:1px solid #b0c0e0;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.82em;font-weight:bold;color:#0a2a4a;margin-bottom:8px;\">Time Shift x[n\u2212k]<\/div>\n        <label style=\"font-size:0.82em;color:#555;display:block;margin-bottom:4px;\">k = <span id=\"dts-shift-val\" style=\"font-family:monospace;color:#b03a00;\">0<\/span><\/label>\n        <input id=\"dts-shift\" type=\"range\" min=\"-10\" max=\"10\" step=\"1\" value=\"0\" style=\"width:100px;accent-color:#b03a00;cursor:pointer;\">\n      <\/div>\n      <div id=\"dts-periodic-wrap\" style=\"background:#f0f4ff;border:1px solid #b0c0e0;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.82em;font-weight:bold;color:#0a2a4a;margin-bottom:8px;\">Periodicity<\/div>\n        <label style=\"font-size:0.85em;cursor:pointer;display:block;margin-bottom:6px;\">\n          <input id=\"dts-periodic\" type=\"checkbox\" style=\"accent-color:#2471a3;cursor:pointer;\"> Make periodic\n        <\/label>\n        <label style=\"font-size:0.82em;color:#555;display:block;margin-bottom:4px;\">Period N = <span id=\"dts-period-val\" style=\"font-family:monospace;color:#1a5a1a;\">10<\/span><\/label>\n        <input id=\"dts-period\" type=\"range\" min=\"3\" max=\"20\" step=\"1\" value=\"10\" style=\"width:100px;accent-color:#1a5a1a;cursor:pointer;\">\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div style=\"background:#fff;border:1px solid #c8c8a8;border-radius:8px;padding:12px;margin-bottom:12px;\">\n    <div style=\"font-size:0.85em;font-weight:bold;color:#1a1a1a;margin-bottom:6px;text-align:center;\" id=\"dts-main-title\">x[n] = \u03b4[n]<\/div>\n    <canvas id=\"dts-cvMain\" width=\"880\" height=\"180\" style=\"width:100%;display:block;border-radius:4px;background:#fafafa;\"><\/canvas>\n  <\/div>\n\n  <div style=\"background:#fff;border:1px solid #c8c8a8;border-radius:8px;padding:12px;margin-bottom:12px;\">\n    <div style=\"font-size:0.85em;font-weight:bold;color:#1a1a1a;margin-bottom:8px;text-align:center;\">\n      Even \/ Odd Decomposition\n      <span style=\"font-weight:normal;font-size:0.9em;color:#666;\">x<sub>e<\/sub>[n] = (x[n]+x[\u2212n])\/2 &nbsp; x<sub>o<\/sub>[n] = (x[n]\u2212x[\u2212n])\/2<\/span>\n    <\/div>\n    <div style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;\">\n      <div>\n        <div style=\"font-size:0.78em;color:#7a1010;font-weight:bold;text-align:center;margin-bottom:4px;\">Even part x<sub>e<\/sub>[n]<\/div>\n        <canvas id=\"dts-cvEven\" width=\"430\" height=\"140\" style=\"width:100%;display:block;border-radius:4px;background:#fafafa;\"><\/canvas>\n      <\/div>\n      <div>\n        <div style=\"font-size:0.78em;color:#1a4a7a;font-weight:bold;text-align:center;margin-bottom:4px;\">Odd part x<sub>o<\/sub>[n]<\/div>\n        <canvas id=\"dts-cvOdd\" width=\"430\" height=\"140\" style=\"width:100%;display:block;border-radius:4px;background:#fafafa;\"><\/canvas>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div style=\"background:#fff;border:1px solid #c8c8a8;border-radius:8px;padding:14px 18px;margin-bottom:12px;\">\n    <div style=\"font-size:0.88em;font-weight:bold;color:#1a1a1a;margin-bottom:12px;letter-spacing:0.5px;\">\u2462 SIGNAL PROPERTIES<\/div>\n    <div style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;\">\n      <div style=\"background:#f8f8f0;border:1px solid #d8d8b8;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.78em;color:#888;margin-bottom:3px;\">Symmetry<\/div>\n        <div id=\"dts-sym\" style=\"font-size:1em;font-weight:bold;color:#2a2a0a;\">\u2014<\/div>\n      <\/div>\n      <div style=\"background:#f8f8f0;border:1px solid #d8d8b8;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.78em;color:#888;margin-bottom:3px;\">Causality<\/div>\n        <div id=\"dts-causal\" style=\"font-size:1em;font-weight:bold;color:#2a2a0a;\">\u2014<\/div>\n      <\/div>\n      <div style=\"background:#f8f8f0;border:1px solid #d8d8b8;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.78em;color:#888;margin-bottom:3px;\">Periodicity<\/div>\n        <div id=\"dts-per-prop\" style=\"font-size:1em;font-weight:bold;color:#2a2a0a;\">\u2014<\/div>\n      <\/div>\n      <div style=\"background:#f8f8f0;border:1px solid #d8d8b8;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.78em;color:#888;margin-bottom:3px;\">Energy E<\/div>\n        <div id=\"dts-energy\" style=\"font-size:1em;font-weight:bold;font-family:monospace;color:#2a2a0a;\">\u2014<\/div>\n      <\/div>\n      <div style=\"background:#f8f8f0;border:1px solid #d8d8b8;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.78em;color:#888;margin-bottom:3px;\">Power P<\/div>\n        <div id=\"dts-power\" style=\"font-size:1em;font-weight:bold;font-family:monospace;color:#2a2a0a;\">\u2014<\/div>\n      <\/div>\n      <div style=\"background:#f8f8f0;border:1px solid #d8d8b8;border-radius:6px;padding:10px 14px;\">\n        <div style=\"font-size:0.78em;color:#888;margin-bottom:3px;\">Signal class<\/div>\n        <div id=\"dts-class\" style=\"font-size:1em;font-weight:bold;color:#2a2a0a;\">\u2014<\/div>\n      <\/div>\n    <\/div>\n    <div style=\"margin-top:10px;font-size:0.78em;color:#888;line-height:1.7;\">\n      <span style=\"color:#b03a00;font-weight:bold;\">Energy signal<\/span>: 0 &lt; E &lt; \u221e, P = 0 &nbsp;\u00b7&nbsp;\n      <span style=\"color:#1a4a7a;font-weight:bold;\">Power signal<\/span>: 0 &lt; P &lt; \u221e, E \u2192 \u221e &nbsp;\u00b7&nbsp;\n      <span style=\"color:#555;font-weight:bold;\">Neither<\/span>: E \u2192 \u221e and P \u2192 \u221e\n    <\/div>\n  <\/div>\n\n  <div style=\"background:#fffbe8;border:1px solid #e0d080;border-radius:8px;padding:10px 18px;text-align:center;font-size:0.9em;\">\n    <span style=\"color:#555;\">Current signal: <\/span>\n    <span id=\"dts-formula\" style=\"font-family:monospace;font-size:1.05em;color:#2a2a0a;font-weight:bold;\">x[n] = \u03b4[n]<\/span>\n  <\/div>\n\n<\/div>\n\n<script>\n(function(){\n\n  var CV = {\n    'dts-cvMain': {w:880, h:180},\n    'dts-cvEven': {w:430, h:140},\n    'dts-cvOdd':  {w:430, h:140}\n  };\n\n  var N_MIN = -20, N_MAX = 20;\n  function range(a,b){ var r=[]; for(var i=a;i<=b;i++) r.push(i); return r; }\n  var nFull = range(N_MIN, N_MAX);\n\n  \/\/ Still completely avoiding || and &#038;&#038; for WordPress HTML sanitizer safety\n  function noperiodic(proto){\n    if(proto === 'step')     return true;\n    if(proto === 'ramp')     return true;\n    if(proto === 'sinusoid') return true;\n    if(proto === 'rand')     return true;\n    return false;\n  }\n\n  var _rc = {};\n  (function(){\n    for(var i = N_MIN * 2; i <= N_MAX * 2; i++){\n      _rc[i] = Math.random() * 2 - 1;\n    }\n  })();\n\n  function evalProto(proto, A, B, n){\n    if(proto === 'delta')     { return n === 0 ? A : 0; }\n    if(proto === 'step')      { return n >= 0  ? A : 0; }\n    if(proto === 'ramp')      { return n >= 0  ? A * n : 0; }\n    if(proto === 'rect')      { var W = Math.round(B); return n >= 0 ? (n < W ? A : 0) : 0; }\n    if(proto === 'exp_real')  { return n >= 0  ? A * Math.pow(B, n) : 0; }\n    if(proto === 'exp_decay') { return A * Math.pow(Math.abs(B), Math.abs(n)); }\n    if(proto === 'sinusoid')  { return A * Math.cos(B * n); }\n    if(proto === 'tri')       { var W2 = Math.round(B); return n >= -W2 ? (n <= W2 ? A * (1 - Math.abs(n) \/ W2) : 0) : 0; }\n    if(proto === 'rand')      { return _rc[n] !== undefined ? A * _rc[n] : 0; }\n    return 0;\n  }\n\n  function buildSignal(proto, A, B, reverse, scale, shift, periodic, period){\n    return nFull.map(function(n){\n      \/\/ Exact alignment to math representation x[\u00b1Mn - k]\n      var sign = 1;\n      if(reverse){ sign = -1; }\n      var m = sign * scale * n - shift;\n\n      if(periodic){\n        if(!noperiodic(proto)){\n          m = ((m % period) + period) % period;\n          if(m > Math.floor(period \/ 2)){ m = m - period; }\n        }\n      }\n      return evalProto(proto, A, B, m);\n    });\n  }\n\n  function evenOdd(xArr){\n    var L = xArr.length, xe = [], xo = [];\n    for(var i = 0; i < L; i++){\n      var mi = L - 1 - i;\n      xe.push(0.5 * (xArr[i] + xArr[mi]));\n      xo.push(0.5 * (xArr[i] - xArr[mi]));\n    }\n    return {xe: xe, xo: xo};\n  }\n\n  function analyticalProps(proto, A, B, periodic, period){\n    var Aabs = Math.abs(A);\n\n    if(periodic){\n      if(!noperiodic(proto)){\n        var pSum = 0;\n        for(var n = 0; n < period; n++){\n          var mn = n;\n          if(mn > Math.floor(period \/ 2)){ mn = mn - period; }\n          var v = evalProto(proto, A, B, mn);\n          pSum = pSum + v * v;\n        }\n        return {Estr:'\u221e', Pstr:(pSum \/ period).toFixed(4), cls:'Power signal', clsColor:'#1a4a7a'};\n      }\n    }\n\n    if(proto === 'delta'){\n      return {Estr:(Aabs*Aabs).toFixed(4), Pstr:'0', cls:'Energy signal', clsColor:'#b03a00'};\n    }\n    if(proto === 'rect'){\n      var W = Math.round(B);\n      return {Estr:(Aabs*Aabs*W).toFixed(4), Pstr:'0', cls:'Energy signal', clsColor:'#b03a00'};\n    }\n    if(proto === 'tri'){\n      var W2 = Math.round(B), eSum = 0;\n      for(var n = -W2; n <= W2; n++){\n        var t = 1 - Math.abs(n) \/ W2;\n        eSum = eSum + t * t;\n      }\n      return {Estr:(Aabs*Aabs*eSum).toFixed(4), Pstr:'0', cls:'Energy signal', clsColor:'#b03a00'};\n    }\n    if(proto === 'exp_real'){\n      var a = Math.abs(B);\n      if(a < 1){\n        return {Estr:(Aabs*Aabs\/(1-a*a)).toFixed(4), Pstr:'0', cls:'Energy signal', clsColor:'#b03a00'};\n      }\n      return {Estr:'\u221e', Pstr:'\u221e', cls:'Neither', clsColor:'#555'};\n    }\n    if(proto === 'exp_decay'){\n      var a2 = Math.abs(B);\n      if(a2 < 1){\n        return {Estr:(Aabs*Aabs*(1+a2*a2)\/(1-a2*a2)).toFixed(4), Pstr:'0', cls:'Energy signal', clsColor:'#b03a00'};\n      }\n      return {Estr:'\u221e', Pstr:'\u221e', cls:'Neither', clsColor:'#555'};\n    }\n    if(proto === 'step'){\n      return {Estr:'\u221e', Pstr:(Aabs*Aabs\/2).toFixed(4), cls:'Power signal', clsColor:'#1a4a7a'};\n    }\n    if(proto === 'sinusoid'){\n      return {Estr:'\u221e', Pstr:(Aabs*Aabs\/2).toFixed(4), cls:'Power signal', clsColor:'#1a4a7a'};\n    }\n    if(proto === 'ramp'){\n      return {Estr:'\u221e', Pstr:'\u221e', cls:'Neither', clsColor:'#555'};\n    }\n    if(proto === 'rand'){\n      var eN = 0;\n      for(var i = N_MIN; i <= N_MAX; i++){\n        var rv = evalProto('rand', Aabs, B, i);\n        eN = eN + rv * rv;\n      }\n      return {Estr:'finite (window)', Pstr:(eN\/(N_MAX-N_MIN+1)).toFixed(4)+' (window)', cls:'Power signal', clsColor:'#1a4a7a'};\n    }\n    return {Estr:'\u2014', Pstr:'\u2014', cls:'\u2014', clsColor:'#555'};\n  }\n\n  function computeSymCausal(xArr){\n    var tol = 1e-6, L = xArr.length;\n    var isEven = true, isOdd = true;\n    var causal = true, anticausal = true;\n    for(var i = 0; i < L; i++){\n      var mi = L - 1 - i;\n      if(Math.abs(xArr[i] - xArr[mi]) > tol){ isEven = false; }\n      if(Math.abs(xArr[i] + xArr[mi]) > tol){ isOdd  = false; }\n      if(nFull[i] < 0){ if(Math.abs(xArr[i]) > tol){ causal     = false; } }\n      if(nFull[i] > 0){ if(Math.abs(xArr[i]) > tol){ anticausal = false; } }\n    }\n    var sym    = isEven ? 'Even (symmetric)' : (isOdd ? 'Odd (anti-symmetric)' : 'Neither');\n    var cstr   = causal ? 'Causal' : (anticausal ? 'Anti-causal' : 'Non-causal (two-sided)');\n    return {sym: sym, causal: cstr};\n  }\n\n  function makeAx(id, ml){\n    var d = CV[id];\n    return {ml: ml ? ml : 46, mt:14, mb:26, mr:12, w:d.w, h:d.h};\n  }\n  function clearCv(ctx, id){\n    var d = CV[id];\n    ctx.clearRect(0, 0, d.w, d.h);\n    ctx.fillStyle = '#fafafa';\n    ctx.fillRect(0, 0, d.w, d.h);\n  }\n  function xMap(ax, n, lo, hi){ return ax.ml + (n - lo) \/ (hi - lo) * (ax.w - ax.ml - ax.mr); }\n  function yMap(ax, v, lo, hi){ return ax.mt + (hi - v) \/ (hi - lo) * (ax.h - ax.mt - ax.mb); }\n\n  function drawAxes(ctx, ax, nLo, nHi, yLo, yHi){\n    var y0 = yMap(ax, 0, yLo, yHi);\n    if(y0 < ax.mt)      y0 = ax.mt;\n    if(y0 > ax.h-ax.mb) y0 = ax.h - ax.mb;\n    ctx.save();\n    ctx.strokeStyle = '#ececec'; ctx.lineWidth = 1; ctx.setLineDash([2,3]);\n    for(var n = nLo; n <= nHi; n += 5){\n      var gx = xMap(ax, n, nLo, nHi);\n      ctx.beginPath(); ctx.moveTo(gx, ax.mt); ctx.lineTo(gx, ax.h-ax.mb); ctx.stroke();\n    }\n    ctx.setLineDash([]);\n    ctx.strokeStyle = '#aaa'; ctx.lineWidth = 1.5;\n    ctx.beginPath(); ctx.moveTo(ax.ml, y0); ctx.lineTo(ax.w-ax.mr, y0); ctx.stroke();\n    var x0 = xMap(ax, 0, nLo, nHi);\n    if(x0 < ax.ml)      x0 = ax.ml;\n    if(x0 > ax.w-ax.mr) x0 = ax.w - ax.mr;\n    ctx.beginPath(); ctx.moveTo(x0, ax.mt); ctx.lineTo(x0, ax.h-ax.mb); ctx.stroke();\n    ctx.fillStyle = '#aaa'; ctx.font = '9px monospace'; ctx.textAlign = 'center';\n    for(var n = nLo; n <= nHi; n += 5){\n      ctx.fillText(n, xMap(ax, n, nLo, nHi), ax.h - 7);\n    }\n    ctx.textAlign = 'right'; ctx.font = '9px monospace';\n    var yticks = [yLo, 0, yHi];\n    for(var ti = 0; ti < yticks.length; ti++){\n      var yv = yticks[ti];\n      if(Math.abs(yv) > 0.001){\n        ctx.fillText(yv.toFixed(1), ax.ml - 3, yMap(ax, yv, yLo, yHi) + 3);\n      }\n    }\n    ctx.restore();\n    return y0;\n  }\n\n  function drawStems(ctx, ax, nArr, yArr, nLo, nHi, yLo, yHi, y0, color){\n    ctx.save();\n    for(var i = 0; i < nArr.length; i++){\n      var v = yArr[i];\n      if(Math.abs(v) < 1e-9){ continue; }\n      var x  = xMap(ax, nArr[i], nLo, nHi);\n      var y  = yMap(ax, v, yLo, yHi);\n      var yb = y0;\n      if(yb < ax.mt)      yb = ax.mt;\n      if(yb > ax.h-ax.mb) yb = ax.h - ax.mb;\n      ctx.strokeStyle = color; ctx.lineWidth = 2;\n      ctx.beginPath(); ctx.moveTo(x, yb); ctx.lineTo(x, y); ctx.stroke();\n      ctx.beginPath(); ctx.arc(x, y, 3.5, 0, 2 * Math.PI);\n      ctx.fillStyle = color; ctx.fill();\n      ctx.strokeStyle = '#fff'; ctx.lineWidth = 1; ctx.stroke();\n    }\n    ctx.restore();\n  }\n\n  var protoConfig = {\n    delta:    {pA:{label:'Amplitude A',  min:'-3',   max:'3',    step:'0.1',  val:'1',   show:true}, pB:{show:false}},\n    step:     {pA:{label:'Amplitude A',  min:'-3',   max:'3',    step:'0.1',  val:'1',   show:true}, pB:{show:false}},\n    ramp:     {pA:{label:'Slope A',      min:'-1',   max:'1',    step:'0.05', val:'0.2', show:true}, pB:{show:false}},\n    rect:     {pA:{label:'Amplitude A',  min:'-3',   max:'3',    step:'0.1',  val:'1',   show:true}, pB:{label:'Width W',      min:'1', max:'10',   step:'1',    val:'5',   show:true}},\n    exp_real: {pA:{label:'Amplitude A',  min:'-3',   max:'3',    step:'0.1',  val:'1',   show:true}, pB:{label:'Base a',       min:'-0.99', max:'0.99', step:'0.01', val:'0.8', show:true}},\n    exp_decay:{pA:{label:'Amplitude A',  min:'-3',   max:'3',    step:'0.1',  val:'1',   show:true}, pB:{label:'Base |a|',     min:'0.1', max:'0.99', step:'0.01', val:'0.7', show:true}},\n    sinusoid: {pA:{label:'Amplitude A',  min:'-3',   max:'3',    step:'0.1',  val:'1',   show:true}, pB:{label:'w0 (rad)',     min:'0.05', max:'3.14', step:'0.05', val:'0.5', show:true}},\n    tri:      {pA:{label:'Amplitude A',  min:'-3',   max:'3',    step:'0.1',  val:'1',   show:true}, pB:{label:'Half-width W', min:'1', max:'10',   step:'1',    val:'5',   show:true}},\n    rand:     {pA:{label:'Amplitude A',  min:'-3',   max:'3',    step:'0.1',  val:'1',   show:true}, pB:{show:false}}\n  };\n\n  function updateParamUI(proto){\n    var cfg  = protoConfig[proto];\n    var pAw  = document.getElementById('dts-pA-wrap');\n    var pBw  = document.getElementById('dts-pB-wrap');\n    var pAsl = document.getElementById('dts-pA');\n    var pBsl = document.getElementById('dts-pB');\n    if(!pAw){ return; }\n    pAw.style.display = cfg.pA.show ? 'block' : 'none';\n    if(cfg.pA.show){\n      document.getElementById('dts-pA-label').textContent = cfg.pA.label;\n      pAsl.min = cfg.pA.min; pAsl.max = cfg.pA.max;\n      pAsl.step = cfg.pA.step; pAsl.value = cfg.pA.val;\n      document.getElementById('dts-pA-val').textContent = parseFloat(cfg.pA.val).toFixed(2);\n    }\n    pBw.style.display = cfg.pB.show ? 'block' : 'none';\n    if(cfg.pB.show){\n      document.getElementById('dts-pB-label').textContent = cfg.pB.label;\n      pBsl.min = cfg.pB.min; pBsl.max = cfg.pB.max;\n      pBsl.step = cfg.pB.step; pBsl.value = cfg.pB.val;\n      document.getElementById('dts-pB-val').textContent = parseFloat(cfg.pB.val).toFixed(2);\n    }\n    var perWrap = document.getElementById('dts-periodic-wrap');\n    var perChk  = document.getElementById('dts-periodic');\n    if(perWrap){\n      if(noperiodic(proto)){\n        perWrap.style.opacity = '0.4';\n        perWrap.style.pointerEvents = 'none';\n        if(perChk){ perChk.checked = false; }\n      } else {\n        perWrap.style.opacity = '1';\n        perWrap.style.pointerEvents = 'auto';\n      }\n    }\n  }\n\n  function buildFormula(proto, A, B, reverse, scale, shift, periodic, period){\n    var aStr = (Math.abs(A - 1) < 0.01) ? (A > 0 ? '' : '-') : A.toFixed(2) + '*';\n    var bStr = B.toFixed(2);\n    var inner = 'n';\n    if(scale > 1){ inner = scale + 'n'; }\n    if(reverse){ inner = inner.replace('n', '-n'); }\n    if(shift !== 0){\n      var sk = shift > 0 ? '-' + shift : '+' + (-shift);\n      inner = '(' + inner + sk + ')';\n    }\n    var core = 'x[' + inner + ']';\n    if(proto === 'delta')    { core = 'd[' + inner + ']'; }\n    if(proto === 'step')     { core = 'u[' + inner + ']'; }\n    if(proto === 'ramp')     { core = 'r[' + inner + ']'; }\n    if(proto === 'rect')     { core = 'rect[' + inner + ', W=' + Math.round(B) + ']'; }\n    if(proto === 'exp_real') { core = bStr + '^' + inner + ' * u[' + inner + ']'; }\n    if(proto === 'exp_decay'){ core = bStr + '^|' + inner + '|'; }\n    if(proto === 'sinusoid') { core = 'cos(' + bStr + '*' + inner + ')'; }\n    if(proto === 'tri')      { core = 'tri[' + inner + ', W=' + Math.round(B) + ']'; }\n    if(proto === 'rand')     { core = 'noise[' + inner + ']'; }\n    var s = 'x[n] = ' + aStr + core;\n    if(periodic){\n      if(!noperiodic(proto)){ s = s + '  [periodic, N=' + period + ']'; }\n    }\n    return s;\n  }\n\n  function draw(){\n    var elProto = document.getElementById('dts-proto');\n    if(!elProto){ return false; }\n    var required = ['dts-pA','dts-pB','dts-reverse','dts-scale','dts-shift',\n                    'dts-periodic','dts-period','dts-cvMain','dts-cvEven','dts-cvOdd',\n                    'dts-sym','dts-causal','dts-per-prop','dts-energy',\n                    'dts-power','dts-class','dts-formula','dts-main-title'];\n    for(var ri = 0; ri < required.length; ri++){\n      if(!document.getElementById(required[ri])){ return false; }\n    }\n\n    var proto    = elProto.value;\n    var A        = parseFloat(document.getElementById('dts-pA').value);\n    var B        = parseFloat(document.getElementById('dts-pB').value);\n    var reverse  = document.getElementById('dts-reverse').checked;\n    var scale    = parseInt(document.getElementById('dts-scale').value, 10);\n    var shift    = parseInt(document.getElementById('dts-shift').value, 10);\n    var perChk   = document.getElementById('dts-periodic').checked;\n    var periodic = false;\n    if(perChk){ if(!noperiodic(proto)){ periodic = true; } }\n    var period   = parseInt(document.getElementById('dts-period').value, 10);\n\n    var xArr = buildSignal(proto, A, B, reverse, scale, shift, periodic, period);\n    var eo   = evenOdd(xArr);\n    var sc   = computeSymCausal(xArr);\n    var ep   = analyticalProps(proto, A, B, periodic, period);\n\n    function yScale(arr){\n      var mx = 0;\n      for(var i = 0; i < arr.length; i++){\n        var av = Math.abs(arr[i]);\n        if(av > mx){ mx = av; }\n      }\n      if(!isFinite(mx)){ mx = 1; }\n      if(mx < 0.01){ mx = 1; }\n      if(mx > 30){ mx = 30; }\n      return {lo: -mx * 1.35, hi: mx * 1.35};\n    }\n\n    var ys  = yScale(xArr);\n    var yse = yScale(eo.xe);\n    var yso = yScale(eo.xo);\n\n    var cvMain = document.getElementById('dts-cvMain');\n    var cvEven = document.getElementById('dts-cvEven');\n    var cvOdd  = document.getElementById('dts-cvOdd');\n    cvMain.width  = CV['dts-cvMain'].w; cvMain.height = CV['dts-cvMain'].h;\n    cvEven.width  = CV['dts-cvEven'].w; cvEven.height = CV['dts-cvEven'].h;\n    cvOdd.width   = CV['dts-cvOdd'].w;  cvOdd.height  = CV['dts-cvOdd'].h;\n\n    var ctxMain = cvMain.getContext('2d');\n    var ctxEven = cvEven.getContext('2d');\n    var ctxOdd  = cvOdd.getContext('2d');\n    var ax, y0;\n\n    clearCv(ctxMain, 'dts-cvMain'); ax = makeAx('dts-cvMain');\n    y0 = drawAxes(ctxMain, ax, N_MIN, N_MAX, ys.lo, ys.hi);\n    drawStems(ctxMain, ax, nFull, xArr, N_MIN, N_MAX, ys.lo, ys.hi, y0, '#2471a3');\n\n    clearCv(ctxEven, 'dts-cvEven'); ax = makeAx('dts-cvEven');\n    y0 = drawAxes(ctxEven, ax, N_MIN, N_MAX, yse.lo, yse.hi);\n    drawStems(ctxEven, ax, nFull, eo.xe, N_MIN, N_MAX, yse.lo, yse.hi, y0, '#c0392b');\n\n    clearCv(ctxOdd, 'dts-cvOdd'); ax = makeAx('dts-cvOdd');\n    y0 = drawAxes(ctxOdd, ax, N_MIN, N_MAX, yso.lo, yso.hi);\n    drawStems(ctxOdd, ax, nFull, eo.xo, N_MIN, N_MAX, yso.lo, yso.hi, y0, '#1a5a9a');\n\n    document.getElementById('dts-sym').textContent      = sc.sym;\n    document.getElementById('dts-causal').textContent   = sc.causal;\n    document.getElementById('dts-per-prop').textContent = periodic ? 'Periodic (N=' + period + ')' : 'Aperiodic';\n    document.getElementById('dts-energy').textContent   = ep.Estr;\n    document.getElementById('dts-power').textContent    = ep.Pstr;\n    document.getElementById('dts-class').textContent    = ep.cls;\n    document.getElementById('dts-energy').style.color   = ep.cls === 'Energy signal' ? '#b03a00' : '#555';\n    document.getElementById('dts-power').style.color    = ep.cls === 'Power signal'  ? '#1a4a7a' : '#555';\n    document.getElementById('dts-class').style.color    = ep.clsColor;\n\n    var formula = buildFormula(proto, A, B, reverse, scale, shift, periodic, period);\n    document.getElementById('dts-formula').textContent    = formula;\n    document.getElementById('dts-main-title').textContent = formula;\n    return true;\n  }\n\n  function onProtoChange(){\n    updateParamUI(document.getElementById('dts-proto').value);\n    syncLabels();\n    draw();\n  }\n  function syncLabels(){\n    document.getElementById('dts-pA-val').textContent     = parseFloat(document.getElementById('dts-pA').value).toFixed(2);\n    document.getElementById('dts-pB-val').textContent     = parseFloat(document.getElementById('dts-pB').value).toFixed(2);\n    document.getElementById('dts-scale-val').textContent  = document.getElementById('dts-scale').value;\n    document.getElementById('dts-shift-val').textContent  = document.getElementById('dts-shift').value;\n    document.getElementById('dts-period-val').textContent = document.getElementById('dts-period').value;\n  }\n  function onAny(){ syncLabels(); draw(); }\n\n  document.getElementById('dts-proto').addEventListener('change', onProtoChange);\n  var inputIds = ['dts-pA','dts-pB','dts-scale','dts-shift','dts-period'];\n  for(var ii = 0; ii < inputIds.length; ii++){\n    document.getElementById(inputIds[ii]).addEventListener('input', onAny);\n  }\n  var chkIds = ['dts-reverse','dts-periodic'];\n  for(var ci = 0; ci < chkIds.length; ci++){\n    document.getElementById(chkIds[ci]).addEventListener('change', onAny);\n  }\n\n  function tryStart(){\n    var ok = false;\n    try { ok = draw(); } catch(e) {}\n    if(!ok){ setTimeout(tryStart, 100); }\n    else   { updateParamUI(document.getElementById('dts-proto').value); }\n  }\n  setTimeout(tryStart, 0);\n\n})();\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Disclaimer: This was built with the help of ClaudeAI as an educational tool for Foundations of Digital Signal Processing Discrete-Time Signal Explorer [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"post-templates\/post_noheader_nofooter2.php","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2375","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/posts\/2375","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/comments?post=2375"}],"version-history":[{"count":8,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/posts\/2375\/revisions"}],"predecessor-version":[{"id":2385,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/posts\/2375\/revisions\/2385"}],"wp:attachment":[{"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/media?parent=2375"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/categories?post=2375"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/tags?post=2375"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}