package com.algobase.share1.ant;

import java.math.BigDecimal;
import java.util.EnumSet;
import java.util.ArrayList;
import java.util.Locale;


import android.content.Context;
import android.app.Activity;
import android.widget.Toast;
import android.util.Log;
import android.os.Looper;


import com.dsi.ant.plugins.pluginlib.version.PluginLibVersionInfo;

import com.dsi.ant.plugins.antplus.pcc.*;
import com.dsi.ant.plugins.antplus.pcc.defines.*;

import com.dsi.ant.plugins.antplus.pccbase.*;
import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc.*;
import com.dsi.ant.plugins.antplus.pccbase.AsyncScanController.*;
import com.dsi.ant.plugins.antplus.pccbase.AntPlusLegacyCommonPcc.*;
import com.dsi.ant.plugins.antplus.pccbase.AntPlusCommonPcc.*;


import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc.*;
import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc.IHeartRateDataReceiver;

import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.*;
import com.dsi.ant.plugins.antplus.pcc.AntPlusBikeCadencePcc.*;
import com.dsi.ant.plugins.antplus.pccbase.AntPlusBikeSpdCadCommonPcc.*;

import com.dsi.ant.plugins.antplus.pcc.AntPlusEnvironmentPcc.*;


public class Ant 
{

/*
  ITemperatureDataReceiver tempReceiver = new ITemperatureDataReceiver() {
     @Override
     public void onNewTemperatureData(long estTimestamp, 
                                     java.util.EnumSet<EventFlag> eventFlags, 
                                     java.math.BigDecimal currentTemperature, 
                                     long eventCount, 
                                     java.math.BigDecimal lowLast24Hours, 
                                     java.math.BigDecimal highLast24Hours) { 

       showToast("Temperature: " + currentTemperature.floatValue()); 
     }
  };
*/
  

  static int instance_count = 0;

  Context ctxt;
  int instance;


  AntPlusEnvironmentPcc tempPcc = null;
  AsyncScanController<AntPlusEnvironmentPcc> tempScanController = null;
  ArrayList<AsyncScanResultDeviceInfo> tempDeviceList;

  AntPlusHeartRatePcc hrPcc = null;
  AsyncScanController<AntPlusHeartRatePcc> hrScanController = null;
  ArrayList<AsyncScanResultDeviceInfo> hrDeviceList;

  AntPlusBikePowerPcc pwrPcc = null;
  AsyncScanController<AntPlusBikePowerPcc> pwrScanController = null;
  ArrayList<AsyncScanResultDeviceInfo> pwrDeviceList;

  AntPlusBikeCadencePcc cadPcc = null;
  AsyncScanController<AntPlusBikeCadencePcc> cadScanController = null;
  ArrayList<AsyncScanResultDeviceInfo> cadDeviceList;
  //ArrayList<BikeSpdCadAsyncScanResultDeviceInfo> cadDeviceList;


  BigDecimal wheelCircumference = new BigDecimal("2.07"); // meter

  long  hrate_battery_sec = 0;
  long  power_battery_sec = 0;
  long  cadence_battery_sec = 0;
  long  battery_interval_sec = 300; // 5 min 

  private long calibration_count = 0;

  private float wheelDistance0 = 0;
  private float wheelDistance = 0;

  public void writeLog(String txt) {}


  public void handleDeviceMessage(String dev, int id, String msg, String val) {}

  public void handleTempEvent(int id, long t,float temp) {}

  public void handleHeartRateEvent(int id, long t,int hrate, long beats) {}
  public void handleRRIntervalEvent(int id, long t, float rr) {}

  public void handlePowerEvent(int id, long t,float power) {}
  public void handlePowerTorqueEvent(int id, long t,float torque) {}
  public void handlePowerWheelSpeedEvent(int id, long t,float speed) {}
  public void handlePowerWheelDistanceEvent(int id, long t,float dist) {}
  public void handlePowerCadenceEvent(int id, long t,float cad) {}
  public void handleCadenceEvent(int id, long t,float cad) {}


  public Ant(Context context) 
  { ctxt = context; 
    instance = ++instance_count;
    writeLog("");
    writeLog("Constructor(" + instance + ")");
    tempDeviceList = new ArrayList<AsyncScanResultDeviceInfo>();
    hrDeviceList = new ArrayList<AsyncScanResultDeviceInfo>();
    pwrDeviceList = new ArrayList<AsyncScanResultDeviceInfo>();
    cadDeviceList = new ArrayList<AsyncScanResultDeviceInfo>();
    //cadDeviceList = new ArrayList<BikeSpdCadAsyncScanResultDeviceInfo>();
   }


  void showToast(final String msg) {
     Toast.makeText(ctxt, msg,Toast.LENGTH_SHORT).show();
  }


/*
 public int getTempDeviceNum() { return tempDeviceList.size(); }
 public int getHeartRateDeviceNum() { return hrDeviceList.size(); }
 public int getPowerDeviceNum() { return pwrDeviceList.size(); }
 public int getCadenceDeviceNum() { return cadDeviceList.size(); }
*/


 public void disconnectTempDevice(String deviceNumber)
 { if (tempPcc != null) 
   { int deviceNum = tempPcc.getAntDeviceNumber();
     tempPcc.releaseAccess();
     tempPcc = null;
     handleDeviceMessage("ant_temperature",deviceNum,"disconnected", "");
    }
 }



 public void disconnectHeartRateDevice(String deviceNumber)
 { if (hrPcc != null) 
   { int deviceNum = hrPcc.getAntDeviceNumber();
     hrPcc.releaseAccess();
     hrPcc = null;
     handleDeviceMessage("ant_hrate",deviceNum,"disconnected", "");
    }
 }


 public void disconnectPowerDevice(String deviceNumber)
 { if (pwrPcc != null)
   { int deviceNum = pwrPcc.getAntDeviceNumber();
     pwrPcc.releaseAccess();
     pwrPcc = null;
     handleDeviceMessage("ant_power",deviceNum,"disconnected", "");
    }
  }


 public void disconnectCadenceDevice(String deviceNumber)
 { if (cadPcc != null) 
   { int deviceNum = cadPcc.getAntDeviceNumber();
     cadPcc.releaseAccess();
     cadPcc = null;
     handleDeviceMessage("ant_cadence",deviceNum,"disconnected", "");
    }
  }




