# How to configure an LED to emit white light of a certain color temperature?

I'm working on an open source hardware project for a video/photo light, and it involves a fair bit of color math. I am trying to find my way from Color Temperature (CT) in Kelvin to current values used to drive the RGB LED's. I've struggled with this for quite a while now :-)

This is what I'm currently doing:

I take any CT value between 1800 and 15000 Kelvin and find the color coordinates. The CT is a value along the planchian locus of the CIE 1931 color space. I found a spline function here: http://en.wikipedia.org/wiki/Planckian_locus (the Approximation section). For a CT of 4000 I get x=0.380528339, y=0.376733566 and that looks correct to me.

Next I convert the xy coordinates to XYZ. I have semi-randomly set Y (brightness) to 0.6 to not saturate the LED current drivers.

Y = 0.6
X = x * (Y/y)
Z = (1-x-y) * (Y/y)


Next, I do a matrix transformation. I take the CIE standard observer at the same wavelengths as the LED emitters:

Xbar    0.907244    0.1096  0.0579
Ybar    0.417036    0.7932  0.1693
Zbar    0.000220    0.0572  0.6162


I take a spectrometer reading of the individual LED emitters. The spectrometer gives me the CIE XYZ values for the Red, Green and Blue LEDs:

X    2.14   0.88    1.14
Y    0.95   2.7 0.59
Z    -0.04  0.33    6.73


I found the MINVERSE() function in Excel to invert the LED XYZ readings. Then use MMULT() to multiply the CIE standard observer with the inverted LED XYZ, and I get:

0.8093637   -0.0775645  -0.1122626
0.0302540   0.7946191   -0.0104841
-0.0136168  0.0499041   0.1587569


So now I take the above array and multiply it by the XYZ values previously calculated from the Color Temperature:

R = (0.8093637 * X) + (-0.0775645 * Y) + (-0.1122626 * Z);
G = (0.0302540 * X) + (0.7946191 * Y) + (-0.0104841 * Z);
B = (-0.0136168 * X) + (0.0499041 * Y) + (0.1587569 * Z);


All this is supposed to transform the CT in CIE 1931 color space to the color response of the LED emitters.

But when I drive the LEDs with these RGB values, I get a compressed scale. 2000 K input measures as 4000 K. 15000 K input measures as 8000 K. I'm not really a math person and I can't for the life of me figure out where I go wrong!

EDIT: to clarify

-
When you say "I take a spectrometer reading of the Red, Green and Blue LED emitters", what are the values of (R,G,B) respectively? (You mean colorimeter because spectrophotometry returns the spectral power distribution over a range of wavelengths.) – Emre Jul 7 '11 at 23:40
@Emre The Spectrometer software gives me the XYZ values as well. – morten Jul 8 '11 at 14:02

OK... first of all, could you clarify a few things:

1. Where is that "CIE standard observer" matrix from, and what is it for? I assume it's for transforming from something to the XYZ color space, but what?
2. Exactly which matrices did you invert and multiply, and in what order, to get the third matrix you gave? I can't get it from any of the obvious permutations of the other two matrices.
3. What do you need the first and third matrices for, anyway? If the second matrix you have, in effect, gives a transformation from LED intensities to the XYZ color space, shouldn't it be enough to just multiply the XYZ color vector with its inverse?

Anyway, doing just that with the values you gave, I get the XYZ to LED intensity transformation matrix $$M = \begin{bmatrix} \phantom{+}2.14 & 0.88 & 1.14 \\ \phantom{+}0.95 & 2.7\phantom{0} & 0.59 \\ -0.04 & 0.33 & 6.73 \end{bmatrix}^{-1} = \begin{bmatrix} \phantom{+}0.5398 & -0.1665 & -0.0768 \\ -0.1927 & \phantom{+}0.4338 & -0.0054 \\ \phantom{+}0.0127 & -0.0223 & \phantom{+}0.1484 \end{bmatrix},$$ the XYZ color vector $$V_{XYZ} = \begin{bmatrix} 0.6060 \\ 0.6\phantom{000} \\ 0.3866 \end{bmatrix}$$ and the LED intensity vector $$V_{RGB} = M \cdot V_{XYZ} = \begin{bmatrix} 0.1975 \\ 0.1414 \\ 0.0517 \end{bmatrix}.$$ Does that give the output you expect?

