Kodgenomgang - Carried Stillness Intruder

Hovra över kod för förklaring

Kod med förklaringar

Instruktioner

Hovra över markerad kod för att se förklaringär. Understrukna delär har extra information.

Sobel Edge Detection
CRT Effekter
CSI Polling
Kalibrering

Sobel Edge Detection

Sobel-operatorn detekterar kanter genom att beräkna gradienter i x- och y-led.

JAVASCRIPT - detectEdges() gui/index.html
function detectEdges(imageData) {
  const width = imageData.width;
  const height = imageData.height;
  const data = imageData.data;

  // Konvertera till grayscale
  const gray = new Uint8Array(width * height);
  for (let i = 0; i < data.length; i += 4) {
    // Viktad konvertering: 0.3R + 0.59G + 0.11B
    gray[i/4] = 0.3*data[i] + 0.59*data[i+1] + 0.11*data[i+2];
  }

  // Kant-resultåt array
  const edges = new Uint8Array(width * height);

  // Sobel-operator (3x3 kernel)
  for (let y = 1; y < height-1; y++) {
    for (let x = 1; x < width-1; x++) {
      const idx = y*width + x;

      // Gx: Horisontell gradient
      const gx = -gray[(y-1)*width+(x-1)] + gray[(y-1)*width+(x+1)] +
               -2*gray[y*width+(x-1)] + 2*gray[y*width+(x+1)] +
               -gray[(y+1)*width+(x-1)] + gray[(y+1)*width+(x+1)];

      // Gy: Vertikal gradient
      const gy = -gray[(y-1)*width+(x-1)] - 2*gray[(y-1)*width+x] - gray[(y-1)*width+(x+1)] +
               gray[(y+1)*width+(x-1)] + 2*gray[(y+1)*width+x] + gray[(y+1)*width+(x+1)];

      // Gradient magnitude
      edges[idx] = Math.sqrt(gx*gx + gy*gy);
    }
  }

  return edges;
}

Sobel Kernlar

Gx (horisontell): [-1, 0, +1; -2, 0, +2; -1, 0, +1]
Gy (vertikal): [-1, -2, -1; 0, 0, 0; +1, +2, +1]

CRT Magnetic Distortion

Simulerar hur en magnet påverkade bilden på gamla katodstralerorsteknik (CRT).

JAVASCRIPT - applyGlitch() gui/index.html
function applyGlitch(ctx, intensity, edges) {
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;
  const width = canvas.width;
  const height = canvas.height;

  // Distorsionscentrum (slumpvist)
  const cx = width * (0.3 + Math.random() * 0.4);
  const cy = height * (0.3 + Math.random() * 0.4);

  const warpFreq = 0.02 + intensity * 0.01;
  const warpAmp = intensity * 3;

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const idx = (y * width + x) * 4;
      const edgeVal = edges[y * width + x] / 255;

      // Avstand från centrum
      const dx = x - cx;
      const dy = y - cy;
      const dist = Math.sqrt(dx*dx + dy*dy);

      // Magnetic warp
      const offsetX = Math.sin(dist * warpFreq) * warpAmp * edgeVal;
      const offsetY = Math.cos(dist * warpFreq) * warpAmp * edgeVal;

      // RGB Separation (chromatic aberration)
      const rgbOffset = Math.floor(intensity * 2 * edgeVal);

      // Beräkna kalla-koordinater
      const srcX = Math.floor(x + offsetX);
      const srcY = Math.floor(y + offsetY);

      // Hamta pixelvärden med offset för varje kanal
      if (srcX >= 0 && srcX < width && srcY >= 0 && srcY < height) {
        const srcIdx = (srcY * width + srcX) * 4;

        // Rod kanal med extra offset
        data[idx] = data[srcIdx + rgbOffset * 4] || data[srcIdx];
        // Gron kanal (oförändrad)
        data[idx+1] = data[srcIdx+1];
        // Bla kanal med negativ offset
        data[idx+2] = data[srcIdx - rgbOffset * 4 + 2] || data[srcIdx+2];
      }

      // Scanlines (var annan rad)
      if (y % 2 === 0) {
        data[idx] *= 0.95;
        data[idx+1] *= 0.95;
        data[idx+2] *= 0.95;
      }
    }
  }

  ctx.putImageData(imageData, 0, 0);
}

CSI Data Polling

Webbläsaren pollar ESP32 varje 30ms för att hamta aktuellt movement score.

JAVASCRIPT - updateCSI() gui/index.html
const ESP32_IP = '192.168.0.124'; // Andras till din ESP32:s IP

async function updateCSI() {
  try {
    const response = await fetch(
      `http://${ESP32_IP}/sensor/movement_score`,
      { mode: 'cors' }
    );

    const json = await response.json();
    // JSON format: { "id": "sensor-movement_score", "value": 4.135, "state": "4.14" }

    const score = parseFloat(json.value);

    // Kolla om score överstiger threshold
    const threshold = calibration.baseline + calibration.threshold;

    if (score > threshold) {
      // Beräkna intensitet baseråt på hur mycket över threshold
      const intensity = (score - threshold) / settings.sensitivity;
      applyGlitch(ctx, Math.min(intensity, 1), currentEdges);
    }

    // Uppdatera UI
    statusEl.textContent = `Score: ${score.toFixed(2)} | Threshold: ${threshold.toFixed(2)}`;
    statusEl.className = 'online';

  } catch (error) {
    statusEl.textContent = 'Offline';
    statusEl.className = 'offline';
  }

  // Polla igen efter 30ms
  setTimeout(updateCSI, 30);
}

Kalibrering

Kalibrering samplär CSI-värden under 3 sekunder för att bestämma baseline vid stillhet.

JAVASCRIPT - startCalibration() gui/index.html
const calibration = {
  baseline: 1.2,        // CSI-värde vid stillhet
  threshold: 0.5,       // Marginal för triggning
  baseThreshold: 0.5,
  isCalibrating: false,
  samples: [],
  sampleCount: 60       // Antal samples (60 * 30ms = ~2s)
};

function startCalibration() {
  calibration.isCalibrating = true;
  calibration.samples = [];

  statusEl.textContent = 'Kalibrering... Sta stilla!';

  // Vanta tills tillräckligt med samples samlats
  const checkCalibration = setInterval(() => {
    if (calibration.samples.length >= calibration.sampleCount) {
      clearInterval(checkCalibration);
      finishCalibration();
    }
  }, 100);
}

function finishCalibration() {
  // Beräkna medelvärde av samples
  const sum = calibration.samples.reduce((a, b) => a + b, 0);
  calibration.baseline = sum / calibration.samples.length;

  // Satt threshold till lite över baseline
  calibration.threshold = calibration.baseThreshold;

  calibration.isCalibrating = false;
  statusEl.textContent = `Kalibrerad! Baseline: ${calibration.baseline.toFixed(2)}`;
}

// Under polling, samla samples om kalibrering pagår
if (calibration.isCalibrating) {
  calibration.samples.push(score);
}
Carried Stillness Intruder Hovra för förklaring