加速度センサのサンプリング周波数を調整できるAndroidアプリを作った話

#概要
私事ですがサクジツ，3軸加速度センサの値がcsvファイルで欲しくなりました．Qiita仲間の皆さんであれば誰しもこのような経験はあると思います

ということでXperiaが手元にあったので，google playから早速検索，加速度センサロガーなるものを片っ端からインストールしてみました

するとなんと！サンプリング周波数が任意に設定できません．．．

しかしiOSには設定できそうなアプリが存在したので，Androidでも作れるのではないかと健闘した物語です

#まず調査
（注意）この章は作業ログならぬ勉強ログっぽく書いていきますので，日記感覚で見てください

すべてのアプリ(サンプル数6，7個)に共通している点で，サンプリングの間隔を大体でしか調整できませんでした

これについて調査していくと，加速度センサにおいては他の端末センサと違い具体的なサンプリングのレートを調整することができないらしいです
[SensorManagerのリファレンス](https://developer.android.com/reference/android/hardware/SensorManager)からregisterListenerについて見てみます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518001/2b8f918e-9a4a-1bce-9ee1-77417531c644.png)
そして，この第3引数であるint sampling PeriodUsには以下4つの変数を基本的に与えます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518001/5286084d-0414-410b-5c14-b392dfcb7423.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518001/0c0a6899-27d0-7b09-299d-fa16f5ad8f7a.png)
こいつらはSensorEventクラスで定義されています[（SensorEventリファレンス）](https://developer.android.com/reference/android/hardware/SensorEvent.html)


しかし，この第3引数に具体的な整数を入力したらどうなるのか，気になりますよね
ということで！　以下サイトで具体的に検証してくれていました←
https://akira-watson.com/android/accelerometer.html

ちなみに，実際の段取りではここまで事前調査を入念に行ったわけではなく，整数を入れてもうまく動作しなかったという沼に数時間はまり，ここに行きつきました(汗

それでは長々と失礼しましたが，先ほど言った通り，この事実が判明した時点でもう実装し始めちゃってます
フィルタリングでも何でもして無理やりサンプリング間隔を調整していきます，当方意地です

#環境
windows 10 home
Android Studio 3.6.3
Android Smartphone Xperia SO-01K API: 28
AVD :Nexus 5X API: 24

#実装
バージョンを見ていただければわかる通り，インストールしたばっかり，Android Studio初めて触ります
事前知識として，ボタンが簡単に作れるのは知っています

保険タイム終了，設計に行きます

##設計
APIはどんな端末でも使えるようにと願いを込めて16に設定しました
![Screenshot_20200419-190507 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518001/27a66cb8-5fd6-31d4-0791-ef344d3a17c0.png)

結論からお見せすると，このスクショのようになります


##サンプリング周波数調整方法
自分がぱっと思いついた実装方法ですが

　・sleep関数のようなものを使い，端末ごとフレームレートを落とす
　・ロー/ハイパスフィルタリングを用いて，ユーザ希望のサンプリング周波数となるような閾値を生成する
　・配列を等間隔に間引いて，サンプリングの間隔を広げることで実装する
　・毎フレームごとにcsvファイルに書き込んでいく

このくらいが挙げることができました

まぁこれは自分が製作中にアプローチした順番です
結果としては3つ目でうまいこといったので毎フレームごとにってやつはやっていません
###まずは加速度を可視化する
まず加速度センサを使う時のサンプルコードです


```MainActivity.java
    private SensorManager mManager;
    private TextView mX,mY,mZ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Get an instance of the SensorManager
        mManager = (SensorManager) getSystemService(SENSOR_SERVICE);

        mX = (TextView)this.findViewById(R.id.textViewX);
        mY = (TextView)this.findViewById(R.id.textViewY);
        mZ = (TextView)this.findViewById(R.id.textViewZ);
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Listenerの登録
        Sensor accSensor= sensorManager.getDefaultSensor(
                                Sensor.TYPE_ACCELEROMETER);

        this.mManager.registerListener((SensorEventListener) this, this.accSensor, SensorManager.SENSOR_DELAY_FASTEST);

    }

    @Override
    protected void onPause() {
        super.onPause();
        //Listenerの解除
        this.mManager.unregisterListener((SensorEventListener)this, this.accSensor);
    }

    @Override
    public void onSensorChanged(SensorEvent sensorEvent) {

        mX.setText("加速度センサーX:" + String.valueOf(sensorEvent.values[0]));
        mY.setText("加速度センサーY:" + String.valueOf(sensorEvent.values[1]));
        mZ.setText("加速度センサーZ:" + String.valueOf(sensorEvent.values[2]));

    }

    public void onAccuracyChanged(Sensor sensor, int n) {
    }
}
```

とりあえずこれで動きます，複数のセンサに対応させたいのであれば，onSensorChanged関数内で
`if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){}`
と条件分岐させます

onSensorChangedメソッドを見ていただくとわかる通り，sensorEventクラスの中のvalues[0]～[2]にx,y,zの加速度が入っています[（SensorEventのリファレンス参照）](https://developer.android.com/reference/android/hardware/SensorEvent.html#values)

したがって，これに計測日時を足したものをcsvに出力すれば，最大サンプリング周波数のcsvファイルが得られます

サンプリング周波数の検出方法については以下2つ（最後の参考資料にも記載あり）
https://qiita.com/taittide/items/2f6a70eae22117266a66
https://akihito104.hatenablog.com/entry/2013/07/22/013000

csv出力については今回のアプリの肝ではないので割愛します

###間引いていく
今回のアプリの全体的な流れは

　①STARTボタンが押される
　　　　　　↓
　②新たなリストにx,y,z加速度を保存していく
　　　　　　↓
　③STOP/SAVEボタンが押される
　　　　　　↓
　④保存されていた加速度をcsvに出力用のリストにコピー
　　　　　　↓
　⑤csv出力用のリストからcsvに出力
　　　　　　↓
　⑥リストデータをすべて破棄．①を待つ

なので，④の工程で適切な大きさに間引いてあげることでサンプリング周波数を調整できます

まずサンプルコードです


```MainActivity.java

public void changeandcopy_information() {

        accX_w = new ArrayList();
        accY_w = new ArrayList();
        accZ_w = new ArrayList();
        timeStamp_Time_forAcc_w = new ArrayList(timeStamp_Time_forAcc);
        int temp_size = timeStamp_Time_forAcc_w.size();
        boolean once = false;

        //配列を希望するサンプリングレートになるように間引くための変数
        int AdjustI = (int)(input_sampling_rate/wantConverting_sampling_rate);

        for (int i = 0; AdjustI*i < temp_size; i++) {
            if(!once){
                timeStamp_Time_forAcc_w.clear();
                once=true;
            }
            accX_w.add(accX.get(AdjustI*i));
            accY_w.add(accY.get(AdjustI*i));
            accZ_w.add(accZ.get(AdjustI*i));
            timeStamp_Time_forAcc_w.add(timeStamp_Time_forAcc.get(AdjustI*i));
        }
}
```

新キャラ変数の説明
accX,axxY,accZに加速度が保存されていて，それをcsv出力用のaccX_w,axxY_w,accZ_wにコピーするために，まず定義
timeStamp_Time_forAcc_wにはフレームの計測日時が格納されています
input_sampling_rateには，ユーザの端末の最大サンプリング周波数，wantConverting_sampling_rateはユーザが希望するサンプリング周波数が整数型で格納されています
AdjustIとonceはコード見た通りです

具体例を出して何をしているのかを説明しますと
端末の最大サンプリング周波数が400(Hz)，ユーザが希望するサンプリング周波数が100(Hz)だとします
ここで，純粋にそのままコピーすれば，変数_wたちの配列サイズは1秒間あたり400増えることになります
そこで，400/100（ユーザの端末の最大サンプリング周波数(Hz) / ユーザが希望するサンプリング周波数(Hz)）の値をfor文の中のiにかけてあげれば，iは0,1*400/100,2*400/100,..となり，配列の1秒間あたりのサイズは1/4である100になります

サンプリング周波数調整の成功です！！！
割り切れないとき？それはしょうがありません．誤差はつきものです

###実際のcsvファイル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518001/8c0aa154-4e63-28e1-0e6a-659ed3c75dd4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518001/83565363-c7e6-9c2a-514f-4246cd812e28.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518001/142f94ad-ddff-2796-6b4a-31b2d2418e07.png)

これは実機のサンプリング周波数423(Hz)で，変換させたいサンプリング周波数を10(Hz)とした時の出力です

理論値0.1秒からの差は-0.001，0.000となっていて，全体でも最大誤差は±0.001でした

ここまで誤差が出ないのはなぜなのか，不思議でたまりませんが，とにかくハッピーエンドですね
##ソースコード
githubにあげておきました．使用方法が少し複雑ですので，READ ME.txtを参考にしてください
https://github.com/miwazawa/accelerator-logger
環境で挙げた，XperiaとNexusでは動作確認済みです

#さいごに
こんなところまで長々とお付き合い頂き，ありがとうございました

実装のあたりからもうgithubにあげちゃえばいいやの精神になってしまい，自分でも駄文だなと感じながら書いていました（笑）

ソースコードについてなのですが，activity_main.xmlあたりで凄い警告を食らっていますが，動いてはいるのでおそらく直しはしません！

参考資料に自分が今回使用したサイトおそらく全て載せてありますので，それを見るだけでも実装できると思います

私自身javaでちゃんとしたプログラムを書くのがほとんど初めてだったため，初心者の方に少しでも勉強方法などで参考にしていただければなぁと思い今回執筆いたしました

途中から，配列をいじるのであればVBAからやったほうが早いと気づいたのはまた別のお話

#参考資料
##Java構文について
型変換方法
https://eng-entrance.com/java-var-change
https://www.sejuku.net/blog/14531
stringの整数判定
https://techacademy.jp/magazine/19075
Thread.sleepの使い方
https://engineer-club.jp/java-sleep
csv出力について
https://tech.pjin.jp/blog/2017/10/17/%E3%80%90java%E3%80%91csv%E5%87%BA%E5%8A%9B%E3%81%AE%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89/
https://teratail.com/questions/25005?link=qa_related_pc_sidebar
https://teratail.com/questions/49794
list・配列周り
https://www.javadrive.jp/start/linkedlist/index2.html
##加速度センサについて
加速度センサ実装
https://akira-watson.com/android/accelerometer.html
サンプリング周波数検出方法
http://akihito104.hatenablog.com/entry/2013/07/22/013000
https://qiita.com/taittide/items/2f6a70eae22117266a66
##Android Studioについて
android studioのインストールからHelloWorldまで
https://akira-watson.com/android/helloworld.html
layout
https://qiita.com/mii-chang/items/ee965c1e8826d4e59414
edittext入力判定
https://qiita.com/HALU5071/items/640652de9e31d4bbdbeb
テクストボックス作成
http://mitoavadn.hatenablog.com/entry/2017/03/11/224529
ボタン配置
https://teratail.com/questions/19171
SharedPreferences
https://akira-watson.com/android/sharedpreferences.html
外部ストレージリクエスト方法
https://qiita.com/folivora/items/f17a125e0bc88c17a6d3











##知識補充
アクティビティのライフサイクル
https://developer.android.com/guide/components/activities/activity-lifecycle?hl=ja
センサーリスナーの使い方
https://techbooster.org/android/device/12524/
https://www.atmarkit.co.jp/ait/articles/1001/15/news119.html
android studioからgithubへのpush方法
https://blog.codecamp.jp/programming-androidstudio-github