Also, just to be sure, are the LED inputs really linear functions of the brightness? If not, you may have to apply an extra correction step for that.

-
I have tried to clarify the question above. The LED drivers are linear. – morten Jul 8 '11 at 14:46
The CIE values are from CIE Standard Colorimetric Observer Data cis.rit.edu/mcsl/online/cie.php – morten Jul 8 '11 at 14:51
That data you link to is the same that I was using; in that data, the XYZ values you use correspond to wavelengths of 485nm, 525nm, and roughly 617nm, which are not the wavelengths you specify for the LEDs. In a comment to my answer, you write "In my question above I did not use the supposedly correct tristimulus values" -- I don't understand that -- are you saying you took the values from the data you linked to, but then used different values instead? – joriki Jul 8 '11 at 15:04
OK, in that case I don't think you need the CIE matrix for anything: just invert the LED-to-XYZ matrix you got from the spectrometer readings and multiply your XYZ colors with that. – Ilmari Karonen Jul 9 '11 at 3:35

This is just a first attempt to figure out what's wrong; it might take some interaction via the comments to get to the bottom of this.

When you say "I take a spectrometer reading of the Red, Green and Blue LED emitters" and then you write a matrix with rows labeled X, Y, Z, do you mean that you send some unit of current through each of the three LEDs and measure the emitted light, and the three columns specify X, Y, Z values that you measure for the red, green and blue LED, respectively? If so, are these CIE XYZ values that you measure?

If that's what you mean, then I don't see how the CIE standard observer gets into it. Since you want to generate light with a particular set of CIE XYZ coordinates, corresponding to a particular colour temperature (and your arbitrary brightness), and you know the CIE XYZ values generated by a unit of current through each of the LEDs, shouldn't you just invert that one matrix and apply it to the desired XYZ values to obtain the required currents?

Another question (which may be irrelevant if you don't actually need the CIE standard observer): You write "I take the CIE standard observer:" and then specify a matrix with rows labeled X, Y, Z. The CIE standard observer is specified by tristimulus values along the entire spectrum; the columns of the matrix seem to correspond roughly to the tristimulus values at wavelengths 485nm, 525nm, 617nm -- why these?

-
The idea was to take a CIE XYZ reading of each emitter color: Red, Green and Blue. Then take the CIE XYZ tristimulus values for the same wavelengths as the LEDs (591, 532, 471). Then multiply the tristimulus XYZ matrix with the inverse of the LED XYZ matrix in order to be able to transform the values. – morten Jul 8 '11 at 14:31
In my question above I did not use the supposedly correct tristimulus values. If I do use the 'correct' tristimulus values, I get a worse result than ever :-) – morten Jul 8 '11 at 14:34
@morten: I'm afraid I'm not following you. If the LEDs emit monochromatic light, then your CIE XYZ measurements should be proportional to the tristimulus values that the CIE standard observer specifies for those wavelengths, no? Then that matrix product would merely be a diagonal matrix with diagonal entries representing the luminous efficiencies of the LEDs. Also, if you have two matrices that transform between XYZ space and certain primaries and you form the product of one with the inverse of the other, then depending on which way around you do it, you get something that transforms from ... – joriki Jul 8 '11 at 14:56
XYZ to XYZ or from primaries to primaries, but never something that transforms from XYZ to the primaries, which, if I understand your question correctly, is what you want? – joriki Jul 8 '11 at 14:57
Hi. I wanted to come back and report that the issue is solved (after many days of banging-head-against-wall). One (main) issue was as both of you suggest that I don't need to figure CIE into this. CIE is already there, as I start out with coordinates in the CIE color space. Brain malfunction on my part. Happens often. Fixing this did not seem to improve things at first. But I found a hardware error in the blue driver. On top of this, the XYZ coordinates I got from the measurements was not the expected CIE XYZ triplet. Well, trippel bug makes life hard:-) Thanks for the help!! – morten Jul 11 '11 at 20:25