Пробелы в данных акселерометра Android Wear

У меня есть сервис, который постоянно работает в фоновом режиме на Moto 360. Он измеряет данные акселерометра в 50 Гц. Я упаковываю сэмплы в блоки (по 15 000 сэмплов каждый блок == 5 минут данных) и отправляю эти фрагменты на мобильное устройство через DataApi. Все отлично работает, но случайно у меня есть пропуски в событиях, поступающих от слушателя смены датчика. Пробелы в отборе проб могут происходить один или два раза в 10 часов, а промежуток времени может составлять 1-10 минут каждый пробел.

Что было сделано до сих пор:

  1. Вся обработка выборки / упаковка / регистрация / отправка выполняется в отдельном потоке, поэтому ничто не блокирует приемник датчика.
  2. Служба не работает, и к обратному вызову жизненного цикла добавляются журналы, поэтому я могу быть уверен, что служба активна во время интервалов выборки.
  3. Служба объявлена ​​приоритетной службой, чтобы получить больше процессорного времени и более высокий приоритет.
  4. Я получаю PARTIAL_WAKE_LOCK в onCreate сервиса и выпускаю его в onDestroy (я знаю возможное влияние на батарею, но на этом этапе непрерывные данные важнее).

Что может быть причиной и решением для пробелов в выборке?

Заранее спасибо.

Мой Сервисный код:

