# Algorithmen der Chronolite-Lichtlogik

Dieses Dokument beschreibt die beiden zentralen Abläufe der Referenzimplementierung:

1. die **Generierung des personalisierten Tagesverlaufs** (zwei Kurven) im Data Hub und
2. die **Steuerungsschleife** im IoT Controller, die daraus konkrete Lichtwerte macht.

## Grundbegriffe

- **mEDI** (melanopisch äquivalente Tageslicht-Beleuchtungsstärke, lx am Auge, nach
  DIN/TS 5031-100): Maß für die biologische Wirksamkeit von Licht auf die innere Uhr.
  Tagsüber sollte sie hoch sein (stabilisiert den zirkadianen Rhythmus, fördert
  Wachheit), abends niedrig (ermöglicht Melatoninausschüttung und Einschlafen).
- **CCT** (Correlated Colour Temperature, Kelvin): die Farbtemperatur des Lichts —
  warmweiß (≈ 2700 K) bis tageslichtweiß (≈ 6500 K).
- **Chronotyp (MSFsc)**: individuelles Timing der inneren Uhr, ermittelt per Munich
  Chronotype Questionnaire (MCTQ) als „Mitte des Schlafs an freien Tagen,
  schlafkorrigiert" in Uhr-Stunden. MSFsc ≈ 4 Uhr ist der Durchschnitt; kleinere Werte
  = Frühtypen („Lerchen"), größere = Spättypen („Eulen").

## 1. Generierung des Tagesverlaufs (Data Hub, `packages/curves`)

Der Hub liefert pro Raum zwei Kurven für den aktuellen Tag, jeweils als **stückweise
Polynomfunktionen** über die Tagesminuten 0–1439 (0–24 Uhr):

- **Ziel-mEDI-Kurve** — wie viel biologisch wirksames Licht die Person zu jeder
  Tageszeit erhalten soll, und
- **CCT-Präferenz-Kurve** — welche Farbtemperatur zu jeder Tageszeit passend ist.

Der Ablauf folgt diesem Schema:

![Ablauf der Kurvengenerierung](assets/algo-tagesverlauf.png)

*(editierbare draw.io-Quelle: [assets/algo-tagesverlauf.drawio.xml](assets/algo-tagesverlauf.drawio.xml))*

### Schritt 1 — Basiskurven laden

Ausgangspunkt sind die Standardkurven einer Referenzperson (durchschnittlicher
Chronotyp, ca. 30 Jahre, keine besonderen Präferenzen), definiert als Stützpunkte in
`packages/curves/src/baseCurves.js`:

- mEDI: nachts nahe 0 lx, steiler Anstieg am Morgen, Tagesplateau ≈ 250 lx, Abfall am
  Abend auf melatoninfreundliche Werte.
- CCT: warmweiß (2700 K) nachts und abends, glockenförmiger Anstieg auf ≈ 6000 K zur
  Mittagszeit.

### Schritt 2 — Chronotypabhängige Verschiebung (X-Achse)

Beide Kurven werden entlang der Zeitachse verschoben: Für einen Spättyp (z. B.
MSFsc = 6) beginnt der „Lichttag" zwei Stunden später, für einen Frühtyp entsprechend
früher. Verschiebung = `(MSFsc − 4,0) × 60` Minuten; die Tagesgrenzen (0:00/23:59)
werden mit den Randwerten neu verankert. → `shiftByChronotype()` in
`packages/curves/src/adapt.js`

### Schritt 3 — Altersabhängige Anpassung des Ziel-mEDI

Mit zunehmendem Alter sinkt die Transmission der Augenlinse; für dieselbe biologische
Wirkung wird mehr Licht benötigt. Die gesamte mEDI-Kurve wird daher **durchgehend
angehoben**: +1 % pro Lebensjahr über 30, gedeckelt beim Zweifachen des Basiswerts.
→ `adjustMediForAge()`

### Schritt 4 — Jahreszeitabhängige Anpassung der CCT-Amplitude

Die Amplitude der CCT-Kurve (Abstand zur warmen Basislinie 2700 K) wird über den Tag
des Jahres skaliert: im Sommer ausgeprägter (Faktor ≈ 1,1), im Winter flacher und
wärmer (Faktor ≈ 0,85), dazwischen kosinusförmig interpoliert.
→ `adjustCctAmplitudeForSeason()`

### Schritt 5 — Anpassung anhand der Routinen

Jede Routine verändert die CCT-Kurve lokal in einem Fenster von ±90 Minuten um ihre
Uhrzeit:

