tmytのらくがき

個人の日記レベルです

NEO-M8UでUDRしてみたい!

UDR(Untethered Dead Reckoning)というのがあります。GNSSが受信できない状態で自車位置を推定する技術のうち、車両信号を利用しないものをこう呼ぶそうです。

u-bloxというスイスのメーカーからリリースされているNEO-M8シリーズのうち、NEO-M8UというモジュールがこのUDRをサポートしています。

NEO-M8Uは、3軸ジャイロと3軸加速度センサを内蔵していてセンサを通して車両の挙動を推定してくれます。なんかしらの計算結果を位置情報に反映してNMEA形式で推定結果を出力してくれるのでとても賢くて使いやすいんじゃないかと思ってこれを使います。

改造する

さっそくNEO-M8Uを使えるようにしたいと思います。まず、M5StackのGPSモジュールを用意します。このGPSモジュールはNEO-M8Nというモジュールが使用されています。似たような名前だけどこっちはUDRできないやつ。なんでM5StackのGPSモジュールを用意したかというと

  • M5Stackが使いたかった
  • M8Uの製品情報に "Compatible with all modules of the NEO family" と書いてあるのでNEOシリーズが搭載されているやつを用意した

という理由です。先に上げた通り、M8UはNEOシリーズの全モジュールと完全な互換性があるそうです。つまり、剥がして貼りなおせば使えるはず。

改造に必要なGPSモジュールとM8UはDigi-keyで用意しました。3日くらいで届いたのでDigi-keyさまさま。

さて改造といっても、剥がして

f:id:tmyt:20200202055408p:plain

貼るだけ

f:id:tmyt:20200202055436p:plain

剥がしスキルが低すぎて、PPSのランドが剥がれてしまったのはそのうち適当に配線しよう…

UDR使えるか試す

散々M5Stackがどうこう言っていたのにGPS部分しか持っていないのでまずはUARTをPCへ接続してu-centerで様子を見ます。Navigation Expert 5を確認すると最初からADR*1/UDRが有効になってました。

f:id:tmyt:20200202052710p:plain

次に自動車へ設置。センサーで自動車の挙動を推定するので車両にしっかり固定しろよな!ということなのでとりあえずテープで止めました。

f:id:tmyt:20200202055546p:plain

このままGNSS信号が受信できている状態でしばらく走っていると、センサーのキャリブレーションが終わってUDRが使えるようになるはず…とドキュメントに書いていました。 3D+DGNSSの状態で15分くらい車を走らせていると、キャリブレーションが完了したようです。

f:id:tmyt:20200202055612p:plain

あとはこのままGNSS信号が受信できない場所へ行けばUDRの動作が確認できるはず!!!ということで近場のトンネルへ出かけてみたところ、UDRの動作が確認できました。

f:id:tmyt:20200202055731p:plain

出口付近は推定結果がちょっと怪しめ。距離が長くなると誤差が累積してしまうので仕方ないといえば仕方ない。固定場所とかの条件にもよるのかもしれない。

f:id:tmyt:20200202055753p:plain

ちなみに、UDR中のNMEAメッセージを見ると

$GNGLL,3438.68839,N,13525.04534,E,181743.00,A,E*7D

ちゃんとModeがEになっているのでちゃんと測位情報がDRで計算されてます。

まとめ

  • UDRができました
  • M8Uは仕組み上車両に固定しないといけないからマイコンで遊ぶ用にはなかなかリリースしてもらえなくて寂しい
  • キャリブレーションが割と時間かかるのが難点
  • UDRの性能は割と悪くないと思うけど長いトンネルとかはちょっと苦しそう

*1:車両と電気的接続をするやつ。GNSS以外を使用して自車位置推定をする機能ってことでひとまとめになってる

Uno PlatformでmacOSアプリを作れるかな?

GitHubのissue*1を眺めていると、どうやらNuGetにXamarin Mac対応のバイナリが放流されているらしい?

NuGetのバイナリを手に入れて、展開してみると確かにxamarinmac20向けのバイナリが含まれているみたい。

