astro-pi / beta-testing

Bug tracker for the Trinket Sense HAT emulator
https://trinket.io/sense-hat
3 stars 0 forks source link

Values returned don't match sliders #34

Closed tracygardner closed 8 years ago

tracygardner commented 8 years ago

The values I'm seeing from the API don't match what I see in on the sliders in the emulator in Trinket.

image

Screenshot from: https://trinket.io/library/trinkets/eaea4cb76c

Not just rounding, they can be quite far out. (Rounding does need to be consistent though for thresholds.)

I also see it in this demo project: https://trinket.io/python/f1c0915cfe

I'm running Chrome on Ubuntu Linux. I've also checked Microsoft Edge on Windows 10 and see the same problem there too.

waveform80 commented 8 years ago

Both emulators also emulate the error ranges of the sensors, although they operate slightly differently in this regard. The error ranges for the HTS221 humidity sensor (from the datasheet) are +/- 3.5 between 20 and 80% humidity and +/- 5 at the outer 0 to 100 range. So, in both emulators (the web-based and desktop ones) the understanding is that the sliders set the actual environment that the Sense HAT is in, while what you read from the library is a value "corrupted" by the average error that the sensors' produce.

The differences between the emulators is in when that calculation takes place. For the web-based emulator, the error is calculated each time the slider is adjusted. Hence, once the slider is static the reading will always remain constant. In the desktop emulator, a background thread (which can be disabled) constantly "jitters" the readings from the sensor, and the reading returned is actually the average from a FIFO fed from the sensor. Hence, you'll see constantly varying readings in the desktop emulator even if you're not moving the sliders.

tracygardner commented 8 years ago

I suspected the answer might be something like that. But I can't imagine trying to explain that to a 9 year old working through a Code Club project.

tracygardner commented 8 years ago

