恵方巻コンパスができた

恵方巻コンパス

できたー。

ちょっと半端だけどできた。良かった。

GooglePlay

https://play.google.com/store/apps/details?id=net.dalomo.luckydirectionrollcompass

不満点

直す気はあんまり湧かない。

較正できない

結局分からないままにした。いろんなアプリ見たけど、できてるやつは出来てるし、やってないやつはやってないし。なので俺もやってないまま出すことにした。

方位角が安定しない

じっとせずにフラフラしてしまいます。一応なんちゃってフィルタを掛けてはいるけども、やっぱり1から2度辺りをフラフラしてしまい安定しない。ここ安定さす方法と、安定させたとして端末の動きに追従するような動きを作れなかった。ここは分かったらまたやってみたい。

デザイン

統一感がないというか、スカってるというか。イラストやさんのおかげで何となくそれっぽくはなるものの、素人感が抜けない。なんかこう、製品っぽくないよな。完全にお手製な感じ。もうちょっと製品っぽくデザインしたかったけど、どうすればいい感じになるかな、そこらへん上手くできないな。

音のオンオフ

なんとなくできそうな予感はするけど、あんまりモチベが湧かない。

感想

GooglePlayで恵方巻コンパス見てみたらやっぱり腐るほどあった。誰かに使ってもらえたらいいなぁ。でもがっかりされるだろうし、やっぱいいかなぁ。

次何しよ。前書いたfirebaseアプリと、Codelab覗いてて見つけたバーコード読み取りアプリ作りたいな。

参考

https://moji-waku.com/kaiso/

カテゴリー: できた | タグ: , , , | コメントする

恵方巻きコンパスを仕上げていく

較正は置いとこう

結局わからんので。音鳴らしたり画像表示したりしてく。

アプリ名を日本語にしたい

https://android.swift-studying.com/entry/20151102/1446464830

あれ、ならない。

http://xn--u9j207iixgbigp2p.xn--tckwe/archives/3659

こっちはなった。

共有ボタン作る

https://dalomo.net/blog/2018/12/16/278/

うむ。

音楽鳴らす

https://dalomo.net/blog/2018/11/27/188/

いぇい。

フルスクリーン全画面にする

https://dalomo.net/blog/2018/12/01/226/#toc2

よいしょー。

恵方の角度に入った時だけImageViewの画像を表示する

if (roundazimuth < 82 && roundazimuth > 68 && !isstarted) {
    startService(BGMintent);
    imageView2.setVisibility(View.VISIBLE);
    isstarted = true;
} else if ((roundazimuth > 82 || roundazimuth < 68) && isstarted) {
    stopService(BGMintent);
    imageView2.setVisibility(View.INVISIBLE);
    isstarted = false;
}

ImageViewにsetVisibilityなるattributesがあったのでそれを使った。方位角の範囲はこれでいいかなぁ?

カテゴリー: したい | タグ: , , , | コメントする

方位角を基に画像を表示させたい②

こんがらがる

上手くいかんなー。

Matrix.setRotate→新たにBitmap作る、だと画像が勝手に縮小される

例えば斜めとかになった時、前のコードだと対角線の長さが縦横の長さに縮められるんだから、当たり前っちゃ当たり前なのか。計算であれこれやればそれ込みで表示させれそうだけどよく分かんない。で、ImageViewにsetRotationなるものがあることを知った。

imageView.setPivotX(imageView.getWidth()/2);
imageView.setPivotY(imageView.getHeight()/2);
imageView.setRotation(azimuth);

setPivotX,setPivotYで中心点を指定して、setRotationで角度を指定してあげると、縮小せずに回転するようになった。わーい。

参考

https://stackoverflow.com/questions/7437732/android-rotatable-imageview

結局、磁気センサーの較正・キャリブレーションって必要なのか

ここらへんあやふやだし全然分からん。センサーの値にはハードウェアでの値、ハードウェアの組み込みソフト内での較正値、スマホ端末での取得値、スマホ端末での較正値と、少なくともこれぐらいの種類の値がある。で、AndroidDeveloper見ると、TYPE_MAGNETIC_FIELDとTYPE_MAGNETIC_FIELD_UNCALIBRATEDと地磁気センサーの値は2つある。

Sensor.TYPE_MAGNETIC_FIELD:

All values are in micro-Tesla (uT) and measure the ambient magnetic field in the X, Y and Z axis.

Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:

Similar to Sensor.TYPE_MAGNETIC_FIELD, but the hard iron calibration is reported separately instead of being included in the measurement. Factory calibration and temperature compensation will still be applied to the “uncalibrated” measurement. Assumptions that the magnetic field is due to the Earth’s poles is avoided.

The values array is shown below:

  • values[0] = x_uncalib
  • values[1] = y_uncalib
  • values[2] = z_uncalib
  • values[3] = x_bias
  • values[4] = y_bias
  • values[5] = z_bias

x_uncalib, y_uncalib, z_uncalib are the measured magnetic field in X, Y, Z axes. Soft iron and temperature calibrations are applied. But the hard iron calibration is not applied. The values are in micro-Tesla (uT).

x_bias, y_bias, z_bias give the iron bias estimated in X, Y, Z axes. Each field is a component of the estimated hard iron calibration. The values are in micro-Tesla (uT).

Hard iron – These distortions arise due to the magnetized iron, steel or permanent magnets on the device. Soft iron – These distortions arise due to the interaction with the earth’s magnetic field.

ハードアイアンとソフトアイアンって何→こちらに説明あった。

value[3][4][5]がバイアスと書いてあるが、これを普通にオフセット値として使っても、TYPE_MAGNETIC_FIELDの値と一致しない。なぜだ。

