One of the important ideas behind Sniff and the way we develop and promote it is that computing is pretty limited if it just lives in the computing classroom. That's why we've covered examples which use Sniff in Maths, Physics, Biology, English, Geography, and just about any other subject we can think of.
Today we add PE to that list!
The "Beep" test (also known as the shuttle test) is an exercise designed to measure aerobic fitness. First we make two lines 20m apart that you're going to run between. You start at the first line, and when you hear the beep you run to the second line. If you get there before the next beep, then congratulations you get a bit of a rest, till the next beep, when you run back. Every minute you'll hear a triple beep, which means that the beeps are going to get a little closer together. Eventually it will speed up to the point that you don't make the line. That's OK - the first time it happens just turn around and start running back. However if you miss a second beep you're out.
The fine print/technical details are that initially you need to run at 8kph. At the first triple beep that increases to 9kph, and it increase a further 0.5kph on each subsequent triple beep. Each level consists of the minimum number of shuttles to last over 1 minute. (though there are variations on this). Technically there are 21 levels, but professional athletes start dropping out as early as level 13. Note because you're running to exhaustion, this isn't recommended of for unfit adults - however kids generally don't have to worry about heart attacks, so its considered pretty good for them!
The traditional way to do this is to use a prerecorded audio track, but now we know the rules we can make our own beep test machine, using an arduino.
.set level to 1
.set speedkph to 8.0.set distance to 20
Lets start, initialising some variables. We stricty don't need a distance variable, as its always 20, but it will make our code look nicer to use the name rather than hard coding the value.
..set speedmps to speedkph*1000/3600
..set interval to distance/speedmps
So make a beep (we'll figure that out later!), and then repeat for the 21 levels of the test. The rules tell us the speed in kph but that doeesn't really help. Problem solving 101: convert to SI units. Multiply by 1000 to get meters per hour, then divide by 3600 to get meters per second.
Now we have the speed in a useful form, we can find the time interval between the beeps as distance/speed.
..set nextLevelTime to timer + 60
..repeat until timer > nextLevelTime
...wait interval secs
Now we know the interval, we calculate the time 1 minute in the future. We're going to keep going on this level until we've done more than a minute. So then wait for 1 time interval (everyone runs!), then beep. Everyone should have got to the end, and start running back. Then back round the loop to check if they've been running for more than 1 minute.
When we have got more than a minute, we sound a triple beep.
Finally we bump the level and the speed, ready to go around again.
..change level by 1
...change speedkph by 1
...change speedkph by 0.5
All we need to add are the variable declarations (which we'll ignore for brevity), and the scripts to play the beeps:
.set buzzer to low
.wait 0.2 secs
.set buzzer to high
..broadcast beep and wait
..wait 0.2 secs
One of the great things about Sniff is that these run at the same time as the main script, so don't affect the timing. You might also note that triple beep will start while beep is still running. That's OK, as it just restarts beep, and it sounds exactly as it should.
All would be great, and this "should" work but there's a nasty edge case. If you check the timing chart you'll notice something fishy about level 8... At that level you should be running at 12kph, which gives an interval of exactly 6 seconds. So how many shuttles should you do? Well 10 shuttles would take 60 seconds, but look at the fine print... you need run each level for more than a minute, and so the official chart says you need 11 shuttles. Technically level 20 is similar, but that's not going to be an issue for pretty much anyone.
So how will our code behave? It looks like it should work, but are you sure? We're asking the computer to measure multiple 6 second intervals, and compare them to one 60 second interval. They should be the same, but that's not going to happen. We can be pretty sure that the code will actually take longer than 6 seconds to go around the loop, as it needs to do the tests and go back to the start of the loop as well as wait the 6 seconds. There's a pretty good chance it could take longer than a minute to measure 10, six second intervals - perhaps just a 10,000th of a second longer, but that would be enough to give us the wrong answer.
So how many shuttles should there be on each level:
..set shuttles to round (60/interval+0.5)
We divide 60 seconds by interval to tell us how many intervals would make exactly one minute, and then we round it to a whole number. However first we add 0.5 so that it always rounds UP. 60/6 is 10, but 10+0.5=10.5, which rounds UP to 11 - the correct answer.
..say join "Level:" [level]
..say join "Speed:" [speedkph]
..say join "Interval:" [interval]
..say join "Shuttles:" [shuttles]
Best of all we can easily check that this works by removing the delays, and adding some "say"s. Without the delays and edgy timing we can run the code in a second, and compare it to the chart, rather than have to wait 25 minutes to check its working.
Once we know shuttles is right for each level, we can replace our previous loop with:
...wait interval secs
I ran the code on an Arduino with a multi shield attached, which has a buzzer build in. It also has 7 segment display, so you can make a really nice device. If you want to build something really cool you can get 7 segment displays over 15cm tall, and build a beep test machine for your school gym!