Hmm, the repeatedly getting the same value definitely threw me (though I guess there's some repeatability to the variance / tendency towards a particular value.) But I'm still seeing bigger differences than I would expect.

The datasheet says that the error for the temperature is +/- 0.5 degrees C within the range 15 to 40. But I'm getting 21 back when the slider says 23.

Am I missing something? Quite possible.

waveform80 commented 8 years ago

I suspected the answer might be something like that. But I can't imagine trying to explain that to a 9 year old working through a Code Club project.

I'm afraid I can't help much there other than to observe that there's an issue whether it's emulated or not. If it's emulated you have the difficulty up front of explaining that sensors aren't perfect, and that's what the emulator attempts to demonstrate. If it's not emulated, you have the difficult upon deployment (to a real Sense HAT) of explaining that their code which triggered some action once neatly when the emulator's temperature slider rose past some value, is now triggering seemingly randomly when their thermometer reads that value, but the sensor is jiggling either side of the trigger value.

In other words, not emulating this merely delays the difficulty (unless of course the intent is never to deploy to a real Sense HAT).

Hmm, the repeatedly getting the same value definitely threw me (though I guess there's some repeatability to the variance / tendency towards a particular value.)

I can't speak for the trinket developers (I'm only really familiar with the desktop emulator), but my understanding is that the lack of threading in JavaScript makes continual adjustment of the values in the background difficult to implement.

I'd also observe that emulating sensor jitter is the majority of the CPU usage in the desktop version; this is one of the reasons the sensor jitter can be disabled on the desktop version, as we observed on low end platforms (like a Pi A+) that sensor jitter ate up nearly 50% of the available CPU time, leaving only 50% available for the user's script.

But I'm still seeing bigger differences than I would expect. The datasheet says that the error for the temperature is +/- 0.5 degrees C within the range 15 to 40. But I'm getting 21 back when the slider says 23.

I'm guessing again here, but I can offer an explanation why this may happen in the desktop version. In the desktop version, the emulator implements a FIFO (a queue) of values read from the sensor. The reading returned by the emulator is the average of the current values in the queue. The effect is that if you set the temperature to, say, 20 degrees, then quickly adjust it to 50 you'll see the sensor reading slowly glide to new setting over the next few seconds. This is how the actual sensors work on the HAT (although I'm not sure the emulator's FIFO length is entirely accurate). Perhaps the trinket emulator is using a similar FIFO and several adjustments are required to get a reading within +/-0.5? This is entirely a guess on my part, though.

As a side note, whilst the datasheet specifies the error for the 15-40 degree range (+/- 0.5), and the 0-60 degree range (+/- 1), the sensor is also listed as having a response range of -40-120. For the -40-120 range I took a complete guess on the desktop emulator and set the error at +/- 2. I'm not sure what the trinket emulator is using as error ranges, but I'd assume they'd get it from the datasheet same as I did. Unless it was misinterpreted as a percentage error? Though that really wouldn't explain excessive error until you got to ranges beyond 100 degrees. Hmm, I'm afraid I can't offer a good answer to this bit.

tracygardner commented 8 years ago

Just a bit more info / thoughts:

rik-cross commented 8 years ago

@eah13: Have you seen the comments above? Do you have any thoughts?

tracygardner commented 8 years ago

Ah, realised I was looking at the temp accuracy for temp from the humidity sensor. It's less accurate but wider range from the temp sensor?

Is there a separate temp sensor as suggested by data sheet info: http://www.farnell.com/datasheets/1958037.pdf

Or just temp from humidity and pressure sensors: https://www.raspberrypi.org/learning/astro-pi-guide/sensors/temperature.md

I've tried calling sense.get_temperature_from_humidity() and sense.get_temperature_from_pressure() and they both return the same (often very inaccurate) value in Trinket.

Looks like this is more complex than I'd realised. I'd assumed that the library was using data from both temperature sensors to approximate ambient temperature but it looks like it's the user that is expected to do this: https://www.raspberrypi.org/forums/viewtopic.php?f=104&t=111457

So actually if you want ambient temperature the results are much less accurate than we're seeing and you would need to account for this or be very confused when moving from emulator to physical SenseHAT.

If we want to create Code Club projects that use approx ambient temperature and can be run on the emulator and SenseHAT what would be the best approach. (I'm waiting for a physical SenseHAT so I can actually try things out.)

waveform80 commented 8 years ago

Is there a separate temp sensor as suggested by data sheet info: ...

Not exactly; both the humidity sensor (HTS221) and the pressure sensor (LPS25H) have temperature sensors, either of which can be queried with get_temperature_from_humidity() or get_temperature_from_pressure() respectively. The other method, get_temperature, is simply an alias for get_temperature_from_humidity so the humidity sensor is used by default (the temp property just reads get_temperature too).

There's also a temperature sensor in the IMU but as far as I know that's not exposed by any interfaces (it isn't by the RTIMU library that underlies the python-sense-hat library).

I've tried calling sense.get_temperature_from_humidity() and sense.get_temperature_from_pressure() and they both return the same (often very inaccurate) value in Trinket.

Hmm, unfortunately I'm unaware of the details of the Trinket implementation but if they're returning exactly the same value then that's likely wrong (the error ranges of the two sensors, and even the operating ranges, are quite different; the desktop emulator calculates the errors separately so the two calls will return different values, unless environmental simulation is disabled).

Looks like this is more complex than I'd realised. I'd assumed that the library was using data from both temperature sensors to approximate ambient temperature but it looks like it's the user that is expected to do this:

Yes - as noted above get_temperature is simply an alias for get_temperature_from_humidity.

I'm not too surprised that precision calibration is considered a problem for the user, though. My expectation was that the library would simply return the reading from the sensors and that calibration would be dependent on the user's particular environment (orientation of the Pi, enclosure, etc.)

So actually if you want ambient temperature the results are much less accurate than we're seeing and you would need to account for this or be very confused when moving from emulator to physical SenseHAT.

Well, based on your observation that the two different temperature calls are returning the same value in the Trinket emulator, I'd suspect there's some bug there.

If we want to create Code Club projects that use approx ambient temperature and can be run on the emulator and SenseHAT what would be the best approach. (I'm waiting for a physical SenseHAT so I can actually try things out.)

One of the things I threw into the desktop emulator was a recording and playback mechanism for sensor readings (on the assumption the emulator's never going to be perfect, so it'd be nice to be able to playback recordings from a "real" SenseHAT).

If you can get hold of the desktop emulator (admittedly it's harder to access than the Trinket emulator, at least on non-Pi platforms), I'd suggest using it to playback recordings from a real HAT which were taken in a "known" ambient temperature. I'd be happy to provide such recordings, but unfortunately I don't have a known good thermometer for comparison. Still, when your SenseHAT arrives, you could take some recordings yourself, so your kids can play around with "real" data from a real HAT.

I don't know if the playback facility could be added to the Trinket emulator too (might be a hefty processing burden) but it might be worth investigating?

tracygardner commented 8 years ago

Ah I didn't know the desktop emulator had playback, I was thinking that would be nice to have. I run Ubuntu and have a Pi so I can certainly give that a try. My SenseHAT has been dispatched so I'll be able try it out for real soon.

Thanks for your help and hopefully we'll hear back about the Trinket behaviour.

eah13 commented 8 years ago

Hey all. I've not had time this week to construct a thorough answer, hence the silence. Apologies.

As a quick reply, the errors we apply in trinket code, not in the open source module. Here's the code:

function _randomGaussian() {
  if (this.haveNextNextGaussian) {
    this.haveNextNextGaussian = false;
    return this.nextNextGaussian;
  }

  var v1, v2, s;
  do {
    v1 = 2 * Math.random() - 1; // between -1.0 and 1.0
    v2 = 2 * Math.random() - 1; // between -1.0 and 1.0
    s = v1 * v1 + v2 * v2;
  } while (s >= 1 || s === 0);

  var multiplier = Math.sqrt(-2 * Math.log(s) / s);
  this.nextNextGaussian = v2 * multiplier;
  this.haveNextNextGaussian = true;

  return v1 * multiplier;
};

function constrain (aNumber, aMin, aMax) {
  return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber;
};

function randomGaussian (mean, error) {
  var _randVal = constrain(_randomGaussian());
  var randomGauss = mean + _randVal * Math.sqrt(error);
  var max = mean + error;
  var min = mean - error;
  return Math.min(Math.max(min, randomGauss), max);
}

Mean is the slider value and error is from the spec.

It was intended as a passable approximation of the errors you see in the real device. To faithfully represent the real Sense HAT behavior we'd need to constantly update the error so you'd see the jumpiness of the real sensors. And the Gaussian distribution is of course not exactly right.

As I see it there are a few options:

Feel free to weigh in. The last path would obviously be the bestfrom an emulation standpoint. but is probably the most. Another consideration would be to sync up the logic/behavior with the desktop emulator as best we can.

@waveform80 thanks for the detailed comments. We probably should implement the humidity and pressure temp sensors separately, with separate errors. For now they read from the same slider value and apply the same errors.

Implementing 'playback' of values is definitely something we've discussed but we'll need to chat with @davehoness about getting it on the roadmap. We're about to embark on a fun journey into three dimensions but maybe sensor playback could be next :)

waveform80 commented 8 years ago

@tracygardner

Ah I didn't know the desktop emulator had playback, I was thinking that would be nice to have. I run Ubuntu and have a Pi so I can certainly give that a try. My SenseHAT has been dispatched so I'll be able try it out for real soon.

Good stuff - I have just realized that I've completely forgotten to upload the emulator to my PPA; I'll do that in a bit once I've cleared out my e-mail backlog!

@eah13

As a quick reply, the errors we apply in trinket code, not in the open source module. Here's the code: [snip]

Hmmm, a few queries on the code:

FWIW, in the desktop emulator I went for a slightly different method. Under the assumption that cropping the gaussian results in a non-normal distribution I merely used a stddev of 0.2 (or divided by 5 if you like) so that output is almost always (but not absolutely guaranteed to be) within the -1..1 range. It's a hack, definitely, but it's also fast (which was important given that the sensor jitter is the major CPU user).

@waveform80 thanks for the detailed comments. We probably should implement the humidity and pressure temp sensors separately, with separate errors. For now they read from the same slider value and apply the same errors.

Ah I figured that might be the case given @tracygardner's findings.

Implementing 'playback' of values is definitely something we've discussed but we'll need to chat with @davehoness about getting it on the roadmap. We're about to embark on a fun journey into three dimensions but maybe sensor playback could be next :)

If you need a hand with the format of what sense_rec spits out, let me know - it's pretty basic though (small header then contiguous fixed size recs containing a pile of IEEE double-prec floats). Have fun with the IMU stuff (I've still got some bits to finish for the desktop emulator there, but no time to do it at the moment!).

eah13 commented 8 years ago

I've got a fix in the works, and I think I've got perpetual updates to the sensor values working performantly.

Looks like there was an error in our temperature ranges (it used +/-2 for all temp values) and our use of the error from the spec as the Gaussian deviation was incorrect, giving much greater deviations than observed in the device and clipping too often. I used error/3 as the new deviation based on the observation that the range of 1000x randomGaussian(0,1) was ~-3, 3 and now I've got >95% of the errors now falling inside the ranges, but still nicely distributed, so this will be much more realistic now. It will also update every 1/4 second via a setTimeout, making the reported fluctuate similar to (though less often than) the real sense hat values.

Thanks all for the bug report and suggestions! Will post here when this is deployed, hopefully later today. The trinket links above can then be used for testing my fix.

tracygardner commented 8 years ago

Thanks Elliott, that sounds like it will be much better for developing a useful mental model of how a Sense HAT behaves.

(I've actually got a physical Sense HAT now too so I've got real data. I'm still looking at the best way of working with ambient room temp (that can be explained to 9 year olds.))

eah13 commented 8 years ago

This should be fixed now! Test code: https://trinket.io/python/610ab2e608

Notice on the "H" setting (use the arrow keys to get to it) the display fluctuates up and down, just like on the actual device. It looks like the published specs might be a little narrower than the empirical values (i.e. the errors we'd have to program in would need to be even higher to simulate the actual fluctuations in the device) but either way this is a much improved simulation. The sensor values now update every 1/4 second on a timeout. There's a buffer built in when you're adjusting the slider to avoid any slowdowns.

Feedback welcome, and feel free to reopen if you don't think this solves the original issue but I think we've nailed it down.

Thanks again!