他のキャリブレーション機能付きコンパスアプリ

を使った時、value[3][4][5]の値が変わった。しかも変わった値が自分のアプリにも適用された。多分。ここの値の変更の仕方がわからない。ここを見てみると(さっきのコンパスアプリとは違うsatstatってやつのIssueのGPSstatusの話題だけど)

To my knowledge, GPS Status does some extra magic inside the app, which is not part of the Android API and thus not available to other applications.

魔法だってさ!!2014年の話題だけど、状況は変わってないんだろうか。

HuaweiP20Proの磁気センサーは旭化成製

みたい。型番は知らん。

ここを見るとセンサーには自動調整機能がついているようだ。ただセンサーに較正機能がついているからといって、それを全部信用することはできないみたい。結局使用者による較正は必要になるみたい。これとかこれとかとか。じゃあどうやって較正するねんって話になるよね。

電子コンパスの較正方法→俺には無理

をググってみると数式がいっぱい出てくる。

二次元の場合

X軸・Y軸の中点を計算し、その値をオフセットとして使うみたい。センサーで値を取得するとプラス方向とマイナス方向の絶対値は一致するはず、でも現実そうじゃないので、そうなるように補正するってことだよね。

float[] midXYZ = new float[3];

for (int i = 0; i < 3; i++) {
    if (fMagnetic[i] > 0 && fMagnetic[i] > maxXYZ[i]) {
        maxXYZ[i] = fMagnetic[i];
    } else if (fMagnetic[i] < 0 && fMagnetic[i] < minXYZ[i]) {
        minXYZ[i] = fMagnetic[i];
    }

    midXYZ[i] = (maxXYZ[i] + minXYZ[i]) / 2;
}
fMagnetic[0] = fMagnetic[0] - midXYZ[0];
fMagnetic[1] = fMagnetic[1] - midXYZ[1];
fMagnetic[2] = fMagnetic[2] - midXYZ[2];

書いてみたけどこれは良くない。最大値・最小値に例外的に飛んだ値が取得された場合を加味してない、てことはそういう場合ちゃんと較正されない。ので駄目だ。これもローパスフィルタかけりゃいいのかな…。

三次元の場合

こっちはコードも書けなかったぜ。複数の三次元上の点から楕円体を求めて、最小二乗法とかいう方法で原点を求めるらしいぞ。言ってて意味が分からんな。こことかここ

でもさ、これで補正値を得られたとして、自分のアプリで使うことはできるかもしれないけれども、↑で書いたvalue[3][4][5]の値を変更する方法がわからないのだからやっぱり片手落ちな気がする。負け惜しみだけどな。このvalue[3][4][5]の値を変更する方法が知りたい。どこかにないかなー、探したけど見つかんない。

getRotationMatrixFromVectorを使って方位角を取得する

switch (event.sensor.getType()) {
    case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
        fGeoRotVec = event.values.clone();
}

if (fGeoRotVec != null) {
    SensorManager.getRotationMatrixFromVector(inR, fGeoRotVec);
    SensorManager.getOrientation(inR, attitude);
    azimuth = (float) Math.toDegrees(attitude[0]);
}

回転ベクトルセンサーを使っても方位を計算できるらしいぞ。回転ベクトルセンサーは、磁力センサ・加速度センサ・ジャイロセンサを使ってソフトウェア上でほにゃららしてるみたい。詳しいことは知らんが、なんかめっちゃ早い。

もうこれでいいんじゃないかとも思ったが、これもやっぱり較正しないと正しい値は取れないんじゃないだろうか。資料が少なくて調べてない。動画にはなってないけど、最初角度がすっ飛んだりはした。

いっかな。

いいよね。結局value[3][4][5]の謎は解けないし。

参考

https://developer.android.com/reference/android/hardware/SensorEvent#values

https://myenigma.hatenablog.com/entry/2016/04/10/211919

https://github.com/mvglasow/satstat/issues/24

https://teratail.com/questions/96167

https://stackoverflow.com/questions/34012677/check-and-enable-magnetic-sensor-calibration-in-android

https://stackoverflow.com/questions/42632370/inbuilt-sensor-calibration-functionality-in-android

https://stackoverflow.com/questions/6047931/magnetic-field-sensor-calibration-on-android

http://m.eclipsim.com/gpsstatus/

https://www.aichi-mi.com/home/%E9%9B%BB%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9/%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9%E3%81%AE%E8%BC%83%E6%AD%A3%E3%82%BD%E3%83%95%E3%83%88%E3%81%AE%E5%8E%9F%E7%90%86/

https://electronics.stackexchange.com/questions/22144/magnetometer-%E2%88%9E-shaped-calibration

https://stackoverflow.com/questions/1884699/android-compass-noise-algorithm

カテゴリー: したい | タグ: , , , | コメントする

方位角を基に画像を表示させたい

SurfaceViewとか

画像とかを連続して描画するのには、SurfaceViewというものを使うといいらしい。他にもTextureViewとかCustomViewとかあるらしいんだけどよく分からん。なんかAndroidのバージョンごとに推奨された変遷があるらしい。みんな使ってそうで、ネット上でも解説記事がある程度見つかるSurfaceViewを使ってみたい。

無理でした

途中まで頑張ったんだけど、やり方がよくわからなくなってしまった。SurfaceView.javaを新しく作ってそこに処理を書いて、MainActivity.javaでsetContentViewする感じにしたんだけど。センサーの値の取得をMainに書いてて、その値をSurfaceの方に渡すやり方がわからない。MainActivityの中でSurfaceViewを扱うやり方も分からない。ので別のやり方を探す。

参考