$ unzip -l 2.0.512-dev.4178.nupkg | grep xamarinmac20
     4039  2019-11-27 03:05   build/xamarinmac20/uno.ui.targets
     4927  2019-11-27 03:11   build/xamarinmac20/Uno.UI.Tasks.targets
  6022624  2019-11-27 03:47   lib/xamarinmac20/Uno.dll
    84960  2019-11-27 03:47   lib/xamarinmac20/Uno.Foundation.dll
    24096  2019-11-27 03:18   lib/xamarinmac20/Uno.Foundation.pdb
   937116  2019-11-27 03:19   lib/xamarinmac20/Uno.pdb
  4546016  2019-11-27 03:47   lib/xamarinmac20/Uno.UI.dll
  1340020  2019-11-27 03:20   lib/xamarinmac20/Uno.UI.pdb
   196576  2019-11-27 03:47   lib/xamarinmac20/Uno.Xaml.dll
   712192  2019-11-27 03:18   lib/xamarinmac20/Uno.Xaml.pdb

macOSではまだHello Worldが動く程度らしいけど、一応ビルドはできるらしい。

ということはcsprojをいい感じに整えてあげれば好きなようにアプリがつくれる?気がしたのでとりあえずプロジェクトテンプレートにmacOSなプロジェクトを追加してみました。

github.com

これを使うとmacOS向けのプロジェクトがソリューションに増えます。

f:id:tmyt:20200130044454p:plain

これをmacでビルドするとiOSと似たような結果が得られて一安心。

f:id:tmyt:20200130044541p:plain

ただ、macOS向けのUno Platformは絶賛開発中なので2020年1月30日現時点で

  • TextBoxが未実装
  • Button, CheckBox, RadioButton は押しても反応しない
  • Selector系コントロールは例外が出る

という感じなのでパッチを書きつつ応援していきましょうね!

G8X ThinQ向け、別画面でブラウザを開くアプリをリリースしました

ダウンロード

今回は最初からPlayストアに公開しました。

play.google.com

機能

  1. デフォルトブラウザにしておくと、カバー画面有効時に別画面でURLを開きます
  2. カバー画面が使えない場合*1時はURLのハンドルを自動で無効にできます*2

機能1について

このアプリをデフォルトブラウザに設定しておくと、ほかのアプリ(ex. Twitter) でURLを開くときにこのアプリがブラウザの代わりになります。

ブラウザとして開かれたこのアプリは、即座に現在アクティブでない側の画面でブラウザを起動します。この時使われるブラウザは、アプリの設定にあるものが使われます。設定される前や、設定した後にそのブラウザが削除された場合は初回のみブラウザ選択画面が出ることがあります。

機能2について

カバー画面の有効/無効に合わせて、このアプリのブラウザの代わりになる機能を一時的に有効/無効を自動的に切り替える機能があります。この機能を有効にすると、Chrome Custom Tabsなどでアプリ内ブラウザを実装しているアプリはカバー画面が無効の時はアプリ内ブラウザが起動するようになります。

まとめるとこういう動作になります。

||自動切換え有効|自動切換え無効| |カバー画面有効|別画面でブラウザ起動|別画面でブラウザ起動| |カバー画面無効|アプリ内ブラウザ|同じ画面でブラウザ起動|

この機能はイベントを受け取るためだけのユーザー補助サービスとして実装されているので必要であれば有効にしてください。*3

設定

今回はランチャにアイコンが追加されてその画面から使用するブラウザの設定ができます。あとはデフォルトのブラウザに設定しておけばOKです。

*1:カバーがない、画面がOff、カバーを開ききってる

*2:ユーザー補助サービスを有効にすると使えます

*3:ユーザー補助サービスは筋悪いかな…とおもいつついったんこれでお願いします。

2019年買ってよかったもの

2019年も今日で終わりなので買ってよかったものをまとめておきます。

PC編

Lenovo C630

LenovoのARM64版Windows PC。リモートデスクトップだけで使っていると12~16時間ぐらいバッテリーで使えるのがとてもよい。 ARM64バイナリを実行する分には十分はやいし、x86エミュレーションをしても実用的な速度で動くのでARM64版Windowsは結構使える子ですよ?

