Is this my code or a weird bug?

I have a 240-LED strip connected to the PB and it has three “sprites” that move along its length. The thing is, sometimes the sprites ‘hiccup’ - jump back and forth a little. When I watch the variable “tailV” - which is supposed to be a scalar - it changes to a multi-member array and this coincides with the ‘hiccups’. I don’t think my code should make it an array so I’m wondering what’s up.

Here’s the complete code:

// arrays of sprite parameters
acc = [0.0, 0.0, 0.0]; // acceleration
vel = [0.0, 0.0, 0.0]; // velocity
var pos = [100, 0, 0]; // position
lowIndex = [0.0, 0.0, 0.0]; // the index in the pixel array of the low end of the sprite (always an int)
centerIndex = [0.0, 0.0, 0.0]; // index in the pixel array of the center of the sprite (float)
highIndex = [0.0, 0.0, 0.0]; // the index in the pixel array of the high end of the sprite (always an int)
relPos = [0.0, 0.0, 0.0]; // fraction from 0-1 of proximity to low index of the center

var spriteHue = [1.0, 0.66, 0.33]; // the colors of the sprites

var k = 1.0;  // spring constant for all sprites

// arrays of hsv values for display
var h = array(240);
var s = array(240);
var v = array(240);

// function for overlapping sprites
//  adds the values mod 1
function addMod(x, y) {
  var val = x + y;
  if (val > 1.0) {
    return val - 1;
  } else {
    return val;
  }
}

// another function for overlapping sprites
//  adds the values and caps at 1
function addMax(x, y) {
  var val = x + y;
  if (val > 1.0) {
    return 1.0;
  } else {
    return val;
  }
}

// another function for overlapping sprites
//  averages the value if the first one is non-zero
function addAvg(x, y) {
  if (x > 0) {
    return (x + y)/2.0;
  } else {
    return y;
  }
}

export function beforeRender(delta) {
  // do the diff eq math to move the sprite
  // oddly, deltas are negative
  dt = (-1) * delta/40000;

  // because coupling is complex, pull accelerations out of the loop
  acc[0] = (-1*(k * pos[0])) + (0.1 * pos[2]);
  acc[1] = (-1*(k * pos[1])) + (0.1 * pos[0]);
  acc[2] = (-1*(k * pos[2])) + (0.1 * pos[1]);

  // x is the sprite index
  for (x = 0; x < acc.length; x++) {
    // move the sprite
    //acc[x] = - (k * pos[x]);
    vel[x] = vel[x] + (acc[x] * dt);
    pos[x] = pos[x] + (vel[x] * dt);
    // find the two adjacent points that determine the sprite's location
    // as well as center (for doing the fade outs)
    // relPos = 0 if at low; 1 if at high
    // and map from -120 to +120 to 0 to 240
    lowIndex[x] = (floor(pos[x]) + 120);
    centerIndex[x] = pos[x] + 120;
    highIndex[x] = (ceil(pos[x]) + 120);
    relPos[x] = centerIndex[x] - lowIndex[x];
  }

  // first, clear the display
  for (i = 0; i < 240; i++) {
    // default is black
    h[i] = 0.0;
    s[i] = 0.0;
    v[i] = 0.0;
  }

  // now, draw in the sprites
  // x is sprite index
  for(x = 0; x < acc.length; x++) {
    var tailV = 1 - (centerIndex[x] - lowIndex[x]); // 0-1 for the sprite's proximity to low dot
    var i = 0;
    while (tailV > 0.001) {
      h[lowIndex[x] + i] = addAvg(h[lowIndex[x] + i], spriteHue[x]);
      s[lowIndex[x] + i] = 1.0;
      v[lowIndex[x] + i] = addMax(v[lowIndex[x] + i], tailV);
      i = i - 1;  // low end tail goes in - direction
      tailV = tailV * 0.1;  // tail fades out
    }

    tailV = 1 - (highIndex[x] - centerIndex[x]); // 0-1 for sprite's proximity to high dot
    i = 0;
    while (tailV > 0.001) {
      h[highIndex[x] + i] = addAvg(h[highIndex[x] + i], spriteHue[x]);
      s[highIndex[x] + i] = 1.0;
      v[highIndex[x] + i] = addMax(v[highIndex[x] + i], tailV);
      i = i + 1; // high end tail goes in + direction
      tailV = tailV * 0.1; // tail fades out
    }
  }
}

export function render(index) {
  hsv(h[index], s[index], v[index]);
}

The array thing in vars watch is a visual glitch. It’s caused because PB doesn’t have type information when it’s inspecting these, very small value variables can end up with the same values as valid array values. It shouldn’t impact your code execution. Arithmetic operations always treat variables as scalars in PB.

However, it does mean that tailV was a very small value, on the order of 1 to 11 1 / 65,536ths, or 0.0000153 to 0.000168 or so given that I see around 11 arrays in your code. That’s under the 0.001 threshold I see in your code, which might be a clue.

One thing that’s almost certainly causing trouble:

dt = (-1) * delta/40000;

This is causing an overflow – 40,000 is outside the range of Pixelblaze’s 16.16 fixed point system. (The maximum value is somewhere around 32767.9998) Delta is actually always positive, but can be a fairly small value. For this reason, I usually work with accumulated delta for things that are functions of time.

For example:
myTime = (myTime + delta / 1000) % 3600
gives you a count of seconds.millis that wraps every hour (to avoid the scaling issue.)

Scaling things to keep numbers within 16.16 range is something you just eventually get used to, then almost forget you’re doing.

1 Like

thanks - that completely makes sense
I need to pay attention to numerical limits - among other things
thanks!
Brian