http://quesera2.hatenablog.jp/entry/2016/08/30/233743

https://blog.keiji.io/2015/12/mincomi-adventcalendar-20.html

https://techbooster.org/android/application/1195/

https://techbooster.org/android/application/1274/

ImageViewを使う

適当に作った画像を用意。

<ImageView
    android:id="@+id/imageView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView"
    app:srcCompat="@drawable/needle" />

ImageViewを設置。

imageView = findViewById(R.id.imageView);
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.needle);
imagewidth = bitmap1.getWidth();
imageheight = bitmap1.getHeight();

onCreateでimageViewの取得とdrawableから画像を取得。

azimuth = (float) Math.toDegrees(attitude[0]);
matrix.setRotate(-azimuth, imagewidth / 2, imageheight / 2);
Bitmap bitmap2 = Bitmap.createBitmap(bitmap1, 0, 0, imagewidth, imageheight, matrix, true);

imageView.setImageBitmap(bitmap2);

onSensorChangedで方位角からMatrixを使ってsetRotateで新たにBitmap作る。でImageViewにセット。これ計算合ってるかな。

回った!!なんかカクカクwあと見て分かる通り、回転するごとに画像が拡大縮小してしまう。すっごい方角がずれる。いっぱい動かしたりすると、アプリが固まる。うーん、なんか思ったような感じになってくれないなぁ。めんどくさくなってきたなぁ。

参考

https://akira-watson.com/android/matrix.html

カテゴリー: したい | タグ: , , , | コメントする

方位角を計算したいけど全然分かんない

笑えるぐらい分かんない

前の記事で加速度と地磁気の値は取得できた。で、方位角を求めるわけだけど、全然わからない。数学難しい。こう書けば取得できるよ!って記事はたくさんあるんだけど、なんでそうすると求められるのかが分からない…。分からないことを分からないとだけ書いてこう。

いろーんな記事に載ってる方位角求めるやつ

private static final int MATRIX_SIZE = 16;
float[] inR = new float[MATRIX_SIZE];
float[] outR = new float[MATRIX_SIZE];
float[] I = new float[MATRIX_SIZE];
float[] attitude = new float[3];
SensorManager.getRotationMatrix(inR, I, fAccell, fMagnetic);
SensorManager.remapCoordinateSystem(inR, SensorManager.AXIS_X, SensorManager.AXIS_Y, outR);
SensorManager.getOrientation(outR, attitude);
sb.append("\n\n方位角 _ ");
sb.append(Math.toDegrees(attitude[0]));
sb.append("\n傾斜角 _ ");
sb.append(Math.toDegrees(attitude[1]));
sb.append("\n回転角 _ ");
sb.append(Math.toDegrees(attitude[2]));

getRotationMatrix…回転行列ってなんですか。remapCoordinateSystem…世界座標系と端末座標ってなんですか。getOrientation…なんでこれで角度が取れるんですか。海は死にますか。山は死にますか。先人たちのサンプルコードの結果みたいにはなるのよ。でも中身何やってるのか全然わかんないの。

参考

https://seesaawiki.jp/w/moonlight_aska/d/%BC%A7%B5%A4/%B2%C3%C2%AE%C5%D9%A5%BB%A5%F3%A5%B5%A1%BC%A4%C7%CA%FD%B0%CC%B3%D1/%B7%B9%A4%AD%A4%F2%B8%A1%BD%D0%A4%B9%A4%EB

http://www.magicvox.net/archive/2015/02072148/

http://yoko-gb.blogspot.com/2012/10/android.html

https://wp.developapp.net/?p=3394

加速度と地磁気センサで取れる値ってなんだ

どっちにもX,Y,Zの軸があって、それぞれの値をとってる。

加速度センサー

単位はm/s^2。水平に端末を置いた場合、X=0,Y=0,Z=9.8になるはず。垂直に立てた場合、X=0,Y=9.8,Z=0になるはず。これを斜めにしてくと、0~9.8の値をとってくみたい(場所や状況によって超えたり、マイナスになったりはする)。そんで、そん時の値によって、どの方向にどれだけ傾いてるかが分かるみたい。

地磁気センサー

単位はμT。最大値、最小値は分からん。というよりかは各軸にどれだけの強度かが分かればいいようだ。強度が分かれば、その対角線みたいなやつ。よく見る図のやつね。の方向が北ってことらしい。Z軸で傾き補正ができるみたい。え、そしたら加速度いらなくね。ここらへんがよくわかんないんだよなー。んーと、地磁気は地面と平行なわけでも、端末と平行なわけでもなくて、地磁気のみの場合だと地磁気自体が傾いてるから、それを補正するために端末の傾きも必要になる。とか。地磁気センサが傾いてる場合、地磁気の傾きをセンサで補正したとしても、端末自体の傾きを考慮していない値になって、それを補正するために端末の傾きが必要になる。とか。二次元の画面に落とし込む際に、水平であった場合、の表示をしなければならないので、みたいな。ね、ほら、分かんない。

エクセル先生を利用して2軸で方位角を算出してみる

この為に百均でコンパス買ってきちゃった。コンパスを撮影してその向きでアプリ起動してスクショ撮ったのが上の画像。なるべく水平にしたつもり。

x y atan2 degrees 90-degrees
撮影時 -39.25 20.125 2.667806 152.854 -62.854
現時点 -55.75 22.3125 2.760893 158.1875 -68.1875

結果がこちらだけど、これは、合ってるのか…?微妙にずれてる気がしないわけでもない。90°ぐらいになると思ったんだけどな。

参考

https://www.sports-sensing.com/support/motionmesurement/motion_biomechanics/basis_accsensor.html

