A while ago, there was a thread concerning choosing PWM-controlled brightness values for displays
https://www.eevblog.com/forum/beginners/dividing-0-to-100-exponetially-for-dimming-light/50/ . There were a number of approaches including, of course, those based on the CIE 1931 “standard”. The basic idea is that you usually do not want to increase or decrease brightness in a linear fashion (at least once you are beyond the very dim levels), but rather, exponentially.
In contrast to starting with an array size and then calculating the values, I had played around with a difference threshold (representing luminosity changes) between values. Array values can then be determined from minimum (0) to maximum (depending on bit size) PWM duty cycle value. The difference threshold is pre-determined rather than the size of the array.
I have been meaning to “finish” that mini-project (at least getting it out of my head) and the result is this post. I’m not saying it is a big deal, I am saying that this is how I finished the project.
The resulting program (listing below) is written in C++ using Visual Studio 2019 and is for the Windows command line. You enter bit size and difference threshold as arguments and it prints out the array values, which can then be cut and pasted into a program. I rarely use C++ and I am sure that I missed some optimization and the like, but it seems to work fine for me.
Pending discovery of some catastrophic blunder, it is finished and out of my head.
(edit: I did fix a >= and replaced a printf)
//----------------------------------------------------------------------------------------------------
// WeberStevens01.cpp
// DrG March, 2021 / Visual Studio C++ command line program
//
// Use at your own risk.
//
// Prints out an array of values based on the command line arguments 'bit size' and 'difference threshold'.
// The array values follow the linear+exponential function associated with CIE 1931 perceived lightness formula
// Rather than specifying the number of values in the array, a value for the difference threshold (in luminosity)
// is specified. Smaller difference threshold values produce larger arrays. The maximum value (defined by bitsize)
// and the minimum value of '0' are always used.
// The values are designed to aid in creating PWM-controlled brightness functions.
#include <iomanip>
#include <iostream>
#include <string>
using std::cout;
// note VS C++ array elements are initialized to '0' - you could explicitedly initialize them
unsigned int Varray[262144]; // 2^18
int main(int argc, char* argv[])
{
int bitsize, y, counter = 0;
float dthreshold; // JND
unsigned int MAXDC;
float L = 0, lastL = 0, PWMp = 0, Idiff;
//std::cout << std::fixed << std::showpoint; // setprecision is probably better
std::cout << std::setprecision(3);
// validate the command line argument number
if (argc < 3) {
std::cout << "Usage: WeberStevens <bit size> <difference_threshold>\n";
std::cout << "where bit size > 3 and < 19\n";
std::cout << "and difference_threshold > 0 and < 1.0\n\n";
exit(1);
}
// validate bitsize argument
bitsize = std::stoi(argv[1]);
if (bitsize < 4 || bitsize > 18) {
std::cout << "Usage error: bit size must be > 3 and < 19\n";
exit(1);
}
// validate difference_threshold argument
dthreshold = std::stof(argv[2]);
if (dthreshold < 0 || dthreshold >= 1.0) {
std::cout << "Usage error: difference_threshold must be > 0 and < 1.0\n";
exit(1);
}
std::cout << "Working using: bit size=" << bitsize;
std::cout << " and difference threshold=" << dthreshold << "\n";
MAXDC = (unsigned int)(pow(2.0, bitsize) - 1.0f);
for (y = MAXDC; y > 0; y--) {
PWMp = (((float)y / (float)MAXDC) * 100.0f);
if (PWMp > 8.0f) {
L = (float)pow(((PWMp + 16.0f) / 116.0f), 3);
}
else {
L = (PWMp / 903.3f);
}
// check if the difference exceeds threshold
Idiff = (L - lastL) / L;
if (abs(Idiff) < dthreshold) {
// nothing here unless you want to print a marker
}
else {
Varray[counter] = y;
lastL = L; // exceeds the difference threshold (JND)
counter++;
}
}
// print out the good values
std::cout << "Finished with " << (counter + 1) << " values.\n\n";
std::cout << "A[" << counter + 1 << "]={";
std::cout << "0"; // 0 will always be valid as the last value since last-1 is non-zero which will always exceed dthreshold
for (y = MAXDC; y >= 0; y--) {
if (Varray[y] != 0) {
std::cout << "," << std::to_string(Varray[y]);
}
}
std::cout << "};\n\n"; //
exit(0);
}
// finished
Here is some sample output:
>weberstevens01 10 .25
Working using: bit size=10 and difference threshold=0.25
Finished with 38 values...
A[38]={0,1,2,3,4,5,7,9,12,15,19,25,32,40,51,64,80,99,120,142,166,192,220,250,282
,317,355,396,440,487,538,593,652,716,784,858,937,1023};
>weberstevens01 8 .10
Working using: bit size=8 and difference threshold=0.1
Finished with 60 values...
A[60]={0,1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,22,25,28,31,34,37,40,43,46,49,52,56
,60,64,68,72,76,80,84,89,94,99,104,109,114,119,125,131,137,143,149,156,163,170,1
77,185,193,201,209,218,227,236,245,255};
>weberstevens01 8 .25
Working using: bit size=8 and difference threshold=0.25
Finished with 30 values...
A[30]={0,1,2,3,4,6,8,11,15,20,25,31,37,44,51,59,67,76,86,96,107,119,132,146,161,
177,194,213,233,255};
Here is a graph of that last output.
That's it