- **Aktivität** (z. B. Arbeiten, Sport) → Farbtemperatur wird um bis zu 600 K
  **angehoben** (aktivierend),
- **Entspannung** (z. B. Lesen, Abendruhe) → Farbtemperatur wird um bis zu 600 K
  **abgesenkt** (beruhigend).

Der Effekt läuft zu den Fensterrändern linear aus; außerhalb bleibt die Kurve
unverändert. → `applyRoutinesToCct()`

### Schritt 6 — Präferenzabhängige Anpassung der CCT-Maxima

Die persönliche Lichtfarb-Präferenz (−1 = „Ich mag sehr kaltes/blaues Licht nicht" bis
+1 = „Ich mag kühles Licht") skaliert die Kurve oberhalb der warmen Basislinie so um,
dass das Maximum auf den bevorzugten Wert abgesenkt (bis −1200 K) bzw. angehoben (bis
+500 K) wird — die Kurvenform bleibt erhalten, physikalische Grenzen (2700–6500 K)
werden eingehalten. → `applyCctPreference()`

### Ausgabe über die API

Die angepassten Stützpunkte werden per Newton-Interpolation in stückweise Polynome
überführt (`computeCurve()` in `packages/curves/src/polygen.js`) und über
`GET /api/rooms/:roomId/curves` ausgegeben:

```json
{
  "roomId": "gate",
  "date": "2025-01-15",
  "basedOnUser": "4a30…",
  "medi": { "segments": [{ "coeffs": [1.36, 0, 0], "start": 0, "end": 420 }, "…"] },
  "cct": { "segments": ["…"] }
}
```

Jedes Segment ist ein Polynom `f(x) = Σ coeffs[i] · x^i`, gültig für
`start ≤ x ≤ end` (x = Tagesminute).

### Wessen Kurven bekommt der Raum?

Die Kurven werden für die **erste im Raum anwesende Person** personalisiert. Ist
niemand (mit hinterlegten Präferenzen) anwesend, liefert der Hub die Basiskurven.
Das Zusammenführen der Präferenzen **mehrerer** Personen in einem Raum wurde im Rahmen
des Projekts noch nicht definiert und ist bewusst als Erweiterungspunkt offen gelassen.

## 2. Steuerungsschleife (IoT Controller)

Die Umsetzung der Kurven in konkrete Lichtwerte folgt diesem Schema:

![Ablauf der Lichtsteuerung](assets/algo-steuerung.png)

*(editierbare draw.io-Quelle: [assets/algo-steuerung.drawio.xml](assets/algo-steuerung.drawio.xml))*

Der Controller (`apps/iot-controller/src/controller.js`) arbeitet ereignis- und
zeitgesteuert:

- **Ereignis:** Über den WebSocket des Hubs abonniert er Anwesenheitsänderungen seines
  Raums. Bei jeder Änderung (Betreten/Verlassen) lädt er die Kurven neu und wendet sie
  sofort an.
- **Zeit:** Eine Dauerschleife berechnet **einmal pro Minute** die aktuellen Lichtwerte:

1. **Aktuelle Kurven auswerten:** Ziel-mEDI und Ziel-CCT an der aktuellen Tagesminute
   (`evaluateSegments()`).
2. **Tatsächliche Helligkeit im Raum abfragen:** In der Referenzimplementierung ein
   virtueller Sensor (`sensor.js`), der den Tageslichtanteil aus einem einfachen
   Sonnenstandsmodell und einem raumabhängigen Tageslichtfaktor schätzt. Im
   Projektsystem stehen hier reale, infrastrukturgebundene oder virtuelle Sensoren.
3. **Stellwerte berechnen und übergeben:** Das Kunstlicht muss nur die Lücke zwischen
   Ziel-mEDI und vorhandenem Tageslicht füllen:

   ```
   fehlendes mEDI = max(0, Ziel-mEDI − Tageslicht-mEDI)
   Dimmwert       = fehlendes mEDI / Leuchten-Maximalbeitrag × 100 %
   Farbtemperatur = Ziel-CCT
   ```

   Die Übergabe an die Beleuchtung erfolgt in der Referenzimplementierung als
   formatierte Konsolenausgabe; in einer realen Installation stünde hier die Anbindung
   an das Lichtsteuerungssystem des Raums (z. B. DALI/KNX-Gateways oder
   Kabinenbeleuchtungssysteme):

   ```
   [controller:gate] 14:31 | target mEDI 395 lx | daylight 452 lx | >>> set luminaires to 0 % @ 5635 K
   ```

Der letzte Steuerungs-Tick ist jederzeit unter `GET http://localhost:4200/status`
abrufbar.