http://www.s3sensor.com/sens_a01/about/shikumi05.html

https://www.aichi-mi.com/home/%E9%9B%BB%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9/%E6%96%B9%E4%BD%8D%E8%A8%88%E7%AE%97%E3%81%AE%E5%9F%BA%E7%A4%8E/

getRotationMatrixと回転行列と

ここら辺もう雰囲気だ。端末が傾いてると、地磁気センサの水平方向がちゃんと取れないらしい。なので、水平になるように補正してあげるそうで、その補正量、どれだけ傾きを戻してあげるのか、というのを得るために、getRotationMatrixを使って回転行列というものを得るのだと思う。

SensorManager.getRotationMatrix(inR, I, fAccell, fMagnetic);

これで言うと、第1引数に回転行列、第2引数にも回転行列ここに指定した変数に、計算結果が入るみたい。2つの違いは?と言われても分かりません。なんかIの方は地磁気の傾斜角を求める際に使うみたい。今使うのはRの方で、X軸が東、Y軸が北、Z軸が空を向いてる時の値に変換できるようなもんらしい。合ってる?第3引数に加速度センサの値、第4引数に地磁気センサの値。

なぜ加速度センサでの傾きが必要か

https://www.aichi-mi.com/home/%E9%9B%BB%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9/%E6%96%B9%E4%BD%8D%E8%A8%88%E7%AE%97-%E5%8A%A0%E9%80%9F%E5%BA%A6%E3%82%BB%E3%83%B3%E3%82%B5%E3%81%AA%E3%81%97%E3%81%A7%E5%82%BE%E6%96%9C%E8%A3%9C%E6%AD%A3%E3%81%AF%E4%B8%8D%E5%8F%AF%E8%83%BD/

ここが分かりやすかった。

参考

https://www.aichi-mi.com/home/%E9%9B%BB%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9/%E6%96%B9%E4%BD%8D%E8%A8%88%E7%AE%97%E3%81%AE%E5%82%BE%E6%96%9C%E8%A3%9C%E6%AD%A3/

https://developer.android.com/reference/android/hardware/SensorManager#getRotationMatrix(float[],%20float[],%20float[],%20float[])

remapCoordinateSystemやら世界座標系やらデバイス座標やら

分からんものを分からんまま分からん感じに理解しようとするからわけわかめだね。回転行列得られたんだからこれ何やってんの?と思ったんだけど、必要みたい。デバイスが縦持ち・横持ちの場合、アプリが画面内で回転したりする。そうすっと例えば横持ちにした場合、縦持ち時のX軸をY軸とみなさないと数値が変になるよね、ということらしい。たぶん。なので回転行列をデバイスの方向に回転させたげるみたい。

SensorManager.remapCoordinateSystem(inR, SensorManager.AXIS_X, SensorManager.AXIS_Y, outR);

このX,Yの値もなぜこれを設定するのかわかんないんだよなぁ…。一応AndroidDeveloperのサイトの例として、

  • Using the camera (Y axis along the camera’s axis) for an augmented reality application where the rotation angles are needed:

remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);

remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);

ってあるけど、イメージが掴めない。なんか図とか絵とかで解説してるとこないかなー。

参考

https://developer.android.com/reference/android/hardware/SensorManager#remapCoordinateSystem(float[],%20int,%20int,%20float[])

https://teratail.com/questions/13175

getOrientationと方位角と傾きと回転と

愛しさと切なさと心強さと。これなんでこんな引数少ないんだろう。

SensorManager.getOrientation(outR, attitude);

しかもこれ第2引数は結果保持用の変数だし。この回転行列は、端末がX軸が東、Y軸が北、X軸が空を向いてるようにするためのものなので、逆に言えばそのある意味デフォルトの位置からどれだけずれているかも求められる、ってことでいいのかな。どうなのかな。もうそんな感じでいいや。X,Y,Zそれぞれの回転角のことをロール、ピッチ、ヨーっていうのね。初めて知った。アジマスも初めて知った。おいしそう。attitudeに格納されてる値はラジアンなので見慣れた度数にするには変換が必要。

第2引数の説明を機械翻訳
  • values [0]:方位角、-z軸を中心とした回転角。この値は、デバイスのy軸と磁北極の間の角度を表します。北向きの場合、この角度は0です。南向きの場合、この角度はπです。同様に、東を向いているとき、この角度はπ/ 2であり、西を向いているとき、この角度は-π/ 2です。値の範囲は-πからπです。
  • values[1]:ピッチ、x軸周りの回転角度。この値は、デバイスの画面に平行な平面と地面に平行な平面の間の角度を表します。デバイスの下端がユーザの方を向き、画面が上を向いていると仮定して、デバイスの上端を地面に向かって傾けると、正のピッチ角が生じます。値の範囲は-πからπです。
  • values[2]:ロール、y軸周りの回転角度。この値は、デバイスの画面に垂直な平面と地面に垂直な平面の間の角度を表します。デバイスの下端がユーザの方を向き、画面が上を向いていると想定して、デバイスの左端を地面に向かって傾けると、ロール角はプラスになります。値の範囲は-π/ 2からπ/ 2です。

参考

https://developer.android.com/reference/android/hardware/SensorManager#getOrientation(float[],%20float[])

https://www.aichi-mi.com/home/%E9%9B%BB%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9/%E6%96%B9%E4%BD%8D%E8%A8%88%E7%AE%97%E3%81%AE%E5%82%BE%E6%96%9C%E8%A3%9C%E6%AD%A3/

https://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%B8%E3%82%A2%E3%83%B3

加速度センサにはノイズが混じるし重力の影響が出る