 public void updateStatus()
 { 
   if (tempPcc != null)
   {  // connected
      int deviceNum = tempPcc.getAntDeviceNumber();
      DeviceState devState= tempPcc.getCurrentDeviceState();
      handleDeviceMessage("ant_temperature",deviceNum,"connected",devState.toString());
    }
   else
   { if (tempScanController == null) {
       handleDeviceMessage("ant_temperature",0,"stopped","");
     }
     else
      { 
        startTempScanning();
/*
        handleDeviceMessage("ant_temperature",0,"scanning","");
        for (int j = 0 ; j < tempDeviceList.size(); j++) 
        { AsyncScanResultDeviceInfo d = tempDeviceList.get(j);
          int devNum = d.getAntDeviceNumber();
          writeLog("TEMP Device: id = " + devNum + "  ant = " + instance);
          handleDeviceMessage("ant_temperature",devNum,"device","");
         }
*/
       }
    }

   if (hrPcc != null)
   {  // connected
      int deviceNum = hrPcc.getAntDeviceNumber();
      DeviceState devState= hrPcc.getCurrentDeviceState();
      handleDeviceMessage("ant_hrate",deviceNum,"connected",devState.toString());
    }
   else
   { if (hrScanController == null) {
       handleDeviceMessage("ant_hrate",0,"stopped","");
     }
     else
      { 
        startHeartRateScanning();
/*
        handleDeviceMessage("ant_hrate",0,"scanning","");
        for (int j = 0 ; j < hrDeviceList.size(); j++) 
        { AsyncScanResultDeviceInfo d = hrDeviceList.get(j);
          int devNum = d.getAntDeviceNumber();
          writeLog("HR Device: id = " + devNum + "  ant = " + instance);
          handleDeviceMessage("ant_hrate",devNum,"device","");
         }
*/
       }
    }

   if (pwrPcc != null)
   {  int deviceNum = pwrPcc.getAntDeviceNumber();
      DeviceState devState= pwrPcc.getCurrentDeviceState();
      handleDeviceMessage("ant_power",deviceNum,"connected",devState.toString());
    }
   else
   { if (pwrScanController == null) {
       handleDeviceMessage("ant_power",0,"stopped","");
     }
     else
      { 
        startPowerScanning();
/*
        handleDeviceMessage("ant_power",0,"scanning","");
        for (int j = 0 ; j < pwrDeviceList.size(); j++) 
        { AsyncScanResultDeviceInfo d = pwrDeviceList.get(j);
          int devNum = d.getAntDeviceNumber();
          writeLog("PWR device: id = " + devNum + "  ant = " + instance);
          handleDeviceMessage("ant_power",devNum,"device","");
         }
*/
       }
    }

   if (cadPcc != null)
   {  int deviceNum = cadPcc.getAntDeviceNumber();
      DeviceState devState= cadPcc.getCurrentDeviceState();
      handleDeviceMessage("ant_cadence",deviceNum,"connected",devState.toString());
    }
   else
   { if (cadScanController == null) {
       handleDeviceMessage("ant_cadence",0,"stopped","");
     }
     else
      { 
        startCadenceScanning();
/*
        handleDeviceMessage("ant_cadence",0,"scanning","");
        for (int j = 0 ; j < cadDeviceList.size(); j++) 
        { AsyncScanResultDeviceInfo d = cadDeviceList.get(j);
          int devNum = d.getAntDeviceNumber();
          writeLog("CAD Device: id = " + devNum + "  ant = " + instance);
          handleDeviceMessage("ant_cadence",devNum,"device","");
         }
*/
       }
    }
}


  // connect to temp device

  private void connectToTempDevice(final AsyncScanResultDeviceInfo deviceInfo)
  {
    if (deviceInfo.isAlreadyConnected())  return;

//showToast("connectToTempDevice: " + deviceInfo.getAntDeviceNumber());

    // access result receiver

    IPluginAccessResultReceiver<AntPlusEnvironmentPcc> accessResultReceiver =
         new IPluginAccessResultReceiver<AntPlusEnvironmentPcc>() {

      @Override
      public void onResultReceived(AntPlusEnvironmentPcc result, 
                                   RequestAccessResult resultCode,
                                   DeviceState initialDeviceState)
      {
        // possible resultCodes
        // SUCCESS
        // CHANNEL_NOT_AVAILABLE
        // ADAPTER_NOT_DETECTED
        // DEPENDENY_NOT_INSTALLED 
        // BAD_PARAMS
        // SEARCH_TIMEOUT
        // OTHER_FAILURE

        // initialDeviceState:
        // CLOSED,DEAD,PROCESSING_REQUEST,SEARCHING,TRACKING,UNRECOGINZED

        writeLog("TMP connect: " + resultCode.toString());


        if (resultCode != RequestAccessResult.SUCCESS) 
        { hrPcc = null;
          int devNum = deviceInfo.getAntDeviceNumber();
          handleDeviceMessage("ant_temperature",devNum,"connect_failed", 
                                              resultCode.toString());
          return;
         }


        tempPcc = result;

        final String deviceName = result.getDeviceName();
        final int deviceNum = result.getAntDeviceNumber();

        writeLog("DeviceNumber: " + deviceNum);
        writeLog("DeviceState: " + initialDeviceState);

        handleDeviceMessage("ant_temperature",deviceNum,"connected",
                                               initialDeviceState.toString());


     // subscribe to temperature events

        tempPcc.subscribeTemperatureDataEvent(new ITemperatureDataReceiver() {
          @Override
          public void onNewTemperatureData(long timeStamp, 
                                     java.util.EnumSet<EventFlag> eventFlags, 
                                     java.math.BigDecimal currentTemperature, 
                                     long eventCount, 
                                     java.math.BigDecimal lowLast24Hours, 
                                     java.math.BigDecimal highLast24Hours) { 

            float temp = currentTemperature.floatValue();
            // correction - why ?
            // 38 --> 20.5 // 0.54
            //temp -= 17.50f;
            //temp *= 0.52f;
            temp *= 0.5f;
            handleTempEvent(deviceNum,timeStamp,temp);
           }
        });

      }

      };


    // device state change receiver
    IDeviceStateChangeReceiver deviceStateChangeReceiver = 
        new IDeviceStateChangeReceiver() {                    

          @Override
          public void onDeviceStateChange(DeviceState newState)
          { int devNum = tempPcc.getAntDeviceNumber();
            writeLog("TEMP (" + devNum + ") state: " + newState);

            //new State: enum 
            // CLOSED,DEAD,PROCESSING_REQUEST,SEARCHING,TRACKING,UNRECOGINZED

            if (newState == DeviceState.DEAD)
            { if (tempPcc != null) tempPcc.releaseAccess();
              tempPcc = null;
             }

            handleDeviceMessage("ant_temperature",devNum,"state",newState.toString());
           }
      };


    // request access to device

//showToast("requestAccess: " + deviceInfo.getAntDeviceNumber());

    try {
      tempScanController.requestDeviceAccess(deviceInfo,
                                             accessResultReceiver,
                                             deviceStateChangeReceiver);
     } catch (Exception e) { writeLog(e.toString()); }

   }


// connect to heartrate device

