diff options
-rw-r--r-- | config.toml | 2 | ||||
-rw-r--r-- | content/posts/led-characterization.rst | 183 | ||||
-rw-r--r-- | static/images/driver_ringing_strong.jpg | bin | 0 -> 285952 bytes | |||
-rw-r--r-- | static/images/driver_ringing_weak.jpg | bin | 0 -> 292100 bytes | |||
-rw-r--r-- | static/images/electronics_whole.jpg | bin | 0 -> 438776 bytes | |||
-rw-r--r-- | static/images/preamp_back.jpg | bin | 0 -> 340446 bytes | |||
-rw-r--r-- | static/images/preamp_front.jpg | bin | 0 -> 308167 bytes | |||
-rw-r--r-- | static/images/preamp_schematic.jpg | bin | 0 -> 277414 bytes | |||
-rw-r--r-- | static/images/spectrograph_step1_parts.jpg | bin | 0 -> 284254 bytes | |||
-rw-r--r-- | static/images/spectrograph_step2.jpg | bin | 0 -> 277234 bytes | |||
-rw-r--r-- | static/images/spectrograph_step3.jpg | bin | 0 -> 256192 bytes | |||
-rw-r--r-- | static/images/spectrograph_step4_complete.jpg | bin | 0 -> 303777 bytes | |||
m--------- | themes/hugo-classic | 0 |
13 files changed, 158 insertions, 27 deletions
diff --git a/config.toml b/config.toml index c9affb8..a89d8df 100644 --- a/config.toml +++ b/config.toml @@ -1,4 +1,4 @@ baseURL = "https://jaseg.net/" languageCode = "en-us" title = "jaseg.net" -theme = "ananke" +theme = "hugo-classic" diff --git a/content/posts/led-characterization.rst b/content/posts/led-characterization.rst index e856a09..411d156 100644 --- a/content/posts/led-characterization.rst +++ b/content/posts/led-characterization.rst @@ -49,14 +49,33 @@ transformation. An illustration of the HSV color space as a cylinder. `Picture by SharkD <https://commons.wikimedia.org/wiki/File:HSV_color_solid_cylinder.png>`_ (`CC BY-SA 3.0`_), from Wikimedia Commons. +CIE 1931 XYZ is much larger than any other color space, which is why it is a good basis to express other color spaces +in. In XYZ there are many coordinates that are outside of what the human eye can perceive. Below is an illustration of +the sRGB space within XYZ. The wireframe cube is (0,0,0) to (1,1,1) in XYZ. The colorful object in the middle is what +of sRGB fits inside XYZ, and the lines extending out from it indicate the space that can be expressed in sRGB but not in +XYZ. The fat white curve is a projection of the *monochromatic spectral locus*, that is the curve of points you get in +XYZ for pure visible wavelengths. + +As you can see, sRGB is *much* smaller than XYZ or even the part within the monochromatic locus that we can perceive. In +particular in the blues and greens we loose *a lot* of colors to sRGB. + +.. raw:: html + + <video width="480" controls loop> + <source src="/video/sRGB.mkv" type="video/h264"> + <source src="/video/sRGB.webm" type="video/webm"> + Your browser does not support the HTML5 video tag. + </video> + +`mkv/h264 download </video/sRGB.mkv>`__/`webm download </video/sRGB.webm>`__ + The wrong colors I got when fading between colors were caused by this coordinate transformation being askew. Thinking over the problem, there are several sources for imperfections: * The LED driver may not be entirely linear. For most modulations such as PWM the brightness will be linear starting from a certain value, but there is probably an offset caused by imperfect edges of the LED current. This offset can be compensated with software calibration. I built a calibration setup for driver linearity in the `multichannel LED - driver`_ project. - .. FIXME picture with ringing on edges + driver`_ project. Below are pictures of ringing on the edges of an LED driver's waveform. * The red, green and blue channels of the LEDs used on the LED tape are not matched. This skews the RGB color space. In practice, the blue channel of my RGB tape to me *looks* much brighter than the red channel. @@ -65,6 +84,23 @@ over the problem, there are several sources for imperfections: may be of a slightly different hue compared to the reference red used in `sRGB`_ which would also skew the RGB color space. +.. raw:: html + + <figure> + <figure class="side-by-side"> + <img src="/images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about + 100% overshoot during about 70% of the cycle time."> + <figcaption>The shift register logic output of the multichannel LED driver directly driving a small mosfet's + gate through an inch or so of PCB trace caused extremely bad ringing at high driving + frequencies.</figcaption> + </figure><figure class="side-by-side"> + <img src="/images/driver_ringing_weak.jpg" alt="Weak ringing on the LED voltage waveform edge at about 30% + overshoot during about 20% of the cycle time."> + <figcaption>Adding a resistor dampened the ringing somewhat, but ultimately it cannot be eliminated + entirely.</figcaption> + </figure> + </figure> + These last two errors are tricky to compensate. What I needed for that was basically a model of the *perceived* colors of the LED tape's color channels. A way of doing his is to record the spectra of all color channels and then evaluate their respective XYZ coordinates. If all three channels are measured in one go with the same setup the relative @@ -74,7 +110,7 @@ To map any color to the LEDs, the color's XYZ coordinates simply have to be mapp produced by these three points within XYZ. LEDs are extremely linear in their luminous flux vs. current characteristic so this model will be adequate. The spectral integrals mapping the channels' measured responses to XYZ need only be calculated once and their results can be used as scaling factors thereafter. -.. FIXME: Add led/current graph here +.. FIXME Add led/current graph here Measuring the spectrum ---------------------- @@ -110,7 +146,29 @@ specific to the semiconductor used and is quite precise. White LEDs are in fact and re-emits a broader spectrum of more yellow-ish wavelengths instead. The final LED spectrum is a superposition of both spectra, with some of the original blue light leaking through the phosphor mixing with the broadband yellow spectrum of the phosphor. -.. FIXME: Cardboard spectrograph pictures + +.. raw:: html + + <figure> + <figure class="side-by-side"> + <img src="/images/spectrograph_step1_parts.jpg"> + <figcaption>The ingredients. The cup of coffee and Madoka Magica DVD set are essential to the eventual + function of the appartus.</figcaption> + </figure><figure class="side-by-side"> + <img src="/images/spectrograph_step2.jpg"> + <figcaption>Step 1: Cut to size and mark down all holes as described in <a + href="http://www.candac.ca/candacweb/sites/default/files/BuildaSpectroscope.pdf">the manual</a></figcaption> + </figure> + <figure class="side-by-side"> + <img src="/images/spectrograph_step3.jpg"> + <figcaption>Step 2: Cut out all holes</figcaption> + </figure><figure class="side-by-side"> + <img src="/images/spectrograph_step4_complete.jpg"> + <figcaption>The finished result with the back side showing. The viewing window is on the bottom of the other + side.</figcaption> + </figure> + </figure> + Now that I had a spectrograph, I needed a somewhat predictable way of measuring the spectrum it gave me. @@ -121,7 +179,7 @@ Pointing a camera at the spectrograph would be the obvious thing to do. This pro flaw: I wanted to acquire quantitative measurements of brightness across the spectrum. Since I don't have a precise technical datasheet specifying the spectral response of any of my cameras I can't compare the absolute brightness of different colors on their pictures. Some other sensor was needed. -.. FIXME: Spectrum picture +.. FIXME Spectrum picture Measuring light intensity ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -150,7 +208,38 @@ rejection and a regular non-inverting amplifier using another op-amp from the sa transimpedance amplifier output. I put all the passives setting amplifier response (the gain-setting resistors and the filter resistor and capacitors) on a small removable adapter so I could easily change them if necessary. I put a small trimpot on the virtual ground both amplifers use as a reference so I could trim that if necessary. -.. FIXME: Add transimp amp schematic and build pics + +.. raw:: html + + <figure> + <img src="/images/preamp_schematic.jpg" alt="A drawing of the photodiode preamplifier's schematic"> + <figcaption>The photodiode preamplifier schematic. Schematic drawn with an unlicensed copy of + DaveCAD.</figcaption> + </figure> + +Following are pictures of the preamplifier board. The connectors on the top-left side are two copies of the analog +signal for the ADC and a small panel meter. The SMA connector is used as the photodiode input since coax cables are +generally low-leakage and have built-in shielding. The circuit is powered via the micro-USB connector and the analog +ground bias voltage can be adjusted using the trimpot. + +For easy replacement, all passives setting gain and frequency response are on a small, pluggable carrier PCB made from a +SMD-to-DIP adapter. + +Flying-wire construction is just fine for this low-frequency circuit. In a high-speed photodiode preamp, the +transimpedance amplifier circuit would be highly sensitive to stray capacitance, but we're not aiming at high speed +here. + +.. raw:: html + + <figure> + <figure class="side-by-side"> + <img src="/images/preamp_front.jpg"> + <figcaption>The front side of the preamplifier board.</figcaption> + </figure><figure class="side-by-side"> + <img src="/images/preamp_back.jpg"> + <figcaption>The wiring of the photodiode preamp.</figcaption> + </figure> + </figure> Given a way to measure intensity what remains missing is a way to scan a single photodiode across the spectrum. @@ -164,8 +253,6 @@ was an `A4988`_ module that required at least 8V motor drive voltage. I used a s module to generate a stable 10V supply for the motor driver, with the USB's 5V rail used as a logic supply for the motor driver. -.. FIXME: Add picture of photodiode stage here - The `SFH2701`_ can easily be mounted to the linear stage using a small SMD breakout board glued in place with thin wires connecting it to the transimpedance amplifier. The DVD drive linear stage is not very strong so it is important that this wire does not put too much strain on it. @@ -176,7 +263,20 @@ parallel to the linear stage allowing precise alignment. The whole unit with photodiode preamplifier, linear stage, photodiode and stepper motor driver finally looks like this: -.. FIXME: pics of linear stage unit electronics +.. raw:: html + + <figure> + <img src="/images/electronics_whole.jpg" alt="The complete electronics setup of the spectrograph. In the back + there is the DVD drive stepper stage. In front of it, mounted on a piece of wood are a small USB-to-12V + switching-regulator module to power the stepper motor in the top left, below on the bottom left is the + photodiode preamp and on the right is a breadboard with the stepper driver module and lots of jumper wires + interconnecting everything. On the right of the breadboard, a buspirate is attached to interface everything to a + computer. On the bottom edge of the piece of wood, two LED panel meters are mounted for readout of the preamp + output and the stepper supply voltages."> + <figcaption>The complete electronics setup. The buspirate on the right interfaces to a computer and controls the + stepper driver and ADC'es the preamp output. The two panel meters show the preamp output and stepper voltage for + setup.</figcaption> + </figure> The projection of the spectrum can be adjusted by moving the light source relative to the entry slot and by moving around the grating DVD. @@ -211,6 +311,8 @@ check for when I made some mistake easy to spot in the resulting data. After one color channel is captured, the LED tape has to be manually set to the next color and the next measurement can begin. +.. FIXME raw measurement plot + Data analysis ~~~~~~~~~~~~~ @@ -244,7 +346,9 @@ estimate is produced by a least-squares fit of a linear function. This fine esti sensitivity correction of all original measurements and the scale is changed from stepper motor step count to wavelength in nanometers. -.. FIXME: calibration for brightness imbalance due to wedge-shaped projection of spectrum +.. FIXME processed spectrum plot + +.. FIXME calibration for brightness imbalance due to wedge-shaped projection of spectrum Color space mapping ******************* @@ -258,31 +362,58 @@ The result are three color coordinates X, Y and Z for each channel R, G and B yi written as a matrix conversion between XYZ color space and LED-RGB color space is as simple as multiplying that matrix (or its inverse) and a vector from one of the color spaces. -If you view the three channels' color coordinates as vectors in XYZ space, the set of colors that can be produced with -this LED tape is described by the `parallelepiped`_ spanned by the three channel vectors. +In XYZ space, the set of colors that can be produced with this LED tape is described by the `parallelepiped`_ spanned by +the three channel's XYZ vectors. In the following figures, you can see a three-dimensional model of the RGB LED's color +space (colorful) as well as sRGB (white) for comparison plotted within CIE 1931 XYZ. There is no natural map to scale +both so for this illustration the LED color space has been scaled to fit. These figures were made with blender and a few +lines of python. The blender project file including all settings and the python script to generate the color space +models can be found in the `project repo`_. + +.. raw:: html + + <video width="480" controls loop> + <source src="/video/led_within_srgb_scale=1.0.mkv" type="video/h264"> + <source src="/video/led_within_srgb_scale=1.0.webm" type="video/webm"> + Your browser does not support the HTML5 video tag. + </video> + +`mkv/h264 download </video/led_within_srgb_scale=1.0.mkv>`__/`webm download </video/led_within_srgb_scale=1.0.webm>`__ + + +As you can see, the result is pretty disappointing. The LED's color space parallepiped is very narrow, which is because +the blue channel is much brighter than the other two channels. An easy fix for this is to scale-up the RGB space and +drop any values outside XYZ. The scaling factor is a trade-off between color space coverage and brightness. You can +produce the most colors when you clip all channels to brightness of the weakest channel (green in this case), but that +will make the result very dim. Scaling brightness like that stretches the RGB parallelepiped along its major axis. Up to +a point the number of possible colors (the gamut) increases at expense of maximum brightness. When the parallelepiped is +stretched far enought for all three channel vectors to be outside the 1,1,1 XYZ-cube, maximum brightness continues to +decrease but the gamut stays constant. I don't know a simple scientific way to solve this problem, so I just played +around with a couple of factors and settled on 2.5 as a reasonable compromise. Below is an illustration. + +.. raw:: html -The last task is to decide on a scaling factor to map XYZ space to RGB space. Both are limited to values between 0.0 and -1.0. The LEDs cannot go below off or above fully on. For any LED tape there will be a set of colors that are outside -the range that this tape can produce. + <video width="480" controls loop> + <source src="/video/led_within_srgb_fancy_camera_path_scale=2.5.mkv" type="video/h264"> + <source src="/video/led_within_srgb_fancy_camera_path_scale=2.5.webm" type="video/webm"> + Your browser does not support the HTML5 video tag. + </video> -A scaling factor can be used to increase the number of XYZ coordinates that can be mapped to RGB colors the tape *can* -produce by stretching the RGB parallelepiped along its major axis. Up to a point the number of possible colors (the -gamut) increases at expense of maximum brightness. When the parallelepiped is stretched far enought for all three -channel vectors to be outside the 1,1,1 XYZ-cube, maximum brightness continues to decrease but the gamut stays constant. +`mkv/h264 download </video/led_within_srgb_fancy_camera_path_scale=2.5.mkv>`__/`webm download </video/led_within_srgb_fancy_camera_path_scale=2.5.webm>`__ Firmware implementation ----------------------- -In the end, the above measurements yield two matrices: One for mapping XYZ to RGB, and one for mapping RGB to XYZ. I -chose the CIE 1931 XYZ color space as a basis for the firmware because it is most popular. Mapping a color coordinate in -one color space to the other is as simple as performing nine floating-point multiplications and six additions. Mapping -Lab or Lch to RGB is done by first mapping Lab/Lch to XYZ, then XYZ to RGB. Lab to XYZ is somewhat complex since it -requires a floating-point power for gamma correction, but any self-respecting libc will have one of those so this is -still no problem. Lch also requires floating-point sine and cosine functions, but these should still be no problem on -most hardware. +In the end, the above measurements yield two matrices: One for mapping XYZ to RGB, and one for mapping RGB to XYZ. Of +the several versions of CIE XYZ I chose the CIE 1931 XYZ color space as a basis for the firmware because it is most +popular. Mapping a color coordinate in one color space to the other is as simple as performing nine floating-point +multiplications and six additions. Mapping Lab or Lch to RGB is done by first mapping Lab/Lch to XYZ, then XYZ to RGB. +Lab to XYZ is somewhat complex since it requires a floating-point power for gamma correction, but any self-respecting +libc will have one of those so this is still no problem. Lch also requires floating-point sine and cosine functions, but +these should still be no problem on most hardware. My implementation of these conversions in the ESP8266 firmware of my `Wifi LED driver`_ can be found `on Github`_. .. _`on Github`: https://github.com/jaseg/esp_led_drv/blob/master/user/led_controller.c +.. _`project repo`: https://github.com/jaseg/led_drv .. _`Wifi LED driver`: {{<ref "posts/wifi-led-driver.rst">}} .. _`small driver`: {{<ref "posts/wifi-led-driver.rst">}} .. _`multichannel LED driver`: {{<ref "posts/multichannel-led-driver.rst">}} diff --git a/static/images/driver_ringing_strong.jpg b/static/images/driver_ringing_strong.jpg Binary files differnew file mode 100644 index 0000000..0419a0e --- /dev/null +++ b/static/images/driver_ringing_strong.jpg diff --git a/static/images/driver_ringing_weak.jpg b/static/images/driver_ringing_weak.jpg Binary files differnew file mode 100644 index 0000000..12f9c5d --- /dev/null +++ b/static/images/driver_ringing_weak.jpg diff --git a/static/images/electronics_whole.jpg b/static/images/electronics_whole.jpg Binary files differnew file mode 100644 index 0000000..faaf751 --- /dev/null +++ b/static/images/electronics_whole.jpg diff --git a/static/images/preamp_back.jpg b/static/images/preamp_back.jpg Binary files differnew file mode 100644 index 0000000..0af495d --- /dev/null +++ b/static/images/preamp_back.jpg diff --git a/static/images/preamp_front.jpg b/static/images/preamp_front.jpg Binary files differnew file mode 100644 index 0000000..62fad28 --- /dev/null +++ b/static/images/preamp_front.jpg diff --git a/static/images/preamp_schematic.jpg b/static/images/preamp_schematic.jpg Binary files differnew file mode 100644 index 0000000..6be7bbd --- /dev/null +++ b/static/images/preamp_schematic.jpg diff --git a/static/images/spectrograph_step1_parts.jpg b/static/images/spectrograph_step1_parts.jpg Binary files differnew file mode 100644 index 0000000..107220a --- /dev/null +++ b/static/images/spectrograph_step1_parts.jpg diff --git a/static/images/spectrograph_step2.jpg b/static/images/spectrograph_step2.jpg Binary files differnew file mode 100644 index 0000000..b678372 --- /dev/null +++ b/static/images/spectrograph_step2.jpg diff --git a/static/images/spectrograph_step3.jpg b/static/images/spectrograph_step3.jpg Binary files differnew file mode 100644 index 0000000..acd6d5e --- /dev/null +++ b/static/images/spectrograph_step3.jpg diff --git a/static/images/spectrograph_step4_complete.jpg b/static/images/spectrograph_step4_complete.jpg Binary files differnew file mode 100644 index 0000000..d23560d --- /dev/null +++ b/static/images/spectrograph_step4_complete.jpg diff --git a/themes/hugo-classic b/themes/hugo-classic new file mode 160000 +Subproject e28d1cb6083bbe0be304650ec1122b4be7862a9 |