センサで取得した値は時々すっ飛ぶことがあるようで、また静止状態だとしても微妙に振れが出てくるもの、ということで、それをできるだけ滑らかな変動にする必要があるみたい。で、その時に使うのがローパスフィルタというそうで、値をまろやかでコクのあるものにしてくれる。コクはない。ハイパスフィルタというものもあり、こっちは重力の影響を取り除いてくれるみたい。

ローパスフィルタのサンプル(Google)

    final float alpha = 0.8;

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

前回値に0.8掛けて、現在値に0.2を掛けて、足し合わせる。指数移動平均というらしい。

エクセル先生で適当に検証

B列に=9.7+(10-9.7)*RAND()と、C列に=C2*$B$1+B3*(1-$B$1)。青が現在値で赤がフィルタ後の値。1000回やってみた。

B1=0.8(alpha=0.8)

B1=0.5(alpha=0.5)

へー、ほんとに滑らかになってるー。

ハイパスフィルタのサンプル(Google)

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0];
    linear_acceleration[1] = event.values[1] - gravity[1];
    linear_acceleration[2] = event.values[2] - gravity[2];

現在値からローパスフィルタ後の前回値を引く。これ結果が大体ゼロ近辺になるんだけどどゆこと?と思ったら、傾き検知とかに使用するんじゃなくて、振動とか運動を検知する時に有効みたい。たぶん。傾きの時にXYZ全部ゼロだったら困るもんね。ハイパスフィルタ後の値が変動する時は端末が動いてる時なんだよね、そういうことよね。たぶん。

参考

https://developer.android.com/guide/topics/sensors/sensors_motion#sensors-motion-accel

http://tomoima525.hatenablog.com/entry/2014/01/13/152559

http://yksris.hatenablog.com/entry/2012/10/09/170849

http://diylabo.sakura.ne.jp/tips/2014/06/accelero2.html

地磁気センサは較正が必要だし磁北と真北は違う

いい加減ゲロ吐きそうになってきたが、まだ考慮することがあるみたい。

較正・キャリブレーション

これが必要なのは分かる。各軸の最大値・最小値の中点をとって原点ズレを補正するって感じみたい。ただサンプルコードがどこにも見つからんのでどうにもこうにもできんのです、わからんのです。他の人はどうやってんだろ。実直に自分で実装してるんだろうか。すごいなぁ、いいなぁ。ハードウェアでやってくれればいいのになぁ。そういうわけにもいかんのだろうなぁ。

磁北と真北は違う

方位磁石が指し示す北と、北極点の位置は、微妙にずれているらしい。そのズレの角度を偏角といって、場所によって違うようだ。地磁気センサで取れる値はまぁ思ったとおり磁北であって、偏角で補正しないと正しい真北は得られない。センサによって偏角を取得することはできず、偏角を集めた表みたいなのを使って補正するみたい。で、それ用のメソッドがあるようで、

GeomagneticField(float gdLatitudeDeg, float gdLongitudeDeg, float altitudeMeters, long timeMillis)

getDeclination()

を使えばいいみたい。でもですよ、風水とかでは磁北を用いてるんですって。いや恵方巻きと風水は全然関係ないけども、あれ?あるの?どっち?ただ方位磁石とのズレは直したいよなぁ…見なかったことにしようかなぁ…。

参考

https://www.aichi-mi.com/home/%E9%9B%BB%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9/%E7%A3%81%E6%B0%97%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%81%AE%E8%BC%83%E6%AD%A3%E4%B8%80%E8%88%AC/

https://www.aichi-mi.com/home/%E9%9B%BB%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9/%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9%E3%81%AE%E8%BC%83%E6%AD%A3%E3%82%BD%E3%83%95%E3%83%88%E3%81%AE%E5%8E%9F%E7%90%86/

https://stackoverflow.com/questions/9462346/compass-give-me-crazy-data-is-calibration-needed-or-its-the-sensor-broken

http://www.gsi.go.jp/buturisokuchi/menu01_index.html

https://www.pasco.co.jp/recommend/word/word046/

https://www.aichi-mi.com/home/%E9%9B%BB%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%91%E3%82%B9/%E5%9C%B0%E7%A3%81%E6%B0%97%E3%81%A8%E3%81%AF/

http://glassonion.hatenablog.com/entry/20091210/1260373724

https://developer.android.com/reference/android/hardware/GeomagneticField

こんなところかな…

まだなんかありそうな気がするけど見つけられん。もういいよ。パトラッシュ。次はいよいよ数値を元に画像を動かそう。できるかなぁ。

カテゴリー: したい | タグ: , , , | 2件のコメント

恵方巻きコンパスを作りたい&地磁気・加速度センサーの値の取得

恵方巻きコンパス

スーパーやコンビニに行くともう恵方巻きの予約やらの広告が掲示されている。GooglePlayで恵方巻きコンパスを探してみたら、普通のコンパスに加えて、恵方巻きコンパスも腐るほど出てきた。これなら俺が作っても大丈夫ではなかろうかと思った。なんとなくだけど、センサーから数値を引っ張るところまではできそうだけど、それを元に画像(磁針とか)を動かすのに苦戦しそうだなと思う。でもまあやってみよう、節分に間に合うかな。間に合わなかったらそれはそれでいいや。

センサーを扱う

とりあえず画像とかは後回しにして、テキストで数値を表示するようなものを作ってみたい。いろんなサイトを見ると、加速度センサーと磁気センサーを使うみたい。方位で、なんで加速度が必要なんだろうと思ったけどよく分かってない。

private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Sensor mMagneticField;
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

センサーを使うときは、SensorManagerを使うみたい。

