Refactor AntSensorManager so each ChannelConfiguration is in its

own class.
This commit is contained in:
Jimmy Shih
2012-06-19 15:13:01 -07:00
parent 4bc19666a2
commit 5d2144f698
8 changed files with 507 additions and 388 deletions
@@ -41,7 +41,7 @@ import java.lang.reflect.Field;
/** /**
* Ant Sensor Manager. * Ant Sensor Manager.
* *
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public class AntSensorManager extends SensorManager { public class AntSensorManager extends SensorManager {
@@ -58,33 +58,12 @@ public class AntSensorManager extends SensorManager {
// recently // recently
OFFLINE // Channel is closed as the result of a search timeout OFFLINE // Channel is closed as the result of a search timeout
} }
private static final String TAG = AntSensorManager.class.getSimpleName();
public static final short WILDCARD = 0; public static final short WILDCARD = 0;
private static final String TAG = AntSensorManager.class.getSimpleName();
private static final int CHANNELS = 4; private static final int CHANNELS = 4;
private static final byte HRM_CHANNEL = (byte) 0; // heart rate monitor
private static final byte SDM_CHANNEL = (byte) 1; // speed distance monitor
private static final byte BCS_CHANNEL = (byte) 2; // bike cadence sensor
private static final byte CBS_CHANNEL = (byte) 3; // combined bike speed and cadence sensor
private static final byte HRM_DEVICE_TYPE = 0x78;
private static final byte SDM_DEVICE_TYPE = 0x7C;
private static final byte BCS_DEVICE_TYPE = 0x7A;
private static final byte CBS_DEVICE_TYPE = 0x79;
private static final short HRM_PERIOD = 8070;
private static final short SDM_PERIOD = 8134;
private static final short BCS_PERIOD = 8102;
private static final short CBS_PERIOD = 8086;
private static final byte DEFAULT_PROXIMITY_SEARCH_BIN = 7;
private static final String RADIO_ANT = "ant"; private static final String RADIO_ANT = "ant";
private static final byte ANT_NETWORK = (byte) 0x01; private static final byte ANT_NETWORK = (byte) 0x01;
private static final int ANT_FREQUENCY = 57; // 2457Mhz (Ant+ frequency)
private static final int ANT_TRANSMISSION_TYPE = 0; // 0 for wild card search
private final Context context; private final Context context;
private final ChannelConfiguration channelConfig[]; private final ChannelConfiguration channelConfig[];
@@ -93,38 +72,26 @@ public class AntSensorManager extends SensorManager {
private boolean serviceConnected = false; private boolean serviceConnected = false;
private boolean hasClaimedInterface = false; private boolean hasClaimedInterface = false;
private ChannelStates hrmState = ChannelStates.CLOSED;
private ChannelStates sdmState = ChannelStates.CLOSED;
private ChannelStates bcsState = ChannelStates.CLOSED;
private ChannelStates cbsState = ChannelStates.CLOSED;
private short hrmDeviceNumber = WILDCARD;
private short sdmDeviceNumber = WILDCARD;
private short bcsDeviceNumber = WILDCARD;
private short cbsDeviceNumber = WILDCARD;
private SensorDataSet sensorDataSet = null; private SensorDataSet sensorDataSet = null;
private int lastHeartRate = -1;
private int lastCadence = -1;
private long lastSensorDataSetTime = 0; private long lastSensorDataSetTime = 0;
private AntSensorValue antSensorValue = new AntSensorValue();
private CadenceCounter bcsCadenceCounter;
private CadenceCounter cbsCadenceCounter;
private boolean requestedReset = false; private boolean requestedReset = false;
/** /**
* Constructor. * Constructor.
* *
* @param context the context * @param context the context
*/ */
public AntSensorManager(Context context) { public AntSensorManager(Context context) {
this.context = context; this.context = context;
channelConfig = new ChannelConfiguration[CHANNELS]; channelConfig = new ChannelConfiguration[CHANNELS];
channelConfig[HRM_CHANNEL] = new ChannelConfiguration(); channelConfig[0] = new HeartRateChannelConfiguration();
channelConfig[SDM_CHANNEL] = new ChannelConfiguration(); channelConfig[1] = new SpeedDistanceChannelConfiguration();
channelConfig[BCS_CHANNEL] = new ChannelConfiguration(); channelConfig[2] = new BikeCadenceChannelConfiguration();
channelConfig[CBS_CHANNEL] = new ChannelConfiguration(); channelConfig[3] = new CombinedBikeChannelConfiguration();
statusIntentFilter = new IntentFilter(); statusIntentFilter = new IntentFilter();
statusIntentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION); statusIntentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION);
@@ -171,7 +138,7 @@ public class AntSensorManager extends SensorManager {
} }
} }
} }
@Override @Override
protected void tearDownChannel() { protected void tearDownChannel() {
try { try {
@@ -214,12 +181,12 @@ public class AntSensorManager extends SensorManager {
} }
private AntInterface.ServiceListener serviceListener = new AntInterface.ServiceListener() { private AntInterface.ServiceListener serviceListener = new AntInterface.ServiceListener() {
@Override @Override
public void onServiceConnected() { public void onServiceConnected() {
handleServiceConnected(); handleServiceConnected();
} }
@Override @Override
public void onServiceDisconnected() { public void onServiceDisconnected() {
serviceConnected = false; serviceConnected = false;
if (hasClaimedInterface) { if (hasClaimedInterface) {
@@ -256,89 +223,26 @@ public class AntSensorManager extends SensorManager {
/** /**
* Opens a channel. * Opens a channel.
* *
* @param channel the channel * @param channel the channel
*/ */
private void openChannel(byte channel) { private void openChannel(byte channel) {
channelConfig[channel].deviceNumber = 0; short deviceNumber = (short) PreferencesUtils.getInt(
channelConfig[channel].deviceType = 0; context, channelConfig[channel].getDeviceIdKey(), WILDCARD);
channelConfig[channel].TransmissionType = ANT_TRANSMISSION_TYPE; channelConfig[channel].setDeviceNumber(deviceNumber);
channelConfig[channel].period = 0; channelConfig[channel].setChannelState(ChannelStates.PENDING_OPEN);
channelConfig[channel].freq = ANT_FREQUENCY;
channelConfig[channel].proxSearch = DEFAULT_PROXIMITY_SEARCH_BIN;
short deviceNumber = -1;
byte deviceType = 0;
short period = 0;
switch (channel) {
case HRM_CHANNEL:
deviceNumber = (short) PreferencesUtils.getInt(
context, R.string.ant_heart_rate_monitor_id_key, WILDCARD);
deviceType = HRM_DEVICE_TYPE;
period = HRM_PERIOD;
hrmDeviceNumber = deviceNumber;
hrmState = ChannelStates.PENDING_OPEN;
break;
case SDM_CHANNEL:
deviceNumber = (short) PreferencesUtils.getInt(
context, R.string.ant_speed_distance_monitor_id_key, WILDCARD);
deviceType = SDM_DEVICE_TYPE;
period = SDM_PERIOD;
sdmDeviceNumber = deviceNumber;
sdmState = ChannelStates.PENDING_OPEN;
break;
case BCS_CHANNEL:
deviceNumber = (short) PreferencesUtils.getInt(
context, R.string.ant_bike_cadence_sensor_id_key, WILDCARD);
deviceType = BCS_DEVICE_TYPE;
period = BCS_PERIOD;
bcsDeviceNumber = deviceNumber;
bcsState = ChannelStates.PENDING_OPEN;
bcsCadenceCounter = new CadenceCounter();
break;
case CBS_CHANNEL:
deviceNumber = (short) PreferencesUtils.getInt(
context, R.string.ant_combined_bike_sensor_id_key, WILDCARD);
deviceType = CBS_DEVICE_TYPE;
period = CBS_PERIOD;
cbsDeviceNumber = deviceNumber;
cbsState = ChannelStates.PENDING_OPEN;
cbsCadenceCounter = new CadenceCounter();
break;
default:
break;
}
if (deviceNumber != -1) {
channelConfig[channel].deviceNumber = deviceNumber;
channelConfig[channel].deviceType = deviceType;
channelConfig[channel].period = period;
}
setupAntChannel(ANT_NETWORK, channel); setupAntChannel(ANT_NETWORK, channel);
} }
/** /**
* Closes a channel. * Closes a channel.
* *
* @param channel the channel * @param channel the channel
*/ */
private void closeChannel(byte channel) { private void closeChannel(byte channel) {
channelConfig[channel].isInitializing = false; channelConfig[channel].setInitializing(false);
channelConfig[channel].isDeinitializing = true; channelConfig[channel].setDeinitializing(true);
switch (channel) { channelConfig[channel].setChannelState(ChannelStates.CLOSED);
case HRM_CHANNEL:
hrmState = ChannelStates.CLOSED;
break;
case SDM_CHANNEL:
sdmState = ChannelStates.CLOSED;
break;
case BCS_CHANNEL:
bcsState = ChannelStates.CLOSED;
break;
case CBS_CHANNEL:
cbsState = ChannelStates.CLOSED;
break;
default:
break;
}
setSensorState(SensorState.DISCONNECTED); setSensorState(SensorState.DISCONNECTED);
try { try {
antInterface.ANTCloseChannel(channel); antInterface.ANTCloseChannel(channel);
@@ -353,10 +257,9 @@ public class AntSensorManager extends SensorManager {
* Clears all channels. * Clears all channels.
*/ */
private void clearAllChannels() { private void clearAllChannels() {
hrmState = ChannelStates.CLOSED; for (int i = 0; i < CHANNELS; i++) {
sdmState = ChannelStates.CLOSED; channelConfig[i].setChannelState(ChannelStates.CLOSED);
bcsState = ChannelStates.CLOSED; }
cbsState = ChannelStates.CLOSED;
setSensorState(SensorState.DISCONNECTED); setSensorState(SensorState.DISCONNECTED);
} }
@@ -427,66 +330,42 @@ public class AntSensorManager extends SensorManager {
}; };
private final BroadcastReceiver dataReceiver = new BroadcastReceiver() { private final BroadcastReceiver dataReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context c, Intent intent) { public void onReceive(Context c, Intent intent) {
if (intent.getAction().equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) { if (intent.getAction().equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) {
byte[] ANTRxMessage = intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE); byte[] antRxMessage = intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE);
switch (ANTRxMessage[AntMesg.MESG_ID_OFFSET]) { byte channel;
switch (antRxMessage[AntMesg.MESG_ID_OFFSET]) {
case AntMesg.MESG_BROADCAST_DATA_ID: case AntMesg.MESG_BROADCAST_DATA_ID:
case AntMesg.MESG_ACKNOWLEDGED_DATA_ID: case AntMesg.MESG_ACKNOWLEDGED_DATA_ID:
// Switch on channel number channel = antRxMessage[AntMesg.MESG_DATA_OFFSET];
switch (ANTRxMessage[AntMesg.MESG_DATA_OFFSET]) { if (channelConfig[channel].getChannelState() != ChannelStates.CLOSED) {
case HRM_CHANNEL: channelConfig[channel].setChannelState(ChannelStates.TRACKING_DATA);
decodeHrmMessage(ANTRxMessage);
break;
case SDM_CHANNEL:
decodeSdmMessage(ANTRxMessage);
break;
case BCS_CHANNEL:
decodeBcsMessage(ANTRxMessage);
break;
case CBS_CHANNEL:
decodeCbsMessage(ANTRxMessage);
break;
default:
break;
} }
if (channelConfig[channel].getDeviceNumber() == WILDCARD) {
try {
antInterface.ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID);
} catch (AntInterfaceException e) {
handleAntError();
}
}
channelConfig[channel].decodeMessage(antRxMessage, antSensorValue);
setSensorDataSet();
break; break;
case AntMesg.MESG_RESPONSE_EVENT_ID: case AntMesg.MESG_RESPONSE_EVENT_ID:
handleResponseEventMessage(ANTRxMessage); handleResponseEventMessage(antRxMessage);
break; break;
case AntMesg.MESG_CHANNEL_ID_ID: case AntMesg.MESG_CHANNEL_ID_ID:
short deviceNum = (short) ((ANTRxMessage[AntMesg.MESG_DATA_OFFSET + 1] & 0xFF channel = antRxMessage[AntMesg.MESG_DATA_OFFSET];
| ((ANTRxMessage[AntMesg.MESG_DATA_OFFSET + 2] & 0xFF) << 8)) & 0xFFFF); short deviceNumber = (short) ((antRxMessage[AntMesg.MESG_DATA_OFFSET + 1] & 0xFF
int keyId = -1; | ((antRxMessage[AntMesg.MESG_DATA_OFFSET + 2] & 0xFF) << 8)) & 0xFFFF);
// Switch on channel number channelConfig[channel].setDeviceNumber(deviceNumber);
switch (ANTRxMessage[AntMesg.MESG_DATA_OFFSET]) { PreferencesUtils.setInt(context, channelConfig[channel].getDeviceIdKey(), deviceNumber);
case HRM_CHANNEL: Toast.makeText(context,
hrmDeviceNumber = deviceNum; context.getString(R.string.settings_sensor_connected, deviceNumber),
keyId = R.string.ant_heart_rate_monitor_id_key; Toast.LENGTH_SHORT).show();
break; setSensorState(SensorState.CONNECTED);
case SDM_CHANNEL:
sdmDeviceNumber = deviceNum;
keyId = R.string.ant_speed_distance_monitor_id_key;
break;
case BCS_CHANNEL:
bcsDeviceNumber = deviceNum;
keyId = R.string.ant_bike_cadence_sensor_id_key;
break;
case CBS_CHANNEL:
cbsDeviceNumber = deviceNum;
keyId = R.string.ant_combined_bike_sensor_id_key;
break;
default:
break;
}
if (keyId != -1) {
PreferencesUtils.setInt(context, keyId, deviceNum);
Toast.makeText(context,
context.getString(R.string.settings_sensor_connected, deviceNum),
Toast.LENGTH_SHORT).show();
setSensorState(SensorState.CONNECTED);
}
break; break;
default: default:
break; break;
@@ -496,72 +375,54 @@ public class AntSensorManager extends SensorManager {
/** /**
* Handles response event message. * Handles response event message.
* *
* @param message the message * @param message the message
*/ */
private void handleResponseEventMessage(byte[] message) { private void handleResponseEventMessage(byte[] message) {
// For a list of possible message codes see ANT Message Protocol and Usage // For a list of possible message codes see ANT Message Protocol and Usage
// section 9.5.6.1 available from thisisant.com // section 9.5.6.1 available from thisisant.com
byte channelNumber = message[AntMesg.MESG_DATA_OFFSET]; byte channel = message[AntMesg.MESG_DATA_OFFSET];
if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_EVENT_ID) if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_EVENT_ID)
&& (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.EVENT_RX_SEARCH_TIMEOUT)) { && (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.EVENT_RX_SEARCH_TIMEOUT)) {
// A channel timed out searching, unassign it // A channel timed out searching, unassign it
channelConfig[channelNumber].isInitializing = false; channelConfig[channel].setInitializing(false);
channelConfig[channelNumber].isDeinitializing = false; channelConfig[channel].setDeinitializing(false);
switch (channelNumber) { channelConfig[channel].setChannelState(ChannelStates.OFFLINE);
case HRM_CHANNEL:
hrmState = ChannelStates.OFFLINE;
break;
case SDM_CHANNEL:
sdmState = ChannelStates.OFFLINE;
break;
case BCS_CHANNEL:
bcsState = ChannelStates.OFFLINE;
break;
case CBS_CHANNEL:
cbsState = ChannelStates.OFFLINE;
break;
default:
break;
}
try { try {
antInterface.ANTUnassignChannel(channelNumber); antInterface.ANTUnassignChannel(channel);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
setSensorState(SensorState.DISCONNECTED); setSensorState(SensorState.DISCONNECTED);
} }
if (channelConfig[channelNumber].isInitializing) { if (channelConfig[channel].isInitializing()) {
if (message[AntMesg.MESG_DATA_OFFSET + 2] != 0) { if (message[AntMesg.MESG_DATA_OFFSET + 2] != 0) {
// Error response // Error response
Log.e(TAG, String.format("Error code(%#02x) on message ID(%#02x) on channel %d", Log.e(TAG, String.format("Error code(%#02x) on message ID(%#02x) on channel %d",
message[AntMesg.MESG_DATA_OFFSET + 2], message[AntMesg.MESG_DATA_OFFSET + 2], message[AntMesg.MESG_DATA_OFFSET + 1],
message[AntMesg.MESG_DATA_OFFSET + 1], channelNumber)); channel));
} else { } else {
// Switch on message id // Switch on message id
switch (message[AntMesg.MESG_DATA_OFFSET + 1]) { switch (message[AntMesg.MESG_DATA_OFFSET + 1]) {
case AntMesg.MESG_ASSIGN_CHANNEL_ID: case AntMesg.MESG_ASSIGN_CHANNEL_ID:
try { try {
antInterface.ANTSetChannelId(channelNumber, antInterface.ANTSetChannelId(channel, channelConfig[channel].getDeviceNumber(),
channelConfig[channelNumber].deviceNumber, channelConfig[channel].getDeviceType(), ChannelConfiguration.TRANSMISSION_TYPE);
channelConfig[channelNumber].deviceType,
channelConfig[channelNumber].TransmissionType);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
break; break;
case AntMesg.MESG_CHANNEL_ID_ID: case AntMesg.MESG_CHANNEL_ID_ID:
try { try {
antInterface.ANTSetChannelPeriod( antInterface.ANTSetChannelPeriod(channel, channelConfig[channel].getMessagPeriod());
channelNumber, channelConfig[channelNumber].period);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
break; break;
case AntMesg.MESG_CHANNEL_MESG_PERIOD_ID: case AntMesg.MESG_CHANNEL_MESG_PERIOD_ID:
try { try {
antInterface.ANTSetChannelRFFreq(channelNumber, channelConfig[channelNumber].freq); antInterface.ANTSetChannelRFFreq(channel, ChannelConfiguration.FREQUENCY);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
@@ -569,7 +430,7 @@ public class AntSensorManager extends SensorManager {
case AntMesg.MESG_CHANNEL_RADIO_FREQ_ID: case AntMesg.MESG_CHANNEL_RADIO_FREQ_ID:
try { try {
// Disable high priority search // Disable high priority search
antInterface.ANTSetChannelSearchTimeout(channelNumber, (byte) 0); antInterface.ANTSetChannelSearchTimeout(channel, (byte) 0);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
@@ -577,23 +438,23 @@ public class AntSensorManager extends SensorManager {
case AntMesg.MESG_CHANNEL_SEARCH_TIMEOUT_ID: case AntMesg.MESG_CHANNEL_SEARCH_TIMEOUT_ID:
try { try {
// Set search timeout to 30 seconds (low priority search) // Set search timeout to 30 seconds (low priority search)
antInterface.ANTSetLowPriorityChannelSearchTimeout(channelNumber, (byte) 12); antInterface.ANTSetLowPriorityChannelSearchTimeout(channel, (byte) 12);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
break; break;
case AntMesg.MESG_SET_LP_SEARCH_TIMEOUT_ID: case AntMesg.MESG_SET_LP_SEARCH_TIMEOUT_ID:
if (channelConfig[channelNumber].deviceNumber == WILDCARD) { if (channelConfig[channel].getDeviceNumber() == WILDCARD) {
try { try {
// Configure proximity search, if using wild card search // Configure proximity search, if using wild card search
antInterface.ANTSetProximitySearch( antInterface.ANTSetProximitySearch(
channelNumber, channelConfig[channelNumber].proxSearch); channel, ChannelConfiguration.PROXIMITY_SEARCH);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
} else { } else {
try { try {
antInterface.ANTOpenChannel(channelNumber); antInterface.ANTOpenChannel(channel);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
@@ -601,162 +462,57 @@ public class AntSensorManager extends SensorManager {
break; break;
case AntMesg.MESG_PROX_SEARCH_CONFIG_ID: case AntMesg.MESG_PROX_SEARCH_CONFIG_ID:
try { try {
antInterface.ANTOpenChannel(channelNumber); antInterface.ANTOpenChannel(channel);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
break; break;
case AntMesg.MESG_OPEN_CHANNEL_ID: case AntMesg.MESG_OPEN_CHANNEL_ID:
channelConfig[channelNumber].isInitializing = false; channelConfig[channel].setInitializing(false);
switch (channelNumber) { channelConfig[channel].setChannelState(ChannelStates.SEARCHING);
case HRM_CHANNEL:
hrmState = ChannelStates.SEARCHING;
break;
case SDM_CHANNEL:
sdmState = ChannelStates.SEARCHING;
break;
case BCS_CHANNEL:
bcsState = ChannelStates.SEARCHING;
break;
case CBS_CHANNEL:
cbsState = ChannelStates.SEARCHING;
break;
default:
break;
}
break; break;
default: default:
break; break;
} }
} }
} else if (channelConfig[channelNumber].isDeinitializing) { } else if (channelConfig[channel].isDeinitializing()) {
if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_EVENT_ID) if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_EVENT_ID)
&& (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.EVENT_CHANNEL_CLOSED)) { && (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.EVENT_CHANNEL_CLOSED)) {
try { try {
antInterface.ANTUnassignChannel(channelNumber); antInterface.ANTUnassignChannel(channel);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
handleAntError(); handleAntError();
} }
} else if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_UNASSIGN_CHANNEL_ID) } else if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_UNASSIGN_CHANNEL_ID)
&& (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.RESPONSE_NO_ERROR)) { && (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.RESPONSE_NO_ERROR)) {
channelConfig[channelNumber].isDeinitializing = false; channelConfig[channel].setDeinitializing(false);
} }
} }
} }
/**
* Decodes HRM message.
*
* @param message the message
*/
private void decodeHrmMessage(byte[] message) {
if (hrmState != ChannelStates.CLOSED) {
hrmState = ChannelStates.TRACKING_DATA;
}
if (hrmDeviceNumber == WILDCARD) {
try {
antInterface.ANTRequestMessage(HRM_CHANNEL, AntMesg.MESG_CHANNEL_ID_ID);
} catch (AntInterfaceException e) {
handleAntError();
}
}
lastHeartRate = message[10] & 0xFF;
setSenorDataSet();
}
/**
* Decodes SDM message.
*
* @param message the message
*/
private void decodeSdmMessage(byte[] message) {
if (sdmState != ChannelStates.CLOSED) {
sdmState = ChannelStates.TRACKING_DATA;
}
if (sdmDeviceNumber == WILDCARD) {
try {
antInterface.ANTRequestMessage(SDM_CHANNEL, AntMesg.MESG_CHANNEL_ID_ID);
} catch (AntInterfaceException e) {
handleAntError();
}
}
// Check page 2 data
if (message[3] == 0x02) {
lastCadence = (int) ((message[6] & 0xFF) + (((message[7] >>> 4) & 0x0F) / 16.0f));
setSenorDataSet();
}
}
/**
* Decodes BCS message.
*
* @param message the message
*/
private void decodeBcsMessage(byte[] message) {
if (bcsState != ChannelStates.CLOSED) {
bcsState = ChannelStates.TRACKING_DATA;
}
if (bcsDeviceNumber == WILDCARD) {
try {
antInterface.ANTRequestMessage(BCS_CHANNEL, AntMesg.MESG_CHANNEL_ID_ID);
} catch (AntInterfaceException e) {
handleAntError();
}
}
int crankRevs = ((int) message[9] & 0xFF) + ((int) message[10] & 0xFF) * 256;
int sensorTime = ((int) message[7] & 0xFF) + ((int) message[8] & 0xFF) * 256;
lastCadence = bcsCadenceCounter.getEventsPerMinute(crankRevs, sensorTime);
setSenorDataSet();
}
/**
* Decodes CBS message.
*
* @param message the message
*/
private void decodeCbsMessage(byte[] message) {
if (cbsState != ChannelStates.CLOSED) {
cbsState = ChannelStates.TRACKING_DATA;
}
if (cbsDeviceNumber == WILDCARD) {
try {
antInterface.ANTRequestMessage(CBS_CHANNEL, AntMesg.MESG_CHANNEL_ID_ID);
} catch (AntInterfaceException e) {
handleAntError();
}
}
int crankRevs = ((int) message[5] & 0xFF) + ((int) message[6] & 0xFF) * 256;
int sensorTime = ((int) message[3] & 0xFF) + ((int) message[4] & 0xFF) * 256;
lastCadence = cbsCadenceCounter.getEventsPerMinute(crankRevs, sensorTime);
setSenorDataSet();
}
}; };
/** /**
* Sets sensor data set. * Sets sensor data set.
*/ */
private void setSenorDataSet() { private void setSensorDataSet() {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
// Data comes in at ~4Hz rate from the sensors, so after >300 msec fresh // Data comes in at ~4Hz rate from the sensors, so after >300 msec fresh
// data is here from all the connected sensors // data is here from all the connected sensors
if (now < lastSensorDataSetTime + 300) { return; } if (now < lastSensorDataSetTime + 300) {
return;
}
lastSensorDataSetTime = now; lastSensorDataSetTime = now;
SensorDataSet.Builder builder = Sensor.SensorDataSet.newBuilder(); SensorDataSet.Builder builder = Sensor.SensorDataSet.newBuilder();
if (lastHeartRate != -1) { int heartRate = antSensorValue.getHeartRate();
if (heartRate != -1) {
builder.setHeartRate(Sensor.SensorData.newBuilder() builder.setHeartRate(Sensor.SensorData.newBuilder()
.setValue(lastHeartRate).setState(Sensor.SensorState.SENDING)); .setValue(heartRate).setState(Sensor.SensorState.SENDING));
} }
if (lastCadence != -1) { int cadence = antSensorValue.getCadence();
if (cadence != -1) {
builder.setCadence(Sensor.SensorData.newBuilder() builder.setCadence(Sensor.SensorData.newBuilder()
.setValue(lastCadence).setState(Sensor.SensorState.SENDING)); .setValue(cadence).setState(Sensor.SensorState.SENDING));
} }
sensorDataSet = builder.setCreationTime(now).build(); sensorDataSet = builder.setCreationTime(now).build();
setSensorState(SensorState.SENDING); setSensorState(SensorState.SENDING);
@@ -764,14 +520,14 @@ public class AntSensorManager extends SensorManager {
/** /**
* Sets up ant channel. * Sets up ant channel.
* *
* @param networkNumber the network number * @param networkNumber the network number
* @param channel the channel * @param channel the channel
*/ */
private void setupAntChannel(byte networkNumber, byte channel) { private void setupAntChannel(byte networkNumber, byte channel) {
try { try {
channelConfig[channel].isInitializing = true; channelConfig[channel].setInitializing(true);
channelConfig[channel].isDeinitializing = false; channelConfig[channel].setDeinitializing(false);
// Assign as slave channel on selected network // Assign as slave channel on selected network
antInterface.ANTAssignChannel(channel, AntDefine.PARAMETER_RX_NOT_TX, networkNumber); antInterface.ANTAssignChannel(channel, AntDefine.PARAMETER_RX_NOT_TX, networkNumber);
@@ -785,24 +541,22 @@ public class AntSensorManager extends SensorManager {
/** /**
* Enables data message. * Enables data message.
* *
* @param enabled true to enable * @param enabled true to enable
*/ */
private void enableDataMessage(boolean enabled) { private void enableDataMessage(boolean enabled) {
if (enabled) { if (enabled) {
context.registerReceiver( context.registerReceiver(
dataReceiver, new IntentFilter(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)); dataReceiver, new IntentFilter(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION));
openChannel(HRM_CHANNEL); for (int i = 0; i < CHANNELS; i++) {
openChannel(SDM_CHANNEL); openChannel((byte) i);
openChannel(BCS_CHANNEL); }
openChannel(CBS_CHANNEL);
} else { } else {
try { try {
context.unregisterReceiver(dataReceiver); context.unregisterReceiver(dataReceiver);
closeChannel(HRM_CHANNEL); for (int i = 0; i < CHANNELS; i++) {
closeChannel(SDM_CHANNEL); closeChannel((byte) i);
closeChannel(BCS_CHANNEL); }
closeChannel(CBS_CHANNEL);
setSensorState(SensorState.DISCONNECTED); setSensorState(SensorState.DISCONNECTED);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// Can safely ignore // Can safely ignore
@@ -0,0 +1,60 @@
/*
* 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;
/**
* Contains the latest ant sensor value.
*
* @author Jimmy Shih
*/
public class AntSensorValue {
private int heartRate = -1;
private int cadence = -1;
/**
* Gets the heart rate.
*/
public int getHeartRate() {
return heartRate;
}
/**
* Sets the heart rate.
*
* @param heartRate the heart rate to set
*/
public void setHeartRate(int heartRate) {
this.heartRate = heartRate;
}
/**
* Gets the cadence.
*/
public int getCadence() {
return cadence;
}
/**
* Sets the cadence.
*
* @param cadence the cadence to set
*/
public void setCadence(int cadence) {
this.cadence = cadence;
}
}
@@ -0,0 +1,55 @@
/*
* 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 com.google.android.maps.mytracks.R;
/**
* Bike cadence sensor channel configuration.
*
* @author Jimmy Shih
*/
public class BikeCadenceChannelConfiguration extends ChannelConfiguration {
private static final int DEVICE_ID_KEY = R.string.ant_bike_cadence_sensor_id_key;
private static final byte DEVICE_TYPE = 0x7A;
private static final short MESSAGE_PERIOD = 8102;
private CadenceCounter cadenceCounter = new CadenceCounter();
@Override
public int getDeviceIdKey() {
return DEVICE_ID_KEY;
}
@Override
public byte getDeviceType() {
return DEVICE_TYPE;
}
@Override
public short getMessagPeriod() {
return MESSAGE_PERIOD;
}
@Override
public void decodeMessage(byte[] message, AntSensorValue antSensorValue) {
int revolutionCount = ((int) message[9] & 0xFF) + ((int) message[10] & 0xFF) * 256;
int eventTime = ((int) message[7] & 0xFF) + ((int) message[8] & 0xFF) * 256;
antSensorValue.setCadence(cadenceCounter.getEventsPerMinute(revolutionCount, eventTime));
}
}
@@ -19,9 +19,9 @@ package com.google.android.apps.mytracks.services.sensors.ant;
import java.util.LinkedList; import java.util.LinkedList;
/** /**
* Counter that processes an Ant+ sensor data (counter + sensor time pair) and * A counter that processes an Ant+ sensor data (count + event time) and returns
* returns the instantaneous cadence value. * the instantaneous cadence value.
* *
* @author Laszlo Molnar * @author Laszlo Molnar
*/ */
public class CadenceCounter { public class CadenceCounter {
@@ -29,7 +29,7 @@ public class CadenceCounter {
private static final int MILLIS_PER_MINUTE = 60000; private static final int MILLIS_PER_MINUTE = 60000;
private static final int MAX_HISTORY_TIME_IN_MILLIS = 5000; // 5 seconds private static final int MAX_HISTORY_TIME_IN_MILLIS = 5000; // 5 seconds
private static final int MAX_HISTORY_SIZE = 100; private static final int MAX_HISTORY_SIZE = 100;
private static final int SENSOR_TIME_PER_MINUTE = 60 * 1024; private static final int EVENT_TIME_PER_MINUTE = 60 * 1024;
/** /**
* Cadence data. * Cadence data.
@@ -38,18 +38,18 @@ public class CadenceCounter {
*/ */
private static class CadenceData { private static class CadenceData {
final long systemTime; final long systemTime;
final int counter; final int count;
final int sensorTime; final int eventTime;
private CadenceData(long systemTime, int counter, int sensorTime) { private CadenceData(long systemTime, int count, int eventTime) {
this.systemTime = systemTime; this.systemTime = systemTime;
this.counter = counter; this.count = count;
this.sensorTime = sensorTime; this.eventTime = eventTime;
} }
} }
// The last counter value // The last count
private int lastCounter; private int lastCount;
// The last calculated cadence // The last calculated cadence
private int eventsPerMinute; private int eventsPerMinute;
@@ -58,7 +58,7 @@ public class CadenceCounter {
private LinkedList<CadenceData> history; private LinkedList<CadenceData> history;
public CadenceCounter() { public CadenceCounter() {
lastCounter = -1; lastCount = -1;
eventsPerMinute = 0; eventsPerMinute = 0;
history = new LinkedList<CadenceData>(); history = new LinkedList<CadenceData>();
} }
@@ -66,35 +66,35 @@ public class CadenceCounter {
/** /**
* Gets the cadence value. * Gets the cadence value.
* *
* @param counter counter * @param count count
* @param sensorTime sensor time * @param eventTime event time
*/ */
public int getEventsPerMinute(int counter, int sensorTime) { public int getEventsPerMinute(int count, int eventTime) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
int counterChange = (counter - lastCounter) & 0xFFFF; int countChange = (count - lastCount) & 0xFFFF;
if (lastCounter < 0) { if (lastCount < 0) {
/* /*
* sensorTime for the initial counter value is probably out of date, so * eventTime for the initial count value is probably out of date, so not
* not updating the history. * updating the history.
*/ */
lastCounter = counter; lastCount = count;
eventsPerMinute = 0; eventsPerMinute = 0;
return 0; return 0;
} }
lastCounter = counter; lastCount = count;
if (counterChange != 0) { if (countChange != 0) {
if (removeOldHistory(now)) { if (removeOldHistory(now)) {
CadenceData lastCadenceData = history.getLast(); CadenceData lastCadenceData = history.getLast();
int sensorTimeChange = (sensorTime - lastCadenceData.sensorTime) & 0xFFFF; int eventTimeChange = (eventTime - lastCadenceData.eventTime) & 0xFFFF;
if (sensorTimeChange != 0) { if (eventTimeChange != 0) {
counterChange = (counter - lastCadenceData.counter) & 0xFFFF; countChange = (count - lastCadenceData.count) & 0xFFFF;
eventsPerMinute = counterChange * SENSOR_TIME_PER_MINUTE / sensorTimeChange; eventsPerMinute = countChange * EVENT_TIME_PER_MINUTE / eventTimeChange;
} }
} }
history.addLast(new CadenceData(now, counter, sensorTime)); history.addLast(new CadenceData(now, count, eventTime));
return eventsPerMinute; return eventsPerMinute;
} else { } else {
// The sensor has resent old data // The sensor has resent old data
@@ -114,9 +114,9 @@ public class CadenceCounter {
eventsPerMinute = 0; eventsPerMinute = 0;
return 0; return 0;
} }
/* /*
* Too much time has passed since the last counter change. A smaller value * Too much time has passed since the last count change. A smaller value
* than eventsPerMinute must be returned. The value is calculated from the * than eventsPerMinute must be returned. The value is calculated from the
* history. * history.
*/ */
@@ -133,12 +133,12 @@ public class CadenceCounter {
private int getValueFromHistory(long now) { private int getValueFromHistory(long now) {
CadenceData firstCadenceData = history.getFirst(); CadenceData firstCadenceData = history.getFirst();
CadenceData lastCadenceData = history.getLast(); CadenceData lastCadenceData = history.getLast();
int sensorTimeChange = (lastCadenceData.sensorTime - firstCadenceData.sensorTime) & 0xFFFF; int eventTimeChange = (lastCadenceData.eventTime - firstCadenceData.eventTime) & 0xFFFF;
int counterChange = (lastCounter - firstCadenceData.counter) & 0xFFFF; int countChange = (lastCount - firstCadenceData.count) & 0xFFFF;
// (now - lastCadenceData) + (lastCadenceData - firstCadenceData) // (now - lastCadenceData) + (lastCadenceData - firstCadenceData)
int systemTimeChange = (int) (now - lastCadenceData.systemTime int systemTimeChange = (int) (now - lastCadenceData.systemTime
+ (sensorTimeChange * MILLIS_PER_MINUTE) / SENSOR_TIME_PER_MINUTE); + (eventTimeChange * MILLIS_PER_MINUTE) / EVENT_TIME_PER_MINUTE);
/* /*
* eventsPerMinute is not updated because it is still needed when a new * eventsPerMinute is not updated because it is still needed when a new
@@ -147,12 +147,12 @@ public class CadenceCounter {
if (systemTimeChange == 0) { if (systemTimeChange == 0) {
return eventsPerMinute; return eventsPerMinute;
} }
int value = (counterChange * MILLIS_PER_MINUTE) / systemTimeChange; int value = (countChange * MILLIS_PER_MINUTE) / systemTimeChange;
/* /*
* Do not return a larger number than eventsPerMinute because this function * Do not return a larger number than eventsPerMinute because this function
* is only called when more time has passed after the last counter change * is only called when more time has passed after the last count change than
* than the interval from eventsPerMinute. * the interval from eventsPerMinute.
*/ */
return value < eventsPerMinute ? value : eventsPerMinute; return value < eventsPerMinute ? value : eventsPerMinute;
} }
@@ -16,20 +16,108 @@
package com.google.android.apps.mytracks.services.sensors.ant; package com.google.android.apps.mytracks.services.sensors.ant;
import com.google.android.apps.mytracks.services.sensors.ant.AntSensorManager.ChannelStates;
/** /**
* Ant channel configuration. * Ant channel configuration.
* *
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public class ChannelConfiguration { public abstract class ChannelConfiguration {
public short deviceNumber; public static final byte FREQUENCY = 57; // 2457Mhz (Ant+ frequency)
public byte deviceType; public static final byte TRANSMISSION_TYPE = 0; // 0 for wild card search
public byte TransmissionType; public static final byte PROXIMITY_SEARCH = 7;
public short period;
public byte freq; private short deviceNumber = AntSensorManager.WILDCARD;
public byte proxSearch; private ChannelStates channelState = ChannelStates.CLOSED;
public boolean isInitializing = false; private boolean isInitializing = false;
public boolean isDeinitializing = false; private boolean isDeinitializing = false;
/**
* Gets the My Tracks settings device id key.
*/
public abstract int getDeviceIdKey();
/**
* Gets the device type.
*/
public abstract byte getDeviceType();
/**
* Gets the message period.
*/
public abstract short getMessagPeriod();
/**
* Decodes the message and places the value in antSensorValue.
*
* @param message the message
* @param antSensorValue the ant sensor value
*/
public abstract void decodeMessage(byte[] message, AntSensorValue antSensorValue);
/**
* Gets the device number.
*/
public short getDeviceNumber() {
return deviceNumber;
}
/**
* Sets the device number.
*
* @param deviceNumber the device number to set
*/
public void setDeviceNumber(short deviceNumber) {
this.deviceNumber = deviceNumber;
}
/**
* Gets the channel state.
*/
public ChannelStates getChannelState() {
return channelState;
}
/**
* Sets the channel state.
*
* @param channelState the channelState to set
*/
public void setChannelState(ChannelStates channelState) {
this.channelState = channelState;
}
/**
* Returns true if initializing.
*/
public boolean isInitializing() {
return isInitializing;
}
/**
* Sets isInitializing.
*
* @param isInitializing true if isInitializing
*/
public void setInitializing(boolean isInitializing) {
this.isInitializing = isInitializing;
}
/**
* Returns true if deinitializing.
*/
public boolean isDeinitializing() {
return isDeinitializing;
}
/**
* Sets isDeinitializing.
*
* @param isDeinitializing true if isDeinitializing
*/
public void setDeinitializing(boolean isDeinitializing) {
this.isDeinitializing = isDeinitializing;
}
} }
@@ -0,0 +1,56 @@
/*
* 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 com.google.android.maps.mytracks.R;
/**
* Combined bike sensor channel configuration.
*
* @author Jimmy Shih
*/
public class CombinedBikeChannelConfiguration extends ChannelConfiguration {
private static final int DEVICE_ID_KEY = R.string.ant_combined_bike_sensor_id_key;
private static final byte DEVICE_TYPE = 0x79;
private static final short MESSAGE_PERIOD = 8086;
private CadenceCounter cadenceCounter = new CadenceCounter();
@Override
public int getDeviceIdKey() {
return DEVICE_ID_KEY;
}
@Override
public byte getDeviceType() {
return DEVICE_TYPE;
}
@Override
public short getMessagPeriod() {
return MESSAGE_PERIOD;
}
@Override
public void decodeMessage(byte[] message, AntSensorValue antSensorValue) {
int revolutionCount = ((int) message[5] & 0xFF) + ((int) message[6] & 0xFF) * 256;
int eventTime = ((int) message[3] & 0xFF) + ((int) message[4] & 0xFF) * 256;
antSensorValue.setCadence(cadenceCounter.getEventsPerMinute(revolutionCount, eventTime));
}
}
@@ -0,0 +1,51 @@
/*
* 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 com.google.android.maps.mytracks.R;
/**
* Heart rate monitor channel configuration.
*
* @author Jimmy Shih
*/
public class HeartRateChannelConfiguration extends ChannelConfiguration {
private static final int DEVICE_ID_KEY = R.string.ant_heart_rate_monitor_id_key;
private static final byte DEVICE_TYPE = 0x78;
private static final short MESSAGE_PERIOD = 8070;
@Override
public int getDeviceIdKey() {
return DEVICE_ID_KEY;
}
@Override
public byte getDeviceType() {
return DEVICE_TYPE;
}
@Override
public short getMessagPeriod() {
return MESSAGE_PERIOD;
}
@Override
public void decodeMessage(byte[] message, AntSensorValue antSensorValue) {
antSensorValue.setHeartRate(message[10] & 0xFF);
}
}
@@ -0,0 +1,55 @@
/*
* 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 com.google.android.maps.mytracks.R;
/**
* Speed distance monitor channel configuration.
*
* @author Jimmy Shih
*/
public class SpeedDistanceChannelConfiguration extends ChannelConfiguration {
private static final int DEVICE_ID_KEY = R.string.ant_speed_distance_monitor_id_key;
private static final byte DEVICE_TYPE = 0x7C;
private static final short MESSAGE_PERIOD = 8134;
@Override
public int getDeviceIdKey() {
return DEVICE_ID_KEY;
}
@Override
public byte getDeviceType() {
return DEVICE_TYPE;
}
@Override
public short getMessagPeriod() {
return MESSAGE_PERIOD;
}
@Override
public void decodeMessage(byte[] message, AntSensorValue antSensorValue) {
// Check page 2 data
if (message[3] == 0x02) {
antSensorValue.setCadence(
(int) ((message[6] & 0xFF) + (((message[7] >>> 4) & 0x0F) / 16.0f)));
}
}
}