/* This file is part of Wattzap Community Edition. * * Wattzap Community Edtion is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Wattzap Community Edition is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Wattzap. If not, see <http://www.gnu.org/licenses/>. */ package com.wattzap.model.ant; import java.util.ArrayList; import java.util.List; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.cowboycoders.ant.events.BroadcastListener; import org.cowboycoders.ant.messages.data.BroadcastDataMessage; import com.wattzap.model.dto.Telemetry; /** * (c) 2013 David George * * Speed and Cadence ANT+ processor. * * @author David George * @date 11 June 2013 * * (c) 2013-2015 David George / Wattzap.com */ public class SpeedCadenceListener implements BroadcastListener<BroadcastDataMessage> { private List<ChangeListener> listeners = new ArrayList<ChangeListener>(); static int lastTs = 0; static int lastTc = 0; static int sRR = 0; // previous speed rotation measurement static int cRR = 0; // previous cadence rotation measurement private static int sCount = 0; private static int cCount = 0; // private double distance = 0.0; private int cadence; /** * Speed and cadence data is contained in the 8 byte data payload in the * message. Speed and Cadence have the same format. A short integer giving * time since the last reading and a short integer giving the number of * revolutions since the last reading. * <p> * The format is:<br/> * [0][1] - Cadence timing<br/> * [2][3] - Cadence revolutions<br/> * [4][5] - Speed timing<br/> * [6][7] - Speed revolutions<br/> * <p> * Values are little Endian (MSB byte is on the right) * <p> * So for timing: [0] + ([1] << 8) / 1024 gives the time in milliseconds * since the last rollover. Note that you have to account for rollovers of * both time and rotations which happen every 16 seconds/16384 revolutions. * <p> * There is another wrinkle. Messages are sent at at 4Hz rate. Below a * certain rate (240rpm) we will see messages with the same number of * rotations. This doesn't mean the wheel is stopped, just there was no new * data since the last reading. To distinguish this from a stopped wheel a * certain number of same value readings are ignored for speed or cadence * updates. */ @Override public void receiveMessage(BroadcastDataMessage message) { int[] data = message.getUnsignedData(); Telemetry t = new Telemetry(); // Bytes 0 and 1: TTTT / 1024 = milliSeconds since the last // rollover for cadence int tC = data[0] + (data[1] << 8); // Bytes 2 and 3: Cadence rotation Count int cR = data[2] + (data[3] << 8); // Bytes 4 and 5: TTTT / 1024 = milliSeconds since the last // rollover for speed int tS = data[4] + (data[5] << 8); // Bytes 6 and 7: speed rotation count. int sR = data[6] + (data[7] << 8); // System.out // .println("tC " + tC + " cR " + cR + " tS " + tS + " sR " + sR); if (lastTs == 0 || lastTc == 0) { // first time through, initialize counters and return lastTs = tS; lastTc = tC; sRR = sR; cRR = cR; return; } int tD; // time delta if (tS < lastTs) { // we have rolled over tD = tS + (65536 - lastTs); } else { tD = tS - lastTs; } int sRD; // speed rotation delta if (sR < sRR) { // we have rolled over sRD = sR + (65536 - sRR); } else { sRD = sR - sRR; } if (tD > 0) { double distanceKM = (sRD * 2013) / 100000; double timeS = ((double) tD) / 1024.0; t.setTime(t.getTime() + (int) (timeS * 1000)); double speed = distanceKM / (timeS / (60.0 * 60.0)); distance += distanceKM; t.setSpeed(speed); t.setDistanceMeters(distance * 1000); t.setCadence(cadence); sCount = 0; } else if (sCount < 12) { sCount++; } int cTD; // cadence time delta if (tC < lastTc) { // we have rolled over cTD = tC + (65536 - lastTc); } else { cTD = tC - lastTc; } int cRD; // cadence rotation delta if (cR < cRR) { // we have rolled over cRD = cR + (65536 - cRR); } else { cRD = cR - cRR; } if (cRD > 0) { double timeC = ((double) cTD) / 1024.0; cadence = ((int) (cRD * ((1 / timeC) * 60.0))); cCount = 0; } else if (cCount < 12) { cCount++; } lastTs = tS; lastTc = tC; cRR = cR; sRR = sR; if (t.getSpeedKMH() >= 0.0) { notifyListeners(t); } } void notifyListeners(Telemetry t) { ChangeEvent event = new ChangeEvent(t); for (ChangeListener l : listeners) { l.stateChanged(event); } } public void addChangeListener(ChangeListener l) { listeners.add(l); } }