mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-06-14 22:55:12 +10:00
Document LOS frequency and k-factor math
Show the connector frequency right next to the Frequency label, display the derived k value, and keep the info dialog tied to the exact
This commit is contained in:
@@ -14,6 +14,7 @@ import '../services/app_settings_service.dart';
|
|||||||
import '../services/line_of_sight_service.dart';
|
import '../services/line_of_sight_service.dart';
|
||||||
import '../services/map_tile_cache_service.dart';
|
import '../services/map_tile_cache_service.dart';
|
||||||
import '../utils/route_transitions.dart';
|
import '../utils/route_transitions.dart';
|
||||||
|
import '../connector/meshcore_connector.dart';
|
||||||
import '../widgets/app_bar.dart';
|
import '../widgets/app_bar.dart';
|
||||||
import '../widgets/quick_switch_bar.dart';
|
import '../widgets/quick_switch_bar.dart';
|
||||||
|
|
||||||
@@ -110,10 +111,13 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final connector = context.read<MeshCoreConnector>();
|
||||||
|
final frequencyMHz = _normalizeFrequencyMHz(connector.currentFreqHz);
|
||||||
final result = await _lineOfSightService.analyzePath(
|
final result = await _lineOfSightService.analyzePath(
|
||||||
[start.point, end.point],
|
[start.point, end.point],
|
||||||
startAntennaHeightMeters: startAntenna,
|
startAntennaHeightMeters: startAntenna,
|
||||||
endAntennaHeightMeters: endAntenna,
|
endAntennaHeightMeters: endAntenna,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (!_isRunRequestCurrent(
|
if (!_isRunRequestCurrent(
|
||||||
@@ -424,6 +428,12 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
Widget _buildControlPanel(bool isImperial) {
|
Widget _buildControlPanel(bool isImperial) {
|
||||||
_sanitizeSelection();
|
_sanitizeSelection();
|
||||||
final segment = _primarySegmentResult();
|
final segment = _primarySegmentResult();
|
||||||
|
final connector = context.watch<MeshCoreConnector>();
|
||||||
|
final reportedFrequencyMHz = _normalizeFrequencyMHz(
|
||||||
|
connector.currentFreqHz,
|
||||||
|
);
|
||||||
|
final displayFrequencyMHz = segment?.frequencyMHz ?? reportedFrequencyMHz;
|
||||||
|
final kFactorUsed = segment?.usedKFactor;
|
||||||
final endpoints = _visibleEndpoints();
|
final endpoints = _visibleEndpoints();
|
||||||
final distanceUnit = isImperial ? 'mi' : 'km';
|
final distanceUnit = isImperial ? 'mi' : 'km';
|
||||||
final heightUnit = isImperial ? 'ft' : 'm';
|
final heightUnit = isImperial ? 'ft' : 'm';
|
||||||
@@ -488,6 +498,52 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
if (displayFrequencyMHz != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 2, bottom: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Frequency',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
color: Colors.grey[700],
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'${displayFrequencyMHz.toStringAsFixed(3)} MHz',
|
||||||
|
style: TextStyle(fontSize: 11, color: Colors.grey[700]),
|
||||||
|
),
|
||||||
|
if (kFactorUsed != null) ...[
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'k=${kFactorUsed.toStringAsFixed(3)}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
color: Colors.grey[700],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
icon: const Icon(Icons.info_outline, size: 16),
|
||||||
|
color: Colors.grey[600],
|
||||||
|
tooltip: 'View calculation details',
|
||||||
|
onPressed: () {
|
||||||
|
_showFrequencyInfoDialog(
|
||||||
|
context,
|
||||||
|
displayFrequencyMHz,
|
||||||
|
kFactorUsed,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
context.l10n.losElevationAttribution,
|
context.l10n.losElevationAttribution,
|
||||||
style: TextStyle(fontSize: 10, color: Colors.grey[700]),
|
style: TextStyle(fontSize: 10, color: Colors.grey[700]),
|
||||||
@@ -896,6 +952,56 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showFrequencyInfoDialog(
|
||||||
|
BuildContext context,
|
||||||
|
double frequencyMHz,
|
||||||
|
double kFactor,
|
||||||
|
) {
|
||||||
|
final baselineFreq = LineOfSightService.baselineFrequencyMHz;
|
||||||
|
final baselineK = LineOfSightService.baselineKFactor;
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (dialogContext) => AlertDialog(
|
||||||
|
title: const Text('Radio horizon calculation'),
|
||||||
|
content: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text:
|
||||||
|
'Starting from k=$baselineK at ${baselineFreq.toStringAsFixed(3)} MHz, ',
|
||||||
|
),
|
||||||
|
const TextSpan(text: 'the calculation multiplies the offset by '),
|
||||||
|
TextSpan(
|
||||||
|
text:
|
||||||
|
'0.15 × (frequency − ${baselineFreq.toStringAsFixed(3)}) / ${baselineFreq.toStringAsFixed(3)} ',
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text:
|
||||||
|
'to get k ≈ ${kFactor.toStringAsFixed(3)} for the current ${frequencyMHz.toStringAsFixed(3)} MHz band, ',
|
||||||
|
),
|
||||||
|
const TextSpan(
|
||||||
|
text: 'which defines the curved radio horizon cap.',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(dialogContext),
|
||||||
|
child: Text(context.l10n.common_ok),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double? _normalizeFrequencyMHz(int? frequencyHz) {
|
||||||
|
if (frequencyHz == null || frequencyHz <= 0) return null;
|
||||||
|
if (frequencyHz >= 1000000) return frequencyHz / 1e6;
|
||||||
|
if (frequencyHz >= 1000) return frequencyHz / 1e3;
|
||||||
|
return frequencyHz.toDouble();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LosProfilePainter extends CustomPainter {
|
class _LosProfilePainter extends CustomPainter {
|
||||||
@@ -985,30 +1091,32 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
..strokeWidth = 2,
|
..strokeWidth = 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
final horizonLine = ui.Path();
|
const refractedLineColor = Color(0xFFFFD57F);
|
||||||
|
final refractedLine = ui.Path();
|
||||||
for (int i = 0; i < samples.length; i++) {
|
for (int i = 0; i < samples.length; i++) {
|
||||||
final p = mapPoint(
|
final p = mapPoint(
|
||||||
samples[i].distanceMeters,
|
samples[i].distanceMeters,
|
||||||
samples[i].radioHorizonMeters,
|
samples[i].refractedHeightMeters,
|
||||||
);
|
);
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
horizonLine.moveTo(p.dx, p.dy);
|
refractedLine.moveTo(p.dx, p.dy);
|
||||||
} else {
|
} else {
|
||||||
horizonLine.lineTo(p.dx, p.dy);
|
refractedLine.lineTo(p.dx, p.dy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const horizonLineColor = Color(0xFF4BC0FF);
|
canvas.drawPath(
|
||||||
final horizonPaint = Paint()
|
refractedLine,
|
||||||
..color = horizonLineColor
|
Paint()
|
||||||
..style = PaintingStyle.stroke
|
..color = refractedLineColor
|
||||||
..strokeWidth = 1.5;
|
..style = PaintingStyle.stroke
|
||||||
canvas.drawPath(horizonLine, horizonPaint);
|
..strokeWidth = 1.5,
|
||||||
|
);
|
||||||
|
|
||||||
final capPath = ui.Path();
|
final capPath = ui.Path();
|
||||||
for (int i = 0; i < samples.length; i++) {
|
for (int i = 0; i < samples.length; i++) {
|
||||||
final p = mapPoint(
|
final p = mapPoint(
|
||||||
samples[i].distanceMeters,
|
samples[i].distanceMeters,
|
||||||
samples[i].radioHorizonMeters,
|
samples[i].refractedHeightMeters,
|
||||||
);
|
);
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
capPath.moveTo(p.dx, p.dy);
|
capPath.moveTo(p.dx, p.dy);
|
||||||
@@ -1024,7 +1132,7 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
capPath.lineTo(p.dx, p.dy);
|
capPath.lineTo(p.dx, p.dy);
|
||||||
}
|
}
|
||||||
capPath.close();
|
capPath.close();
|
||||||
const horizonFillColor = Color(0x404BC0FF);
|
const horizonFillColor = Color(0x40FFD57F);
|
||||||
canvas.drawPath(
|
canvas.drawPath(
|
||||||
capPath,
|
capPath,
|
||||||
Paint()
|
Paint()
|
||||||
@@ -1032,12 +1140,7 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
..style = PaintingStyle.fill,
|
..style = PaintingStyle.fill,
|
||||||
);
|
);
|
||||||
|
|
||||||
_drawLegend(
|
_drawLegend(canvas, refractedLineColor, losLineColor, terrainLineColor);
|
||||||
canvas,
|
|
||||||
horizonLineColor,
|
|
||||||
losLineColor,
|
|
||||||
terrainLineColor,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1090,15 +1193,13 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
return painter;
|
return painter;
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
final maxTextWidth = painters.map((p) => p.width).fold<double>(
|
final maxTextWidth = painters.map((p) => p.width).fold<double>(0, math.max);
|
||||||
0,
|
|
||||||
math.max,
|
|
||||||
);
|
|
||||||
|
|
||||||
final legendWidth =
|
final legendWidth =
|
||||||
legendPadding * 2 + swatchSize + swatchTextGap + maxTextWidth;
|
legendPadding * 2 + swatchSize + swatchTextGap + maxTextWidth;
|
||||||
|
|
||||||
final legendHeight = legendPadding * 2 +
|
final legendHeight =
|
||||||
|
legendPadding * 2 +
|
||||||
entries.length * swatchSize +
|
entries.length * swatchSize +
|
||||||
(entries.length - 1) * entrySpacing;
|
(entries.length - 1) * entrySpacing;
|
||||||
|
|
||||||
@@ -1125,10 +1226,7 @@ class _LosProfilePainter extends CustomPainter {
|
|||||||
swatchSize,
|
swatchSize,
|
||||||
swatchSize,
|
swatchSize,
|
||||||
);
|
);
|
||||||
canvas.drawRect(
|
canvas.drawRect(swatchRect, Paint()..color = entry.color);
|
||||||
swatchRect,
|
|
||||||
Paint()..color = entry.color,
|
|
||||||
);
|
|
||||||
|
|
||||||
painter.paint(
|
painter.paint(
|
||||||
canvas,
|
canvas,
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ class LineOfSightSample {
|
|||||||
final double distanceMeters;
|
final double distanceMeters;
|
||||||
final double terrainMeters;
|
final double terrainMeters;
|
||||||
final double lineHeightMeters;
|
final double lineHeightMeters;
|
||||||
final double radioHorizonMeters;
|
final double refractedHeightMeters;
|
||||||
final double clearanceMeters;
|
final double clearanceMeters;
|
||||||
|
|
||||||
const LineOfSightSample({
|
const LineOfSightSample({
|
||||||
required this.distanceMeters,
|
required this.distanceMeters,
|
||||||
required this.terrainMeters,
|
required this.terrainMeters,
|
||||||
required this.lineHeightMeters,
|
required this.lineHeightMeters,
|
||||||
required this.radioHorizonMeters,
|
required this.refractedHeightMeters,
|
||||||
required this.clearanceMeters,
|
required this.clearanceMeters,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,8 @@ class LineOfSightResult {
|
|||||||
final double? firstObstructionDistanceMeters;
|
final double? firstObstructionDistanceMeters;
|
||||||
final List<LineOfSightSample> samples;
|
final List<LineOfSightSample> samples;
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
final double usedKFactor;
|
||||||
|
final double? frequencyMHz;
|
||||||
|
|
||||||
const LineOfSightResult({
|
const LineOfSightResult({
|
||||||
required this.hasData,
|
required this.hasData,
|
||||||
@@ -40,6 +42,8 @@ class LineOfSightResult {
|
|||||||
required this.maxObstructionMeters,
|
required this.maxObstructionMeters,
|
||||||
required this.firstObstructionDistanceMeters,
|
required this.firstObstructionDistanceMeters,
|
||||||
required this.samples,
|
required this.samples,
|
||||||
|
required this.usedKFactor,
|
||||||
|
this.frequencyMHz,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -50,7 +54,9 @@ class LineOfSightResult {
|
|||||||
isClear = false,
|
isClear = false,
|
||||||
maxObstructionMeters = 0,
|
maxObstructionMeters = 0,
|
||||||
firstObstructionDistanceMeters = null,
|
firstObstructionDistanceMeters = null,
|
||||||
samples = const [];
|
samples = const [],
|
||||||
|
usedKFactor = 4.0 / 3.0,
|
||||||
|
frequencyMHz = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class LineOfSightPathSegment {
|
class LineOfSightPathSegment {
|
||||||
@@ -91,6 +97,11 @@ class LineOfSightService {
|
|||||||
static const Duration _cacheTtl = Duration(hours: 24);
|
static const Duration _cacheTtl = Duration(hours: 24);
|
||||||
static const int _maxFetchAttempts = 4; // initial try + 3 retries
|
static const int _maxFetchAttempts = 4; // initial try + 3 retries
|
||||||
static const Duration _initialBackoff = Duration(milliseconds: 300);
|
static const Duration _initialBackoff = Duration(milliseconds: 300);
|
||||||
|
static const double _baselineFrequencyMHz = 915.0;
|
||||||
|
static const double _baselineKFactor = 4.0 / 3.0;
|
||||||
|
|
||||||
|
static double get baselineFrequencyMHz => _baselineFrequencyMHz;
|
||||||
|
static double get baselineKFactor => _baselineKFactor;
|
||||||
|
|
||||||
final http.Client _httpClient;
|
final http.Client _httpClient;
|
||||||
final bool _ownsHttpClient;
|
final bool _ownsHttpClient;
|
||||||
@@ -108,7 +119,7 @@ class LineOfSightService {
|
|||||||
List<LatLng> points, {
|
List<LatLng> points, {
|
||||||
double startAntennaHeightMeters = 1.5,
|
double startAntennaHeightMeters = 1.5,
|
||||||
double endAntennaHeightMeters = 1.5,
|
double endAntennaHeightMeters = 1.5,
|
||||||
double kFactor = 4.0 / 3.0,
|
double? frequencyMHz,
|
||||||
double obstructionToleranceMeters = 0.0,
|
double obstructionToleranceMeters = 0.0,
|
||||||
}) async {
|
}) async {
|
||||||
if (points.length < 2) {
|
if (points.length < 2) {
|
||||||
@@ -125,6 +136,7 @@ class LineOfSightService {
|
|||||||
var blockedSegments = 0;
|
var blockedSegments = 0;
|
||||||
var unknownSegments = 0;
|
var unknownSegments = 0;
|
||||||
|
|
||||||
|
final kFactor = _kFactorForFrequency(frequencyMHz);
|
||||||
for (int i = 0; i < points.length - 1; i++) {
|
for (int i = 0; i < points.length - 1; i++) {
|
||||||
final result = await analyzeLink(
|
final result = await analyzeLink(
|
||||||
points[i],
|
points[i],
|
||||||
@@ -132,6 +144,7 @@ class LineOfSightService {
|
|||||||
startAntennaHeightMeters: startAntennaHeightMeters,
|
startAntennaHeightMeters: startAntennaHeightMeters,
|
||||||
endAntennaHeightMeters: endAntennaHeightMeters,
|
endAntennaHeightMeters: endAntennaHeightMeters,
|
||||||
kFactor: kFactor,
|
kFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
obstructionToleranceMeters: obstructionToleranceMeters,
|
obstructionToleranceMeters: obstructionToleranceMeters,
|
||||||
);
|
);
|
||||||
segments.add(
|
segments.add(
|
||||||
@@ -165,7 +178,8 @@ class LineOfSightService {
|
|||||||
LatLng end, {
|
LatLng end, {
|
||||||
double startAntennaHeightMeters = 1.5,
|
double startAntennaHeightMeters = 1.5,
|
||||||
double endAntennaHeightMeters = 1.5,
|
double endAntennaHeightMeters = 1.5,
|
||||||
double kFactor = 4.0 / 3.0,
|
required double kFactor,
|
||||||
|
double? frequencyMHz,
|
||||||
double obstructionToleranceMeters = 0.0,
|
double obstructionToleranceMeters = 0.0,
|
||||||
}) async {
|
}) async {
|
||||||
final totalDistanceMeters = _distance.as(LengthUnit.Meter, start, end);
|
final totalDistanceMeters = _distance.as(LengthUnit.Meter, start, end);
|
||||||
@@ -177,6 +191,8 @@ class LineOfSightService {
|
|||||||
maxObstructionMeters: 0,
|
maxObstructionMeters: 0,
|
||||||
firstObstructionDistanceMeters: null,
|
firstObstructionDistanceMeters: null,
|
||||||
samples: const [],
|
samples: const [],
|
||||||
|
usedKFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +221,8 @@ class LineOfSightService {
|
|||||||
required List<double> elevations,
|
required List<double> elevations,
|
||||||
double startAntennaHeightMeters = 1.5,
|
double startAntennaHeightMeters = 1.5,
|
||||||
double endAntennaHeightMeters = 1.5,
|
double endAntennaHeightMeters = 1.5,
|
||||||
double kFactor = 4.0 / 3.0,
|
required double kFactor,
|
||||||
|
double? frequencyMHz,
|
||||||
double obstructionToleranceMeters = 0.0,
|
double obstructionToleranceMeters = 0.0,
|
||||||
}) {
|
}) {
|
||||||
if (points.length < 2 || elevations.length != points.length) {
|
if (points.length < 2 || elevations.length != points.length) {
|
||||||
@@ -240,7 +257,10 @@ class LineOfSightService {
|
|||||||
(2 * effectiveEarthRadius);
|
(2 * effectiveEarthRadius);
|
||||||
final terrainHeight = elevations[i] + earthBulge;
|
final terrainHeight = elevations[i] + earthBulge;
|
||||||
final clearance = lineHeight - terrainHeight;
|
final clearance = lineHeight - terrainHeight;
|
||||||
final radioHorizonHeight = lineHeight - earthBulge;
|
final unrefBulge =
|
||||||
|
(distanceFromStart * (totalDistanceMeters - distanceFromStart)) /
|
||||||
|
(2 * _earthRadiusMeters);
|
||||||
|
final refractedHeight = lineHeight + (unrefBulge - earthBulge);
|
||||||
|
|
||||||
if (clearance < -obstructionToleranceMeters) {
|
if (clearance < -obstructionToleranceMeters) {
|
||||||
isClear = false;
|
isClear = false;
|
||||||
@@ -256,7 +276,7 @@ class LineOfSightService {
|
|||||||
distanceMeters: distanceFromStart,
|
distanceMeters: distanceFromStart,
|
||||||
terrainMeters: terrainHeight,
|
terrainMeters: terrainHeight,
|
||||||
lineHeightMeters: lineHeight,
|
lineHeightMeters: lineHeight,
|
||||||
radioHorizonMeters: radioHorizonHeight,
|
refractedHeightMeters: refractedHeight,
|
||||||
clearanceMeters: clearance,
|
clearanceMeters: clearance,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -269,9 +289,20 @@ class LineOfSightService {
|
|||||||
maxObstructionMeters: maxObstructionMeters,
|
maxObstructionMeters: maxObstructionMeters,
|
||||||
firstObstructionDistanceMeters: firstObstructionDistanceMeters,
|
firstObstructionDistanceMeters: firstObstructionDistanceMeters,
|
||||||
samples: samples,
|
samples: samples,
|
||||||
|
usedKFactor: kFactor,
|
||||||
|
frequencyMHz: frequencyMHz,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double _kFactorForFrequency(double? frequencyMHz) {
|
||||||
|
if (frequencyMHz == null) return _baselineKFactor;
|
||||||
|
final delta =
|
||||||
|
(frequencyMHz - _baselineFrequencyMHz) / _baselineFrequencyMHz;
|
||||||
|
final adjustment = delta * 0.15;
|
||||||
|
final scaled = _baselineKFactor * (1 + adjustment);
|
||||||
|
return scaled.clamp(1.1, 1.6).toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
List<LatLng> _buildSamplePoints(
|
List<LatLng> _buildSamplePoints(
|
||||||
LatLng start,
|
LatLng start,
|
||||||
LatLng end,
|
LatLng end,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ void main() {
|
|||||||
elevations: elevations,
|
elevations: elevations,
|
||||||
startAntennaHeightMeters: 2,
|
startAntennaHeightMeters: 2,
|
||||||
endAntennaHeightMeters: 2,
|
endAntennaHeightMeters: 2,
|
||||||
|
kFactor: 4.0 / 3.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.hasData, isTrue);
|
expect(result.hasData, isTrue);
|
||||||
@@ -36,6 +37,7 @@ void main() {
|
|||||||
elevations: elevations,
|
elevations: elevations,
|
||||||
startAntennaHeightMeters: 1.5,
|
startAntennaHeightMeters: 1.5,
|
||||||
endAntennaHeightMeters: 1.5,
|
endAntennaHeightMeters: 1.5,
|
||||||
|
kFactor: 4.0 / 3.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.hasData, isTrue);
|
expect(result.hasData, isTrue);
|
||||||
|
|||||||
Reference in New Issue
Block a user