Как регистрировать данные с Android Motion Sensors с фиксированной скоростью

Я изучаю основы программирования на Android.

У меня есть простое приложение для тестирования Android, в котором я записываю акселерометр, магнитометр и данные ориентации во внешний файл, а также отображаю его. Я инициирую процесс регистрации нажатием кнопки Начать (registerListener для соответствующих датчиков), вызывая метод initLogger.

Что выглядит похоже на это...

public void initLogger(View view)
{
    boolean bFlag = false;

    Button btnStart = (Button)findViewById(R.id.btnStartLog);
    Button btnStop = (Button)findViewById(R.id.btnStopLog);

    btnStart.setEnabled(bFlag);
    btnStop.setEnabled(!bFlag);

    bEnableLogging = true;
    //Start reading the sensor values
    sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_UI);
    sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI);

   //so on.... 

Существует также кнопка Stop, которая останавливает процесс регистрации (и, наконец, отменяет регистрацию, вызывая unregisterListener для каждого датчика)

Процесс поиска данных происходит внутри обработчика onSensorChanged, который должен извлекать данные из соответствующих датчиков, задает значение соответствующим элементам пользовательского интерфейса и, наконец, регистрирует данные во внешнем CSV файле.

onSensorChanged обработчик событий выглядит примерно так:

public void onSensorChanged(SensorEvent event) {


    // TODO Auto-generated method stub
    // accelerometer
    TextView tAX = (TextView) findViewById(R.id.txtViewAxValue);
    TextView tAY = (TextView) findViewById(R.id.txtViewAyValue);
    TextView tAZ = (TextView) findViewById(R.id.txtViewAzValue);

    // magnetic field
    TextView tMX = (TextView) findViewById(R.id.txtViewMx);
    TextView tMY = (TextView) findViewById(R.id.txtViewMy);
    TextView tMZ = (TextView) findViewById(R.id.txtViewMz);

    if (bEnableLogging) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

            accelerometerdata = event.values.clone();

            tAX.setText(Double.toString(accelerometerdata[0]));
            tAY.setText(Double.toString(accelerometerdata[1]));
            tAZ.setText(Double.toString(accelerometerdata[2]));


        }

        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {

            magneticmatrixdata = event.values.clone();

            tMX.setText(Double.toString(magneticmatrixdata[0]));
            tMY.setText(Double.toString(magneticmatrixdata[1]));
            tMZ.setText(Double.toString(magneticmatrixdata[2]));

        }

               // so on ....

Хотя я получаю данные от всех сконфигурированных датчиков, у меня нет контроля над скоростью, с которой данные получены. iee

Я знаю, что событие SensorChanged запускается как и когда данные датчика изменены. Однако я хочу, чтобы это событие было запущено с фиксированной скоростью. Например: каждые 40 мс

Вопрос:

  • Как обеспечить, чтобы событие SensorChanged запускалось с постоянной скоростью?
  • Является ли класс TimerTask в Java какой-либо помощью в этом случае?

Эксперты здесь, в SO.Пожалуйста, помогите мне:)

Ответ 1

Так как вы знаете, что если не было запущено событие SensorChanged, изменений не было, вы можете просто использовать свое старое значение. Поскольку вы запрашивали данные LOG в определенные интервалы времени, я бы не делал никакого вывода в методе onSensorChanged, просто клонировал новые данные в вашу переменную activeometerdata. А чем регистрировать значение данных акселерометра каждые 40 мс. Таким образом, вы регистрируете фактическое значение каждые 40 мс, даже если данные не изменялись....

Примечание. Согласно Ridcullys Answer, также представляется возможным получить данные датчиков "доставленными" в определенные промежутки времени. Но поскольку на этих "поставках" есть задержка, как всегда, с сенсорными данными на Android, с моим решением вы будете более точно на интервале 40 мс. С другой стороны, может случиться так, что если данные датчика меняются в момент регистрации, может случиться, что вы задерживаете новые данные за один интервал. И я думаю (не уверен в этом) - так как это всего лишь запись, а не как "как можно быстрее в реальном времени", так что это не является обязательным требованием - Timer-Solution снижает нагрузку на CPU.

Ответ 2

