You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter安卓应用中获取GPS/GLNSS等卫星数据?

Flutter安卓获取GNSS卫星数据(SNR、信号强度)及实现星座图方案

获取卫星原始数据(SNR、信号强度)

Flutter官方没有直接提供GNSS卫星底层数据的API,可通过以下两种方式实现:

1. 使用第三方GNSS插件

推荐使用flutter_gnss插件,它封装了安卓原生的GNSS状态监听能力,可直接获取卫星的SNR、信号强度、星座类型、方位角、仰角等数据。

步骤:

  • 添加依赖到pubspec.yaml:
    dependencies:
      flutter_gnss: ^latest_version
    
  • 安卓Manifest添加权限:
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <!-- Android 10+ 后台监听需添加 -->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    
  • 监听卫星数据:
    import 'package:flutter_gnss/flutter_gnss.dart';
    
    void initGnssListener() {
      FlutterGnss().onGnssStatusChanged.listen((status) {
        for (var sat in status.satellites) {
          // 提取所需数据
          final svid = sat.svid;
          final constellationType = sat.constellationType;
          final snr = sat.snr; // 信噪比,单位dBHz
          final signalStrength = sat.signalStrength;
          final azimuth = sat.azimuth;
          final elevation = sat.elevation;
          
          // 收集数据用于绘制星座图
          // ...
        }
      });
    }
    

2. 自定义平台通道调用安卓原生API

如果第三方插件无法满足定制需求,可直接通过平台通道调用安卓的GnssStatus.Callback:

安卓原生端(Kotlin):

class MainActivity : FlutterActivity() {
    private val CHANNEL = "your.package.name/gnss"
    private var gnssCallback: GnssStatus.Callback? = null
    private var eventSink: EventChannel.EventSink? = null

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        EventChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setStreamHandler(
            object : EventChannel.StreamHandler {
                override fun onListen(arguments: Any?, sink: EventChannel.EventSink?) {
                    eventSink = sink
                    startGnssMonitoring()
                }

                override fun onCancel(arguments: Any?) {
                    stopGnssMonitoring()
                    eventSink = null
                }
            }
        )
    }

    private fun startGnssMonitoring() {
        val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
        gnssCallback = object : GnssStatus.Callback() {
            override fun onSatelliteStatusChanged(status: GnssStatus) {
                val satellites = mutableListOf<Map<String, Any>>()
                for (i in 0 until status.satelliteCount) {
                    satellites.add(
                        mapOf(
                            "svid" to status.getSvid(i),
                            "constellation" to status.getConstellationType(i),
                            "snr" to status.getCn0DbHz(i),
                            "azimuth" to status.getAzimuthDegrees(i),
                            "elevation" to status.getElevationDegrees(i)
                        )
                    )
                }
                eventSink?.success(satellites)
            }
        }
        locationManager.registerGnssStatusCallback(gnssCallback)
    }

    private fun stopGnssMonitoring() {
        val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
        gnssCallback?.let { locationManager.unregisterGnssStatusCallback(it) }
    }
}

Flutter端监听:

import 'package:flutter/services.dart';

Stream<List<dynamic>> getSatelliteStream() {
  const channel = EventChannel('your.package.name/gnss');
  return channel.receiveBroadcastStream().map((event) => event as List<dynamic>);
}

// 使用示例
void initStream() {
  getSatelliteStream().listen((satellites) {
    for (var sat in satellites) {
      // 解析卫星数据
      final snr = sat['snr'] as double;
      // ...
    }
  });
}

实现GPS Test风格的星座图

星座图核心是将卫星的方位角和仰角映射到圆形画布上:

  • 仰角90°对应画布中心(天顶),0°对应画布边缘(地平线)
  • 方位角0°为正北,顺时针递增,需转换为Flutter画布的坐标系统(右为0,逆时针)

自定义绘制实现

使用CustomPainter绘制星座图:

import 'dart:math';
import 'package:flutter/material.dart';

