feat: Farbrechnung mit CIE-Helligkeit und CW/WW-Mischung

This commit is contained in:
Michael Sauer
2026-09-11 13:06:19 +02:00
parent 6e6cfeaf3a
commit 42eeb0e0eb
6 changed files with 236 additions and 1 deletions
+73
View File
@@ -0,0 +1,73 @@
#include "light/nl_color.h"
uint16_t nl_cie_level(uint16_t bri)
{
float lightness, y;
uint32_t v;
if (bri == 0)
return 0;
if (bri >= NL_BRI_SCALE)
return 65535;
lightness = (float)bri / 100.0f; /* 0..100 */
if (lightness <= 8.0f) {
y = lightness / 903.3f;
} else {
float t = (lightness + 16.0f) / 116.0f;
y = t * t * t;
}
v = (uint32_t)(y * 65535.0f + 0.5f);
return (uint16_t)(v == 0 ? 1 : (v > 65535 ? 65535 : v));
}
uint16_t nl_ct_cw_fraction(uint16_t ct, uint16_t ct_min, uint16_t ct_max)
{
uint32_t m, mw, mc;
if (ct <= ct_min)
return 0;
if (ct >= ct_max)
return 65535;
m = 1000000u / ct;
mw = 1000000u / ct_min;
mc = 1000000u / ct_max;
return (uint16_t)(((mw - m) * 65535u) / (mw - mc));
}
static uint8_t to8(uint32_t lin)
{
return (uint8_t)((lin * 255u + 32767u) / 65535u);
}
void nl_visual_to_channels(const nl_visual_t *v, uint16_t ct_min, uint16_t ct_max, uint8_t out[NL_CH_COUNT])
{
uint32_t level = nl_cie_level(v->bri);
for (int i = 0; i < NL_CH_COUNT; i++)
out[i] = 0;
if (level == 0)
return;
if (v->mode == NL_MODE_COLOR) {
uint32_t lin[3];
int maxi = 0;
lin[0] = level * v->r / 255u;
lin[1] = level * v->g / 255u;
lin[2] = level * v->b / 255u;
for (int i = 0; i < 3; i++) {
out[NL_CH_R + i] = to8(lin[i]);
if (lin[i] > lin[maxi])
maxi = i;
}
/* keep the lowest brightness step visible */
if (!out[NL_CH_R] && !out[NL_CH_G] && !out[NL_CH_B] && lin[maxi] > 0)
out[NL_CH_R + maxi] = 1;
} else {
uint32_t cw = level * nl_ct_cw_fraction(v->ct, ct_min, ct_max) / 65535u;
uint32_t ww = level - cw;
out[NL_CH_CW] = to8(cw);
out[NL_CH_WW] = to8(ww);
if (!out[NL_CH_CW] && !out[NL_CH_WW])
out[cw >= ww ? NL_CH_CW : NL_CH_WW] = 1;
}
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef NL_COLOR_H
#define NL_COLOR_H
#include <stdint.h>
#include "board/nl_board.h"
#include "light/nl_light_types.h"
/* Perceived brightness (0..NL_BRI_SCALE) to linear light (0..65535), CIE 1931 lightness.
* Every bri > 0 yields at least 1. */
uint16_t nl_cie_level(uint16_t bri);
/* Share of cold white (0..65535) for ct, linear in mired between ct_min and ct_max. */
uint16_t nl_ct_cw_fraction(uint16_t ct, uint16_t ct_min, uint16_t ct_max);
/* 8 bit channel values for the LED drivers. In white mode cw + ww equals the brightness. */
void nl_visual_to_channels(const nl_visual_t *v, uint16_t ct_min, uint16_t ct_max, uint8_t out[NL_CH_COUNT]);
#endif
+29
View File
@@ -0,0 +1,29 @@
#ifndef NL_LIGHT_TYPES_H
#define NL_LIGHT_TYPES_H
#include <stdbool.h>
#include <stdint.h>
#define NL_BRI_SCALE 10000u /* visual brightness fixed point: 10000 = 100.00 % */
typedef enum { NL_MODE_COLOR = 0, NL_MODE_WHITE = 1 } nl_light_mode_t;
typedef enum { NL_POB_LAST = 0, NL_POB_ON = 1, NL_POB_OFF = 2 } nl_pob_t;
/* Light state as seen by the API (brightness in percent). */
typedef struct {
bool on;
uint8_t brightness; /* 1..100 */
nl_light_mode_t mode;
uint8_t r, g, b;
uint16_t ct; /* Kelvin */
} nl_light_state_t;
/* What is currently visible; bri = 0 means dark. */
typedef struct {
uint16_t bri; /* 0..NL_BRI_SCALE */
nl_light_mode_t mode;
uint8_t r, g, b;
uint16_t ct;
} nl_visual_t;
#endif