  private void connectToHeartRateDevice(final AsyncScanResultDeviceInfo deviceInfo)
  {
    hrate_battery_sec = 0;

    if (deviceInfo.isAlreadyConnected())  return;

    // access result receiver

    IPluginAccessResultReceiver<AntPlusHeartRatePcc> accessResultReceiver =
         new IPluginAccessResultReceiver<AntPlusHeartRatePcc>() {

      @Override
      public void onResultReceived(AntPlusHeartRatePcc result, 
                                   RequestAccessResult resultCode,
                                   DeviceState initialDeviceState)
      {
        // possible resultCodes
        // SUCCESS
        // CHANNEL_NOT_AVAILABLE
        // ADAPTER_NOT_DETECTED
        // DEPENDENY_NOT_INSTALLED 
        // BAD_PARAMS
        // SEARCH_TIMEOUT
        // OTHER_FAILURE

        // initialDeviceState:
        // CLOSED,DEAD,PROCESSING_REQUEST,SEARCHING,TRACKING,UNRECOGINZED

        writeLog("HR connect: " + resultCode.toString());


        if (resultCode != RequestAccessResult.SUCCESS) 
        { hrPcc = null;
          int devNum = deviceInfo.getAntDeviceNumber();
          handleDeviceMessage("ant_hrate",devNum,"connect_failed", 
                                                  resultCode.toString());
          return;
         }


        hrPcc = result;

        final String deviceName = result.getDeviceName();
        final int deviceNum = result.getAntDeviceNumber();

        writeLog("DeviceNumber: " + deviceNum);
        writeLog("DeviceState: " + initialDeviceState);

        handleDeviceMessage("ant_hrate",deviceNum,"connected",
                                               initialDeviceState.toString());


     // subscribe to heart rate events

        hrPcc.subscribeHeartRateDataEvent(new IHeartRateDataReceiver() {
          @Override
          public void onNewHeartRateData(long timeStamp, 
                                         EnumSet<EventFlag> eventFlags,
                                         int heartRate, 
                                         long beatCount,
                                         BigDecimal eventTime,
                                         DataState dataState) {

            //double s = eventTime.doubleValue()/1000;
            //writeLog(String.format("Heartbeat: %.3f",s));
            handleHeartRateEvent(deviceNum,timeStamp,heartRate,beatCount);
           }
        });


        hrPcc.subscribeCalculatedRrIntervalEvent(
           new ICalculatedRrIntervalReceiver() {
           @Override
           public void onNewCalculatedRrInterval(long timeStamp, 
                                             EnumSet<EventFlag> eventFlags, 
                                             BigDecimal rrInterval,
                                             RrFlag rrFlag)
          { 
            float t = rrInterval.floatValue(); // prec: 1/1024 sec

            // rrFlag 
            // DATA_SOURCE_AVERAGED, DATA_SOURCE_CACHED, DATA_SOURCE_PAGE4,
            // HEART_RATE_ZERO_DETECTED, UNRECOGNIZED

            if (rrFlag == RrFlag.HEART_RATE_ZERO_DETECTED)
            { writeLog("onNewCalculatedRRInterval:  HEART_RATE_ZERO_DETECTED");
              return;
            }
/*
            if (t <= 0 || t > 3000)
            { writeLog("onNewCalculatedRRInterval: t = " + t); 
              return;
            }
*/
            handleRRIntervalEvent(deviceNum,timeStamp,t/1.024f);
          }

      });


/*
      hrPcc.subscribePage4AddtDataEvent(new IPage4AddtDataReceiver() {
        @Override
        public void onNewPage4AddtData(long timeStamp, 
                           EnumSet<EventFlag> eventFlags,
                           int manufacturerSpecificByte,
                           BigDecimal timestampOfPreviousToLastHeartBeatEvent)
        {
          double t = timestampOfPreviousToLastHeartBeatEvent.doubleValue();
         }
      });
*/


      hrPcc.subscribeCumulativeOperatingTimeEvent(
        new ICumulativeOperatingTimeReceiver() {
        @Override
        public void onNewCumulativeOperatingTime( long timeStamp,
                                              EnumSet<EventFlag> eventFlags,
                                              long sec)
        { 
          if (sec < hrate_battery_sec + battery_interval_sec) return;
          hrate_battery_sec = sec;

          long h = sec/3600;
          long m = (sec%3600)/60;
          long s = sec%60;
          String msg = String.format("%d:%02d",h,m);
          handleDeviceMessage("ant_hrate",deviceNum,"battery", msg);
         }
      });


      hrPcc.subscribeManufacturerAndSerialEvent(
        new IManufacturerAndSerialReceiver() {
          @Override
          public void onNewManufacturerAndSerial(long timeStamp, 
                                             EnumSet<EventFlag> eventFlags,
                                             int manufacturerID,
                                             int serialNumber)
          { }
      });


      hrPcc.subscribeVersionAndModelEvent(new IVersionAndModelReceiver() {
         @Override
        public void onNewVersionAndModel(long timeStamp, 
                                         EnumSet<EventFlag> eventFlags,
                                         int hardwareVersion,
                                         int softwareVersion, 
                                         int modelNumber)
          { }
      });

     }
   };


    // device state change receiver
    IDeviceStateChangeReceiver deviceStateChangeReceiver = 
        new IDeviceStateChangeReceiver() {                    

          @Override
          public void onDeviceStateChange(DeviceState newState)
          { int devNum = hrPcc.getAntDeviceNumber();
            writeLog("HR (" + devNum + ") state: " + newState);

            //new State: enum 
            // CLOSED,DEAD,PROCESSING_REQUEST,SEARCHING,TRACKING,UNRECOGINZED

            if (newState == DeviceState.DEAD)
            { if (hrPcc != null) hrPcc.releaseAccess();
              hrPcc = null;
             }

            handleDeviceMessage("ant_hrate",devNum,"state",newState.toString());
           }
      };

    // request access to device

    try {
      hrScanController.requestDeviceAccess(deviceInfo, 
                                           accessResultReceiver,
                                           deviceStateChangeReceiver);
     } catch (Exception e) { writeLog(e.toString()); }

/*
    int deviceNum = deviceInfo.getAntDeviceNumber();
    hrPcc.requestAccess(ctxt,deviceNum,0,accessResultReceiver,
                                         deviceStateChangeReceiver);
*/

 }

 public void connectToHeartRateDevice(String deviceString)
 { int devNum = Integer.parseInt(deviceString);
   connectToHeartRateDevice(devNum);
  }

 public void connectToHeartRateDevice(int devNum)
 { AsyncScanResultDeviceInfo dev = null;
   for (int j = 0 ; j < hrDeviceList.size(); j++) 
   { AsyncScanResultDeviceInfo d = hrDeviceList.get(j);
     if (d.getAntDeviceNumber() == devNum) dev = d;
    }
   if (dev != null) connectToHeartRateDevice(dev);
  }



// connect to power device

