Skip to content

Commit 9a02915

Browse files
committed
Implemented noisefloor graph
1 parent f31bfaf commit 9a02915

File tree

11 files changed

+1486
-5
lines changed

11 files changed

+1486
-5
lines changed

android/local.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
sdk.dir=/opt/homebrew/share/android-commandlinetools
22
flutter.sdk=/opt/homebrew/share/flutter
3-
flutter.buildMode=release
3+
flutter.buildMode=debug
44
flutter.versionName=1.0.0
5-
flutter.versionCode=1769992189
5+
flutter.versionCode=1

lib/main.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:hive_flutter/hive_flutter.dart';
77
import 'package:permission_handler/permission_handler.dart';
88
import 'package:provider/provider.dart';
99

10+
import 'models/noise_floor_session.dart';
1011
import 'providers/app_state_provider.dart';
1112
import 'screens/main_scaffold.dart';
1213
import 'services/background_service.dart';
@@ -34,6 +35,21 @@ void main() async {
3435
await Hive.initFlutter();
3536
debugLog('[APP] Hive initialized');
3637

38+
// Register noise floor session adapters before opening any boxes
39+
if (!Hive.isAdapterRegistered(10)) {
40+
Hive.registerAdapter(NoiseFloorSampleAdapter());
41+
}
42+
if (!Hive.isAdapterRegistered(11)) {
43+
Hive.registerAdapter(PingEventTypeAdapter());
44+
}
45+
if (!Hive.isAdapterRegistered(12)) {
46+
Hive.registerAdapter(PingEventMarkerAdapter());
47+
}
48+
if (!Hive.isAdapterRegistered(13)) {
49+
Hive.registerAdapter(NoiseFloorSessionAdapter());
50+
}
51+
debugLog('[APP] Noise floor session adapters registered');
52+
3753
// Request permissions on startup for mobile platforms
3854
if (!kIsWeb) {
3955
await _requestPermissions();
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:hive/hive.dart';
3+
4+
part 'noise_floor_session.g.dart';
5+
6+
/// A single noise floor sample recorded at a point in time
7+
@HiveType(typeId: 10)
8+
class NoiseFloorSample extends HiveObject {
9+
@HiveField(0)
10+
final DateTime timestamp;
11+
12+
@HiveField(1)
13+
final int noiseFloor; // dBm
14+
15+
NoiseFloorSample({
16+
required this.timestamp,
17+
required this.noiseFloor,
18+
});
19+
}
20+
21+
/// Type of ping event for graph markers
22+
@HiveType(typeId: 11)
23+
enum PingEventType {
24+
@HiveField(0)
25+
txSuccess, // Green: TX heard by repeater
26+
27+
@HiveField(1)
28+
txFail, // Red: TX not heard
29+
30+
@HiveField(2)
31+
rx, // Blue: Passive RX received
32+
33+
@HiveField(3)
34+
discSuccess, // Purple: Discovery got response
35+
36+
@HiveField(4)
37+
discFail, // Grey: Discovery no response
38+
}
39+
40+
/// A ping event marker with timestamp, type, and noise floor at time of event
41+
@HiveType(typeId: 12)
42+
class PingEventMarker extends HiveObject {
43+
@HiveField(0)
44+
final DateTime timestamp;
45+
46+
@HiveField(1)
47+
final PingEventType type;
48+
49+
@HiveField(2)
50+
final int noiseFloor; // dBm at time of event
51+
52+
PingEventMarker({
53+
required this.timestamp,
54+
required this.type,
55+
required this.noiseFloor,
56+
});
57+
58+
/// Get the color for this event type
59+
Color get color => switch (type) {
60+
PingEventType.txSuccess => Colors.green,
61+
PingEventType.txFail => Colors.red,
62+
PingEventType.rx => Colors.blue,
63+
PingEventType.discSuccess => Colors.purple,
64+
PingEventType.discFail => Colors.grey,
65+
};
66+
67+
/// Get a display label for this event type
68+
String get label => switch (type) {
69+
PingEventType.txSuccess => 'TX Success',
70+
PingEventType.txFail => 'TX Fail',
71+
PingEventType.rx => 'RX',
72+
PingEventType.discSuccess => 'DISC Success',
73+
PingEventType.discFail => 'DISC Fail',
74+
};
75+
}
76+
77+
/// A recording session for noise floor and ping events
78+
/// A session starts when the user enables Active or Passive Mode,
79+
/// and ends when they disable it (or disconnect)
80+
@HiveType(typeId: 13)
81+
class NoiseFloorSession extends HiveObject {
82+
@HiveField(0)
83+
final String id; // UUID
84+
85+
@HiveField(1)
86+
final DateTime startTime;
87+
88+
@HiveField(2)
89+
DateTime? endTime;
90+
91+
@HiveField(3)
92+
final String mode; // 'active' or 'passive'
93+
94+
@HiveField(4)
95+
final List<NoiseFloorSample> samples;
96+
97+
@HiveField(5)
98+
final List<PingEventMarker> markers;
99+
100+
NoiseFloorSession({
101+
required this.id,
102+
required this.startTime,
103+
this.endTime,
104+
required this.mode,
105+
List<NoiseFloorSample>? samples,
106+
List<PingEventMarker>? markers,
107+
}) : samples = samples ?? [],
108+
markers = markers ?? [];
109+
110+
/// Duration of the session
111+
Duration get duration => (endTime ?? DateTime.now()).difference(startTime);
112+
113+
/// Whether the session is currently active (recording)
114+
bool get isActive => endTime == null;
115+
116+
/// Display name for the mode
117+
String get modeDisplay => mode == 'active' ? 'Active Mode' : 'Passive Mode';
118+
119+
/// Formatted duration string (M:SS or H:MM:SS for long sessions)
120+
String get durationDisplay {
121+
final d = duration;
122+
if (d.inHours > 0) {
123+
return '${d.inHours}:${(d.inMinutes % 60).toString().padLeft(2, '0')}:${(d.inSeconds % 60).toString().padLeft(2, '0')}';
124+
}
125+
return '${d.inMinutes}:${(d.inSeconds % 60).toString().padLeft(2, '0')}';
126+
}
127+
128+
/// Get min and max noise floor values for chart scaling
129+
({int min, int max}) get noiseFloorRange {
130+
if (samples.isEmpty) {
131+
return (min: -120, max: -60);
132+
}
133+
int minVal = samples.first.noiseFloor;
134+
int maxVal = samples.first.noiseFloor;
135+
for (final sample in samples) {
136+
if (sample.noiseFloor < minVal) minVal = sample.noiseFloor;
137+
if (sample.noiseFloor > maxVal) maxVal = sample.noiseFloor;
138+
}
139+
// Add some padding
140+
return (min: minVal - 5, max: maxVal + 5);
141+
}
142+
}

lib/models/noise_floor_session.g.dart

Lines changed: 187 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)