public class CompassActivity extends AppCompatActivity implements SensorEventListener {

implements SensorEventListnerを追加すると

@Override
public void onSensorChanged(SensorEvent event) {

}

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

}

の2つを追加するように言われる。2つの違いがあまり良く分からんが、普通上のメソッドの中に処理を書くみたい。

@Override
protected void onResume() {
    super.onResume();
    mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    mMagneticField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
    mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    mSensorManager.registerListener(this, mMagneticField, SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
protected void onPause() {
    super.onPause();
    mSensorManager.unregisterListener(this);
}

onResumeとonPauseでリスナの登録と解除。onStopで解除してるとこもあったけどなぜかな。これで使う準備できたはず。

private float[] fAccell;
private float[] fMagnetic;
@Override
public void onSensorChanged(SensorEvent event) {
    switch (event.sensor.getType()) {
        case Sensor.TYPE_ACCELEROMETER:
            fAccell = event.values.clone();
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            fMagnetic = event.values.clone();
            break;
    }
}

switchでセンサーの種類を判断し、それぞれ値をfloat型の変数に代入する。とりあえずTextViewに表示してみよう。

StringBuilder sb = new StringBuilder();
if (fAccell != null && fMagnetic != null) {
    sb.append("加速度\nX _ ");
    sb.append(fAccell[0]);
    sb.append("\nY _ ");
    sb.append(fAccell[1]);
    sb.append("\nZ _ ");
    sb.append(fAccell[2]);

    sb.append("\n\n地磁気\nX _ ");
    sb.append(fMagnetic[0]);
    sb.append("\nY _ ");
    sb.append(fMagnetic[1]);
    sb.append("\nZ _ ");
    sb.append(fMagnetic[2]);

    textView.setText(sb.toString());
}

どっちもnullじゃないことを確認して表示。

できた。ぐるーっと回したり、傾けてみたり、数字が変わるが、意味がよく分かってないぞ。

参考

https://developer.android.com/guide/topics/sensors/sensors_overview

https://developer.android.com/guide/topics/sensors/sensors_motion

https://developer.android.com/guide/topics/sensors/sensors_position

https://qiita.com/InoueDaiki/items/568b3328557bc44fb5d6

http://www.magicvox.net/archive/2015/02072148/

カテゴリー: したい | タグ: , , , | コメントする

Androidアプリを公開する時にやったことリンク

忘れてた。ってか忘れてる。とにかく公開したかったからなぁ。クリスマスも終わったことだし、思い出しつつ書き留めとこ。

なにしたっけ…

あれマジで何したっけ。

アプリの準備

バージョンの記載と署名付きビルド

https://developer.android.com/studio/publish/?hl=ja

最初ここを見た…のかな。で、ここでバージョンの記載と署名付きビルドを知った。

https://developer.android.com/guide/topics/manifest/manifest-element?hl=ja

https://developer.android.com/studio/publish/app-signing?hl=ja

https://developer.android.com/studio/publish/versioning?hl=ja

App Bundleはよく分からんかったので使わなかった。

APIKeyってアプリ内に直で書いていいの?

と思ってちょっこす調べた。

https://teratail.com/questions/59248

GoogleAPIの場合、フィンガープリント発行してあーだこーだすればそのアプリ以外で使えなくなるので、気にせんでいいらしい。じゃあ、MapBoxのAPIは?と思ったけどよく分からん。ていうかフィンガープリントなんざ使った覚えがないので多分ダメなんじゃないか。いや嘘、よくわかんない、ほんとはあるのかも。で、他の方法はというと理解できんものばかり。

http://www.isus.jp/encryption/sample-code-data-encryption-application/

データの暗号化?

https://teratail.com/questions/34281

サーバー上?

http://k16.hatenablog.jp/entry/20110728/1311847312

SSL?

https://stackoverflow.com/questions/49610269/securing-api-key-using-ndk

C++で書く?

https://www.techjini.com/blog/securing-api-key-and-secret-key-in-android/

gradleをなんちゃらする?

使ったやつ

https://qiita.com/mickamy/items/ee55e8fab2dfb114c2f2

https://github.com/shamanland/simple-string-obfuscator

こちらを使わせていただいた。これ使うにも大変だった。

$ ./obfuscate_string.sh

みたいなのってなんだろうと思ったら、macのターミナルってやつらしい。winでもできるかなと探してみたら

http://pmw1415.hateblo.jp/entry/2015/10/17/222111

というのがあってのでインストールした。これも使えるようになるまで大変だったな…。

http://www.ritsumei.ac.jp/~tomori/unix.html

GooglePlayに公開する

https://support.google.com/googleplay/android-developer/answer/6112435?hl=ja&ref_topic=3450769

基本ここ見てやった。

紹介ページとかの画像を用意するのが面倒だったが適当にでっち上げた。この公開の部分に関しては、そんな詰まらなかったかなぁ。

AdMob広告を貼る

AdMobっていうのが定番みたい。なのだが、Mobile Ads SDKのやり方とFirebaseのやり方がGoogleのガイド中にあって、若干混乱した。公開後でないと広告を貼れないのも初めて知った。

Mobile Ads SDK

Firebase

ここらへんで詰まった気がするけど、今思えばあれはアプリのバグのせいだったのかもしれない。

その他

あとなんかあったっけかなー。

gradleで赤波線が出た時の対処

All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes).

http://tokyo.supersoftware.co.jp/code/7474

https://stackoverflow.com/questions/42374151/all-com-android-support-libraries-must-use-the-exact-same-version-specification

バージョン合わせないといけないみたい。

カテゴリー: のーと | タグ: , , , | コメントする

AndroidStudioでデータベースを使ってみたい&BMI計算②

やっとSQLite

BMIの計算ができたので、計算結果をDBに保存しよー。で、保存したのを取り出そー。

DBヘルパークラス

なるものが必要らしい。コピペしてこう。

public class DatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "bmihistory.db";
    private static final int DATABASE_VERSION = 1;

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        StringBuilder sb = new StringBuilder();
        sb.append("CREATE TABLE bmihistory (");
        sb.append("_id INTEGER PRIMARY KEY,");
        sb.append("date TEXT,");
        sb.append("height REAL,");
        sb.append("weight REAL,");
        sb.append("result REAL");
        sb.append(");");
        String sql = sb.toString();

        db.execSQL(sql);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

SQL文の「,」が抜けてて数時間ハマった。コンストラクタの引数はcontextだけでよかったみたい。IDEさんに作ってもらってたら、なんか色んなのがついてきてて、それは消した。onCreateはDBがない時一回だけ呼ばれるみたい。

DatabaseHelper helper = new DatabaseHelper(BMIActivity.this);
        SQLiteDatabase db = helper.getWritableDatabase();

        try {
            String sqlInsert = "INSERT INTO bmihistory (_id, date, height, weight, result) VALUES (?, ?, ?, ?, ?)";
            SQLiteStatement statement = db.compileStatement(sqlInsert);
//            statement.bindLong(1, 1);
            statement.bindString(2, resTime);
            statement.bindDouble(3, dHeight);
            statement.bindDouble(4, valueWeight.doubleValue());
            statement.bindDouble(5, Double.valueOf(result));

            statement.executeInsert();
        } finally {
            db.close();
        }

これでデータ保存ができてるはず…。今はtry-finallyじゃなくて

        try (SQLiteDatabase db = helper.getWritableDatabase()){
            String sqlInsert = "INSERT INTO bmihistory (_id, date, height, weight, result) VALUES (?, ?, ?, ?, ?)";
            SQLiteStatement statement = db.compileStatement(sqlInsert);
//            statement.bindLong(1, 1);
            statement.bindString(2, resTime);
            statement.bindDouble(3, dHeight);
            statement.bindDouble(4, valueWeight.doubleValue());
            statement.bindDouble(5, Double.valueOf(result));

            statement.executeInsert();
        }

こうでいいみたい。意味はよく分かってない。

で、ここで確認のためにSQLAndroidを使おうとしたんだけど

run-as: package has corrupt installation: net.dalomo.lifecalculator

と出てしまって何も表示されない。なぜだ。諦めて他のサイトで見た、Terminalからadb shell→run-asもやってみたけどダメ。なんかroot権限が必要らしい。しょうがないので普通にコード書いて取得してみる。趣旨がずれてきてるな。

DBからデータを取得

DatabaseHelper helper = new DatabaseHelper(BMIActivity.this);
try (SQLiteDatabase db = helper.getWritableDatabase()) {
    String sql = "SELECT * FROM bmihistory";
    Cursor cursor = db.rawQuery(sql, null);
    StringBuilder sb = new StringBuilder();

    while (cursor.moveToNext()) {
        int idxDate = cursor.getColumnIndex("date");
        int idxheight = cursor.getColumnIndex("height");
        int idxweight = cursor.getColumnIndex("weight");
        int idxresult = cursor.getColumnIndex("result");
        sb.append(cursor.getString(idxDate));
        sb.append(", ");
        sb.append(cursor.getString(idxheight));
        sb.append(", ");
        sb.append(cursor.getString(idxweight));
        sb.append(", ");
        sb.append(cursor.getString(idxresult));
        sb.append("\n");
    }

    TextView tv = findViewById(R.id.textView2);
    tv.setText(sb.toString());

    cursor.close();
}

できたー。できたんだけど、なんだかなぁ。DBをDBのまま編集してみたくて始めたんだけど、最終的にBottun押してTextViewに表示するいつものやつになった。まぁ、取得の仕方がさわりだけでも分かったからいっかー。

ただ、この場合だと、一つの計算につき一つのDBって感じになるの?あ、いや、TABLEを増やせばいいのかな?あれ、でもコンストラクタは一回しか呼ばれないしな…。ちょっとこんがらがってきたからまたあとで考えよう。ていうかこんなんだからGUIで確認したいんだけどな。CUIわかんないよ。

参考

[affi id=2]

https://www.dbonline.jp/sqlite/type/index1.html

https://qiita.com/zuccyi/items/d9c185588a5628837137

https://www.dbonline.jp/sqlite/insert/index1.html

https://wa3.i-3-i.info/word12448.html

https://stackoverflow.com/questions/25179590/android-sqlite-db-pull-error

カテゴリー: したい | タグ: , , , , | コメントする

AndroidStudioでデータベースを使ってみたい&BMI計算

SQLite

Pluginを探していたら、SQL AndroidというPluginを見つけた。なんかすごい便利そう!と思ったので使ってみたくなった。ので、RDBを使ったアプリを作ってみたい。RDBに関しては、ほとんど知識がない。仕事でちょろっとAccessを使ったことがあるけど、すっごい簡単なSQL文書くぐらいで、リレーション張ったこともないぐらい。できるかな。できるだろ。ついでにちょこちょこ作ってくやつも作ろう。生活電卓って名前にしよう。

とりあえずBMI計算機を作ろう

そんでその記録を保存するためにSQLite使えばいいんだ。そうしよう。

画面作る

色んな計算のリスト→個別の計算→その計算の履歴みたいな遷移にしたい。リストを作るにはListViewを使うみたい。見てみたらLegacyに入ってる。今はRecyclerViewに置き換わってるみたい。えー、どっち使えばいいん。色々見るとRecyclerViewはクリックイベントを自分で実装するらしい。それは自分には無理そうなので、ListViewにすることにした。

string.xml
<resources>
    <string name="app_name">My Application</string>
    <string-array name="calc_menu">
        <item>BMI計算</item>
        <item>日数計算</item>
        <item>現年齢</item>
        <item>時給給与計算</item>
        <item>子供の肥満</item>
        <item>消費税</item>
        <item>時間計算</item>
        <item>適正・美容・モデル体重</item>
        <item>底辺と高さから角度と斜辺を計算</item>
        <item>子供の身長予測</item>
    </string-array>
</resources>
activity_main.xml
<ListView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:entries="@array/calc_menu"/>

で、一番最初の画面ができた。とりあえずBMIということでそっちのActivityの画面を作る。

ま、てきとーに。

リストをクリックしたらBMIに飛ぶようにする

ListItemClickListener
private class ListItemClickListener implements AdapterView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        Intent intent;

        switch (position) {
            case (0):
                intent = new Intent(LifeCalcActivity.this, BMIActivity.class);
                startActivity(intent);
                break;

            case (1):

                break;
        }
    }
}