Вы можете изменить интервал, изменив задержку при регистрации для датчика.

int SENSOR_DELAY_FASTEST    get sensor data as fast as possible 
int SENSOR_DELAY_GAME       rate suitable for games 
int SENSOR_DELAY_NORMAL     rate (default) suitable for screen orientation changes
int SENSOR_DELAY_UI         rate suitable for the user interface

Согласно this, самая быстрая задержка - это то, что вам нужно, и если оно не будет достаточно быстро для вас, потому что не было изменения. Метод getSensorData отсутствует.

Вы можете указать другие задержки данных, такие как SENSOR_DELAY_GAME (20 000 микросекундная задержка), SENSOR_DELAY_UI (задержка в 60 000 микросекунд) или SENSOR_DELAY_FASTEST (0 микросекундная задержка). Начиная с Android 3.0 (API Уровень 11), вы также можете указать задержку как абсолютное значение (в микросекунд).

Задержка, которую вы указываете, является только предполагаемой задержкой. Android система и другие приложения могут изменить эту задержку. Как лучший практики, вы должны указать наибольшую задержку, которую вы можете, поскольку система обычно использует меньшую задержку, чем та, которую вы указываете (то есть вы должны выбрать самую медленную частоту дискретизации, которая все еще встречается потребности вашего приложения). Использование большей задержки налагает более низкую нагрузки на процессор и, следовательно, потребляет меньше энергии.

Ответ 3

При регистрации вашего прослушивателя с помощью SensorManager с помощью registerListener вместо фиксированных констант SENSOR_DELAY_... вы можете передать интервал в микросекундах, как описано в JavaDocs:

скорость. События датчика скорости передаются на. Это лишь намек на система. События могут быть получены быстрее или медленнее, чем указано ставка. Обычно события получают быстрее. Значение должно быть одним из SENSOR_DELAY_NORMAL, SENSOR_DELAY_UI, SENSOR_DELAY_GAME или SENSOR_DELAY_FASTEST или желаемая задержка между событиями в микросекунды.

Итак, просто пройдите 40000, чтобы соответствовать вашему примеру, также, как указано, это только подсказка, поэтому я не буду полагаться на получение значений с интервалами, точными до микросекунд.

Ответ 4

Я пытался выяснить, как я могу очень долго задерживаться с датчиками, и до сих пор я был успешным. Документация на developer.android говорит о том, что время задержки может быть указано в микросекундах, ниже большое количество я пробовал:

// Initialize in onCreate
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE)
mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

// Register the listener, and play around with delay
mSensorManager.registerListener(this, mLight, 200000000);

Однако обратите внимание на дамп LogCat ниже - onSensorChanged() - получает вызов до 4 раз в секунду... Я пытаюсь понять, что до сих пор не удается добиться больших задержек.

01-14 06: 58: 07.088: Датчики D/IOE (20933): onSensorChanged, событие: [email protected] 01-14 06: 58: 07.268: Датчики D/IOE (20933): onSensorChanged, событие: [email protected] 01-14 06: 58: 07.448: Датчики D/IOE (20933): onSensorChanged, событие: [email protected] 01-14 06: 58: 07.628: Датчики D/IOE (20933): onSensorChanged, событие: [email protected] 01-14 06: 58: 07.808: Датчики D/IOE (20933): onSensorChanged, событие: [email protected] 01-14 06: 58: 07.989: Датчики D/IOE (20933): onSensorChanged, событие: [email protected] 01-14 06: 58: 08.169: Датчики D/IOE (20933): onSensorChanged, событие: [email protected]

Ответ 5

Кажется, что скорость, с которой вызывается onSensorChanged, должна быть одним из 4 предложенных значений.

Что вы можете сделать для "медленных чтений", это использовать SENSOR_DELAY_UI и (in onSensorChanged) измерять время, прошедшее с момента последнего считывания сенсордата:

long lastUpdate = System.currentTimeMillis();

// In onSensorChanged:
long curTime = System.currentTimeMillis();

        if ((curTime - lastUpdate) > 500){ // only reads data twice per second
            lastUpdate = curTime;
            // do stuff
        }

Единственный недостаток, который я вижу здесь, заключается в том, что оператор if будет вызываться много раз с ложным результатом.