国内モデルはOfficeが付いていて14万するのが難点。

Ryzen 9 3900X

ADMの12コア24スレッドCPU。メニーコアは大正義。Xamarin Androidのビルドをしたり、UWPのリリースビルドをしたときに明らかにパフォーマンスに効いてくるのが買ってよかったポイント。 Facebook MessengerのUWP版はXboxコントローラを接続するとCPUを100%使う不具合があるんですけど、24スレッドあると1個つぶされても何も気にならないというのもかなりいい。

最近は供給が増えてきた感じもするけど、手に入れづらいのが難点

カメラ/レンズ

SIGMA Art 135mm/f1.4

これはニコン用。年初に14-24/2.8を買ったので今年はもうレンズを買わないぞ…と心に決めていたのに、諸事情で135mmが必要になったのでこれは今年買った2本目のレンズ。

中心から端まできれいに解像するし、明るくてきれいにボケるのがやっぱりArtなんだなぁ…ははぁ…としょーもない感想を抱きました。 こんな感じのが撮れています。

f:id:tmyt:20191231234718p:plain

Hasselblad X1D II

中版ミラーレスカメラを買いました。中版すごい…さすがに奇麗だし、なに撮っても奇麗に写ってるなぁ…となってしまってよくない。もっとちゃんと写真撮らねば。という気持ちになる。 白飛び、黒飛びしたとおもっても結構ちゃんと階調が残っているのがDfと比べてすごいなぁと思ったところ。

レンズはXCD 90mm/3.2 を使ってます。いわゆる35mm版換算という表現をすると、71mmらしいです。こんな感じのが撮れています。

f:id:tmyt:20191231234753p:plain

小物

Aukey PA-D5

AukeyのPD充電器で、Type-Cが2ポート。ポートAは18W、ポートBは60Wもしくは45Wが出力される。

夏頃に出たAnkerのPowerPort Atom PD 2も2個ぐらいかって使っているのだけど、これは2個接続すると30Wx2になってしまうのでPC/iPadみたいな組み合わせにしたときにPCが充電されないところが難点。 Aukey PA-D5はその点で片ポートは45W確保されるので、どちらもちゃんと充電されるのがかなりおすすめポイント。

ただ、Type-Cの仕様に適合してるかとかは何も確認していないので気になる人は調査情報を待ってからにしたほうがいいと思います。

KOPI Mug

自動温めマグカップ。Qi充電器と共用の保温パッドが付属しているので、パッドに置いておくとマグカップに入れたものがずっとあったかいままになる便利グッズ。ようするに、ミニIHヒーターです。

これはかなり気に入っていて、コーヒーを入れてFF14をだらだらプレイして2時間とか3時間たっても中身はあったかいまま。とても優秀。まだ日本で売ってないかもしれない?ので、購入する場合はアメリカからの輸入になります。

自動車

マツダコネクトのAndroid Auto/Car Play対応レトロフィットキット

マツダ純正のマツコネをAndroid Auto/Car Playに対応させる改造パーツで、マツコネに接続したスマートフォンがナビになるやつ。 マツコネのナビが結構頭悪い*1ので、Google Mapsに置き換えたりができます。

ただ、Andorid Autoはタッチパネルが使えないのでちょっと不便。iPhoneをつなぐとちゃんとタッチパネルも使えるので便利ですよ。

まとめ

もっと家電買ったり、本棚かったりもしたんだけど省略しました。よかったなぁ、というのはこんな感じで2019年を締めたいと思います。2020年もよろしくお願いいたします。

*1:最近割とましになった

シーリングライトを力押しでGoogle Assistantに対応させる

シーリングライトPanasonicのHH-XCB1283Aというモデルを使っているのですが、このモデルはリモコンが赤外線ではなくBluetoothになっているのでスマートフォンから操作できるのでとても便利です。なんですが、Bluetoothゆえいわゆる学習リモコンは全滅だしもちろんGoogle Assistantにも対応していないのでしかたなく力押しでGoogle Assistantに対応させました。