うーんと、AdapterViewがListView、ViewがListViewのView(この場合TextViewが取れてる。string_arrayからだからかな?)、positionが行番号、idが…なんのidだろこれ。ま、いいや。とりあえずpositionでswitchしてみた。

onCreate内
ListView listView = findViewById(R.id.calc_menu);
listView.setOnItemClickListener(new ListItemClickListener());

んで、リスナの設定。

BMI計算

画面の処理色々
android:focusable="true"
android:focusableInTouchMode="true"

最初画面作った際、身長欄にフォーカスが当たってたので、TextViewにフォーカスを設定してEditTextのフォーカスを外す。

android:hint="@string/body_height"

入力説明用にTextを入力しといたら、フォーカスしても残ってるのでなんじゃこりゃ、と思ったらandroid:hintに設定するんだったみたい。

android:inputType="numberDecimal"

キーボードが文字入力で出てくるので、数字入力にする。これで入力制限もできるみたい。

入力終了時にフォーカスが当たりっぱなのが気に食わないけど、改善方法が分からんのでほっとく。

計算する

電卓作ったときに見なかったことにしたBigDesimalを使ってみる。

EditText etHeight;
EditText etWeight;
TextView tvResult;
public void onClickCalc(View v) {
    String result;

    etHeight = findViewById(R.id.editTextHeight);
    etWeight = findViewById(R.id.editTextWeight);
    tvResult = findViewById(R.id.result_textView);

    BigDecimal valueHeight = new BigDecimal(etHeight.getText().toString());
    BigDecimal valueWeight = new BigDecimal(etWeight.getText().toString());

    valueHeight = valueHeight.scaleByPowerOfTen(-2).pow(2);
    result = valueWeight.divide(valueHeight, 2, RoundingMode.HALF_UP).toPlainString();

    tvResult.setText("あなたのBMIは…" + "\n" + result);

}

