Add pictures and styling
|
|
@ -1,4 +1,4 @@
|
|||
baseURL = "https://jaseg.net/"
|
||||
languageCode = "en-us"
|
||||
title = "jaseg.net"
|
||||
theme = "ananke"
|
||||
theme = "hugo-classic"
|
||||
|
|
|
|||
|
|
@ -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`_.
|
||||
|
||||
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.
|
||||
.. raw:: html
|
||||
|
||||
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.
|
||||
<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
|
||||
|
||||
<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>
|
||||
|
||||
`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">}}
|
||||
|
|
|
|||
BIN
static/images/driver_ringing_strong.jpg
Normal file
|
After Width: | Height: | Size: 279 KiB |
BIN
static/images/driver_ringing_weak.jpg
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
static/images/electronics_whole.jpg
Normal file
|
After Width: | Height: | Size: 428 KiB |
BIN
static/images/preamp_back.jpg
Normal file
|
After Width: | Height: | Size: 332 KiB |
BIN
static/images/preamp_front.jpg
Normal file
|
After Width: | Height: | Size: 301 KiB |
BIN
static/images/preamp_schematic.jpg
Normal file
|
After Width: | Height: | Size: 271 KiB |
BIN
static/images/spectrograph_step1_parts.jpg
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
static/images/spectrograph_step2.jpg
Normal file
|
After Width: | Height: | Size: 271 KiB |
BIN
static/images/spectrograph_step3.jpg
Normal file
|
After Width: | Height: | Size: 250 KiB |
BIN
static/images/spectrograph_step4_complete.jpg
Normal file
|
After Width: | Height: | Size: 297 KiB |
1
themes/hugo-classic
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit e28d1cb6083bbe0be304650ec1122b4be7862a97
|
||||