gnss_rtk 1.0.0 copy "gnss_rtk: ^1.0.0" to clipboard
gnss_rtk: ^1.0.0 copied to clipboard

PlatformAndroid

Production-ready GNSS RTK positioning library for industrial applications with NMEA parsing and multi-provider support

GNSS RTK - Production-Ready GNSS Positioning Library #

pub package License

A production-ready, industrial-grade Flutter library for high-precision GNSS/RTK positioning. Designed for professional GIS and surveying applications that require centimeter-level accuracy.

Features #

Multi-Provider Architecture - TCP, UDP, Broadcast, File, Mock
Complete NMEA 0183 Support - GGA, RMC, GSA, GSV, VTG, GST, ZDA, GLL
RTK/DGPS Support - RTK Fixed, RTK Float, DGPS fix quality detection
Multi-Constellation - GPS, GLONASS, Galileo, BeiDou, QZSS
Health Monitoring - Real-time diagnostics and issue detection
Stream-Based - Reactive programming with RxDart
Memory Safe - No memory leaks, proper resource management
Background Support - Foreground service for Android
Production Ready - Tested, documented, and optimized

Installation #

Add to your pubspec.yaml:

dependencies:
  gnss_rtk: ^1.0.0

Quick Start #

Basic Usage #

import 'package:gnss_rtk/gnss_rtk.dart';

// Create a mock provider for testing
final provider = MockGnssProvider(
  const MockGnssProviderConfig(
    mockScenario: MockScenario.stationary,
  ),
);

// Create GNSS service
final gnssService = GnssService(
  provider: provider,
  enableHealthMonitoring: true,
);

// Listen to position updates
gnssService.positionStream.listen((fix) {
  print('Position: ${fix.latitude}, ${fix.longitude}');
  print('Accuracy: ${fix.estimatedAccuracy}m');
  print('Fix Quality: ${fix.fixQuality.description}');
});

// Start the service
await gnssService.start();

// Wait for RTK fixed quality
final fix = await gnssService.waitForValidFix(
  minimumQuality: FixQuality.rtkFixed,
  timeout: Duration(seconds: 60),
);

TCP Provider (Most Common for Industrial GNSS) #

final provider = TcpGnssProvider(
  TcpGnssProviderConfig(
    host: '192.168.1.100',
    port: 2947,
    reconnectAttempts: 5,
  ),
);

final gnssService = GnssService(provider: provider);
await gnssService.start();

UDP Provider #

final provider = UdpGnssProvider(
  UdpGnssProviderConfig(
    port: 5000,
    bindAddress: '0.0.0.0',
  ),
);

Android Broadcast Provider #

final provider = BroadcastGnssProvider(
  BroadcastGnssProviderConfig(
    intentAction: 'com.gnss.tool.NMEA_DATA',
    dataKey: 'nmea_sentence',
  ),
);

File Tail Provider #

final provider = FileTailProvider(
  FileTailProviderConfig(
    filePath: '/sdcard/gnss_data.log',
    pollInterval: Duration(milliseconds: 100),
  ),
);

Architecture #

Clean Architecture Layers #

┌─────────────────────────────────────┐
│         GnssService (API)           │
├─────────────────────────────────────┤
│       GnssHealthMonitor             │
├─────────────────────────────────────┤
│      GnssRepository (Logic)         │
├─────────────────────────────────────┤
│        NmeaParser (Parser)          │
├─────────────────────────────────────┤
│  GnssProvider (Data Source)         │
│  ├─ TcpGnssProvider                 │
│  ├─ UdpGnssProvider                 │
│  ├─ BroadcastGnssProvider           │
│  ├─ FileTailProvider                │
│  └─ MockGnssProvider                │
└─────────────────────────────────────┘

Why This Architecture? #

  1. Separation of Concerns: Each layer has a single responsibility
  2. Testability: Easy to mock and test individual components
  3. Flexibility: Swap providers without changing business logic
  4. Maintainability: Clear boundaries make code easy to understand
  5. Scalability: Add new features without breaking existing code

