inner classes moved to their own files

This commit is contained in:
ml1050
2012-01-20 18:06:30 +01:00
parent 8afc6b4c04
commit bee9f46eaf
7 changed files with 394 additions and 236 deletions
@@ -29,240 +29,14 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import java.util.LinkedList;
/**
* A sensor manager to connect ANT+ sensors.
* This can include heart rate monitors.
*
* @author Sandor Dornbush
* @author Laszlo Molnar
*/
public class AntDirectSensorManager extends AntSensorManager {
/*
* These constants are defined by the ANT+ spec.
*/
public static final byte NETWORK_NUMBER = 1;
public static final byte RF_FREQUENCY = 57;
public abstract class AntSensorBase {
protected byte channel;
protected short deviceNumber;
private byte deviceType;
private short channelPeriod;
AntSensorBase(byte channel, short deviceNumber, byte deviceType,
String deviceTypeString, short channelPeriod) {
this.channel = channel;
this.deviceNumber = deviceNumber;
this.deviceType = deviceType;
this.channelPeriod = channelPeriod;
Log.i(TAG, "Will pair with " + deviceTypeString + " device: " + ((int) deviceNumber & 0xFFFF));
}
protected void resolveWildcardDeviceNumber() {
if (!isPaired()) {
try {
getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID);
} catch (AntInterfaceException e) {
Log.e(TAG, "ANT error handling broadcast data", e);
}
Log.d(TAG, "Requesting channel id id on channel: " + channel);
}
}
public abstract void handleBroadcastData(byte[] antMessage);
public boolean setupChannel() {
Log.i(TAG, "ant sensor setupChannel " + channel + " " + deviceType);
return setupAntSensorChannel(NETWORK_NUMBER, channel, deviceNumber,
deviceType, (byte) 0x01, channelPeriod,
RF_FREQUENCY, (byte) 0);
}
public void setDeviceNumber(short dn) {
deviceNumber = dn;
}
public boolean isPaired() {
return deviceNumber != WILDCARD;
}
};
// Heart reate monitor sensor
public class HeartRateSensor extends AntSensorBase {
/*
* These constants are defined by the ANT+ heart rate monitor spec.
*/
public static final byte HEART_RATE_DEVICE_TYPE = 120;
public static final short HEART_RATE_CHANNEL_PERIOD = 8070;
HeartRateSensor(byte channel, short devNum) {
super(channel, devNum, HEART_RATE_DEVICE_TYPE,
"heart rate monitor", HEART_RATE_CHANNEL_PERIOD);
}
/**
* Decode an ANT+ heart rate monitor message.
* @param antMessage The byte array received from the heart rate monitor.
*/
public void handleBroadcastData(byte[] antMessage) {
resolveWildcardDeviceNumber();
int bpm = (int) antMessage[8] & 0xFF;
Log.d(TAG, "now:" + System.currentTimeMillis() + " heart rate=" + bpm);
sendHeartRate(bpm);
}
};
/**
* Processes an ANT sensor data + timestamp pair,
* and returns the instantaneous value of the sensor
*/
public class SensorDataProcessor {
/**
* History is used for looking back at the old data
* when no new data is present, but an instantaneous value is needed
*/
private class HistoryElement {
public long sysTime;
public int data;
public int sensorTime;
HistoryElement(long sys, int d, int sens) {
sysTime = sys;
data = d;
sensorTime = sens;
}
};
// Removes old data from the history.
// Returns true if the remaining history is not empty.
protected boolean removeOldHistory(long now) {
HistoryElement h;
while ((h = history.peek()) != null) {
if (now - h.sysTime <= historyLengthMillis) {
return true;
}
history.removeFirst();
}
return false;
}
private int counter = -1; // the latest counter value reported by the sensor
private int actValue = 0;
public static final int historyLengthMillis = 5000; // 5 sec
private LinkedList<HistoryElement> history = new LinkedList<HistoryElement>();
public int getValue(int data, int sensorTime) {
long now = System.currentTimeMillis();
int dDelta = (data - counter) & 0xFFFF;
Log.d(TAG, "now=" + now + " data=" + data + " sensortime=" + sensorTime);
if (counter < 0) {
// store the actual counter value from the sensor
counter = data;
return actValue = 0;
}
counter = data;
if (dDelta != 0) {
if (removeOldHistory(now)) {
HistoryElement h = history.getLast();
actValue = ((int) ((data - h.data) & 0xFFFF)) * 1024 * 60
/ (int) ((sensorTime - h.sensorTime) & 0xFFFF);
}
history.addLast(new HistoryElement(now, data, sensorTime));
} else if (!history.isEmpty()) {
HistoryElement h = history.getLast();
if (60000 < (now - h.sysTime) * actValue) {
if (!removeOldHistory(now)) {
actValue = 0;
} else {
HistoryElement f = history.getFirst();
HistoryElement l = history.getLast();
int sDelta = (l.sensorTime - f.sensorTime) & 0xFFFF;
int cDelta = (data - f.data) & 0xFFFF;
// the saved actValue is not overwritten by this
// the returned value is computed from the history
int v = (int) (cDelta * 60 * 1000 / (now - l.sysTime + (sDelta / 1024) * 1000));
Log.d(TAG, "getValue returns (2):" + v);
return v < actValue ? v : actValue;
}
} else {
// the current actValue is still valid, nothing to do here
}
} else {
actValue = 0;
}
Log.d(TAG, "getValue returns:" + actValue);
return actValue;
}
}
// Cadence sensor
public class CadenseSensor extends AntSensorBase {
/*
* These constants are defined by the ANT+ bike speed and cadence sensor spec.
*/
public static final byte CADENCE_DEVICE_TYPE = 122;
public static final short CADENCE_CHANNEL_PERIOD = 8102;
SensorDataProcessor cadence = new SensorDataProcessor();
CadenseSensor(byte channel, short devNum) {
super(channel, devNum, CADENCE_DEVICE_TYPE,
"cadence sensor", CADENCE_CHANNEL_PERIOD);
}
/**
* Decode an ANT+ cadence sensor message.
* @param antMessage The byte array received from the cadence sensor.
*/
public void handleBroadcastData(byte[] antMessage) {
resolveWildcardDeviceNumber();
int sensorTime = ((int) antMessage[5] & 0xFF) + ((int) antMessage[6] & 0xFF) * 256;
int crankRevs = ((int) antMessage[7] & 0xFF) + ((int) antMessage[8] & 0xFF) * 256;
sendCadence(cadence.getValue(crankRevs, sensorTime));
}
};
// Combined cadence and speed sensor
public class CadenceSpeedSensor extends AntSensorBase {
/*
* These constants are defined by the ANT+ bike speed and cadence sensor spec.
*/
public static final byte CADENCE_SPEED_DEVICE_TYPE = 121;
public static final short CADENCE_SPEED_CHANNEL_PERIOD = 8086;
SensorDataProcessor cadence = new SensorDataProcessor();
CadenceSpeedSensor(byte channel, short devNum) {
super(channel, devNum, CADENCE_SPEED_DEVICE_TYPE,
"speed&cadence sensor", CADENCE_SPEED_CHANNEL_PERIOD);
}
/**
* Decode an ANT+ cadence&speed sensor message.
* @param antMessage The byte array received from the sensor.
*/
public void handleBroadcastData(byte[] antMessage) {
resolveWildcardDeviceNumber();
int sensorTime = ((int) antMessage[1] & 0xFF) + ((int) antMessage[2] & 0xFF) * 256;
int crankRevs = ((int) antMessage[3] & 0xFF) + ((int) antMessage[4] & 0xFF) * 256;
sendCadence(cadence.getValue(crankRevs, sensorTime));
}
};
public class AntDirectSensorManager extends AntSensorManager
implements AntSensorDataCollector {
// allocating one channel for each sensor type
private static final byte HEART_RATE_CHANNEL = 0;
@@ -305,7 +79,10 @@ public class AntDirectSensorManager extends AntSensorManager {
AntSensorBase sensor = sensors[channel];
switch (messageId) {
case AntMesg.MESG_BROADCAST_DATA_ID:
sensor.handleBroadcastData(messageData);
if (sensor.getDeviceNumber() == WILDCARD) {
resolveWildcardDeviceNumber((byte) channel);
}
sensor.handleBroadcastData(messageData, this);
break;
case AntMesg.MESG_RESPONSE_EVENT_ID:
handleMessageResponse(messageData);
@@ -320,7 +97,7 @@ public class AntDirectSensorManager extends AntSensorManager {
return true;
}
short handleChannelId(byte[] rawMessage) {
private short handleChannelId(byte[] rawMessage) {
AntChannelIdMessage message = new AntChannelIdMessage(rawMessage);
short deviceNumber = message.getDeviceNumber();
byte channel = message.getChannelNumber();
@@ -347,9 +124,9 @@ public class AntDirectSensorManager extends AntSensorManager {
connectingChannelsBitmap &= ~(1 << channel);
Log.i(TAG, "ANT channel " + channel + " disconnected.");
if (sensors[channel].isPaired()) {
if (sensors[channel].getDeviceNumber() != WILDCARD) {
Log.i(TAG, "Retrying....");
if (sensors[channel].setupChannel()) {
if (setupChannel(sensors[channel], channel)) {
connectingChannelsBitmap |= 1 << channel;
}
}
@@ -381,6 +158,15 @@ public class AntDirectSensorManager extends AntSensorManager {
}
}
private void resolveWildcardDeviceNumber(byte channel) {
try {
getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID);
} catch (AntInterfaceException e) {
Log.e(TAG, "ANT error handling broadcast data", e);
}
Log.d(TAG, "Requesting channel id id on channel: " + channel);
}
@Override
protected void setupAntSensorChannels() {
short devIds[] = new short[sensorIdKeys.length];
@@ -394,14 +180,14 @@ public class AntDirectSensorManager extends AntSensorManager {
}
sensors = new AntSensorBase[] {
new HeartRateSensor(HEART_RATE_CHANNEL, devIds[HEART_RATE_CHANNEL]),
new CadenseSensor(CADENCE_CHANNEL, devIds[CADENCE_CHANNEL]),
new CadenceSpeedSensor(CADENCE_SPEED_CHANNEL, devIds[CADENCE_SPEED_CHANNEL]),
new HeartRateSensor(devIds[HEART_RATE_CHANNEL]),
new CadenceSensor(devIds[CADENCE_CHANNEL]),
new CadenceSpeedSensor(devIds[CADENCE_SPEED_CHANNEL]),
};
connectingChannelsBitmap = 0;
for (int i = 0; i < sensors.length; ++i) {
if (sensors[i].setupChannel()) {
if (setupChannel(sensors[i], (byte) i)) {
connectingChannelsBitmap |= 1 << i;
}
}
@@ -410,6 +196,19 @@ public class AntDirectSensorManager extends AntSensorManager {
}
}
private boolean setupChannel(AntSensorBase sensor, byte channel) {
Log.i(TAG, "setup channel=" + channel + " deviceType=" + sensor.getDeviceType());
return setupAntSensorChannel(sensor.getNetworkNumber(),
channel,
sensor.getDeviceNumber(),
sensor.getDeviceType(),
(byte) 0x01,
sensor.getChannelPeriod(),
sensor.getFrequency(),
(byte) 0);
}
private void sendSensorData(byte index, int value) {
if (index >= currentSensorData.length) {
Log.w(TAG, "invalid index in sendSensorData:" + index);
@@ -0,0 +1,73 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services.sensors.ant;
import static com.google.android.apps.mytracks.Constants.TAG;
import android.util.Log;
/**
* Base class for ANT+ sensors.
*
* @author Laszlo Molnar
*/
public abstract class AntSensorBase {
/*
* These constants are defined by the ANT+ spec.
*/
public static final byte NETWORK_NUMBER = 1;
public static final byte RF_FREQUENCY = 57;
protected short deviceNumber;
private byte deviceType;
private short channelPeriod;
AntSensorBase(short deviceNumber, byte deviceType,
String deviceTypeString, short channelPeriod) {
this.deviceNumber = deviceNumber;
this.deviceType = deviceType;
this.channelPeriod = channelPeriod;
Log.i(TAG, "Will pair with " + deviceTypeString + " device: " + ((int) deviceNumber & 0xFFFF));
}
public abstract void handleBroadcastData(byte[] antMessage, AntSensorDataCollector c);
public void setDeviceNumber(short dn) {
deviceNumber = dn;
}
public short getDeviceNumber() {
return deviceNumber;
}
public byte getNetworkNumber() {
return NETWORK_NUMBER;
}
public byte getFrequency() {
return RF_FREQUENCY;
}
public byte getDeviceType() {
return deviceType;
}
public short getChannelPeriod() {
return channelPeriod;
}
};
@@ -0,0 +1,30 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services.sensors.ant;
/**
* Interface for collecting data from the sensor classes
*
* @author Laszlo Molnar
*/
public interface AntSensorDataCollector {
public void sendCadence(int value);
public void sendHeartRate(int value);
}
@@ -0,0 +1,48 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services.sensors.ant;
/**
* Cadence sensor.
*
* @author Laszlo Molnar
*/
public class CadenceSensor extends AntSensorBase {
/*
* These constants are defined by the ANT+ bike speed and cadence sensor spec.
*/
public static final byte CADENCE_DEVICE_TYPE = 122;
public static final short CADENCE_CHANNEL_PERIOD = 8102;
SensorDataProcessor cadence = new SensorDataProcessor();
CadenceSensor(short devNum) {
super(devNum, CADENCE_DEVICE_TYPE,
"cadence sensor", CADENCE_CHANNEL_PERIOD);
}
/**
* Decode an ANT+ cadence sensor message.
* @param antMessage The byte array received from the cadence sensor.
*/
public void handleBroadcastData(byte[] antMessage, AntSensorDataCollector c) {
int sensorTime = ((int) antMessage[5] & 0xFF) + ((int) antMessage[6] & 0xFF) * 256;
int crankRevs = ((int) antMessage[7] & 0xFF) + ((int) antMessage[8] & 0xFF) * 256;
c.sendCadence(cadence.getValue(crankRevs, sensorTime));
}
};
@@ -0,0 +1,48 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services.sensors.ant;
/**
* Combined cadence and speed sensor.
*
* @author Laszlo Molnar
*/
public class CadenceSpeedSensor extends AntSensorBase {
/*
* These constants are defined by the ANT+ bike speed and cadence sensor spec.
*/
public static final byte CADENCE_SPEED_DEVICE_TYPE = 121;
public static final short CADENCE_SPEED_CHANNEL_PERIOD = 8086;
SensorDataProcessor cadence = new SensorDataProcessor();
CadenceSpeedSensor(short devNum) {
super(devNum, CADENCE_SPEED_DEVICE_TYPE,
"speed&cadence sensor", CADENCE_SPEED_CHANNEL_PERIOD);
}
/**
* Decode an ANT+ cadence&speed sensor message.
* @param antMessage The byte array received from the sensor.
*/
public void handleBroadcastData(byte[] antMessage, AntSensorDataCollector c) {
int sensorTime = ((int) antMessage[1] & 0xFF) + ((int) antMessage[2] & 0xFF) * 256;
int crankRevs = ((int) antMessage[3] & 0xFF) + ((int) antMessage[4] & 0xFF) * 256;
c.sendCadence(cadence.getValue(crankRevs, sensorTime));
}
};
@@ -0,0 +1,49 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services.sensors.ant;
import static com.google.android.apps.mytracks.Constants.TAG;
import android.util.Log;
/**
* Heart reate monitor sensor.
*
* @author Laszlo Molnar
*/
public class HeartRateSensor extends AntSensorBase {
/*
* These constants are defined by the ANT+ heart rate monitor spec.
*/
public static final byte HEART_RATE_DEVICE_TYPE = 120;
public static final short HEART_RATE_CHANNEL_PERIOD = 8070;
HeartRateSensor(short devNum) {
super(devNum, HEART_RATE_DEVICE_TYPE,
"heart rate monitor", HEART_RATE_CHANNEL_PERIOD);
}
/**
* Decode an ANT+ heart rate monitor message.
* @param antMessage The byte array received from the heart rate monitor.
*/
public void handleBroadcastData(byte[] antMessage, AntSensorDataCollector c) {
int bpm = (int) antMessage[8] & 0xFF;
Log.d(TAG, "now:" + System.currentTimeMillis() + " heart rate=" + bpm);
c.sendHeartRate(bpm);
}
};
@@ -0,0 +1,111 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services.sensors.ant;
import static com.google.android.apps.mytracks.Constants.TAG;
import android.util.Log;
import java.util.LinkedList;
/**
* Processes an ANT sensor data (counter) + timestamp pair,
* and returns the instantaneous value of the sensor
*
* @author Laszlo Molnar
*/
public class SensorDataProcessor {
/**
* History is used for looking back at the old data
* when no new data is present, but an instantaneous value is needed
*/
private class HistoryElement {
public long sysTime;
public int data;
public int sensorTime;
HistoryElement(long sys, int d, int sens) {
sysTime = sys;
data = d;
sensorTime = sens;
}
};
// Removes old data from the history.
// Returns true if the remaining history is not empty.
protected boolean removeOldHistory(long now) {
HistoryElement h;
while ((h = history.peek()) != null) {
if (now - h.sysTime <= historyLengthMillis) {
return true;
}
history.removeFirst();
}
return false;
}
private int counter = -1; // the latest counter value reported by the sensor
private int actValue = 0;
public static final int historyLengthMillis = 5000; // 5 sec
private LinkedList<HistoryElement> history = new LinkedList<HistoryElement>();
public int getValue(int data, int sensorTime) {
long now = System.currentTimeMillis();
int dDelta = (data - counter) & 0xFFFF;
Log.d(TAG, "now=" + now + " data=" + data + " sensortime=" + sensorTime);
if (counter < 0) {
// store the actual counter value from the sensor
counter = data;
return actValue = 0;
}
counter = data;
if (dDelta != 0) {
if (removeOldHistory(now)) {
HistoryElement h = history.getLast();
actValue = ((int) ((data - h.data) & 0xFFFF)) * 1024 * 60
/ (int) ((sensorTime - h.sensorTime) & 0xFFFF);
}
history.addLast(new HistoryElement(now, data, sensorTime));
} else if (!history.isEmpty()) {
HistoryElement h = history.getLast();
if (60000 < (now - h.sysTime) * actValue) {
if (!removeOldHistory(now)) {
actValue = 0;
} else {
HistoryElement f = history.getFirst();
HistoryElement l = history.getLast();
int sDelta = (l.sensorTime - f.sensorTime) & 0xFFFF;
int cDelta = (data - f.data) & 0xFFFF;
// the saved actValue is not overwritten by this
// the returned value is computed from the history
int v = (int) (cDelta * 60 * 1000 / (now - l.sysTime + (sDelta / 1024) * 1000));
Log.d(TAG, "getValue returns (2):" + v);
return v < actValue ? v : actValue;
}
} else {
// the current actValue is still valid, nothing to do here
}
} else {
actValue = 0;
}
Log.d(TAG, "getValue returns:" + actValue);
return actValue;
}
}