こんな。若干めんどくせぇな。

なかなかに普通だ。そしたら今度は計算結果をDBに保存してくようにすればいいね。ようやくだな、記事分けよっと。

参考

[affi id=2]

https://keisan.casio.jp/keisan/ranking100.php

https://knowledge.moshimore.jp/entry/android_application_edittext_focus

https://akira-watson.com/android/edittext.html

https://akira-watson.com/android/edittext_input_display.html

https://monoworks.co.jp/post/android_develop_memo_2015111601/

https://developer.android.com/reference/android/widget/TextView?hl=ja#attr_android:inputType

https://qiita.com/ota-meshi/items/967304d406d668febe1d

http://androidlab.blog119.fc2.com/blog-entry-7.html

カテゴリー: したい | タグ: , , , | コメントする

AndroidStudioにプラグイン・拡張機能を入れた

AndroidStudioのPlugin

QiitaのAdventcalenderを見ていたら、プラグインというものがあることを知った。色々便利になるみたい。ただ正直、Pluginで出来たところでそれがどんなに便利かを理解できないレベルなので、分かる範囲で便利だな、と思ったやつを入れた。

CodeGlance

右側にコードの縮小図を出してくれる。ながーくなったコードを追おうとすると、いちいちマウスホイールをくるくるしてたけど、これのおかげである程度はスパッと移動できるようになった。

参考

https://plugins.jetbrains.com/plugin/7275-codeglance

ADB WiFi Connect

Wifi経由でADB接続ができるようになる。すごい。いつもデバッグの度にケーブル抜き差ししていたのでコネクタ部の耐久性が心配だった。これでもう大丈夫だね。

参考

https://plugins.jetbrains.com/plugin/9717-adb-wifi-connect

Save Actions

ついこの間、ctrl+alt+Lでコードを整形してくれることを知ったのだけど、それをなんと自動でやってくれる、らしい。実は保存を意識的にやったことがなく、なんか知らんけどいつも閉じたとこから再開できてたからやってなかった。あ、VCSに保存はしてたけど。だとしてもその時に整形やってくれるのかな。あとなんか変ななったら怖いので、import削除とformatだけにしてる。他のは分かったらチェックいれよ。

参考

https://plugins.jetbrains.com/plugin/7642-save-actions

Nyan Progress Bar

かわいい~。

参考

https://plugins.jetbrains.com/plugin/8575-nyan-progress-bar

参考

https://qiita.com/Slowhand0309/items/da7cc2d577c47a8bde1a

https://github.com/balsikandar/Android-Studio-Plugins

カテゴリー: のーと | タグ: , , | コメントする