  private void connectToPowerDevice(final AsyncScanResultDeviceInfo deviceInfo)
  {
    power_battery_sec = 0;

    if (deviceInfo.isAlreadyConnected())  return;

    // define a power access result receiver

    IPluginAccessResultReceiver<AntPlusBikePowerPcc> accessResultReceiver =
         new IPluginAccessResultReceiver<AntPlusBikePowerPcc>() {

      @Override
      public void onResultReceived(AntPlusBikePowerPcc result, 
                                   RequestAccessResult resultCode,
                                   DeviceState initialDeviceState)
      {

        writeLog("PWR connect: " + resultCode.toString());

        if (resultCode != RequestAccessResult.SUCCESS) 
        { pwrPcc = null;
          int devNum = deviceInfo.getAntDeviceNumber();
          handleDeviceMessage("ant_power",devNum,"connect_failed", 
                                              resultCode.toString());
          return;
         }


        pwrPcc = result;

        final String deviceName = pwrPcc.getDeviceName();
        final int deviceNum = pwrPcc.getAntDeviceNumber();

        writeLog("DeviceNumber: " + deviceNum);
        writeLog("DeviceState: " + initialDeviceState);

        handleDeviceMessage("ant_power",deviceNum,"connected",
                                               initialDeviceState.toString());

        // subscribe to power events

        pwrPcc.subscribeCalculatedPowerEvent(
            new ICalculatedPowerReceiver() {

            @Override
            public void onNewCalculatedPower(long timeStamp, 
                                             EnumSet<EventFlag> eventFlags, 
                                             DataSource dataSource,
                                             BigDecimal calculatedPower)
            {

              String source = dataSource.toString();

              // The calculated power event will send an initial value code 
              // if it needed to calculate a NEW average.
              // This is important if using the calculated power event to 
              // record user data, as an initial value indicates an average 
              // could not be guaranteed. The event prioritizes calculating 
              // with torque data over power only data.

              switch (dataSource)
              { 
                case POWER_ONLY_DATA:
                case INITIAL_VALUE_POWER_ONLY_DATA:
                case WHEEL_TORQUE_DATA:
                case INITIAL_VALUE_WHEEL_TORQUE_DATA:
                case CRANK_TORQUE_DATA:
                case INITIAL_VALUE_CRANK_TORQUE_DATA:
                case CTF_DATA:
                case INITIAL_VALUE_CTF_DATA:
                     break;

                case INVALID_CTF_CAL_REQ:
                     // The event cannot calculate power from CTF until a 

                case UNRECOGNIZED:
                     break;
               }

              float power = calculatedPower.floatValue();
              handlePowerEvent(deviceNum,timeStamp,power);

             }
          });


/*
          pwrPcc.subscribeRawCrankTorqueDataEvent(
             new IRawCrankTorqueDataReceiver() {
             int count = 0;
             @Override
             public void onNewRawCrankTorqueData(long timeStamp, 
                                               EnumSet<EventFlag> eventFlags, 
                             long crankTorqueUpdateEventCount,
                             long accumulatedCrankTicks,
                             BigDecimal accumulatedCrankPeriod,
                             BigDecimal accumulatedCrankTorque) {

               float torque = accumulatedCrankTorque.floatValue();
               if (count++ % 10 == 0)
                 showToast(String.format(Locale.US,
                                       "RawCrankTorque: %d  %f",count,torque));

            }
          });
*/

          pwrPcc.subscribeCalculatedTorqueEvent(
             new ICalculatedTorqueReceiver() {
             @Override
             public void onNewCalculatedTorque(long timeStamp, 
                                               EnumSet<EventFlag> eventFlags, 
                                               DataSource dataSource,
                                               BigDecimal calculatedTorque)
             {
               String source = dataSource.toString();

               switch(dataSource)
               {
                 case WHEEL_TORQUE_DATA:
                      break;
                 case INITIAL_VALUE_WHEEL_TORQUE_DATA:
                      break;
                 case CRANK_TORQUE_DATA:
                      break;
                 case INITIAL_VALUE_CRANK_TORQUE_DATA:
                      break;
                 case CTF_DATA:
                      break;
                 case INITIAL_VALUE_CTF_DATA:
                      break;
                 case INVALID_CTF_CAL_REQ:
                      break;
                 case UNRECOGNIZED:
                      break;
                }

               float torque = calculatedTorque.floatValue();
               handlePowerTorqueEvent(deviceNum,timeStamp,torque);

               }
          });

          pwrPcc.subscribeCalculatedWheelSpeedEvent(
             new CalculatedWheelSpeedReceiver(wheelCircumference)
             {
               @Override
               public void onNewCalculatedWheelSpeed(
                                             long timeStamp,
                                             EnumSet<EventFlag> eventFlags,
                                             DataSource dataSource,
                                             BigDecimal calculatedWheelSpeed)
               {

                 String source = dataSource.toString();

                 switch(dataSource)
                 { case WHEEL_TORQUE_DATA:
                        break;
                   case INITIAL_VALUE_WHEEL_TORQUE_DATA:
                        break;
                   case UNRECOGNIZED:
                  break;
                }

                float wheelSpeed = calculatedWheelSpeed.floatValue(); // km/h ?
                handlePowerWheelSpeedEvent(deviceNum,timeStamp,wheelSpeed/3.6f);
              }
           });

           pwrPcc.subscribeCalculatedWheelDistanceEvent(
             new CalculatedWheelDistanceReceiver(wheelCircumference)
             {
               @Override
               public void onNewCalculatedWheelDistance(
                                          long timeStamp, 
                                          EnumSet<EventFlag> eventFlags, 
                                          DataSource dataSource,
                                          BigDecimal calculatedWheelDistance)
              { 
                String source = dataSource.toString();

                switch(dataSource)
                { case WHEEL_TORQUE_DATA:
                       break;
                  case INITIAL_VALUE_WHEEL_TORQUE_DATA:
                       break;
                  case UNRECOGNIZED:
                       break;
                 }

               wheelDistance = calculatedWheelDistance.floatValue();
               handlePowerWheelDistanceEvent(deviceNum,timeStamp,
                                        wheelDistance-wheelDistance0);

               }
            });



          pwrPcc.subscribeCalculatedCrankCadenceEvent(
              new ICalculatedCrankCadenceReceiver() {
                @Override
                public void onNewCalculatedCrankCadence(
                                          long timeStamp, 
                                          EnumSet<EventFlag> eventFlags, 
                                          DataSource dataSource,
                                          BigDecimal calculatedCrankCadence)
                {
                   String source = dataSource.toString();

                   float cadence = calculatedCrankCadence.floatValue();

//showToast("calculated crank cadence: " + cadence);

                   switch(dataSource)
                   { case CRANK_TORQUE_DATA:
                     case INITIAL_VALUE_CRANK_TORQUE_DATA:
                     case CTF_DATA:
                     case INITIAL_VALUE_CTF_DATA:
                          break;
                     case INVALID_CTF_CAL_REQ:
                          break;
                     case UNRECOGNIZED:
                          break;
                   }
                }
          });

            pwrPcc.subscribeInstantaneousCadenceEvent(
               new IInstantaneousCadenceReceiver()
               {
                 @Override
                 public void onNewInstantaneousCadence(
                                              long timeStamp, 
                                              EnumSet<EventFlag> eventFlags,
                                              DataSource dataSource,
                                              int instantaneousCadence)
                 {
                   String source = dataSource.toString();

                   switch(dataSource)
                   {
                     case POWER_ONLY_DATA:
                     case WHEEL_TORQUE_DATA:
                     case CRANK_TORQUE_DATA:
                          break;
                     case UNRECOGNIZED:
                          break;
                    }

                    float cadence = instantaneousCadence;
                    handlePowerCadenceEvent(deviceNum,timeStamp,cadence);
                  }
              });

/*
              pwrPcc.subscribeRawPowerOnlyDataEvent(
                new IRawPowerOnlyDataReceiver()
                {
                  @Override
                  public void onNewRawPowerOnlyData(
                                              long timeStamp, 
                                              EnumSet<EventFlag> eventFlags,
                                              long powerOnlyUpdateEventCount,
                                              int instantaneousPower,
                                              long accumulatedPower) { 
                  }
              });
*/

/*
              pwrPcc.subscribeRawWheelTorqueDataEvent(
                                           BigDecimal accumulatedWheelTorque)
                 { 
                   double torque = accumulatedWheelTorque.doubleValue();
                   showToast(String.format(locale.US,
                                           "RawWheelTorqueEvent: %.1f", torque));
                  }
              });
*/


              pwrPcc.subscribeTorqueEffectivenessEvent(
                new ITorqueEffectivenessReceiver()
                {
                  @Override
                  public void onNewTorqueEffectiveness(
                                         long timeStamp, 
                                         EnumSet<EventFlag> eventFlags,
                                         long powerOnlyUpdateEventCount,
                                         BigDecimal leftTorqueEffectiveness,
                                         BigDecimal rightTorqueEffectiveness)
                  {
                    //if (leftTorqueEffectiveness.intValue() != -1)
                    //if (rightTorqueEffectiveness.intValue() != -1)
                   }

              });

              pwrPcc.subscribePedalPowerBalanceEvent(
                new IPedalPowerBalanceReceiver()
                {
                  @Override
                  public void onNewPedalPowerBalance(
                                               long timeStamp, 
                                               EnumSet<EventFlag> eventFlags,
                                               boolean rightPedalIndicator,
                                               int pedalPowerPercentage)
                  { }
              });


              pwrPcc.subscribePedalSmoothnessEvent(
                new IPedalSmoothnessReceiver()
                {
                  @Override
                  public void onNewPedalSmoothness(
                                   long timeStamp, 
                                   EnumSet<EventFlag> eventFlags,
                                   long powerOnlyUpdateEventCount,
                                   boolean separatePedalSmoothnessSupport,
                                   BigDecimal leftOrCombinedPedalSmoothness,
                                   BigDecimal rightPedalSmoothness)
                  {
                    //if (leftOrCombinedPedalSmoothness.intValue() != -1)
                    //if (rightPedalSmoothness.intValue() != -1)
                   }

              });

              pwrPcc.subscribeRawCtfDataEvent(
                new IRawCtfDataReceiver()
                {
                  @Override
                  public void onNewRawCtfData(long timeStamp, 
                                           EnumSet<EventFlag> eventFlags,
                                           long ctfUpdateEventCount,
                                           BigDecimal instantaneousSlope,
                                           BigDecimal accumulatedTimeStamp,
                                           long accumulatedTorqueTicksStamp)
                  { }
              });

              pwrPcc.subscribeCalibrationMessageEvent(
                new ICalibrationMessageReceiver()
                {
                  @Override
                  public void onNewCalibrationMessage(
                                      long timeStamp, 
                                      EnumSet<EventFlag> eventFlags,
                                      CalibrationMessage calibrationMessage)
                  {

/*
                    if (timeStamp < cali_time + cali_interval) return;
                    cali_time = timeStamp;
*/
                  
                    Integer data = calibrationMessage.calibrationData;
                    CalibrationId id = calibrationMessage.calibrationId;
                    Integer ctf_off = calibrationMessage.ctfOffset;

                    String bytes = "";

                    switch(id)
                    {
                      case GENERAL_CALIBRATION_FAIL:
                      case GENERAL_CALIBRATION_SUCCESS:
                           break;

                      case CUSTOM_CALIBRATION_RESPONSE:
                      case CUSTOM_CALIBRATION_UPDATE_SUCCESS:
                           for(byte b : 
                               calibrationMessage.manufacturerSpecificData)
                             bytes += "[" + b + "]";
                           break;

                      case CTF_ZERO_OFFSET:
                           break;
                      case UNRECOGNIZED:
                           break;
                     }

                    if (calibration_count++ > 0) return;

                     String msg = "";

                     msg += id.toString();

                     if (data != null) 
                       msg += "\n data = " + data.toString();

                     if (bytes.length() > 0) 
                       msg += "\n bytes = " + bytes;

                     if (ctf_off != null) 
                       msg += "\n offset = " + ctf_off.toString();

                     showToast(msg);


                  }
              });

              pwrPcc.subscribeAutoZeroStatusEvent(
                new IAutoZeroStatusReceiver()
                {
                 @Override
                 public void onNewAutoZeroStatus(
                                              long timeStamp, 
                                              EnumSet<EventFlag> eventFlags, 
                                              AutoZeroStatus status)
                 {
                   handleDeviceMessage("ant_power", deviceNum, "auto_zero",
                                                           status.toString());

/*
                    showToast("AutoZero: " + autoZeroStatus.toString());

                    boolean auto_zero = false;

                    switch(autoZeroStatus)
                    { case NOT_SUPPORTED:
                           break;
                      case UNKNOWN:
                           break;
                      case INVALID:
                           break;
                      case UNRECOGNIZED:
                           break;
                      case ON:
                           auto_zero = true;
                           break;
                      case OFF:
                           break;
                     }
*/

                  }
              });

              pwrPcc.subscribeCrankParametersEvent(
                new ICrankParametersReceiver()
                {
                  @Override
                  public void onNewCrankParameters(
                                            long timeStamp, 
                                            EnumSet<EventFlag> eventFlags,
                                            CrankParameters crankParameters)
                  {
                    switch(crankParameters.getCrankLengthStatus())
                    { case INVALID_CRANK_LENGTH:
                           break;
                      case DEFAULT_USED:
                      case SET_AUTOMATICALLY:
                      case SET_MANUALLY:
                           //crankParameters.getFullCrankLength()
                           break;
                      default:
                           break;
                    }
                 }
              });

              pwrPcc.subscribeBatteryStatusEvent(
                new AntPlusCommonPcc.IBatteryStatusReceiver()
                {                            
                  @Override
                  public void onNewBatteryStatus(
                                        long timeStamp, 
                                        EnumSet<EventFlag> eventFlags, 
                                        long cumulativeOperatingTime,
                                        java.math.BigDecimal voltage, 
                                        BatteryStatus status,
                                        int timeResolution,
                                        int numberOfBatteries,
                                        int batteryIdentifier)
                  { 
                    long sec = cumulativeOperatingTime * timeResolution;

                    if (sec < power_battery_sec + battery_interval_sec) return;
                    power_battery_sec = sec;

                    long h = sec/3600;
                    long m = (sec%3600)/60;
                    long s = sec%60;
                    String msg = "Status: " + status.toString();
                    msg += String.format(Locale.US,
                                         ";Voltage: %.1f V",
                                         voltage.floatValue());
                    msg += String.format(";Time: %d:%02d:%02d",h,m,s);
                    handleDeviceMessage("ant_power",deviceNum,"battery", msg);
                   }
               });


              pwrPcc.subscribeManufacturerIdentificationEvent(
                new IManufacturerIdentificationReceiver()
                {                            
                  @Override
                  public void onNewManufacturerIdentification(
                                               long timeStamp, 
                                               EnumSet<EventFlag> eventFlags, 
                                               int hardwareRevision,
                                               int manufacturerID, 
                                               int modelNumber)
                  { }
               });

              pwrPcc.subscribeProductInformationEvent(
                new IProductInformationReceiver()
                {                                    
                  @Override
                  public void onNewProductInformation(
                                               long timeStamp, 
                                               EnumSet<EventFlag> eventFlags,
                                               int xxxx,
                                               int softwareRevision,
                                               long serialNumber)
                  { }
              });

        }

      };

    // device state change receiver
    IDeviceStateChangeReceiver deviceStateChangeReceiver = 
        new IDeviceStateChangeReceiver() {                    

          @Override
          public void onDeviceStateChange(DeviceState newState)
          { int devNum = pwrPcc.getAntDeviceNumber();
            writeLog("PWR (" + devNum + ") state: " + newState);

            if (newState == DeviceState.DEAD)
            { if (pwrPcc != null) pwrPcc.releaseAccess();
              pwrPcc = null;
             }

            handleDeviceMessage("ant_power",devNum,"state",newState.toString());
           }
      };

    // request access to device

    try {
      pwrScanController.requestDeviceAccess(deviceInfo, 
                                            accessResultReceiver,
                                            deviceStateChangeReceiver);
     } catch (Exception e) { writeLog(e.toString()); }

/*
    int deviceNum = deviceInfo.getAntDeviceNumber();
    pwrPcc.requestAccess(ctxt,deviceNum,0,accessResultReceiver,
                                            deviceStateChangeReceiver);
*/

 }