public class MeasurementService extends Service implements SensorEventListener {

public final static int SENS_ACCELEROMETER = Sensor.TYPE_ACCELEROMETER;

private static int mCounter;

private ArrayList<AccelerometerSampleData> mAccelerometerSensorSamples;

@Inject
SensorManager mSensorManager;

@Inject
DataTransferHolder mDataTransferHolder;

@Inject
EventBus mEventBus;

@Inject
WearSharedPrefsController mSharedPrefsController;

@Inject
WearConfigController mConfigController;

@Inject
MyWearLogger mMyWearLogger;

private Sensor mAccelerometerSensor;

private AccelerometerSampleData mLastEventData;

protected HandlerThread handlerThread;

private PowerManager.WakeLock mWakeLock;

@Override
public void onCreate() {
    super.onCreate();
    ((MyWearApplication)getApplication()).getApplicationComponent().inject(this);
    mMyWearLogger.writeToLogFile(DateUtils.getCurrentTimeString() + " MeasurementService: " +
            "onCreate");
    initSensors();
    mEventBus.register(this);
    startThread();
    acquireWakeLock();
    resetPackageValues();
    mSharedPrefsController.setMessagePackageIndex(0);
    startForeground();
    startMeasurement();
    mEventBus.postSticky(new MeasurementServiceStatus(true));
}

private void startForeground() {
    Notification.Builder builder = new Notification.Builder(this);
    builder.setContentTitle("Measurement Service");
    builder.setContentText("Collecting sensor data..");
    builder.setSmallIcon(R.drawable.ic_play_circle_outline_black_48dp);
    startForeground(1, builder.build());
}

private void acquireWakeLock() {
    if (mWakeLock == null || !mWakeLock.isHeld()) {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SensorAdapter Lock");
        mWakeLock.acquire();
    }
}

private void releaseWakeLock() {
    if (mWakeLock != null && mWakeLock.isHeld()) {
        mWakeLock.release();
        mWakeLock = null;
    }
}

@SuppressWarnings("unused")
@Subscribe
public void onSamplingRateUpdated(UpdateSamplingRateMessage updateSamplingRateMessage) {
    stopMeasurement();
    startMeasurement();
}

@SuppressWarnings("unused")
@Subscribe
public void onSamplesPerPackageLimitUpdated(UpdateChunkLimitMessage chunkLimitMessage) {
    stopMeasurement();
    startMeasurement();
}

private void startThread() {
    if (handlerThread == null) {
        handlerThread = new HandlerThread(this.getClass().getSimpleName() + "Thread");
    }
    if (handlerThread.getState() == Thread.State.NEW) {
        handlerThread.start();
    }
}

private void resetPackageValues() {
    mCounter = 0;
    mAccelerometerSensorSamples = new ArrayList<>();
}

private void initSensors() {
    Timber.d("initiating sensors");

    if (mAccelerometerSensor == null) {
        mAccelerometerSensor = mSensorManager.getDefaultSensor(SENS_ACCELEROMETER, true);
        if (mAccelerometerSensor == null){
            mAccelerometerSensor = mSensorManager.getDefaultSensor(SENS_ACCELEROMETER);
        }
    }

}

private void startMeasurement() {
    Timber.d("starting measurement");
    if (checkNotNull()) {
        Timber.d("sensors are valid, registering listeners");
        Handler handler = new Handler(handlerThread.getLooper());
        // This buffer is max 300 on Moto 360, so we use 250;
        int maxSamplesBuffer = 250 * mConfigController.getSamplingRateMicrosecond();
        mSensorManager.registerListener(this,
                mAccelerometerSensor,
                mConfigController.getSamplingRateMicrosecond(),
                maxSamplesBuffer,
                handler);
    } else {
        Timber.w("sensors are null");
    }
}

private void stopMeasurement() {
    mSensorManager.unregisterListener(this);
}

private boolean checkNotNull() {
    Timber.d("checking sensors validity");
    return mSensorManager != null
            && mAccelerometerSensor != null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    mMyWearLogger.writeToLogFile(DateUtils.getCurrentTimeString() + " MeasurementService: " +
            "onStartCommand");
    return START_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    mMyWearLogger.writeToLogFile(DateUtils.getCurrentTimeString() + " MeasurementService: " +
            "onDestroy");
    stopMeasurement();
    releaseWakeLock();
    stopThread();
    mEventBus.unregister(this);
    mEventBus.postSticky(new MeasurementServiceStatus(false));
}

private void stopThread() {
    if (handlerThread != null) {
        handlerThread.quit();
    }
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onSensorChanged(SensorEvent newEvent) {

    AccelerometerSampleData newEventData = new AccelerometerSampleData(
            System.currentTimeMillis() + ((newEvent.timestamp - SystemClock.elapsedRealtimeNanos()) / 1000000L),
            newEvent.values[0],
            newEvent.values[1],
            newEvent.values[2]);

    if (DefaultConfiguration.LOG_EACH_SAMPLE) {
        logData(newEventData, calculateTimeDiff(newEventData));
    }

    addNewEventToPackage(newEventData);
    updateCurrentValues(newEventData);

    if (mCounter >= mConfigController.getSamplesPerChunk()) {
        float batteryPercentage = getBatteryStatus();
        sendPackageToMobileDevice(batteryPercentage);
        resetPackageValues();
    }
}

private void sendPackageToMobileDevice(float batteryPercentage) {
    Timber.i("sending package to processing service");

    long messagePackageId = System.currentTimeMillis();
    MessagePackage messagePackage = createMessagePackage(mAccelerometerSensorSamples, batteryPercentage);
    mMyWearLogger.logChunkToFile(messagePackage);

    // Sending package in singleton holder
    mDataTransferHolder.getQueueOfMessagePackages().put(messagePackageId, messagePackage);

    Intent sendPackageIntent = new Intent(this, DataProcessingService.class);
    sendPackageIntent.putExtra(MESSAGE_PACKAGE_ID, messagePackageId);
    startService(sendPackageIntent);
}

private MessagePackage createMessagePackage(ArrayList<AccelerometerSampleData> mAccelerometerSensorSamples, float batteryPercentage) {
    MessagePackage messagePackage = new MessagePackage();
    messagePackage.setAccelerometerSamples(mAccelerometerSensorSamples);
    messagePackage.setBatteryPercentage(batteryPercentage);
    return messagePackage;
}

private float getBatteryStatus() {

    IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    Intent batteryStatus = this.registerReceiver(null, ifilter);

    float batteryPercentage;
    if (batteryStatus != null) {
        int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        batteryPercentage = level / (float) scale;
    } else {
        Timber.w("failed to retrieve battery percentage");
        batteryPercentage = -1;
    }

    return batteryPercentage;
}

private void addNewEventToPackage(AccelerometerSampleData newEventData) {
    mAccelerometerSensorSamples.add(newEventData);
}

private void updateCurrentValues(AccelerometerSampleData newEventData) {
    mLastEventData = newEventData;
    mCounter++;
}

private void logData(AccelerometerSampleData newEventData, long diff) {
//        if (diff > MAX_ALLOWED_SAMPLES_DIFF_IN_MILLIS
//                || diff < MIN_ALLOWED_SAMPLES_DIFF_IN_MILLIS) {
    Timber.d("new accelerometer event, timestamp: %s, time difference: %s milliseconds",
            newEventData.getTimestamp(), diff);
//        }
}

private long calculateTimeDiff(AccelerometerSampleData newEventData) {

    if (mLastEventData == null) {
        return 0;
    }

    return (newEventData.getTimestamp() - mLastEventData.getTimestamp());

}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}

}

0 ответов

Другие вопросы по тегам