構成

今日は天気もよく、室温が高くて寝ているのか起きているのかわからない境目を漂っているときに、今回の構成を思いつきました。

f:id:tmyt:20191227192942p:plain

いろいろ起きた結果、USBで接続されたAndroidに対してadb shell input touchscreen tap x yでリクエストして、あらかじめ起動しておいたPanasonicの純正アプリを操作するだけ、簡単!天才!

アプリのどこを押すか決める

シーリングライトとの通信を取り持ってくれるAndroidをどうやって操作するか結構悩んだ*1結果、ADB経由でアプリを操作すれば解決やーんとなりました。 このアプリはPlay Storeで★1.9ととても微妙な評価なのですが、見た目はこんな感じです。

どうみてもiPhoneのスクショですが、Androidも見た目は全く同じ見た目です。これは1~6に電灯の状態*2をプリセットできる仕組みで、1がよく使うやつ、6が消灯、2~5が時々使うものです。とりあえず点灯と消灯ができればいいので、1と6を押すようにします。

ADB経由で任意の地点を操作するには、inputコマンドを利用すれば簡単です。たとえば、X:100, Y:100 地点をタップするには

$ adb shell input touchscreen tap 100 100

とか呼べばOKです。では今回どこを押せばいいのかというと、今回のADBを受けてくれるNextbit Robinでは次の通りでした。

状態 X Y
ON 267 439
OFF 804 1162

この値は後で使います。

Google AssistantからのWebHookを受けるサーバを書く

次に、WebHookを受け付けるサーバを書きます。ハンドリングは、actions-to-googleっていうパッケージを使えばいいらしいので使いました。OAuth2しないといけない風だったので仕方なく空実装を作りました。ADBを中継するサーバ(後で出てきます)とはSocket.IOを使っています。

const express = require('express');
const bodyParser = require('body-parser');
const util = require('util');
const { smarthome } = require('actions-on-google');

const expressApp = express();
const app = smarthome();
const server = require('http').createServer(expressApp);
const io = require('socket.io')(server);

app.onExecute((body, headers) => {
  io.sockets.emit('execute', body);
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: [ '1' ],
        status: 'SUCCESS',
        state: { on: true, online: true },
      }],
    },
  };
});
app.onQuery((body, headers) => ({
  requestId: body.requestId,
  payload: {
    devices: {
      '1': { on: true, online: true },
    },
  },
}));
app.onSync((body, headers) => ({
  requestId: body.requestId,
  payload: {
    agentUserId: '1',
    devices: [{
      id: '1',
      type: 'action.devices.types.LIGHT',
      traits: [ 'action.devices.traits.OnOff' ],
      name: {
        defaultNames: ['シーリング ライト'],
        name: 'シーリング ライト',
        nicknames: ['シーリング ライト'],
      },
      willReportState: false,
    }],
  },
}));

const assistantApp = express().use(bodyParser.json());
const oauthApp = express().use(bodyParser.urlencoded({ extended: true }));

assistantApp.post('/fulfillment', app);

oauthApp.get('/authorize', (req, res) => {
  console.log('authorize');
  const uri = `${req.query.redirect_uri}?state=${req.query.state}&code=12345678`;
  res.redirect(uri);
});
oauthApp.post('/token', (req, res) => {
  console.log('token');
  res.send({
    token_type: 'Bearer',
    access_token: '12345678' + Date.now(),
    refresh_token: '12345678',
    expires_in: 86400 * 90,
  });
});

expressApp.use('/assistant', assistantApp);
expressApp.use('/oauth', oauthApp);

io.on('connection', () => { });
server.listen(process.env.PORT);

OAuth2がまったく中身のない実装になっている、うえに、リクエストはトークンの検証していないなどとても雑です。これ、エンドポイントをたたかれると普通に動いてしまうので、皆さんはちゃんと認可を実装しましょうね

ADBを呼び出すサーバを実装する

あとはこのサーバに接続して、ExecuteをSocket.IO経由で受け取り、接続されたAndroidに対してADBでリクエストをするサーバが必要です。これはsocket.io-clientパッケージを使えば一瞬で書けます。

