@smartnet360/svelte-components 0.0.102 → 0.0.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/antenna-pattern/index.d.ts +1 -0
- package/dist/apps/antenna-pattern/index.js +1 -0
- package/dist/apps/antenna-pattern/utils/load-static-antennas.d.ts +17 -0
- package/dist/apps/antenna-pattern/utils/load-static-antennas.js +83 -0
- package/dist/apps/site-check/SiteCheck.svelte +4 -6
- package/dist/core/Charts/ChartCard.svelte +122 -12
- package/dist/core/Charts/ChartCard.svelte.d.ts +2 -0
- package/dist/core/Charts/ChartComponent.svelte +8 -6
- package/dist/core/CoverageMap/ai/AITools.d.ts +117 -0
- package/dist/core/CoverageMap/ai/AITools.js +380 -0
- package/dist/core/CoverageMap/core/CoverageCalculator.d.ts +138 -0
- package/dist/core/CoverageMap/core/CoverageCalculator.js +375 -0
- package/dist/core/CoverageMap/core/GridCalculator.d.ts +115 -0
- package/dist/core/CoverageMap/core/GridCalculator.js +484 -0
- package/dist/core/CoverageMap/core/PathLossModels.d.ts +253 -0
- package/dist/core/CoverageMap/core/PathLossModels.js +380 -0
- package/dist/core/CoverageMap/core/SignalProcessor.d.ts +288 -0
- package/dist/core/CoverageMap/core/SignalProcessor.js +424 -0
- package/dist/core/CoverageMap/data/AntennaStore.d.ts +165 -0
- package/dist/core/CoverageMap/data/AntennaStore.js +327 -0
- package/dist/core/CoverageMap/data/SiteStore.d.ts +155 -0
- package/dist/core/CoverageMap/data/SiteStore.js +355 -0
- package/dist/core/CoverageMap/index.d.ts +74 -0
- package/dist/core/CoverageMap/index.js +103 -0
- package/dist/core/CoverageMap/types.d.ts +252 -0
- package/dist/core/CoverageMap/types.js +7 -0
- package/dist/core/CoverageMap/utils/geoUtils.d.ts +223 -0
- package/dist/core/CoverageMap/utils/geoUtils.js +374 -0
- package/dist/core/CoverageMap/utils/rfUtils.d.ts +329 -0
- package/dist/core/CoverageMap/utils/rfUtils.js +434 -0
- package/dist/core/CoverageMap/visualization/ColorSchemes.d.ts +149 -0
- package/dist/core/CoverageMap/visualization/ColorSchemes.js +377 -0
- package/dist/core/TreeView/index.d.ts +4 -4
- package/dist/core/TreeView/index.js +5 -5
- package/dist/core/TreeView/tree-utils.d.ts +12 -0
- package/dist/core/TreeView/tree-utils.js +115 -6
- package/dist/core/TreeView/tree.store.svelte.d.ts +94 -0
- package/dist/core/TreeView/tree.store.svelte.js +274 -0
- package/dist/map-v2/features/cells/controls/CellFilterControl.svelte +16 -27
- package/dist/map-v2/features/repeaters/controls/RepeaterFilterControl.svelte +33 -42
- package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +12 -19
- package/dist/map-v3/core/components/Map.svelte +4 -0
- package/dist/map-v3/core/stores/map.store.svelte.js +2 -0
- package/dist/map-v3/features/cells/components/CellFilterControl.svelte +24 -30
- package/dist/map-v3/features/coverage/index.d.ts +12 -0
- package/dist/map-v3/features/coverage/index.js +16 -0
- package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte +198 -0
- package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte.d.ts +10 -0
- package/dist/map-v3/features/coverage/logic/coloring.d.ts +28 -0
- package/dist/map-v3/features/coverage/logic/coloring.js +77 -0
- package/dist/map-v3/features/coverage/logic/geometry.d.ts +33 -0
- package/dist/map-v3/features/coverage/logic/geometry.js +112 -0
- package/dist/map-v3/features/coverage/stores/coverage.data.svelte.d.ts +46 -0
- package/dist/map-v3/features/coverage/stores/coverage.data.svelte.js +95 -0
- package/dist/map-v3/features/coverage/stores/coverage.display.svelte.d.ts +33 -0
- package/dist/map-v3/features/coverage/stores/coverage.display.svelte.js +90 -0
- package/dist/map-v3/features/coverage/types.d.ts +52 -0
- package/dist/map-v3/features/coverage/types.js +7 -0
- package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +14 -20
- package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +23 -33
- package/dist/map-v3/index.d.ts +4 -0
- package/dist/map-v3/index.js +5 -0
- package/package.json +2 -2
- package/dist/core/TreeView/tree.store.d.ts +0 -10
- package/dist/core/TreeView/tree.store.js +0 -320
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage Calculator - Main Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for coverage calculations.
|
|
5
|
+
* It orchestrates all components and provides a clean API for:
|
|
6
|
+
* - Single sector coverage calculation
|
|
7
|
+
* - Multi-sector site coverage
|
|
8
|
+
* - Progress reporting
|
|
9
|
+
* - AI-ready result formatting
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const calculator = new CoverageCalculator();
|
|
13
|
+
* const result = await calculator.calculate(config, progressCallback);
|
|
14
|
+
* // Result includes grid, statistics, and AI-ready summary
|
|
15
|
+
*/
|
|
16
|
+
import { calculateSingleSectorCoverage, calculateMultiSectorCoverage, extractMetrics } from './GridCalculator';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// COVERAGE CALCULATOR CLASS
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* Main Coverage Calculator
|
|
22
|
+
*
|
|
23
|
+
* Provides high-level API for coverage calculations with:
|
|
24
|
+
* - Configuration validation
|
|
25
|
+
* - Progress reporting
|
|
26
|
+
* - Error handling
|
|
27
|
+
* - Result formatting
|
|
28
|
+
* - AI-ready summaries
|
|
29
|
+
*/
|
|
30
|
+
export class CoverageCalculator {
|
|
31
|
+
/**
|
|
32
|
+
* Calculate coverage for a site configuration
|
|
33
|
+
*
|
|
34
|
+
* This is the main calculation method. It:
|
|
35
|
+
* 1. Validates configuration
|
|
36
|
+
* 2. Determines if single or multi-sector
|
|
37
|
+
* 3. Performs calculation
|
|
38
|
+
* 4. Generates statistics and summary
|
|
39
|
+
* 5. Formats result for consumption (including AI)
|
|
40
|
+
*
|
|
41
|
+
* @param config - Complete coverage configuration
|
|
42
|
+
* @param onProgress - Optional progress callback
|
|
43
|
+
* @returns Promise resolving to complete coverage result
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* const calculator = new CoverageCalculator();
|
|
47
|
+
* const result = await calculator.calculate({
|
|
48
|
+
* site: siteConfig,
|
|
49
|
+
* gridSettings: { cellSizeMeters: 50, maxRadiusKm: 10, ... },
|
|
50
|
+
* signalThresholds: { excellent: -70, good: -85, ... },
|
|
51
|
+
* calculateInterference: true
|
|
52
|
+
* }, (progress) => {
|
|
53
|
+
* console.log(`Progress: ${progress.progress}% - ${progress.message}`);
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* // Access results
|
|
57
|
+
* console.log(`Coverage: ${result.grid.stats.coveragePercentage}%`);
|
|
58
|
+
* console.log(`Summary: ${result.summary.description}`);
|
|
59
|
+
* // Pass to AI for analysis
|
|
60
|
+
* const aiAnalysis = await analyzeWithAI(result);
|
|
61
|
+
*/
|
|
62
|
+
async calculate(config, onProgress) {
|
|
63
|
+
const startTime = Date.now();
|
|
64
|
+
// Validate configuration
|
|
65
|
+
this.validateConfiguration(config);
|
|
66
|
+
// Determine calculation type
|
|
67
|
+
const enabledSectors = config.site.sectors.filter((s) => s.enabled);
|
|
68
|
+
let grid;
|
|
69
|
+
if (enabledSectors.length === 0) {
|
|
70
|
+
throw new Error('No enabled sectors in configuration');
|
|
71
|
+
}
|
|
72
|
+
else if (enabledSectors.length === 1) {
|
|
73
|
+
// Single sector calculation
|
|
74
|
+
onProgress?.({
|
|
75
|
+
stage: 'initializing',
|
|
76
|
+
progress: 0,
|
|
77
|
+
message: 'Starting single sector calculation...'
|
|
78
|
+
});
|
|
79
|
+
grid = calculateSingleSectorCoverage(enabledSectors[0], config.gridSettings, config.signalThresholds, onProgress);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Multi-sector calculation
|
|
83
|
+
onProgress?.({
|
|
84
|
+
stage: 'initializing',
|
|
85
|
+
progress: 0,
|
|
86
|
+
message: `Starting ${enabledSectors.length} sector calculation...`
|
|
87
|
+
});
|
|
88
|
+
grid = calculateMultiSectorCoverage(enabledSectors, config.gridSettings, config.signalThresholds, onProgress);
|
|
89
|
+
}
|
|
90
|
+
// Calculate total time
|
|
91
|
+
const calculationTimeMs = Date.now() - startTime;
|
|
92
|
+
// Generate AI-ready summary
|
|
93
|
+
const summary = this.generateSummary(config, grid, enabledSectors.length);
|
|
94
|
+
// Create final result
|
|
95
|
+
const result = {
|
|
96
|
+
config,
|
|
97
|
+
grid,
|
|
98
|
+
timestamp: new Date(),
|
|
99
|
+
calculationTimeMs,
|
|
100
|
+
summary
|
|
101
|
+
};
|
|
102
|
+
onProgress?.({
|
|
103
|
+
stage: 'complete',
|
|
104
|
+
progress: 100,
|
|
105
|
+
message: 'Coverage calculation complete!'
|
|
106
|
+
});
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Validate configuration before calculation
|
|
111
|
+
*
|
|
112
|
+
* Checks for common configuration errors:
|
|
113
|
+
* - Invalid distances (negative, zero, too large)
|
|
114
|
+
* - Invalid frequencies (outside valid ranges)
|
|
115
|
+
* - Invalid antenna heights
|
|
116
|
+
* - Missing required data
|
|
117
|
+
*
|
|
118
|
+
* Throws descriptive errors if validation fails.
|
|
119
|
+
*
|
|
120
|
+
* @param config - Configuration to validate
|
|
121
|
+
* @throws Error if configuration is invalid
|
|
122
|
+
*/
|
|
123
|
+
validateConfiguration(config) {
|
|
124
|
+
// Validate grid settings
|
|
125
|
+
const { cellSizeMeters, maxRadiusKm } = config.gridSettings;
|
|
126
|
+
if (cellSizeMeters <= 0 || cellSizeMeters > 1000) {
|
|
127
|
+
throw new Error(`Invalid cell size: ${cellSizeMeters}m. Must be between 1m and 1000m.`);
|
|
128
|
+
}
|
|
129
|
+
if (maxRadiusKm <= 0 || maxRadiusKm > 100) {
|
|
130
|
+
throw new Error(`Invalid max radius: ${maxRadiusKm}km. Must be between 0.1km and 100km.`);
|
|
131
|
+
}
|
|
132
|
+
// Check grid size doesn't exceed reasonable limits
|
|
133
|
+
const gridSize = (maxRadiusKm * 2000) / cellSizeMeters;
|
|
134
|
+
const totalCells = gridSize * gridSize;
|
|
135
|
+
if (totalCells > 1000000) {
|
|
136
|
+
// 1 million cells
|
|
137
|
+
throw new Error(`Grid too large: ${totalCells} cells. ` +
|
|
138
|
+
`Consider increasing cell size or decreasing radius. ` +
|
|
139
|
+
`Max recommended: 1 million cells.`);
|
|
140
|
+
}
|
|
141
|
+
// Validate sectors
|
|
142
|
+
if (!config.site.sectors || config.site.sectors.length === 0) {
|
|
143
|
+
throw new Error('Site configuration must include at least one sector');
|
|
144
|
+
}
|
|
145
|
+
for (const sector of config.site.sectors) {
|
|
146
|
+
// Validate frequency
|
|
147
|
+
if (sector.frequency < 100 || sector.frequency > 10000) {
|
|
148
|
+
throw new Error(`Invalid frequency for sector ${sector.sectorId}: ${sector.frequency}MHz. ` +
|
|
149
|
+
`Must be between 100 MHz and 10 GHz.`);
|
|
150
|
+
}
|
|
151
|
+
// Validate TX power
|
|
152
|
+
if (sector.txPower < 0 || sector.txPower > 80) {
|
|
153
|
+
throw new Error(`Invalid TX power for sector ${sector.sectorId}: ${sector.txPower}dBm. ` +
|
|
154
|
+
`Must be between 0 and 80 dBm.`);
|
|
155
|
+
}
|
|
156
|
+
// Validate antenna height
|
|
157
|
+
if (sector.position.height < 0 || sector.position.height > 500) {
|
|
158
|
+
throw new Error(`Invalid antenna height for sector ${sector.sectorId}: ${sector.position.height}m. ` +
|
|
159
|
+
`Must be between 0 and 500 meters.`);
|
|
160
|
+
}
|
|
161
|
+
// Validate azimuth
|
|
162
|
+
if (sector.azimuth < 0 || sector.azimuth >= 360) {
|
|
163
|
+
throw new Error(`Invalid azimuth for sector ${sector.sectorId}: ${sector.azimuth}°. ` +
|
|
164
|
+
`Must be between 0 and 359 degrees.`);
|
|
165
|
+
}
|
|
166
|
+
// Validate antenna pattern
|
|
167
|
+
if (!sector.antennaPattern) {
|
|
168
|
+
throw new Error(`Missing antenna pattern for sector ${sector.sectorId}`);
|
|
169
|
+
}
|
|
170
|
+
if (!sector.antennaPattern.pattern ||
|
|
171
|
+
sector.antennaPattern.pattern.length !== 360) {
|
|
172
|
+
throw new Error(`Invalid horizontal pattern for sector ${sector.sectorId}. ` +
|
|
173
|
+
`Must have exactly 360 values.`);
|
|
174
|
+
}
|
|
175
|
+
if (!sector.antennaPattern.vertical_pattern ||
|
|
176
|
+
sector.antennaPattern.vertical_pattern.length !== 360) {
|
|
177
|
+
throw new Error(`Invalid vertical pattern for sector ${sector.sectorId}. ` +
|
|
178
|
+
`Must have exactly 360 values.`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Validate thresholds
|
|
182
|
+
const { excellent, good, fair, edge } = config.signalThresholds;
|
|
183
|
+
if (excellent <= good || good <= fair || fair <= edge) {
|
|
184
|
+
throw new Error(`Invalid signal thresholds. Must be in descending order: ` +
|
|
185
|
+
`excellent (${excellent}) > good (${good}) > fair (${fair}) > edge (${edge})`);
|
|
186
|
+
}
|
|
187
|
+
if (excellent > -50 || edge < -120) {
|
|
188
|
+
throw new Error(`Signal thresholds out of reasonable range. ` +
|
|
189
|
+
`Excellent should be < -50 dBm, edge should be > -120 dBm.`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Generate human-readable and AI-ready summary
|
|
194
|
+
*
|
|
195
|
+
* Creates a comprehensive summary of coverage results including:
|
|
196
|
+
* - Description: Human-readable text summary
|
|
197
|
+
* - Issues: List of detected problems/warnings
|
|
198
|
+
* - Metrics: Key numerical metrics for AI analysis
|
|
199
|
+
*
|
|
200
|
+
* This summary is designed to be consumed by:
|
|
201
|
+
* 1. Human users (description text)
|
|
202
|
+
* 2. UI components (structured data)
|
|
203
|
+
* 3. AI analysis tools (metrics + issues)
|
|
204
|
+
*
|
|
205
|
+
* @param config - Original configuration
|
|
206
|
+
* @param grid - Calculation results
|
|
207
|
+
* @param sectorCount - Number of sectors calculated
|
|
208
|
+
* @returns Summary object
|
|
209
|
+
*/
|
|
210
|
+
generateSummary(config, grid, sectorCount) {
|
|
211
|
+
const stats = grid.stats;
|
|
212
|
+
const issues = [];
|
|
213
|
+
// Generate description
|
|
214
|
+
const description = this.createDescription(config, stats, sectorCount);
|
|
215
|
+
// Detect issues
|
|
216
|
+
this.detectIssues(config, stats, issues);
|
|
217
|
+
// Extract metrics for AI
|
|
218
|
+
const metrics = extractMetrics(grid);
|
|
219
|
+
return {
|
|
220
|
+
description,
|
|
221
|
+
issues,
|
|
222
|
+
metrics
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Create human-readable description
|
|
227
|
+
*/
|
|
228
|
+
createDescription(config, stats, sectorCount) {
|
|
229
|
+
const lines = [];
|
|
230
|
+
// Site info
|
|
231
|
+
lines.push(`Coverage Analysis for ${config.site.siteName}`);
|
|
232
|
+
lines.push(`Sectors: ${sectorCount}`);
|
|
233
|
+
lines.push('');
|
|
234
|
+
// Coverage summary
|
|
235
|
+
lines.push(`Total Coverage: ${stats.coveragePercentage.toFixed(1)}% of ${stats.totalAreaKm2.toFixed(2)} km²`);
|
|
236
|
+
lines.push('');
|
|
237
|
+
// Quality breakdown
|
|
238
|
+
lines.push('Signal Quality Distribution:');
|
|
239
|
+
lines.push(` Excellent: ${stats.excellentAreaKm2.toFixed(2)} km² (${((stats.excellentAreaKm2 / stats.totalAreaKm2) * 100).toFixed(1)}%)`);
|
|
240
|
+
lines.push(` Good: ${stats.goodAreaKm2.toFixed(2)} km² (${((stats.goodAreaKm2 / stats.totalAreaKm2) * 100).toFixed(1)}%)`);
|
|
241
|
+
lines.push(` Fair: ${stats.fairAreaKm2.toFixed(2)} km² (${((stats.fairAreaKm2 / stats.totalAreaKm2) * 100).toFixed(1)}%)`);
|
|
242
|
+
lines.push(` Poor: ${stats.poorAreaKm2.toFixed(2)} km² (${((stats.poorAreaKm2 / stats.totalAreaKm2) * 100).toFixed(1)}%)`);
|
|
243
|
+
lines.push('');
|
|
244
|
+
// Per-sector stats
|
|
245
|
+
if (stats.sectorStats) {
|
|
246
|
+
lines.push('Per-Sector Performance:');
|
|
247
|
+
// @ts-ignore - Object.entries returns unknown type
|
|
248
|
+
for (const [sectorId, sectorStat] of Object.entries(stats.sectorStats)) {
|
|
249
|
+
lines.push(` ${sectorId}:`);
|
|
250
|
+
lines.push(` Max Range: ${sectorStat.maxRangeKm.toFixed(2)} km`);
|
|
251
|
+
lines.push(` Avg Signal: ${sectorStat.avgSignalDbm.toFixed(1)} dBm`);
|
|
252
|
+
lines.push(` Coverage Area: ${sectorStat.coverageAreaKm2.toFixed(2)} km²`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return lines.join('\n');
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Detect common issues and warnings
|
|
259
|
+
*/
|
|
260
|
+
detectIssues(config, stats, issues) {
|
|
261
|
+
// Low coverage
|
|
262
|
+
if (stats.coveragePercentage < 50) {
|
|
263
|
+
issues.push(`Low overall coverage: ${stats.coveragePercentage.toFixed(1)}%. ` +
|
|
264
|
+
`Consider increasing TX power, antenna height, or optimizing azimuth/tilt.`);
|
|
265
|
+
}
|
|
266
|
+
// Poor quality dominance
|
|
267
|
+
const poorRatio = stats.poorAreaKm2 / stats.coveredAreaKm2;
|
|
268
|
+
if (poorRatio > 0.5) {
|
|
269
|
+
issues.push(`Over 50% of coverage is poor quality. ` +
|
|
270
|
+
`Consider increasing TX power or reducing coverage area with downtilt.`);
|
|
271
|
+
}
|
|
272
|
+
// Excessive range (potential interference)
|
|
273
|
+
if (stats.sectorStats) {
|
|
274
|
+
// @ts-ignore - Object.entries returns unknown type
|
|
275
|
+
for (const [sectorId, sectorStat] of Object.entries(stats.sectorStats)) {
|
|
276
|
+
if (sectorStat.maxRangeKm > 15) {
|
|
277
|
+
issues.push(`Sector ${sectorId} has excessive range (${sectorStat.maxRangeKm.toFixed(1)} km). ` +
|
|
278
|
+
`Consider adding downtilt to reduce interference to neighboring sites.`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Very short range (undershooting)
|
|
283
|
+
if (stats.sectorStats) {
|
|
284
|
+
// @ts-ignore - Object.entries returns unknown type
|
|
285
|
+
for (const [sectorId, sectorStat] of Object.entries(stats.sectorStats)) {
|
|
286
|
+
if (sectorStat.maxRangeKm < 1) {
|
|
287
|
+
issues.push(`Sector ${sectorId} has very short range (${sectorStat.maxRangeKm.toFixed(1)} km). ` +
|
|
288
|
+
`Check TX power, antenna height, and tilt settings.`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Weak average signal
|
|
293
|
+
if (stats.sectorStats) {
|
|
294
|
+
// @ts-ignore - Object.entries returns unknown type
|
|
295
|
+
for (const [sectorId, sectorStat] of Object.entries(stats.sectorStats)) {
|
|
296
|
+
if (sectorStat.avgSignalDbm < -90) {
|
|
297
|
+
issues.push(`Sector ${sectorId} has weak average signal (${sectorStat.avgSignalDbm.toFixed(1)} dBm). ` +
|
|
298
|
+
`Most coverage area is at poor signal levels.`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Minimal excellent coverage
|
|
303
|
+
const excellentRatio = stats.excellentAreaKm2 / stats.coveredAreaKm2;
|
|
304
|
+
if (excellentRatio < 0.1) {
|
|
305
|
+
issues.push(`Less than 10% of coverage is excellent quality. ` +
|
|
306
|
+
`Users will experience reduced data speeds in most areas.`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// ============================================================================
|
|
311
|
+
// CONVENIENCE FUNCTIONS
|
|
312
|
+
// ============================================================================
|
|
313
|
+
/**
|
|
314
|
+
* Quick coverage calculation (simplified API)
|
|
315
|
+
*
|
|
316
|
+
* For simple use cases where you don't need full configuration control.
|
|
317
|
+
* Uses sensible defaults for most parameters.
|
|
318
|
+
*
|
|
319
|
+
* @param site - Site configuration
|
|
320
|
+
* @param options - Optional overrides
|
|
321
|
+
* @returns Coverage result
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* const result = await quickCalculate(mySite, {
|
|
325
|
+
* cellSizeMeters: 100, // Faster calculation
|
|
326
|
+
* maxRadiusKm: 5 // Smaller area
|
|
327
|
+
* });
|
|
328
|
+
*/
|
|
329
|
+
export async function quickCalculate(site, options) {
|
|
330
|
+
const calculator = new CoverageCalculator();
|
|
331
|
+
const config = {
|
|
332
|
+
site,
|
|
333
|
+
gridSettings: {
|
|
334
|
+
centerPosition: site.position,
|
|
335
|
+
cellSizeMeters: options?.cellSizeMeters || 50,
|
|
336
|
+
maxRadiusKm: options?.maxRadiusKm || 10,
|
|
337
|
+
pathLossModel: options?.pathLossModel || 'free-space'
|
|
338
|
+
},
|
|
339
|
+
signalThresholds: {
|
|
340
|
+
excellent: -70,
|
|
341
|
+
good: -85,
|
|
342
|
+
fair: -95,
|
|
343
|
+
edge: -105
|
|
344
|
+
},
|
|
345
|
+
calculateInterference: true
|
|
346
|
+
};
|
|
347
|
+
return calculator.calculate(config);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Calculate coverage and export for AI analysis
|
|
351
|
+
*
|
|
352
|
+
* Convenience function that calculates coverage and formats
|
|
353
|
+
* the result specifically for AI consumption.
|
|
354
|
+
*
|
|
355
|
+
* @param config - Coverage configuration
|
|
356
|
+
* @returns AI-ready analysis object
|
|
357
|
+
*/
|
|
358
|
+
export async function calculateForAI(config) {
|
|
359
|
+
const calculator = new CoverageCalculator();
|
|
360
|
+
const result = await calculator.calculate(config);
|
|
361
|
+
// Format for AI
|
|
362
|
+
return {
|
|
363
|
+
summary: result.summary.description,
|
|
364
|
+
metrics: result.summary.metrics,
|
|
365
|
+
issues: result.summary.issues,
|
|
366
|
+
sectorData: config.site.sectors.map((s) => ({
|
|
367
|
+
sectorId: s.sectorId,
|
|
368
|
+
azimuth: s.azimuth,
|
|
369
|
+
tilt: s.mechanicalTilt + s.electricalTilt,
|
|
370
|
+
power: s.txPower,
|
|
371
|
+
frequency: s.frequency,
|
|
372
|
+
stats: result.grid.stats.sectorStats[s.sectorId]
|
|
373
|
+
}))
|
|
374
|
+
};
|
|
375
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grid Calculator
|
|
3
|
+
*
|
|
4
|
+
* This module implements grid-based coverage calculation.
|
|
5
|
+
* It divides the area around an antenna into a grid of cells
|
|
6
|
+
* and calculates signal strength at each cell center.
|
|
7
|
+
*
|
|
8
|
+
* Grid-based approach advantages:
|
|
9
|
+
* - Regular, predictable structure
|
|
10
|
+
* - Easy to visualize as heatmap
|
|
11
|
+
* - Efficient for large areas
|
|
12
|
+
* - Simple to parallelize (future enhancement)
|
|
13
|
+
*
|
|
14
|
+
* Process:
|
|
15
|
+
* 1. Create grid around antenna position
|
|
16
|
+
* 2. For each grid cell:
|
|
17
|
+
* a. Calculate distance to antenna
|
|
18
|
+
* b. Calculate bearing to antenna
|
|
19
|
+
* c. Calculate path loss
|
|
20
|
+
* d. Calculate antenna gain in direction
|
|
21
|
+
* e. Calculate received signal strength
|
|
22
|
+
* 3. Aggregate results and generate statistics
|
|
23
|
+
*/
|
|
24
|
+
import type { Position2D, GridSettings, CoverageGrid, RFParameters, SectorConfig, SignalThresholds, ProgressCallback } from '../types';
|
|
25
|
+
/**
|
|
26
|
+
* Generate grid structure
|
|
27
|
+
*
|
|
28
|
+
* Creates a 2D grid of geographic positions centered on a point.
|
|
29
|
+
* Grid cells are square (in meters), though they appear slightly
|
|
30
|
+
* rectangular on the map due to Earth's curvature.
|
|
31
|
+
*
|
|
32
|
+
* Grid sizing:
|
|
33
|
+
* - Total size = 2 × maxRadius (covers circle)
|
|
34
|
+
* - Number of cells = (2 × maxRadius) / cellSize in each dimension
|
|
35
|
+
* - Example: 10 km radius, 50m cells → 400 × 400 = 160,000 cells
|
|
36
|
+
*
|
|
37
|
+
* Cell numbering:
|
|
38
|
+
* - Rows increase North to South (row 0 = northernmost)
|
|
39
|
+
* - Cols increase West to East (col 0 = westernmost)
|
|
40
|
+
* - [row][col] indexing for 2D array
|
|
41
|
+
*
|
|
42
|
+
* Memory consideration:
|
|
43
|
+
* - Each GridCell object: ~100 bytes
|
|
44
|
+
* - 160,000 cells × 100 bytes = 16 MB
|
|
45
|
+
* - Acceptable for modern browsers
|
|
46
|
+
*
|
|
47
|
+
* @param settings - Grid configuration
|
|
48
|
+
* @returns 2D array of positions (not yet populated with signal data)
|
|
49
|
+
*/
|
|
50
|
+
export declare function generateGridPositions(settings: GridSettings): Position2D[][];
|
|
51
|
+
/**
|
|
52
|
+
* Calculate coverage grid for a single antenna/sector
|
|
53
|
+
*
|
|
54
|
+
* This is the core coverage calculation function. It:
|
|
55
|
+
* 1. Generates a grid of positions
|
|
56
|
+
* 2. For each position, calculates:
|
|
57
|
+
* - Distance to antenna
|
|
58
|
+
* - Path loss based on distance and model
|
|
59
|
+
* - Signal strength based on antenna pattern
|
|
60
|
+
* 3. Classifies signal quality
|
|
61
|
+
* 4. Generates statistics
|
|
62
|
+
*
|
|
63
|
+
* Progress reporting:
|
|
64
|
+
* - Calls progress callback after each row (or every N cells)
|
|
65
|
+
* - Allows UI to show progress bar
|
|
66
|
+
* - Keeps browser responsive for large grids
|
|
67
|
+
*
|
|
68
|
+
* Optimization notes:
|
|
69
|
+
* - Path loss calculation is expensive (log operations)
|
|
70
|
+
* - Could cache path loss vs distance (future optimization)
|
|
71
|
+
* - Could use Web Workers for parallel calculation
|
|
72
|
+
* - Current implementation: ~0.5ms per cell on modern CPU
|
|
73
|
+
* (160,000 cells = ~80 seconds, acceptable for one-time calculation)
|
|
74
|
+
*
|
|
75
|
+
* @param rfParams - Complete RF configuration
|
|
76
|
+
* @param gridSettings - Grid size and resolution
|
|
77
|
+
* @param thresholds - Signal quality thresholds
|
|
78
|
+
* @param onProgress - Progress callback (optional)
|
|
79
|
+
* @returns Complete coverage grid with signal data
|
|
80
|
+
*/
|
|
81
|
+
export declare function calculateSingleSectorCoverage(rfParams: RFParameters, gridSettings: GridSettings, thresholds: SignalThresholds, onProgress?: ProgressCallback): CoverageGrid;
|
|
82
|
+
/**
|
|
83
|
+
* Calculate coverage grid for multiple sectors
|
|
84
|
+
*
|
|
85
|
+
* Common scenario: Cell site with 3 sectors covering 360°.
|
|
86
|
+
* This function calculates signal from all sectors at each point
|
|
87
|
+
* and determines which sector provides strongest signal.
|
|
88
|
+
*
|
|
89
|
+
* Additional capabilities:
|
|
90
|
+
* - Identifies dominant server (which sector serves each location)
|
|
91
|
+
* - Calculates overlap areas (where multiple sectors are strong)
|
|
92
|
+
* - Detects potential interference zones
|
|
93
|
+
*
|
|
94
|
+
* Performance consideration:
|
|
95
|
+
* - Calculates N × grid_size signal strengths (N = number of sectors)
|
|
96
|
+
* - 3 sectors × 160,000 cells = 480,000 calculations
|
|
97
|
+
* - Still manageable (few minutes on modern hardware)
|
|
98
|
+
*
|
|
99
|
+
* @param sectors - Array of sector configurations
|
|
100
|
+
* @param gridSettings - Grid size and resolution
|
|
101
|
+
* @param thresholds - Signal quality thresholds
|
|
102
|
+
* @param onProgress - Progress callback (optional)
|
|
103
|
+
* @returns Coverage grid with multi-sector data
|
|
104
|
+
*/
|
|
105
|
+
export declare function calculateMultiSectorCoverage(sectors: SectorConfig[], gridSettings: GridSettings, thresholds: SignalThresholds, onProgress?: ProgressCallback): CoverageGrid;
|
|
106
|
+
/**
|
|
107
|
+
* Extract summary metrics for AI analysis
|
|
108
|
+
*
|
|
109
|
+
* Converts coverage grid into structured metrics suitable for
|
|
110
|
+
* AI interpretation and recommendation generation.
|
|
111
|
+
*
|
|
112
|
+
* @param grid - Coverage grid
|
|
113
|
+
* @returns Metrics object
|
|
114
|
+
*/
|
|
115
|
+
export declare function extractMetrics(grid: CoverageGrid): Record<string, number>;
|