 public void connectToPowerDevice(int devNum)
 { AsyncScanResultDeviceInfo dev = null;
   for (int i = 0 ; i < pwrDeviceList.size(); i++) 
   { AsyncScanResultDeviceInfo d = pwrDeviceList.get(i);
     if (d.getAntDeviceNumber() == devNum) dev = d;
    }
   if (dev != null) connectToPowerDevice(dev);
  }

 public void connectToPowerDevice(String deviceNumber)
 { int devNum = Integer.parseInt(deviceNumber);
   connectToPowerDevice(devNum);
 }



// connect to cadence device



 private void connectToCadenceDevice(final AsyncScanResultDeviceInfo deviceInfo)
  {
    cadence_battery_sec = 0;

    if (deviceInfo.isAlreadyConnected())  return;

    IPluginAccessResultReceiver<AntPlusBikeCadencePcc> accessResultReceiver =
         new IPluginAccessResultReceiver<AntPlusBikeCadencePcc>() {

      @Override
      public void onResultReceived(AntPlusBikeCadencePcc result, 
                                   RequestAccessResult resultCode,
                                   DeviceState initialDeviceState)
     { 
       writeLog("CAD connect: " + resultCode.toString());

       if (resultCode != RequestAccessResult.SUCCESS) 
       { cadPcc = null;
         int devNum = deviceInfo.getAntDeviceNumber();
         handleDeviceMessage("ant_cadence",devNum,"connect_failed", 
                                               resultCode.toString());
         return;
        }


       cadPcc = result;

        final String deviceName = cadPcc.getDeviceName();
        final int deviceNum = cadPcc.getAntDeviceNumber();

        writeLog("DeviceNumber: " + deviceNum);
        writeLog("DeviceState: " + initialDeviceState);

        handleDeviceMessage("ant_cadence",deviceNum,"connected",
                                                 initialDeviceState.toString());

       // subscribe to cadence events

/*
         cadPcc.subscribeRawCadenceDataEvent(
                         new IRawCadenceDataReceiver() {
          @Override
          public void onNewRawCadenceData(long timeStamp, 
                                         EnumSet<EventFlag> eventFlags,
                                         BigDecimal timeStampLast,
                                         long cumulativeRevolutions) {
           }
        });
*/

	cadPcc.subscribeCalculatedCadenceEvent(
                         new ICalculatedCadenceReceiver() {
          @Override
          public void onNewCalculatedCadence(long timeStamp, 
                                         EnumSet<EventFlag> eventFlags,
                                         BigDecimal calculatedCadence)
         {
             float cadence = calculatedCadence.floatValue();
             handleCadenceEvent(deviceNum,timeStamp,cadence);
           }
        });


/*
       cadPcc.subscribeMotionAndCadenceDataEvent(
                        new IMotionAndCadenceDataReceiver() {
         @Override
         public onNewMotionAndCadenceData(long estTimestamp, 
                                          EnumSet<EventFlag> eventFlags, 
                                          boolean isPedallingStopped) {}
       });
*/
      cadPcc.subscribeCumulativeOperatingTimeEvent(
        new ICumulativeOperatingTimeReceiver() {
        public void onNewCumulativeOperatingTime( long timeStamp,
                                              EnumSet<EventFlag> eventFlags,
                                              long sec)
        {
          if (sec < cadence_battery_sec + battery_interval_sec) return;
          cadence_battery_sec = sec;
          long h = sec/3600;
          long m = (sec%3600)/60;
          long s = sec%60;
          String msg = String.format("Time: %d:%02d:%02d" ,h,m,s);
          handleDeviceMessage("ant_cadence",deviceNum, "battery", msg);
         }
      });


      }
    };

    IDeviceStateChangeReceiver deviceStateChangeReceiver = 
        new IDeviceStateChangeReceiver() {                    

          @Override
          public void onDeviceStateChange(DeviceState newState)
          { int devNum = cadPcc.getAntDeviceNumber();
            writeLog("CAD (" + devNum + ") state: " + newState);


            if (newState == DeviceState.DEAD)
            { if (cadPcc != null) cadPcc.releaseAccess();
              cadPcc = null;
             }

            handleDeviceMessage("ant_cadence",devNum,"state",newState.toString());
           }
      };

    // request access to device
    try {
      cadScanController.requestDeviceAccess(deviceInfo, 
                                            accessResultReceiver,
                                            deviceStateChangeReceiver);
     } catch (Exception e) { writeLog(e.toString()); }

/*
    int deviceNum = deviceInfo.getAntDeviceNumber();
    cadPcc.requestAccess(ctxt,deviceNum,0,false,accessResultReceiver,
                                                  deviceStateChangeReceiver);
*/
   }

