Math operations between integer and float

Hello,

I am having issues with math operations between integers in blobs and floats in arrays. I am attempting to create a dimming function (for DMX) that takes a new targetblob and the current blob and with a predetermined fade time, determine the number of steps (dependent on refresh rate) and the step size. The issue is, the step size is always nearly a float (i.e. 2.879…) and the DMX output needs to be an integer (blob).

`

dmxoutput <- hardware.uart57;
andgate <- hardware.pin2;
dmxoutput.configure(250000, 8, PARITY_NONE, 2, NO_CTSRTS);
andgate.configure(DIGITAL_OUT);
numberofchannels <- 12; // Number of channels we wish to control over DMX
dmxsize <- numberofchannels +1; // size of DMX blob (+1 for DMX start address)

DMXblob <- blob(dmxsize);
stepsizeblob <- blob(dmxsize); // Blob with channel value differences for dimming loop
targetblob <- blob(dmxsize); // Blob with channel value differences for dimming loop
temparray <- array(dmxsize);
dmxrefreshrate <- 0.02; // DMX refresh rate (in seconds)
refreshrate <- 0.02; // refresh rate (in seconds -> 1/refreshrate = 44hz)
fudgefactor <- 44; // fudge factor to get fading to correct time (512ch = 20)
fadetime <- 2; // Total fade time (in seconds)
fadeincrement <- blob(dmxsize); // Fade increment (loop value increment for dimming)
dmx_refresh <- null;
process_DIM <- null;
numberofsteps <- null;
processedsteps <- null;
stepsizearray <- array(dmxsize);
stepcounter <- 0;
dimstart <- 0;
dimend <- 0;
calcstart <- 0;
calcend <- 0;

for (local i = 0; i < dmxsize; i++) { // populate the blob with null data
DMXblob.writen(0x00, ‘b’);
}

function DMXrefresh() {
andgate.write(0);
andgate.write(1);
dmxoutput.write(DMXblob);
dmx_refresh <- imp.wakeup(dmxrefreshrate, DMXrefresh);
}

function DMXwrite() {
andgate.write(0);
andgate.write(1);
dmxoutput.write(DMXblob);
}

function calculate() {
for (local i = 1; i < dmxsize; i++) {
local stepsize = (targetblob[i] - DMXblob[i])/numberofsteps;
stepsizearray[i] = stepsize;
server.log("Step size = " + stepsize + " type: " + typeof stepsize);
}
}

function DIM() {
if (stepcounter == numberofsteps) {
for (local i = 1; i < dmxsize; i++) {
if (stepsizearray[i] != 0) {
//temparray[i] += stepsizearray[i]
DMXblob[i] += stepsizearray[i];
}
}
dimend = hardware.micros();
server.log(“Total dim time = " + ((dimend - dimstart)/1000000.00000) + " seconds”);
imp.cancelwakeup(process_DIM);
server.log(“End of Dimming”);
stepcounter = 0;
//dispDMXblob();
}
else if (stepcounter < numberofsteps) {
//calcstart = hardware.micros();
for (local i = 1; i < dmxsize; i++) {
if (stepsizearray[i] != 0) {
DMXblob[i] += stepsizearray[i];
//temparray[i] += stepsizearray[i];
//DMXblob[i] += temparray[i].tointeger();
//server.log(dispDMXblob());
}
}
//server.log(stepsizearray[1]);
//server.log(DMXblob[1] = DMXblob[1] + stepsizearray[1] + typeof (DMXblob[1] = DMXblob[1] + stepsizearray[1]));
//server.log(DMXblob[1] + typeof DMXblob[1]);
stepcounter++;
//calcend = hardware.micros();
//server.log(“Calculation time = " + ((calcend - calcstart)/1000000.0000) + " seconds”);
process_DIM <- imp.wakeup(dmxrefreshrate, DIM);
}

else {
    server.log("error");
}

}

function dispDMXblob() {
for (local i = 1; i < dmxsize; i++) {
server.log(DMXblob[i]);
}
}

agent.on(“newDMX”, function(value) {
targetblob = value;
for (local i = 1; i < dmxsize; i++) {
temparray[i] = DMXblob[i].tofloat();
}
processedsteps = fadetime*fudgefactor;
numberofsteps = processedsteps.tofloat();
server.log("# of steps = " + numberofsteps);
server.log(“Dimming Executed!!”);
calculate();
dimstart = hardware.micros();
DIM();
})

server.log("Connected to " + imp.getssid() + ", Signal strength = " + imp.rssi());

DMXrefresh()
server.log("Device firmware version: " + imp.getsoftwareversion());
`

