Android GNSS statusの取得 Kotlin

AndroidからGNSS衛星情報を取り出す

もともとはRTCMの動作確認をするうえでGNSSアンテナとレシーバを自費で購入するわけにもいかず(そこまで興味はない)、android端末で代わりにならないかと考えたことが発端です。
rawデータを取得する前段階として、android端末が受信している衛星情報(svid、方位角、仰角、CNR等)を取得するプログラムを考えます。
自位置を取得するだけならばFusedLocationProviderClientを使用して取得できるので、衛星情報を使う必要性は一般的にありません。

衛星情報取得までの流れ

衛星情報を取得するためにAPI level 24で追加されたGnssStatusを使用します。API Level 24なのでAndroid 7以降のプラットフォームが必要です。 f:id:misatospring:20190808214159j:plain

GnssStatus.Callback

GnssStatus.CallbackはGNSSステータスが変化したときに呼び出されます。
onSatelliteStatusChangedは「定期的」に呼び出されると定義されており、実測では1秒ごとに呼び出されているようです。

private val gnssCallback: GnssStatus.Callback = object:GnssStatus.Callback() {
    override fun onFirstFix(ttffMillis: Int) {
        super.onFirstFix(ttffMillis)
        // 最初のFixまでの時間(ミリ秒)が設定される
    }
    
    override fun onStarted() {
        super.onStarted()
    }

    override fun onSatelliteStatusChanged(status: GnssStatus?) {
        super.onSatelliteStatusChanged(status)
        val satelliteCount: Int = status?.satelliteCount ?: 0
        for(i in 0 until satelliteCount) {
            val constellation: Int? = status?.getConstellationType(i) // 衛星システム
            val svid: Int? = status?.getSvid(i)                           // svid
            val azimuth: Float? = status?.getAzimuthDegrees(i)            // 方位角
            val elevation: Float? = status?.getElevationDegrees(i)        // 仰角
            val cnr: Float? = status?.getCn0DbHz(i)                       // carrier-to-noise
            val usedInFix: Boolean? = status?.usedInFix(i)                // Fixに使用したか
        }
    }

    override fun onStopped() {
        super.onStopped()
    }
}
パーミッションチェックからCallback登録、削除
private val permissionRequestCode: Int = 1000
private lateinit var locationManager: LocationManager
    
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
}

override fun onStart() {
    super.onStart()
    getGnssStatus()
}

override fun onStop() {
    closeGnssStatus()
    super.onStop()
}

// GnssCallbackの設定
private fun getGnssStatus() {
    // 位置情報のパーミッションを取得
    if(ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        // パーミッションが設定されていなければ許可リクエスト
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), permissionRequestCode)
    } else {
        // パーミッションが設定されていればGnssCallbackを登録
        locationManager.registerGnssStatusCallback(gnssCallback)
    }
}

// GnssCallbackの削除
private fun closeGnssStatus() {
    locationManager.unregisterGnssStatusCallback(gnssCallback)
}

// パーミッション許可、不許可時に呼び出される
override fun onRequestPermissionsResult(requestCode: Int,
        permissions: Array<String>, grantResults: IntArray) {
    when(requestCode) {
        permissionRequestCode ->
        if((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
            getGnssStatus()
        } else {
            // パーミッションが許可されなければ終了
            finish()
        }
    }
}