 public void connectToCadenceDevice(String deviceNumber)
 { int devNum = Integer.parseInt(deviceNumber);
   connectToCadenceDevice(devNum);
 }

 public void connectToCadenceDevice(int devNum)
 { AsyncScanResultDeviceInfo dev = null;
   for (int i = 0 ; i < cadDeviceList.size(); i++) 
   { AsyncScanResultDeviceInfo d = cadDeviceList.get(i);
     if (d.getAntDeviceNumber() == devNum) dev = d;
    }
   if (dev != null) connectToCadenceDevice(dev);
  }


 public void connectToTempDevice(String deviceNumber)
 { int devNum = Integer.parseInt(deviceNumber);
   connectToTempDevice(devNum);
 }

 public void connectToTempDevice(int devNum)
 { AsyncScanResultDeviceInfo dev = null;
   for (int i = 0 ; i < tempDeviceList.size(); i++) 
   { AsyncScanResultDeviceInfo d = tempDeviceList.get(i);
     if (d.getAntDeviceNumber() == devNum) dev = d;
    }
   if (dev != null) connectToTempDevice(dev);
  }





 public void pwrManualCalibration()
 {
    if (pwrPcc == null) {
      showToast("Manual Calibration: not connected.");
      return;
    }

    final int deviceNum = pwrPcc.getAntDeviceNumber();

/*
    pwrPcc.requestManualCalibration(finishedReceiver,
                                    calibrationMessageReceiver);
                                    measurementOutputReceiver);
*/
    calibration_count = 0;

    pwrPcc.requestManualCalibration(
          new IRequestFinishedReceiver() { 
             @Override
             public void onNewRequestFinished(//long timeStamp,
                                   //java.util.EnumSet<EventFlag> flags,
                                   RequestStatus status) {
               handleDeviceMessage("ant_power",deviceNum,"calibration", 
                                                     status.toString());
             }
    });
 }