As you can see I have tried debugging the operation and variable types throughout the process. Is there a simpler way to do this? I need to keep track of the “excess” float values that are lost in the integer conversion or else the blob never reaches the target value (a step size of 2.897… is actually only 2, then never reaches 255). I am hoping there is a better way to do this in squirrel. Any help would be appreciated…

try changing things like this

local stepsize = (targetblob[i] - DMXblob[i])/numberofsteps;

to this

local stepsize = 1.0 * (targetblob[i] - DMXblob[i])/numberofsteps;

and you can also change

fadetime <- 2;

to this

fadetime <- 2.0;

so that fadetime is a float.

you can use the typeof() function to return the type of a variable to check it.

Thanks. That helped, also found an issue with adding something i already added. Now I need to get this calculation time down:

calcstart = hardware.micros(); for (local i = 1; i < dmxsize; i++) { //if (stepsizearray[i] != 0.0) { temparray[i] += stepsizearray[i] DMXblob[i] = temparray[i]*1; // } } stepcounter++;

With 512 byte blob, this process takes ~0.0607 seconds. To put into perspective, I need the imp to wakeup every 0.02s to output the updated values from each calculation run. If I uncomment the inner for loop, this helps the calc time, since most of the time not every channel is changing. But there can be instances where it will. Are there any squirrel tricks or memory lookup issues I am missing?

Thanks!

A simple improvement is to make dmxsize a local (eg local dmxsize2=dmxsize) then use that in the loop, as then there’s no lookup for that. You can do the same with temparray but as you’re persisting it between cycles that may not work well.

I’ll leave Peter to comment more :slight_smile:

That dropped .017s off the time! Thanks… This is almost a must!

  1. Make all your “globals” (members of the root table) locals instead:
    DMXblob <- blob(dmxsize);
    local DMXblob = blob(dmxsize);

  2. Fixed-point is faster than float (in temparray and stepsizearray, instead of storing 2.879 (or whatever) as a float, store 2.879*16777216 as an integer. That large number is 2^24, and so the conversion from temparray to DMXblob is just a shift:
    DMXblob[i] = temparray[i] * 1
    DMXblob[i] = temparray[i] >> 24

  3. tofloat() is faster than multiplying by 1.0, and multiplying by 1 (integer) doesn’t do anything: float*integer is float. The inverse operation of tofloat() is of course tointeger().

  4. 256 different values times 88 steps is 22K. You could, just about, precalculate all the values; you can’t fit a 25688 array of integers in an imp (nor the code to populate it), but you could produce a string 25688 characters long (by generating the Squirrel code using a program). Then the loop becomes:
    for (local i=1; i<dmxsize; ++i) { DMXblob[i] = hugestring[targetblob[i]*88 + stepcounter] }

Peter

Thanks Peter. Using the shift conversion dropped the calc time about .01 per iteration (down to ~0.04s). However, I only reach 176, not 255 at the end (2*88=176). Maybe I am missing something in the bitwise conversion?

As for pre-calculating a huge string, there will be cases where the fade will be over 30 minutes, or even 1.5 hours (1800s and 5400s, 79200 steps and 237600). I fear this will not be good, or at least add a few seconds delay before dimming occurs. I am not sure, thoughts?

Also, 2.879*16777216 = 48301604.864, or 4^7 not 2^24. Or is the ^notation referring to some bitwise placement and not scientific notation (I am sure you are right and I am misunderstanding this…)? The other issue with this is, the fade time can change, from 1 second to virtually anything (probably at most two hours). This will change the step size decimals considerably. Will the >>24 apply for all cases?

`function calculate() {
for (local i = 1; i < dmxsize; i++) {
local stepsize = ((targetblob[i] - DMXblob[i])/numberofsteps).tointeger();
stepsizearray[i] = stepsize*16777216;
//server.log("Step size = " + stepsize);
}
server.log("Step size = " + stepsizearray[1] + typeof stepsizearray[1]);
}

function DIM() {
local dmxsize2 = dmxsize;
local dmxblob = blob(dmxsize);
dmxblob = DMXblob;
if (stepcounter == numberofsteps) {
dimend = hardware.micros();
server.log(“Total dim time = " + ((dimend - dimstart)/1000000.00000) + " seconds”);
imp.cancelwakeup(process_DIM);
server.log(“End of Dimming”);
stepcounter = 0;
DMXblob = dmxblob;
//dispDMXblob();
}
else if (stepcounter < numberofsteps) {
local calcstart = hardware.micros();
for (local i = 1; i < dmxsize2; i++) {
//if (stepsizearray[i] != 0.0) {
temparray[i] += stepsizearray[i];
dmxblob[i] = temparray[i] >> 24;
//}
}
stepcounter++;
//server.log(typeof stepsizearray[1]);
//server.log(typeof temparray[1]);
//local calcend = hardware.micros();
//server.log(“Calculation time = " + ((calcend - calcstart)/1000000.0000) + " seconds”);
process_DIM <- imp.wakeup(dmxrefreshrate, DIM);
}

else {
    server.log("error");
}

}`

Maybe I am going about this dimming from a wrong angle. For short dim times (i.e. 2 seconds) the number of steps are less than longer dim times (5400s). And when they are long, I really don’t need to send the dmx every 0.02s. I could calculate the only times necessary to send the packet for the smallest DMX increment change (1). Over 5400s, a 255 change would only need an increase (or decrease) of 1 every 21 seconds (however a minimum of 2/sec is required). I am unsure whether creating a variable dimming engine will be more beneficial than the current method.

I really appreciate everyone’s time and effort on this. I have never had any level of support from another company come close to the help Electric Imp provides. Thank you again.

Ok, made the temparray and stepsizearray locals, redifening them at the beginning of the dim loop iteration, calc time at 0.012s. Much better! would like a little faster… a fade time of 2s ends up at 3.5s… But better than over 6. Still need the decimal values to run over…

`function calculate() {
for (local i = 1; i < dmxsize; i++) {
local stepsize = ((targetblob[i] - DMXblob[i])/numberofsteps).tointeger();
stepsizearray[i] = stepsize*16777216;
//server.log("Step size = " + stepsize);
}
server.log("Step size = " + stepsizearray[1] + typeof stepsizearray[1]);
}

function DIM() {
local dmxsize2 = dmxsize;
local dmxblob = blob(dmxsize);
dmxblob = DMXblob;
local Stepsizearray = array(dmxsize);
Stepsizearray = stepsizearray;
local Temparray = array(dmxsize);
Temparray = temparray;
if (stepcounter == numberofsteps) {
dimend = hardware.micros();
server.log(“Total dim time = " + ((dimend - dimstart)/1000000.00000) + " seconds”);
imp.cancelwakeup(process_DIM);
server.log(“End of Dimming”);
stepcounter = 0;
DMXblob = dmxblob;
dispDMXblob();
}
else if (stepcounter < numberofsteps) {
local calcstart = hardware.micros();
for (local i = 1; i < dmxsize2; i++) {
//if (stepsizearray[i] != 0.0) {
Temparray[i] += Stepsizearray[i];
dmxblob[i] = Temparray[i] >> 24;
//}
}
stepcounter++;
//server.log(typeof stepsizearray[1]);
//server.log(typeof temparray[1]);
//local calcend = hardware.micros();
//server.log(“Calculation time = " + ((calcend - calcstart)/1000000.0000) + " seconds”);
temparray = Temparray;
process_DIM <- imp.wakeup(dmxrefreshrate, DIM);
}

else {
    server.log("error");
}

}`

By “2^24” I mean two to-the-power-of 24. This code isn’t right…

`function calculate() {
	for (local i = 1; i < dmxsize; i++) {
	    local stepsize = ((targetblob[i] - DMXblob[i])/numberofsteps).tointeger();
	    stepsizearray[i] = stepsize*16777216;
	    //server.log("Step size = " + stepsize);
	}
	server.log("Step size = " + stepsizearray[1] + typeof stepsizearray[1]);
}`

…because it truncates the step size to an integer before shifting, not after. What you need is…

`function calculate() {
	for (local i = 1; i < dmxsize; i++) {
	    local stepsize = ((targetblob[i] - DMXblob[i]).tofloat()/numberofsteps;
	    stepsizearray[i] = (stepsize*16777216).tointeger();
	    //server.log("Step size = " + stepsize);
	}
	server.log("Step size = " + stepsizearray[1] + typeof stepsizearray[1]);
}`

…where the full precision is shifted.

When I suggested turning your globals into locals, I meant locals of the outermost main function, not of DIM(). TheWriting Efficient Squirrel page calls them “top-level locals”.

Note that in

`    local Stepsizearray = array(dmxsize);
    Stepsizearray = stepsizearray;`

the second assignment assigns the array reference, not the contents of the array. So the array created in the first assignment is immediately discarded again, meaning that those two statements are equivalent to:
local Stepsizearray = stepsizearray;

Peter

Also note that imp.wakeup() starts counting from the time that it’s called. If you want a function to be called every 0.02s, but the function itself takes a large fraction of 0.02s to run, then you need to put the imp.wakeup() at the top of the function.

Peter

Thanks Peter. Now 512 calculations are not an issue. Thank you for your help!! Just curious, will the Imp ever be capable of millisecond (0.001) resolution?