const socket = require('socket.io-client')('https://ないしょだよ!!!!.example.com');
const { exec } = require('child_process');
const util = require('util');

const on = '267 439';
const off = '804 1162';

socket.on('connect', () => {
  console.log('connect');
});
socket.on('execute', data => {
  const input = data.inputs[0];
  for(const execution of input.payload.commands[0].execution){
    if(execution.command === 'action.devices.commands.OnOff'){
      const arg = execution.params.on ? on : off;
      exec(`adb.exe shell input touchscreen tap ${arg}`);
    }
  }
  console.log(util.inspect(data, true, null));
});

child_process.execでADBを呼び出しています。このプロセスは、Windows上のWSLで動くNodeJSから、Windows側のPATH環境変数に含まれているadb.exeを呼び出しているので".exe"まで含んでいます。Linuxなら不要です。

おわり

あとは、Google Assistantから呼んでもらえるようにいろいろを設定すればできあがり。

console.actions.google.com に新しいアクションを作って、WebHookサーバのURLを設定し、ADBを呼び出すサーバを実行し、Socket.IOの接続が確立できたのを確認したのちにGoogle Assistantに呼びかけます。

ねぇGoogle 電気つけて!!!!!!

きっとシーリングがつくはず…

*1:オートメーション的ななにかを使う?とかいろいろ

*2:明るさ、色など

G8X ThinQのワイドモードを操作するアプリをTaskerに対応しました

G8X ThinQのワイドモードを無理やり有効にする例のボタンですが、Taskerに対応してくれないか?と要望をもらったので、Taskerに対応しました。

Taskerは特定のアプリが起動したときをはじめいろいろなタイミングで定型アクションを実行するツールなんですが、外部アプリをプラグインとして実行できるらしく、プラグインとして対応しました。

play.google.com

Taskerのアクションを選択する際に、プラグインを選ぶと"G8X WideMode"*1があるのでそこからこのアプリを呼び出せます。

Taskerに提供している機能は、ワイドモードを「ONにする」、「OFFにする」、「トグルする」の3種類です。プラグインの設定で動作を切り替えることができます。 例えば、「Kindleを起動したときにワイドモードをONにする」というような感じで使うことができます。便利ですね。

Tasker連携はバージョン1.1.0から利用可能です。アップデートしてご利用ください。

play.google.com

*1:ワイドモードを切り替えっていうキャプションにしたはず…

G8X ThinQ向け、ワイドモードボタンを増やすアプリを作ってみました

TL;DR

  • 右側アクティビティを強制的にワイドモードにするボタンをクイック設定パネルに出せるアプリです
  • 運が悪いと描画が崩壊する可能性があります
  • ダウンロードはここから。 Playストアからどうぞ

G8X ThinQのワイドモード不便ですよね

ごく一部というか実質Chromeしかワイドモードが使えなくて不便すぎるので、簡単なアプリを用意しました。 インストールするとクイック設定パネルに強制的にワイドモードに切り替えるボタンを追加できるようになります。

2019/12/13時点でPlayストアに未公開なので、不明なソースからのインストールを許可しないといけないのでリスクを理解している人向けです。

Playストアに公開しましした🎉

使い方

クイック設定パネルの設定を開きます

f:id:tmyt:20191213224747p:plain:w300

Wide Mode というアイコンが増えているので任意の場所に追加して、保存します。

f:id:tmyt:20191213225000p:plain:w300

ワイドモードにしたいアプリを右画面で起動して、クイック設定パネルの Wide Mode を押します。

f:id:tmyt:20191213224852p:plain:w300

ワイドモードになりました。

f:id:tmyt:20191213224900p:plain:w300

注意点

  • 内部APIを直接呼び出しているので時々描画が崩壊するかもしれません。
  • 最悪デバイスを再起動すればすべて元に戻ります。
  • 実績として、右側画面の画面分割と組み合わせたりしていじってたら描画が崩壊したことが1回、その後Chromeを正規手順でワイドモードにしたり戻したりしていたらSystemServerが再起動したことが1回あります。