Advanced Usage #

Health Monitoring #

// Enable health monitoring
final gnssService = GnssService(
  provider: provider,
  enableHealthMonitoring: true,
  healthCheckInterval: Duration(seconds: 5),
);

// Get diagnostics
final diagnostics = gnssService.getDiagnostics();
print('Overall Health: ${diagnostics.overallHealth}');
print('Signal Strength: ${diagnostics.signalStrength}%');
print('Fix Stability: ${diagnostics.fixStabilityScore}%');

// Check for issues
for (final issue in diagnostics.criticalIssues) {
  print('CRITICAL: ${issue.message}');
  print('Recommendation: ${issue.recommendation}');
}

Satellite Information #

gnssService.satellitesStream.listen((satellites) {
  print('Visible satellites: ${satellites.length}');
  
  for (final sat in satellites) {
    print('${sat.constellation.name}-${sat.prn}: SNR ${sat.snr} dB');
  }
  
  // Get satellites by constellation
  final gpsSats = satellites.where(
    (s) => s.constellation == ConstellationType.gps
  );
});

Performance Metrics #

final metrics = gnssService.getMetrics();
print('Data Rate: ${metrics.dataRate.toStringAsFixed(2)} pps');
print('Success Rate: ${(metrics.successRate * 100).toStringAsFixed(1)}%');
print('Uptime: ${metrics.uptimeString}');
print('Total Packets: ${metrics.totalPackets}');
print('Valid Packets: ${metrics.validPackets}');

Android Integration #

Permissions #

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Foreground Service (for Background Tracking) #

// Start foreground service via method channel
await MethodChannel('com.gnss.rtk/method').invokeMethod(
  'startForegroundService',
  {
    'title': 'GNSS Tracking',
    'message': 'High-precision positioning active',
  },
);

Broadcast Receiver Setup #

Add to AndroidManifest.xml:

<receiver
    android:name="com.gnss.rtk.GnssBroadcastReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.your.gnss.tool.NMEA_DATA" />
    </intent-filter>
</receiver>

NMEA Sentences Supported #

Sentence Description Parsed Fields
GGA Fix data Lat, Lon, Alt, Fix Quality, Sats, HDOP, Age of Correction
RMC Recommended minimum Lat, Lon, Speed, Heading, Date/Time, Magnetic Variation
GSA DOP and active sats Fix Mode, PDOP, HDOP, VDOP, Satellite PRNs
GSV Satellites in view Satellite PRN, Elevation, Azimuth, SNR
VTG Track and speed True/Magnetic heading, Speed (knots/km/h)
GST Position error Horizontal/Vertical accuracy, Error ellipse
ZDA Date and time UTC Date/Time with milliseconds
GLL Geographic position Lat, Lon, UTC Time, Status

Field Use Cases & Edge Cases #

1. RTK Connection Loss #

Problem: Base station connection drops, RTK fix degrades to Float/DGPS
Solution: Health monitor detects quality degradation and triggers alerts

gnssService.stateStream.listen((state) {
  if (state.currentFix?.fixQuality == FixQuality.rtkFloat) {
    showWarning('RTK fix degraded to Float');
  }
});

2. Tunnel / Indoor Scenarios #

Problem: Complete signal loss in tunnels or buildings
Solution: Library maintains last valid fix and tracks time since last update

if (state.isFixStale) {
  print('No update for ${state.timeSinceLastValidFix?.inSeconds}s');
  useLastValidPosition(state.lastValidFix);
}

3. Mock Location Detection #

Problem: Developer options enable fake GPS, corrupting data
Solution: Built-in mock location detection

if (diagnostics.mockLocationDetected) {
  showError('Mock location is enabled - disable in settings');
}

4. GNSS Drift #

Problem: Position slowly drifts even when stationary
Solution: Monitor fix stability score