 public void pwrSetAutoZero(final boolean autoZero)
 {
    final String cmd = autoZero ? "auto_zero_on" : "auto_zero_off";

    if (pwrPcc == null) {
      handleDeviceMessage("ant_power",0,cmd,"not connected");
      return;
    }

    final int deviceNum = pwrPcc.getAntDeviceNumber();

    pwrPcc.requestSetAutoZero(autoZero,
          new IRequestFinishedReceiver() { 
             @Override
             public void onNewRequestFinished(//long timeStamp,
                                   //java.util.EnumSet<EventFlag> flags,
                                   RequestStatus status) {
               handleDeviceMessage("ant_power",deviceNum,cmd,status.toString());
             }
    });
 }




  public void startTempScanning()
  { 
    writeLog("Start TMP Scanning: ant = " + instance);

    if (tempScanController != null)
    { // stopTempScanning();
      disconnectTempDevice("");
      tempScanController.closeScanController();
      tempScanController = null;
    }

    tempDeviceList.clear();

    // request for asynchronous scan receiver

    IAsyncScanResultReceiver receiver = 
       new IAsyncScanResultReceiver() {

          @Override
          public void onSearchStopped(RequestAccessResult reason)
          { writeLog("TEMP Search Stop : " + reason.toString());
            handleDeviceMessage("ant_temperature",0,"stopped", reason.toString());
            tempScanController = null; // indicates that scanning has stopped
           }
          
          @Override
          public void onSearchResult(AsyncScanResultDeviceInfo deviceFound)
          {
            int deviceNum = deviceFound.getAntDeviceNumber();
            String deviceName = deviceFound.getDeviceDisplayName();

            // check for duplicates
            int count = 0;
            for(AsyncScanResultDeviceInfo inf: tempDeviceList) {  
              if (inf.getAntDeviceNumber() == deviceNum) count++;
             }

            if (count > 0) {
              //writeLog(deviceName + "  already scanned.");
            }
            else
            { writeLog("New TEMP Device: " + deviceNum);
              tempDeviceList.add(deviceFound);
              handleDeviceMessage("ant_temperature",deviceNum,"device","");
             }

          }
      };


    tempScanController = null;
    String err = null;

    try {
      tempScanController = 
         AntPlusEnvironmentPcc.requestAsyncScanController(ctxt,0,receiver);
    } catch (Exception e) { err = e.toString(); }

    if (tempScanController == null) 
    { writeLog("tempScanController: " + err);
      handleDeviceMessage("ant_temperature",0,"error",err);
    }
    else
      handleDeviceMessage("ant_temperature",instance, "scanning", "");
  }



  public void startHeartRateScanning()
  { 
    writeLog("Start HR Scanning: ant = " + instance);

    if (hrScanController != null)
    { // stopHeartRateScanning();
      disconnectHeartRateDevice("");
      hrScanController.closeScanController();
      hrScanController = null;
    }

    hrDeviceList.clear();

    // request for asynchronous scan receiver

    IAsyncScanResultReceiver receiver = 
       new IAsyncScanResultReceiver() {

          @Override
          public void onSearchStopped(RequestAccessResult reason)
          { writeLog("HR Search Stop : " + reason.toString());
            handleDeviceMessage("ant_hrate",0,"stopped", reason.toString());
            hrScanController = null; // indicates that scanning has stopped
           }
          
          @Override
          public void onSearchResult(AsyncScanResultDeviceInfo deviceFound)
          {
            int deviceNum = deviceFound.getAntDeviceNumber();
            String deviceName = deviceFound.getDeviceDisplayName();

            // check for duplicates
            int count = 0;
            for(AsyncScanResultDeviceInfo inf: hrDeviceList) {  
              if (inf.getAntDeviceNumber() == deviceNum) count++;
             }

            if (count > 0) {
              //writeLog(deviceName + "  already scanned.");
            }
            else
            { writeLog("New HR Device: " + deviceNum);
              hrDeviceList.add(deviceFound);
              handleDeviceMessage("ant_hrate",deviceNum,"device","");
             }

          }
      };


    hrScanController = null;
    String err = null;

    try {
      hrScanController = 
         AntPlusHeartRatePcc.requestAsyncScanController(ctxt,0,receiver);
    } catch (Exception e) { err = e.toString(); }

    if (hrScanController == null) 
    { writeLog("hrScanController: " + err);
      handleDeviceMessage("ant_hrate",0,"error",err);
    }
    else
      handleDeviceMessage("ant_hrate",instance, "scanning", "");
  }




