-
Agree - looked up ringing on wikipedia and the upshot is that ringing is enevitable and complex to get rid of.
https://en.wikipedia.org/wiki/Ringing_artifacts
BUT - Eureka - I think I have worked out a way to deal with this - but will need to do the experiments.
1) the filter rings. But it is fairly consistant and does a good job in ampliying the walking cadence.
2) Using STEPO (one of my apps in the repo) with trip counter I can see that rolling over or moving my arm over to the bedside table and back to switch the light on will cause on averge between 6-9 steps. This suggests to me that making the threshold for starting step counting should be 10 steps in 10 seconds. Your X in Y algorithm works. The only difference with the state machine is that after we get to step counting (STEPPING) state - that the criteria is removed and only a gap of 2 seconds will put you back into STEP1 state (waiting for X steps in Y seconds). With your C code 5 stepHistory[] approach (much simpler and very elegant) - the constraint continues for all steps and if not met will drop the odd step. In reality the difference may not be significant or matter. Will have to measure it. Your 5 line approach just took a lot longer to understand - but it is a lot less code, which is usually a good thing.
3) I have observed after stopping walking on average I see 2-3 extra steps due to the ringing. So the solution is that when you come out of 10 steps in 10 seconds you count 7 steps to start with knowing that the ringing will provide the other 3 steps at the end of the STEPPING period. This may sound a bit hacky at first but the more I think about it the more I like it.
Will build an App tonight and test it.
-
I think it's possible that just raising stepCounterThreshold to maybe 1500, lowering >>STEPCOUNTERHISTORY to 2 and STEPCOUNTERHISTORY_TIME to 40ish might actually have a >>pretty good effect.
I'm not convinced based on my observations about the ringing.
Tuning the thresholds is quite tricky as I briefly experimented with taking out the filter.
It would be good to know how much amplication the convolve() function gives. If the input is -128 to +127, what is the expected output range. How do you scale it back to the -128 to +127 range.
Another thought I have had is PEAK detection. You then just need 3 points A B C where B > C AND and B > A to detect a PEAK. You would have to take off the clipping though otherwise you get a plateau. The advantage of PEAK detection is that you dont need to worry about setting a threshold for that part. However you do need to determine a band in which you say any magnitude M between -X and +X is considered to be too weak to be walking. I would suggest a good start for that band is -50 to +50. These are the max/min values that I observe for raw_clipped when I lift my hand to rub my hair and drop it back to my side. These are clearly not working values for walking which will be much higher.
var m = a.mag; var v = ((m-1)*8192)>>5; var raw_clipped = E.clip(v, -128, 127);
I might try PEAK detection and removing the filter once I understand how to scale the output of convolve().
-
Well, I did spend time doing that 2 months ago, but then nobody bothered to do anything with it!
Appreciated. It was me that requested it then I had other stuff to do. I'm making use of it now though.
I've built the App below. It has allowed me to get a much better understanding of what is going on. The main ISSUE IS THE RINGING produced by the filter. One impulse (eg raising your hand to scratch the back of your neck and lowering it back) can cause a ring that will produce 7 steps. The step counting is fairly accurate - the big issue is the ringing. This is why I think it counts 1200 steps when you sleep for 8 hours. A few shuffles every hour or so adds up over that 8 hour period.
Maybe a simpler single stage filter would produce less ringing. Maybe we dont actually need a filter. The act of walking produces a strong enough up/down cycle in the magnitude of the accelerometer to drown out noise etc.
Not sure if I understood how the approach to X steps in Y seconds is working. It took a while for me to figure out what is going on. I think what is happening is 1) step comes in 2) did it occur within 75 samples (6 seconds, 6x12.5=75) Yes/No record it as a step in the history, 3) the 5 element step history fills up with steps that could each have occured between 0-X samples. When it fills up you check the OLDEST entry in the step history step that it arrived within 75 samples ago (ie within 6 seconds ago). Hmm - ok - I think I go it.
I have done it as a state machine. First step is detected move to STEP_1 state. If the next step is within 1 second move to STEP_225 state otherwise go back to STEP_1 state. When you get to 5 steps each arriving within 1 second of each other then you are in STEPPING state. After that 2 seconds of in activity means go back to STEP_1. When you get to STEPPING state the 5 held back steps are released to be counted. The state machine works fine. The problem is with the filter ringing.
To test BTN1 to start. Walk 10 steps across the room, stop. You will over count by 2,3,4 steps maybe. More if you are walking fast / stomping.
Here's my test code:
/** * javascript step counting app, with state machine to ensure * we only start counting after 5 steps in 5 seconds * * FIR filter designed with http://t-filter.appspot.com * sampling frequency: 12.5 Hz * fixed point precision: 10 bits * * 0 Hz - 1.1 Hz, gain = 0 desired attenuation = -40 dB * 1.3 Hz - 2.5 Hz, gain = 1 desired ripple = 5 dB * 2.7 Hz - 6.25 Hz, gain = 0 desired attenuation = -40 dB */ var filter_taps = new Int8Array([ -2, 4, 4, 1, -1, 0, 2, -3, -12, -13, 2, 24, 29, 6, -25, -33, -13, 10, 11, -1, 3, 29, 41, 4, -62, -89, -34, 62, 110, 62, -34, -89, -62, 4, 41, 29, 3, -1, 11, 10, -13, -33, -25, 6, 29, 24, 2, -13, -12, -3, 2, 0, -1, 1, 4, 4, -2 ]); // create a history buffer the same lenght as the filter taps array var history = new Int8Array(filter_taps.length); // value used in the f/m by default const stepCounterThreshold = 1000; /// has filtered acceleration passed stepCounterThreshold var stepWasLow = false; var step_count = 0; // total steps since app start // acceleromter operates at 12.5Hz function onAccel(a) { // scale to fit and clip var m = a.mag; var v = ((m-1)*8192)>>5; /** * create a new Int8Array from the existing but starting from pos 1 of the existing history * this drops off index 0 and moves everything up, leaving the last entry to be filled * with the new value */ history.set(new Int8Array(history.buffer,1)); // set last value to the clipped value from the accel var raw_clipped = E.clip(v, -128, 127); history[history.length-1] = raw_clipped; //console.log("history: " + history); // digital filter, output has to be scaled down as the calculation is integer based, no floating point var accFiltered = E.convolve(filter_taps, history, 0) >> 2; console.log("m: " + m + " r: " + v + " rc: " + raw_clipped + " f: " + accFiltered); // check for steps, a bottom, followed by top threshold crossing = a step var hadStep = false; if (accFiltered < -stepCounterThreshold) { stepWasLow = true; console.log(" LOW"); } else if ((accFiltered > stepCounterThreshold) && stepWasLow) { stepWasLow = false; console.log(" HIGH"); // We now have something resembling a step // now call state machine to ensure we only count steps when we have done 5 steps in 5 seconds // 2s of silence will reset the state to STEP1 state hadStep = true; step_count += step_machine.step_state(); console.log(" *STEP* " + step_count); } } function STEP_STATE() { this.S_STILL = 0; // just created state m/c no steps yet this.S_STEP_1 = 1; // first step recorded this.S_STEP_225 = 2; // counting 2-5 steps this.S_STEPPING = 3; // we've had 5 steps in 5 seconds this.state = this.S_STILL; this.hold_steps = 0; this.t_prev = getTime(); } STEP_STATE.prototype.step_state = function() { var st = this.state; var t; switch (st) { case this.S_STILL: console.log("S_STILL"); this.state = this.S_STEP_1; this.t_prev = getTime(); this.hold_steps = 1; return 0; case this.S_STEP_1: t = Math.round((getTime() - this.t_prev)*1000); console.log(t + " S_STEP_1"); this.t_prev = getTime(); // we got a step within 1 second if (t <= 1000) { this.state = this.S_STEP_225; this.hold_steps = 2; } else { // we stay in STEP_1 state } return 0; case this.S_STEP_225: t = Math.round((getTime() - this.t_prev)*1000); console.log(t + " S_STEP_225"); this.t_prev = getTime(); // we got a step within 1 second if (t <= 1000) { this.hold_steps++; if (this.hold_steps >= 5) { this.state = this.S_STEPPING; return 5; } } else { // we did not get the step in time, back to STEP_1 this.state = this.S_STEP_1; this.hold_steps = 1; } return 0; case this.S_STEPPING: t = Math.round((getTime() - this.t_prev)*1000); console.log(t + " S_STEPPING"); this.t_prev = getTime(); // we got a step within 2 seconds, otherwise we stopped stepping if (t <= 2000) { this.state = this.S_STEPPING; return 1; } else { // we did not get the step in time, back to STEP_1 this.state = this.S_STEP_1; this.hold_steps = 1; } return 0; } // should never get here return 0; }; STEP_STATE.prototype.get_state = function() { switch(this.state) { case this.S_STILL: return "S_STILL"; case this.S_STEP_1: return "S_STEP_1"; case this.S_STEP_225: return "S_STEP_255"; case this.S_STEPPING: return "S_STEPPING"; default: return "ERROR"; } } let step_machine = new STEP_STATE(); function draw() { g.clear(); g.setColor(0); g.setColor(1,1,1); g.setFont("Vector",20); g.setFontAlign(0,-1); g.drawString(" " + step_machine.get_state() + " ", 120, 30, true); if (running) { g.drawString(step_count, 120, 100, true); } else { g.drawString("(" + step_count + ") BTN1 to START", 120, 100, true); } } var running = false; function onStartStop() { running = !running; if (running) { step_count = 0; // reset Bangle.on('accel',onAccel); } else { Bangle.removeListener('accel', onAccel); console.log("STOP -------------------------------------"); } } // handle switch display on by pressing BTN1 Bangle.on('lcdPower', function(on) { if (on) draw(); }); g.clear(); Bangle.loadWidgets(); Bangle.drawWidgets(); setInterval(draw, 1000); // refresh every second draw(); setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); setWatch(onStartStop, BTN1, {repeat:true,edge:"rising"});
Attached is a test log of me doing 10 steps, stopping and then watching steps 11,12 and 13 count themselves by ringing.
-
Had a look at the firmware code and translated it to a javascript version.
@Gordon - could you give it a quick eyeball to see if I have got it right./** * javascript version of the Bangle firmware step counter 2v09.90 * * See: https://github.com/espruino/Espruino/blob/master/libs/misc/stepcount.c * */ // values for debug plotting var lastAccel = 120; // BLUE - raw var lastAccelFilt = 120; // GREEN - filtered var lastThresh = 120; // RED /** * FIR filter designed with http://t-filter.appspot.com * sampling frequency: 12.5 Hz * fixed point precision: 10 bits * * 0 Hz - 1.1 Hz, gain = 0 desired attenuation = -40 dB * 1.3 Hz - 2.5 Hz, gain = 1 desired ripple = 5 dB * 2.7 Hz - 6.25 Hz, gain = 0 desired attenuation = -40 dB */ var filter_taps = new Int8Array([ -2, 4, 4, 1, -1, 0, 2, -3, -12, -13, 2, 24, 29, 6, -25, -33, -13, 10, 11, -1, 3, 29, 41, 4, -62, -89, -34, 62, 110, 62, -34, -89, -62, 4, 41, 29, 3, -1, 11, 10, -13, -33, -25, 6, 29, 24, 2, -13, -12, -3, 2, 0, -1, 1, 4, 4, -2 ]); // create a history buffer the same lenght as the filter taps array var history = new Int8Array(filter_taps.length); // value used in the f/m by default const stepCounterThreshold = 1000; /// has filtered acceleration passed stepCounterThreshold var stepWasLow = false; const STEPCOUNTERHISTORY = 5; // keep a history of last 5 steps const STEPCOUNTERHISTORY_TIME = 75; // 6*12.5Hz = 75 values samples ~ 6 seconds var stepHistory = new Int16Array(STEPCOUNTERHISTORY); var i; // initialise the history with values 255 (timedout) for (i=0;i<STEPCOUNTERHISTORY;i++) stepHistory[i]=255; // acceleromter operates at 12.5Hz function onAccel(a) { // scale to fit and clip var v = ((a.mag-1)*8192)>>5; /** * create a new Int8Array from the existing but starting from pos 1 of the existing history * this drops off index 0 and moves everything up, leaving the last entry to be filled * with the new value */ history.set(new Int8Array(history.buffer,1)); // set last value to the clipped value from the accel var raw_clipped = E.clip(v, -128, 127); history[history.length-1] = raw_clipped; // digital filter, output has to be scaled down as the calculation is integer based, no floating point var accFiltered = E.convolve(filter_taps, history, 0) >> 2; // increment step count history counters, each slot records how many samples ago the // step was from the currently detected step for (var i = 0 ;i < STEPCOUNTERHISTORY; i++) if (stepHistory[i] < 255) stepHistory[i]++; // check for steps, a bottom, followed by top threshold crossing = a step var hadStep = false; if (accFiltered < -stepCounterThreshold) { stepWasLow = true; } else if ((accFiltered > stepCounterThreshold) && stepWasLow) { stepWasLow = false; // We now have something resembling a step! // Don't register it unless we've already had X steps within Y time period if (stepHistory[0] < STEPCOUNTERHISTORY_TIME) { hadStep = true; } // Add it to our history anyway so we can keep track of how many steps we have for (i=0;i<STEPCOUNTERHISTORY-1;i++) stepHistory[i] = stepHistory[i+1]; stepHistory[STEPCOUNTERHISTORY-1] = 0; } // output data g.scroll(0,1); var n; // cyan markers on the edge of the screen mean that we crossed the threshold if (accFiltered < -stepCounterThreshold) g.setColor("#0ff").fillRect(0,0,8,0); if (accFiltered > stepCounterThreshold) g.setColor("#0ff").fillRect(232,0,240,0); // plot raw value BLUE n = 120+v; g.setColor("#00f").fillRect(lastAccel,0,n,0); lastAccel = n; // plot filtered value, in GREEN n = 120+(accFiltered>>6); g.setColor("#0f0").fillRect(lastAccelFilt,0,n,0); lastAccelFilt = n; // plot threshold in RED n = 120+(stepCounterThreshold>>6); g.setColor("#f00").fillRect(lastThresh,0,n,0); lastThresh = n; if (hadStep) { g.setColor(-1).drawString("STEP",60,0); } } g.clear(); Bangle.on('accel',onAccel); Bangle.setLCDTimeout(0);
-
This paper suggests that you get a 10% error rate when using a 10Hz sampling rate, this reduces to 2.5% for a 50Hz sampling rate. In the chart below magnitude is the algorithm Bangle is using.
Having said that the problem is still down to the detection of steps when basically sat still.
So its not filtering out the one off movements whens sleeping, sat at a desk etc. -
-
Thanks for filling in the gaps in my knowledge. I spent a good couple of hours looking at the code.
is it substantially worse than it was before, or only marginally better?
Gut feel ...about the same. Thats not bery scientific I know. I have have generally got used to measuring activepedom and then using the latest firmware through activepedom. That felt better. I have previously only tested walks and done comparisons against my AmizFit Bip. OR I have tested sitting still or watching TV to watch out for false step detecting. This was the first time I decided to do a midnight to 6pm test.
I wouldn't add your widget to the main app store
I think we need a javascript implementation that people can tweak. Its a lot easier than having to flash the firmware and means you can test multiple iterations on the same firmware. For me to go back to the previous version I will have to reflash older cutting edge firmware.
I have started reading a few papers that I have downloaded. A number of people have tested multiple approaches BUT they never show any code that you can look at. One thought I had was about writing to all the authors of papers in the last 5 years and asking if they were aware of Bangle and it they would like to attempt a step counter in javascript. If we provide a basic template App or Widget then it is easier to get started. Might not get much engagement but you never know. There is a substantial amount of time and effort needed for these kind of things as you need to repeat multiple tests many times with different people and different versions of software and algorithms.
Another idea might be to crowd fund for a cash prize for someome to write an accurate step counter that will be open source for Bangle. A free Bangle is not going to attract that many to have a go otherwise. I think there might be enough of us wanting a good step counter that we could each chip in £20 to get it done. 20 such donations would make a cash prize of £400.
Ref the accelerometer being 12.5Hz - has anyone done any tests to validate that it is regular ?
Also the papers I read suggested 20Hz as an ideal sampling freq. Thinking outloud - I reckon I generally walk at 2 steps per second. Thats only 6 samples per step at 12.5Hz, where as 20Hz would give 10 samples per step. Granted you only need to see the crossing of the thresholds.
It feels like there is an assumption somewehere that is not correct in practice. Again not very scientific.
Is there any accuracy data on the how the stock firmware step counter performs ? Could it be limited by the hardware ?
-
Wondering if the layout render will be smart enough to avoid flicker with the direct screen writes. I often write bits of code to reduce flicker by checking that the text I am about to g.drawString() is not just the same as the previous one I wrote. I usually keep a prevText variable and check before doing the clearRect() and drawString(). Now I know you can use an ArrayBuffer but I found they had a performance impact and that they caused memory fragmentation and then OOM errors when switching between watch faces in a multiclock format.
In terms of BTNs I think I might want to redesign my App to work with a single button. Probably use more swipes etc for some of the cycling through features I have done using BTN1 etc.
Hoping that we will still be free to code the old way if needed and that the layout manager is not the only option. The layout manager will need to be aware of parts of the screen where an ArrayBuffer might be used or a dynamic image (eg a rotating arrow for a compass) constructed from g.fillPoly() etc.
-
Trying to understand your code example - have added questions as comments.
// values for debug plotting var lastAccel = 120; // BLUE var lastAccelFilt = 120; // YELLOW var lastThresh = 120; // RED // question: where do these values come from ? what does tap mean ? // question: is this a low pass 3Hz filter ? var filter_taps = new Int8Array([ -2, 4, 4, 1, -1, 0, 2, -3, -12, -13, 2, 24, 29, 6, -25, -33, -13, 10, 11, -1, 3, 29, 41, 4, -62, -89, -34, 62, 110, 62, -34, -89, -62, 4, 41, 29, 3, -1, 11, 10, -13, -33, -25, 6, 29, 24, 2, -13, -12, -3, 2, 0, -1, 1, 4, 4, -2 ]); // create a history buffer and populate it with the tap values ? var history = new Int8Array(filter_taps.length); // what units is this in ? const stepCounterThresholdMin = 1500; const stepCounterAvr = 1; /// Theshold in filtered acceleration for detecting a step var stepCounterThreshold = stepCounterThresholdMin; /// has filtered acceleration passed stepCounterThresholdLow? var stepWasLow = false; function onAccel(a) { // question - onAccel is event driven, so events could be irregular, papers seem to suggest 20Hz sampling frequency // will this event driven approach provide regular sampling of approx 20Hz or will it be subject to CPU performance etc // scale to fit and clip var v = ((a.mag-1)*8192)>>5; // create a new Int8Array from the existing but starting from pos 1 of the existing history // this drops off index 0 and moves everything up, leaving the last entry to be filled // with the new value history.set(new Int8Array(history.buffer,1)); // set last value to the clipped value from the accel history[history.length-1] = E.clip(v, -128, 127); console.log("history"); console.log(history); // do filtering // what is convolution ?? //https://homepages.inf.ed.ac.uk/rbf/HIPR2/convolve.htm#:~:text=Convolution%20provides%20a%20way%20of,numbers%20of%20the%20same%20dimensionality.&text=In%20an%20image%20processing%20context,normally%20just%20a%20graylevel%20image. // question: - is this a low pass filter at 3Hz to get rid of noise ? // question: - why is accFiltered much larger than v , why >>2 to make it bigger ? var accFiltered = E.convolve(filter_taps, history, 0) >> 2; console.log("accFiltered"); console.log(accFiltered); /* // Simple average-based threshold if (stepCounterAvr) { var a = accFiltered; if (a<0) a=-a; stepCounterThreshold = (stepCounterThreshold*(32-stepCounterAvr) + a*stepCounterAvr) >> 5; if (stepCounterThreshold < stepCounterThresholdMin) stepCounterThreshold = stepCounterThresholdMin; }*/ // Set threshold based on the 'middle' history item - the one making the big spikes. // Try and scale it appropriately /* question: why would middle history item represent a big spike ? the history starts out blank and samples shuffle along in a FIFO manner initially history[32] will be 0 until the 32nd accel event has been captured ? question: having filtered why are we using the raw data again and not a scalled form of accFiltered ? */ var a = history[32] * 55; if (a<0) a=-a; if (a > stepCounterThreshold) stepCounterThreshold = a;//(stepCounterThreshold+a) >> 1; stepCounterThreshold -= 48; // question: why -= 48 ?? if (stepCounterThreshold < stepCounterThresholdMin) stepCounterThreshold = stepCounterThresholdMin; // check for steps, a bottom, followed by top threshold crossing = a step var hadStep = false; if (accFiltered < -stepCounterThreshold) stepWasLow = true; else if ((accFiltered > stepCounterThreshold) && stepWasLow) { stepWasLow = false; hadStep = true; } // output data g.scroll(0,1); var n; // question: cyan markers on the edge of the screen mean that // we crossed the threshold ?? if (accFiltered < -stepCounterThreshold) g.setColor("#0ff").fillRect(0,0,8,0); if (accFiltered > stepCounterThreshold) g.setColor("#0ff").fillRect(232,0,240,0); n = 120+v; g.setColor("#00f").fillRect(lastAccel,0,n,0); lastAccel = n; n = 120+(accFiltered>>6); g.setColor("#ff0").fillRect(lastAccelFilt,0,n,0); lastAccelFilt = n; n = 120+(stepCounterThreshold>>6); g.setColor("#f00").fillRect(lastThresh,0,n,0); lastThresh = n; if (hadStep) { g.setColor(-1).drawString("STEP",60,0); } } Bangle.on('accel',onAccel); Bangle.setLCDTimeout(0);
-
OK - its not good news I'm afraid. My test strategoy was to wear the Bangle and an Amafit bip from midnight onwards. Sleep with the watches on, go about my normal work day, mostly sat down, a couple of short walks of the dog etc. At 6pm I took both watches off and recorded the step count.
Bangle: 9902 and Amazfit Bip: 4479.
When I woke up in the morning the Bangle was on about 1200 steps to the Amizfit Bip 200 or so.
The idea of testing recorded accelerometer samples against the C code is good, but something is making it not work out in practice. At the end of the day its only the field test result that counts.
I like the javascript idea and might try and produce a widget out of the code you did.
I looked at the javascript code and made a few comments and have some questions that I will post. Apologies in advance I have never done anything with digital filters before and there will be a bit of learning curve on some of the javascript.I have had a look at a couple of papers on step counting out of curiosity so might try out a few things. I'm away for a few weeks in July so progress might be slow.
-
I think there should be seperate logs for GPS, steps, heart rate and the logging needs to be configurable and self managing, aggregating and automatically avoid storage overruns etc. It would be nice to just be able to view your heart rate graph for the last 7 days minute by minute. Then after 7 days it would be per hour for last 30 days, then just max, min, average per day after that. Something similar for steps maybe. GPS is a different kind of data from steps and heart rate. You can't aggregate GPS data in the same way you can aggregate step counts. Whilst steps and heart rate could log all the time. It has to be a decision to turn the GPS on (even in low power mode).
I'd really like to see a polling based model for steps, heart rate and GPS. Something like Bangle.getHeartRate() Bangle.getSteps() Bange.getFix() should return a reliable value with no need to filter / clean up etc etc. This avoids having to register / unregister listenners and check the power state of those devices - it also makes it much easier to share the state of the GPS (powered off, waiting for first fix, tracking) across apps in a multi-clock format.
But we really must have a really good step counter first. No point logging lots of inaccurate data.
-
I'm in favour as long as the results look nice at the end of the day.
Nothing worse than being forced to build to a butt ugly ui layout.Also would be nice to keep it a simple as possible with sensible defaults but with the ability to augment for more complex things.
If BTN handling is going to be part of this can we have long press built into the firmware so we can assign a call back for a short press and long press to different functions. I do this through timers at the moment.
Really looking forward to the new watch going on Sale. I'd like to pre-order if I could.
-
-
Hi @Gordon - I have flashed 2.09.90. And have done some tests. I just want to check a couple of things. 1) All that is necessary is to uplift to the cutting edge firmware. 2) there is no need to configure anything. Not sure what X steps in Y seconds is though. Is it 5 steps in 5 seconds or 3 steps in 3 seconds. To test it I am just using the widpedom widget and using the getSteps() call.
-
-
Recommended is good. Popular is what it says on the tin - easy to understand. IE most people liked it and would recommend it. If we want to get complicated then you could use the Net Promoter Score system (NPS). You ask people 'how likely are you to recommend this app to others'. 7 is discounted as sitting on the fence. 1-6 are detractors and 8,9,10 are promoters. https://en.wikipedia.org/wiki/Net_Promoter. But I think keep it simple (#thumbs up - #thumbs down).
-
Great discussion. Another option might be a thumbs up and a thumbs down. As an App developer I'd want to see at least a 1 line comment as to the reason for the thumbs up / down though. Eg - dont dis my app if you dont tell me why and please give me some credit / thanks if you like the App. Download stats probably is misleading. I've tried most of the Apps - often as there was no README or screenshots and it was the only way to make an assessment of the App. Taking photos of the Apps with your phone is quite time consuming. I often have to take 5 ot 6 shots before I get a decent one and before the LCD times out etc. Then I have to crop them and resize etc. Some ability to grab a standard 240x240 pixel png screenshot through IDE would be useful.
As for those sending the watch back as they dont like the Apps - then why not get learning javascript and start writing better apps. The whole point of an open source smartwatch is that its for tweaking and configuring. The bangle is not the prettiest of watches but it is the only one I know of where you can truly get control of the GPS chip. The Amafit Bip has reasonably good GPS but all you can get out of it is a lat and lon - such a waste of the hardware, and 6 out of 10 to Amizfit for not going the extra mile and writing the software to make a useful wrist based GPS app. Likewise when I use a FitBit Charge HR - I was always wondering why they never had a built in sedentary timer in them as it would have been an easy feature to provide. Its something I could code one day on the Bangle.
-
-
-
but they're still very much beta as I have to decide exactly what to do about making existing apps compatible (the screen/buttons are different).
I've been thinking about this. I think the best way would be to say, the screen is different size, its got one button, its a different watch but same API, you will have to port your App. Hopefully the original authors of the Apps would do the porting. The thing with a one button watch is that the whole way of interacting with an App will be totally changed. With it being a full touch screen I would expect use of Up, Down, Left, Right swipe would provide a lot more options for navigating round things.
I'm definitely up for having a go at the low power GPS again. It was a lot of work but worth it. There were a few comments made about me stepping outside the house a lot to get a signal :)
-
Good point. I have bought 3. Do you have any code that reads the temperture onto a Bangle ? There is quite a big temperature difference between the floors in our house. Think I might use it to check the temperature upstairs that its not too cold in winter around bedtime. The puck will have to wait a bit - I'm really waiting for the new Bangle watch to go up for sale.
-
-
It probably is a waste of a Puck but its a way to support the project and have a bit of func tinkering.
Getting a year out of the battery would be great. Probably only need the temperature to be accurate to within 5 minutes, if that would make any difference. Once the novelty has worn off I might reuse the puck for something else. Will have a look at the cheapo one as well. Might get both. -
I think once you actually get walking the ringing will be more consistant. I think it might also depend on a persons weight and momentum. Once I have a prototype that does not record many steps whilst sleeping I will think up a set of tests. My gold standard is a 1.03 mile circuit I have used many times that FitBit used to consistantly come back with 2000 steps. I would do a test at 2mph 3mph,4pm and jogging. I'm afraid all my distances are in miles - I know what an 8 minute mile feels like to run :) Another test will be a standard working day with odd walks round the house etc, will have to wear another tracker to compare.
If this line of attack does not come up with improved accuracy then will have to start looking at how other filter specs perform in terms of ringing. Its interesting to note that I have not seen mention of ringing in the various papers I have read. They just say a low pass filter was used.