class ConstellationPainter extends CustomPainter {
  final List<Satellite> satellites;
  final double radius;

  ConstellationPainter({required this.satellites, required this.radius});

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    
    // 绘制地平线和辅助圆
    final outlinePaint = Paint()
      ..color = Colors.grey.shade400
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;
    canvas.drawCircle(center, radius, outlinePaint);
    canvas.drawCircle(center, radius * sin(45 * pi / 180), outlinePaint..color = Colors.grey.shade200);

    // 绘制卫星点
    for (var sat in satellites) {
      // 转换方位角为画布坐标角度
      final azimuthRad = (90 - sat.azimuth) * pi / 180;
      // 计算卫星到中心的距离(仰角越大越靠近中心)
      final distance = radius * cos(sat.elevation * pi / 180);
      final satOffset = Offset(
        center.dx + distance * cos(azimuthRad),
        center.dy + distance * sin(azimuthRad),
      );

      // 按星座设置颜色
      final color = _getConstellationColor(sat.constellation);
      // 按SNR设置点大小和透明度
      final pointSize = 4 + (sat.snr / 60) * 8;
      final alpha = (sat.snr / 60) * 255;

      final satPaint = Paint()
        ..color = color.withAlpha(alpha.toInt())
        ..style = PaintingStyle.fill;
      canvas.drawCircle(satOffset, pointSize, satPaint);

      // 绘制卫星ID
      final textPainter = TextPainter(
        text: TextSpan(text: sat.svid.toString(), style: TextStyle(fontSize: 10, color: Colors.black87)),
        textDirection: TextDirection.ltr,
      );
      textPainter.layout();
      textPainter.paint(canvas, Offset(satOffset.dx - textPainter.width/2, satOffset.dy - textPainter.height/2));
    }
  }

  Color _getConstellationColor(int constellationType) {
    // 对应安卓ConstellationType枚举值
    switch (constellationType) {
      case 1: return Colors.blue; // GPS
      case 3: return Colors.red; // GLONASS
      case 4: return Colors.green; // Galileo
      case 5: return Colors.orange; // Beidou
      default: return Colors.grey;
    }
  }

  @override
  bool shouldRepaint(ConstellationPainter oldDelegate) {
    return oldDelegate.satellites != satellites;
  }
}

// 卫星数据模型
class Satellite {
  final int svid;
  final int constellation;
  final double snr;
  final double azimuth;
  final double elevation;

  Satellite({
    required this.svid,
    required this.constellation,
    required this.snr,
    required this.azimuth,
    required this.elevation,
  });
}

集成到Widget

class ConstellationMap extends StatefulWidget {
  const ConstellationMap({super.key});

  @override
  State<ConstellationMap> createState() => _ConstellationMapState();
}

class _ConstellationMapState extends State<ConstellationMap> {
  List<Satellite> _satellites = [];

  @override
  void initState() {
    super.initState();
    // 监听卫星数据并更新状态
    getSatelliteStream().listen((satDataList) {
      setState(() {
        _satellites = satDataList.map((data) => Satellite(
          svid: data['svid'] as int,
          constellation: data['constellation'] as int,
          snr: data['snr'] as double,
          azimuth: data['azimuth'] as double,
          elevation: data['elevation'] as double,
        )).toList();
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    final radius = min(size.width, size.height) / 2 - 20;
    return CustomPaint(
      painter: ConstellationPainter(satellites: _satellites, radius: radius),
      size: Size(size.width, size.height),
    );
  }
}

关键注意事项

  • 权限:必须在运行时申请ACCESS_FINE_LOCATION权限,Android 10+后台监听需额外申请ACCESS_BACKGROUND_LOCATION。
  • 数据更新:GNSS数据更新频率约1Hz,避免过于频繁的UI重绘。
  • 星座映射:安卓ConstellationType枚举值对应不同卫星系统,需根据实际值调整颜色映射。

内容的提问来源于stack exchange,提问作者user18107302

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 16:15:54