  public void startPowerScanning()
  {
    final boolean connect = false;

    writeLog("Start PWR Scanning: ant = " + instance);

    if (pwrScanController != null)
    { // stopPowerScanning();
      disconnectPowerDevice("");
      pwrScanController.closeScanController();
      pwrScanController = null;
    }

    pwrDeviceList.clear();

    // request for asynchronous scan receiver

    IAsyncScanResultReceiver receiver = 
       new IAsyncScanResultReceiver() {

          @Override
          public void onSearchStopped(RequestAccessResult reason)
          { writeLog("PWR Search Stop : " + reason.toString());
            handleDeviceMessage("ant_power",0,"stopped",reason.toString());
            pwrScanController = null; // indicates that scanning has stopped
           }
          
          @Override
          public void onSearchResult(AsyncScanResultDeviceInfo deviceFound)
          {
            int deviceNum = deviceFound.getAntDeviceNumber();
            String deviceName = deviceFound.getDeviceDisplayName();

            int count = 0;
            for(AsyncScanResultDeviceInfo inf: pwrDeviceList) { 
              if (inf.getAntDeviceNumber() == deviceNum) count++;
            }

            if (count > 0) {
              //writeLog(deviceName + "  already scanned.");
            }
            else
            { writeLog("New PWR Device: " + deviceNum);
              pwrDeviceList.add(deviceFound);
              handleDeviceMessage("ant_power",deviceNum,"device","");
             }


          }
      };


    pwrScanController = null;
    String err = null;

    try {
      pwrScanController = 
          AntPlusBikePowerPcc.requestAsyncScanController(ctxt,0,receiver);
    } catch (Exception e) { err = e.toString(); }

    if (pwrScanController == null) 
    { writeLog("pwrScanController: " + err);
      handleDeviceMessage("ant_power",0,"error",err);
     }
    else
      handleDeviceMessage("ant_power",instance, "scanning", "");
  }



  public void startCadenceScanning()
  { 
    final boolean connect = false;

    writeLog("Start CAD Scanning: ant = " + instance);

    if (cadScanController != null)
    { // stopCadenceScanning();
      disconnectCadenceDevice("");
      cadScanController.closeScanController();
      cadScanController = null;
    }


    cadDeviceList.clear();

    // request for asynchronous scan receiver

    IBikeSpdCadAsyncScanResultReceiver receiver =    
           new IBikeSpdCadAsyncScanResultReceiver() {

          @Override
          public void onSearchStopped(RequestAccessResult reason)
          { writeLog("CAD Search Stop: " + reason.toString());
            handleDeviceMessage("ant_cadence",0,"stopped",reason.toString());
            cadScanController = null; // indicates that scanning has stopped
           }
          
          @Override
          public void onSearchResult(BikeSpdCadAsyncScanResultDeviceInfo deviceFound)
          {
            int deviceNum = deviceFound.resultInfo.getAntDeviceNumber();
            String deviceName = deviceFound.resultInfo.getDeviceDisplayName();


            int count = 0;
            for(AsyncScanResultDeviceInfo inf: cadDeviceList) { 
              if (inf.getAntDeviceNumber() == deviceNum) count++;
            }

            if (count > 0) {
              //writeLog(deviceName + "  already scanned.");
            }
            else
            { writeLog("New CAD Device: " + deviceNum);
              cadDeviceList.add(deviceFound.resultInfo);
              handleDeviceMessage("ant_cadence",deviceNum,"device","");
             }


          }
      };


    cadScanController = null;
    String err = null;

    try { 
      cadScanController = 
        AntPlusBikeCadencePcc.requestAsyncScanController(ctxt,0,receiver);
    } catch (Exception e) { err = e.toString(); }

    if (cadScanController == null) {
      writeLog("cadScanController: " + err);
      handleDeviceMessage("ant_cadence",0,"error",err);
    }
    else
      handleDeviceMessage("ant_cadence",instance, "scanning", "");
  }


  public void stopTempScanning()
  { writeLog("Stop Temp Scanning");

    // close possible connection 
    disconnectTempDevice("");

    // stop scanning 
    if (tempScanController != null) tempScanController.closeScanController();
    tempScanController = null;
    tempDeviceList.clear();

    // message generated by onSearchStopped callback
    // handleDeviceMessage("ant_temperature",0,"stopped", "USER_CANCELLED");
  }



  public void stopHeartRateScanning()
  { writeLog("Stop HR Scanning");

    // close possible connection 
    disconnectHeartRateDevice("");

    // stop scanning 
    if (hrScanController != null) hrScanController.closeScanController();
    hrScanController = null;
    hrDeviceList.clear();

    // message generated by onSearchStopped callback
    // handleDeviceMessage("ant_hrate",0,"stopped", "USER_CANCELLED");
  }


  public void stopPowerScanning()
  { writeLog("Stop PWR Scanning");

    // close possible connection 
    disconnectPowerDevice("");

    // stop scanning
    if (pwrScanController != null) pwrScanController.closeScanController();
    pwrScanController = null;
    pwrDeviceList.clear();

    // message generated by onSearchStopped callback
    // handleDeviceMessage("ant_power",0,"stopped","");
   }


  public void stopCadenceScanning()
  { writeLog("Stop CAD Scanning");

    // close possible connection 
    disconnectCadenceDevice("");

    // stop scanning
    if (cadScanController != null) cadScanController.closeScanController();
    cadScanController = null;
    cadDeviceList.clear();

    // message generated by onSearchStopped callback
    // handleDeviceMessage("ant_cadence",0,"stopped", "");
   }



  public void startScanning(String what)
  { writeLog("Start Scanning: " + what);
    if (what.equals("temp") || what.equals("all")) startTempScanning();
    if (what.equals("hrate")   || what.equals("all")) startHeartRateScanning();
    if (what.equals("power")   || what.equals("all")) startPowerScanning();
    if (what.equals("cadence") || what.equals("all")) startCadenceScanning();
  }

  public void stopScanning(String what)
  { writeLog("Stop Scanning: " + what);
    if (what.equals("temp") || what.equals("all")) stopTempScanning();
    if (what.equals("hrate")   || what.equals("all")) stopHeartRateScanning();
    if (what.equals("power")   || what.equals("all")) stopPowerScanning();
    if (what.equals("cadence") || what.equals("all")) stopCadenceScanning();
  }

  public boolean isScanning(String what) 
  { if (what.equals("temp")) return tempScanController != null; 
    if (what.equals("hrate")) return hrScanController != null; 
    if (what.equals("power")) return pwrScanController != null; 
    if (what.equals("cadence")) return cadScanController != null; 
    return false;
   }


  public void resetWheelDistance() { wheelDistance0 = wheelDistance; }

}