if (diagnostics.fixStabilityScore < 80) {
  print('Position unstable - check for multipath interference');
}

5. Battery Optimization Issues #

Problem: Android kills background service to save battery
Solution: Use foreground service (automatically handled)

// Foreground service prevents Android from killing the app
// Already implemented in GnssForegroundService.kt

6. High Baseline Distance #

Problem: Too far from RTK base station (>50km) prevents RTK fix
Solution: Monitor age of correction and baseline

if (fix.ageOfCorrection != null && fix.ageOfCorrection! > 10) {
  print('RTK corrections too old: ${fix.ageOfCorrection}s');
}

7. Multipath Interference #

Problem: Signals reflect off buildings, causing position errors
Solution: Check DOP values and satellite geometry

if (fix.hdop != null && fix.hdop! > 5.0) {
  showWarning('Poor satellite geometry (HDOP: ${fix.hdop})');
}

8. Industrial Tablet Issues #

Common Problems:

  • Manufacturer modifications to Android
  • Custom GNSS apps with proprietary protocols
  • Non-standard broadcast intents
  • Serial port access restrictions

Solution: Multiple provider types support various integration methods

// Try TCP first (most reliable)
var provider = TcpGnssProvider(...);

// Fall back to Broadcast if TCP fails
if (!provider.isConnected) {
  provider = BroadcastGnssProvider(...);
}

// Last resort: File tail
if (!provider.isConnected) {
  provider = FileTailProvider(...);
}

Performance Optimizations #

1. Backpressure Management #

Prevents memory overflow from high-frequency data:

// Built-in throttling (default 100ms)
final repository = GnssRepository(
  provider: provider,
  throttleDuration: Duration(milliseconds: 100),
  bufferSize: 100,
);

2. Duplicate Filtering #

Automatically filters duplicate NMEA sentences:

// Checksum validation
// Duplicate detection
// Invalid packet filtering
// All built-in to NmeaParser

3. Stream Optimization #

// Use distinct() to prevent unnecessary rebuilds
positionStream
  .distinct((prev, next) => 
    prev.latitude == next.latitude &&
    prev.longitude == next.longitude
  )
  .listen((fix) => updateUI(fix));

4. Memory Management #

// Always dispose when done
@override
void dispose() {
  gnssService?.dispose();
  super.dispose();
}

Testing #

Run unit tests:

flutter test

Run with coverage:

flutter test --coverage

Example App #

See example/lib/main.dart for a complete working example with:

  • Real-time position display
  • Satellite visualization
  • Health monitoring dashboard
  • Diagnostics viewer
  • Start/stop controls

Run example:

cd example
flutter run

Troubleshooting #

No Data Received #

  1. Check provider configuration (IP, port, intent action)
  2. Verify network connectivity
  3. Check Android permissions
  4. Enable verbose logging
import 'package:logging/logging.dart';

Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
  print('${record.level.name}: ${record.time}: ${record.message}');
});

Low Accuracy #

  1. Ensure clear sky view
  2. Check satellite count (need 8+ for good accuracy)
  3. Verify RTK corrections are being received
  4. Check HDOP value (should be <2.0)

Memory Leaks #

  1. Always call dispose() on services
  2. Cancel stream subscriptions
  3. Use StreamSubscription.cancel()

License #

MIT License - see LICENSE file

Contributing #

Contributions welcome! Please read CONTRIBUTING.md

Support #

Credits #

Developed for industrial GIS and surveying applications requiring professional-grade GNSS positioning.

0
likes
150
points
33
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Production-ready GNSS RTK positioning library for industrial applications with NMEA parsing and multi-provider support

Homepage
Repository
View/report issues

Topics

#gnss #rtk #nmea #gps #geospatial

License

MIT (license)

Dependencies

collection, flutter, logging, meta, rxdart

More

Packages that depend on gnss_rtk

Packages that implement gnss_rtk