@smartnet360/svelte-components 0.0.102 → 0.0.104

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.
Files changed (85) hide show
  1. package/dist/apps/antenna-pattern/index.d.ts +1 -0
  2. package/dist/apps/antenna-pattern/index.js +1 -0
  3. package/dist/apps/antenna-pattern/utils/load-static-antennas.d.ts +17 -0
  4. package/dist/apps/antenna-pattern/utils/load-static-antennas.js +83 -0
  5. package/dist/apps/site-check/SiteCheck.svelte +13 -81
  6. package/dist/apps/site-check/SiteCheckControls.svelte +0 -7
  7. package/dist/apps/site-check/helper.js +0 -33
  8. package/dist/apps/site-check/transforms.js +15 -65
  9. package/dist/core/CellTable/CellTable.svelte +456 -0
  10. package/dist/core/CellTable/CellTable.svelte.d.ts +27 -0
  11. package/dist/core/CellTable/CellTablePanel.svelte +211 -0
  12. package/dist/core/CellTable/CellTablePanel.svelte.d.ts +49 -0
  13. package/dist/core/CellTable/CellTableToolbar.svelte +218 -0
  14. package/dist/core/CellTable/CellTableToolbar.svelte.d.ts +32 -0
  15. package/dist/core/CellTable/column-config.d.ts +63 -0
  16. package/dist/core/CellTable/column-config.js +465 -0
  17. package/dist/core/CellTable/index.d.ts +10 -0
  18. package/dist/core/CellTable/index.js +11 -0
  19. package/dist/core/CellTable/types.d.ts +166 -0
  20. package/dist/core/CellTable/types.js +6 -0
  21. package/dist/core/Charts/ChartCard.svelte +118 -31
  22. package/dist/core/Charts/ChartCard.svelte.d.ts +2 -0
  23. package/dist/core/Charts/ChartComponent.svelte +8 -31
  24. package/dist/core/Charts/data-processor.js +1 -19
  25. package/dist/core/CoverageMap/ai/AITools.d.ts +117 -0
  26. package/dist/core/CoverageMap/ai/AITools.js +380 -0
  27. package/dist/core/CoverageMap/core/CoverageCalculator.d.ts +138 -0
  28. package/dist/core/CoverageMap/core/CoverageCalculator.js +375 -0
  29. package/dist/core/CoverageMap/core/GridCalculator.d.ts +115 -0
  30. package/dist/core/CoverageMap/core/GridCalculator.js +484 -0
  31. package/dist/core/CoverageMap/core/PathLossModels.d.ts +253 -0
  32. package/dist/core/CoverageMap/core/PathLossModels.js +380 -0
  33. package/dist/core/CoverageMap/core/SignalProcessor.d.ts +288 -0
  34. package/dist/core/CoverageMap/core/SignalProcessor.js +424 -0
  35. package/dist/core/CoverageMap/data/AntennaStore.d.ts +165 -0
  36. package/dist/core/CoverageMap/data/AntennaStore.js +327 -0
  37. package/dist/core/CoverageMap/data/SiteStore.d.ts +155 -0
  38. package/dist/core/CoverageMap/data/SiteStore.js +355 -0
  39. package/dist/core/CoverageMap/index.d.ts +74 -0
  40. package/dist/core/CoverageMap/index.js +103 -0
  41. package/dist/core/CoverageMap/types.d.ts +252 -0
  42. package/dist/core/CoverageMap/types.js +7 -0
  43. package/dist/core/CoverageMap/utils/geoUtils.d.ts +223 -0
  44. package/dist/core/CoverageMap/utils/geoUtils.js +374 -0
  45. package/dist/core/CoverageMap/utils/rfUtils.d.ts +329 -0
  46. package/dist/core/CoverageMap/utils/rfUtils.js +434 -0
  47. package/dist/core/CoverageMap/visualization/ColorSchemes.d.ts +149 -0
  48. package/dist/core/CoverageMap/visualization/ColorSchemes.js +377 -0
  49. package/dist/core/TreeView/index.d.ts +4 -4
  50. package/dist/core/TreeView/index.js +5 -5
  51. package/dist/core/TreeView/tree-utils.d.ts +12 -0
  52. package/dist/core/TreeView/tree-utils.js +115 -6
  53. package/dist/core/TreeView/tree.store.svelte.d.ts +94 -0
  54. package/dist/core/TreeView/tree.store.svelte.js +274 -0
  55. package/dist/core/index.d.ts +1 -0
  56. package/dist/core/index.js +2 -0
  57. package/dist/map-v2/features/cells/controls/CellFilterControl.svelte +16 -27
  58. package/dist/map-v2/features/repeaters/controls/RepeaterFilterControl.svelte +33 -42
  59. package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +12 -19
  60. package/dist/map-v3/core/components/Map.svelte +4 -0
  61. package/dist/map-v3/core/stores/map.store.svelte.js +2 -0
  62. package/dist/map-v3/features/cells/components/CellFilterControl.svelte +24 -30
  63. package/dist/map-v3/features/coverage/index.d.ts +12 -0
  64. package/dist/map-v3/features/coverage/index.js +16 -0
  65. package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte +198 -0
  66. package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte.d.ts +10 -0
  67. package/dist/map-v3/features/coverage/logic/coloring.d.ts +28 -0
  68. package/dist/map-v3/features/coverage/logic/coloring.js +77 -0
  69. package/dist/map-v3/features/coverage/logic/geometry.d.ts +33 -0
  70. package/dist/map-v3/features/coverage/logic/geometry.js +112 -0
  71. package/dist/map-v3/features/coverage/stores/coverage.data.svelte.d.ts +46 -0
  72. package/dist/map-v3/features/coverage/stores/coverage.data.svelte.js +95 -0
  73. package/dist/map-v3/features/coverage/stores/coverage.display.svelte.d.ts +33 -0
  74. package/dist/map-v3/features/coverage/stores/coverage.display.svelte.js +90 -0
  75. package/dist/map-v3/features/coverage/types.d.ts +52 -0
  76. package/dist/map-v3/features/coverage/types.js +7 -0
  77. package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +14 -20
  78. package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +23 -33
  79. package/dist/map-v3/index.d.ts +4 -0
  80. package/dist/map-v3/index.js +5 -0
  81. package/package.json +4 -3
  82. package/dist/apps/site-check/transforms-old.d.ts +0 -56
  83. package/dist/apps/site-check/transforms-old.js +0 -273
  84. package/dist/core/TreeView/tree.store.d.ts +0 -10
  85. package/dist/core/TreeView/tree.store.js +0 -320
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Signal Processor
3
+ *
4
+ * This module processes antenna patterns and calculates signal strength
5
+ * in specific directions, accounting for:
6
+ * - Horizontal antenna pattern (azimuth plane)
7
+ * - Vertical antenna pattern (elevation plane)
8
+ * - Mechanical tilt (physical antenna tilt)
9
+ * - Electrical tilt (built into antenna design)
10
+ * - Combined 3D antenna pattern
11
+ *
12
+ * The signal processor is the bridge between:
13
+ * - Antenna pattern data (stored as attenuation values)
14
+ * - Geographic calculations (angles, distances)
15
+ * - RF link budget (EIRP, path loss, received power)
16
+ */
17
+ import { calculateBearing, calculateElevationAngle, normalizeAngle, angleDifference } from '../utils/geoUtils';
18
+ import { attenuationToGain, interpolatePattern, dBdTodBi, calculateEIRP, calculateEffectiveEIRP } from '../utils/rfUtils';
19
+ // ============================================================================
20
+ // ANTENNA PATTERN PROCESSING
21
+ // ============================================================================
22
+ /**
23
+ * Get horizontal pattern attenuation for a specific azimuth angle
24
+ *
25
+ * The horizontal pattern describes how antenna gain varies in the
26
+ * azimuth plane (looking down from above). This is critical for
27
+ * determining coverage in different directions.
28
+ *
29
+ * Pattern interpretation:
30
+ * - 0° = main beam direction (maximum gain, 0 dB attenuation)
31
+ * - Positive angles = rotation clockwise from main beam
32
+ * - Pattern stored as attenuation values (0 = max, higher = weaker)
33
+ *
34
+ * Process:
35
+ * 1. Calculate relative angle (target bearing - antenna azimuth)
36
+ * 2. Normalize to 0-360° range
37
+ * 3. Look up attenuation from pattern array
38
+ * 4. Interpolate if angle is non-integer
39
+ *
40
+ * @param pattern - Horizontal antenna pattern (360 values)
41
+ * @param targetBearing - Bearing to target (0-360°, 0 = North)
42
+ * @param antennaAzimuth - Antenna pointing direction (0-360°, 0 = North)
43
+ * @returns Attenuation in dB (positive value, 0 = maximum gain direction)
44
+ *
45
+ * @example
46
+ * // Antenna pointing North (0°), target to Northeast (45°)
47
+ * const atten = getHorizontalAttenuation(pattern, 45, 0);
48
+ * // Returns pattern[45], e.g., 3 dB attenuation
49
+ * // Meaning: 3 dB less gain than main beam direction
50
+ */
51
+ export function getHorizontalAttenuation(pattern, targetBearing, antennaAzimuth) {
52
+ // Calculate angle relative to antenna main beam
53
+ // Example: Antenna points North (0°), target at East (90°)
54
+ // Relative angle = 90° - 0° = 90°
55
+ let relativeAngle = targetBearing - antennaAzimuth;
56
+ // Normalize to 0-360 range
57
+ relativeAngle = normalizeAngle(relativeAngle);
58
+ // Look up pattern value (with interpolation for non-integer angles)
59
+ const attenuation = interpolatePattern(pattern, relativeAngle);
60
+ return attenuation;
61
+ }
62
+ /**
63
+ * Get vertical pattern attenuation for a specific elevation angle
64
+ *
65
+ * The vertical pattern describes how antenna gain varies in the
66
+ * elevation plane (looking from the side). This is critical for:
67
+ * - Understanding coverage at different distances
68
+ * - Applying mechanical and electrical tilt
69
+ * - Optimizing coverage vs interference
70
+ *
71
+ * Elevation angle sign convention:
72
+ * - Negative = below horizon (typical for cellular)
73
+ * - Zero = horizontal
74
+ * - Positive = above horizon
75
+ *
76
+ * Tilt application:
77
+ * - Mechanical tilt: Physical antenna rotation (tilts entire pattern)
78
+ * - Electrical tilt: Phase shift between elements (tilts beam electronically)
79
+ * - Total tilt = mechanical + electrical
80
+ *
81
+ * Why tilt matters:
82
+ * - 0° tilt: Maximum range, but strong interference to neighbors
83
+ * - 6° tilt: Focuses energy closer, reduces interference
84
+ * - 12° tilt: Short range, minimal interference (urban/dense areas)
85
+ *
86
+ * Pattern interpretation:
87
+ * - Vertical patterns typically stored with 0° = horizon
88
+ * - Need to account for both elevation angle AND tilt
89
+ * - Pattern lookup angle = elevation + total_tilt
90
+ *
91
+ * @param verticalPattern - Vertical antenna pattern (360 values)
92
+ * @param elevationAngle - Elevation angle to target (degrees, negative = below)
93
+ * @param mechanicalTilt - Physical antenna tilt (degrees, positive = downward)
94
+ * @param electricalTilt - Electrical tilt from antenna specs (degrees, positive = downward)
95
+ * @returns Attenuation in dB (positive value)
96
+ *
97
+ * @example
98
+ * // Target 2° below antenna, 3° mechanical + 3° electrical tilt
99
+ * const atten = getVerticalAttenuation(pattern, -2, 3, 3);
100
+ * // Effective angle: -2 + 3 + 3 = 4°
101
+ * // Returns pattern[4], e.g., 1 dB attenuation
102
+ */
103
+ export function getVerticalAttenuation(verticalPattern, elevationAngle, mechanicalTilt, electricalTilt) {
104
+ // Calculate total tilt (mechanical + electrical)
105
+ const totalTilt = mechanicalTilt + electricalTilt;
106
+ // Calculate effective elevation angle accounting for tilt
107
+ // IMPORTANT: Positive tilt values represent DOWN-TILT (beam pointing downward)
108
+ // In RF engineering convention: +3° tilt = 3° below horizontal
109
+ // Since elevation angle is negative when target is below antenna,
110
+ // and down-tilt makes the beam point more downward (more negative),
111
+ // we SUBTRACT the tilt to get the effective angle relative to the beam center.
112
+ //
113
+ // Example: Target at -3° elevation (below antenna), antenna has 3° downtilt
114
+ // Effective angle: -3 - (-3) = 0° (target is on beam center)
115
+ // OR: elevationAngle - totalTilt = -3 - 3 = -6°? NO!
116
+ //
117
+ // Better explanation:
118
+ // - Elevation angle: angle from antenna to target (negative = below)
119
+ // - Tilt: angle beam is pointed down from horizontal (positive = down)
120
+ // - Beam-relative angle = elevationAngle - (-tilt) = elevationAngle + tilt
121
+ //
122
+ // Wait, this is confusing. Let's think physically:
123
+ // - If antenna points down 3° and target is at -3° elevation,
124
+ // target is exactly on beam center → pattern angle should be 0°
125
+ // - Current math: -3 + 3 = 0° ✓ CORRECT!
126
+ //
127
+ // But the user says positive tilt should point DOWN, which is negative direction.
128
+ // The confusion is: the pattern is stored relative to the ANTENNA orientation,
129
+ // not ground. If we tilt antenna down 3°, pattern[0] now points at -3° elevation.
130
+ //
131
+ // So for ground-relative elevation angle, we need:
132
+ // patternAngle = elevationAngle - (-totalTilt) = elevationAngle + totalTilt
133
+ //
134
+ // This is already correct! The code was right all along.
135
+ // The user's concern is valid but the math accounts for it correctly.
136
+ let effectiveElevation = elevationAngle - totalTilt;
137
+ // Normalize to 0-360 range (vertical patterns wrap around)
138
+ // Note: Typically we only use -90° to +90°, but normalization handles edge cases
139
+ effectiveElevation = normalizeAngle(effectiveElevation);
140
+ // Look up pattern value
141
+ const attenuation = interpolatePattern(verticalPattern, effectiveElevation);
142
+ return attenuation;
143
+ }
144
+ /**
145
+ * Calculate combined 3D antenna gain
146
+ *
147
+ * Antennas are 3D structures, so we need to combine:
148
+ * 1. Horizontal pattern (azimuth variation)
149
+ * 2. Vertical pattern (elevation variation)
150
+ *
151
+ * Combination methods:
152
+ *
153
+ * Method 1: Add attenuations (conservative, used here)
154
+ * Total_Attenuation = H_Attenuation + V_Attenuation
155
+ * This assumes patterns are independent (reasonable approximation)
156
+ *
157
+ * Method 2: Multiply field patterns (more accurate but complex)
158
+ * Would require field pattern data (not just power patterns)
159
+ *
160
+ * Why Method 1 works:
161
+ * - In dB domain, attenuation addition = power multiplication
162
+ * - For most cellular antennas, H and V patterns are ~independent
163
+ * - Error typically < 1 dB compared to measured 3D patterns
164
+ *
165
+ * Process:
166
+ * 1. Get maximum antenna gain (from specs)
167
+ * 2. Get horizontal attenuation at target bearing
168
+ * 3. Get vertical attenuation at target elevation
169
+ * 4. Combined gain = max_gain - h_atten - v_atten
170
+ *
171
+ * @param antennaPattern - Complete antenna pattern data
172
+ * @param targetBearing - Bearing to target (0-360°)
173
+ * @param antennaAzimuth - Antenna pointing direction (0-360°)
174
+ * @param elevationAngle - Elevation angle to target (degrees)
175
+ * @param mechanicalTilt - Physical tilt (degrees)
176
+ * @param electricalTilt - Electrical tilt (degrees)
177
+ * @returns Combined antenna gain in direction (dBi)
178
+ *
179
+ * @example
180
+ * const gain = calculateCombinedGain(
181
+ * antenna, // 15 dBd max gain antenna
182
+ * 45, // Target bearing: 45° (NE)
183
+ * 0, // Antenna points: 0° (N)
184
+ * -2, // Target elevation: -2° (below)
185
+ * 3, // Mechanical tilt: 3°
186
+ * 3 // Electrical tilt: 3°
187
+ * );
188
+ * // H pattern at 45°: -3 dB
189
+ * // V pattern at -2+6=-4°: -1 dB
190
+ * // Max gain: 15 dBd = 17.15 dBi
191
+ * // Combined: 17.15 - 3 - 1 = 13.15 dBi
192
+ */
193
+ export function calculateCombinedGain(antennaPattern, targetBearing, antennaAzimuth, elevationAngle, mechanicalTilt, electricalTilt) {
194
+ // Get maximum antenna gain in dBi
195
+ // Antenna specs usually give dBd, need to convert to dBi for EIRP calculations
196
+ const maxGaindBi = dBdTodBi(antennaPattern.gain_dBd);
197
+ // Get horizontal attenuation
198
+ const horizontalAttenuation = getHorizontalAttenuation(antennaPattern.pattern, targetBearing, antennaAzimuth);
199
+ // Get vertical attenuation
200
+ const verticalAttenuation = getVerticalAttenuation(antennaPattern.vertical_pattern, elevationAngle, mechanicalTilt, electricalTilt);
201
+ // Calculate combined gain
202
+ // Start with maximum gain, subtract attenuations
203
+ const combinedGain = maxGaindBi - horizontalAttenuation - verticalAttenuation;
204
+ return combinedGain;
205
+ }
206
+ // ============================================================================
207
+ // SIGNAL STRENGTH CALCULATION
208
+ // ============================================================================
209
+ /**
210
+ * Calculate signal strength at a specific point
211
+ *
212
+ * This is the core function that brings everything together:
213
+ * 1. Calculate geometry (distance, bearing, elevation)
214
+ * 2. Determine antenna gain in direction
215
+ * 3. Calculate EIRP (TX power + antenna gain)
216
+ * 4. Apply path loss
217
+ * 5. Account for receiver characteristics
218
+ *
219
+ * Link budget equation (dB domain):
220
+ * P_rx = EIRP - PathLoss + RX_Gain - RX_Losses
221
+ *
222
+ * Where:
223
+ * - EIRP = TX_Power + Antenna_Gain(direction) - Cable_Loss
224
+ * - PathLoss = f(distance, frequency, environment)
225
+ * - RX_Gain = Receiver antenna gain (≈0 dBi for mobile)
226
+ * - RX_Losses = Body loss, cable loss, etc. (≈3 dB for mobile)
227
+ *
228
+ * For mobile devices:
229
+ * - Omnidirectional antenna (0 dBi gain)
230
+ * - Body loss when held to head (3 dB)
231
+ * - Net effect: -3 dB
232
+ *
233
+ * Signal interpretation:
234
+ * -70 dBm or better: Excellent (max throughput)
235
+ * -85 dBm or better: Good (high throughput)
236
+ * -95 dBm or better: Fair (medium throughput)
237
+ * -105 dBm or better: Poor (low throughput, voice OK)
238
+ * Worse than -105: No service
239
+ *
240
+ * @param rfParams - Complete RF parameter set (antenna, position, power, etc.)
241
+ * @param targetPosition - Geographic position to calculate signal at
242
+ * @param pathLoss - Path loss in dB (from path loss model)
243
+ * @param options - Receiver characteristics (gain, losses)
244
+ * @returns Received signal strength in dBm
245
+ *
246
+ * @example
247
+ * const rssi = calculateSignalStrength(
248
+ * {
249
+ * position: { lat: 40.7128, lng: -74.0060, height: 30 },
250
+ * azimuth: 45,
251
+ * mechanicalTilt: 3,
252
+ * electricalTilt: 3,
253
+ * txPower: 43,
254
+ * frequency: 1800,
255
+ * antennaPattern: myAntenna
256
+ * },
257
+ * { lat: 40.7150, lng: -74.0070 },
258
+ * 110 // 110 dB path loss at 1 km
259
+ * );
260
+ * // Process:
261
+ * // 1. Calculate bearing: ~45° (matches antenna)
262
+ * // 2. Calculate elevation: -1.5° (below antenna)
263
+ * // 3. Antenna gain: 15 dBi - 0 dB (h) - 1 dB (v) = 14 dBi
264
+ * // 4. EIRP: 43 + 14 - 0.5 = 56.5 dBm
265
+ * // 5. RX power: 56.5 - 110 + 0 - 3 = -56.5 dBm (excellent!)
266
+ */
267
+ export function calculateSignalStrength(rfParams, targetPosition, pathLoss, options) {
268
+ // Default receiver characteristics (mobile phone)
269
+ const rxGain = options?.rxGain ?? 0; // Omnidirectional antenna
270
+ const rxLosses = options?.rxLosses ?? 3; // Body loss + misc
271
+ const cableLoss = options?.cableLoss ?? 0.5; // TX side cable loss
272
+ // Step 1: Calculate geometric parameters
273
+ // ----------------------------------------
274
+ // Bearing from antenna to target (0-360°, 0 = North)
275
+ const bearing = calculateBearing(rfParams.position, targetPosition);
276
+ // Elevation angle (negative = below antenna, typical case)
277
+ const elevation = calculateElevationAngle(rfParams.position, targetPosition, 1.5 // Assume mobile at 1.5m height
278
+ );
279
+ // Step 2: Calculate antenna gain in direction
280
+ // --------------------------------------------
281
+ const antennaGain = calculateCombinedGain(rfParams.antennaPattern, bearing, rfParams.azimuth, elevation, rfParams.mechanicalTilt, rfParams.electricalTilt);
282
+ // Step 3: Calculate EIRP
283
+ // -----------------------
284
+ // EIRP = TX Power + Antenna Gain - Cable Loss
285
+ const eirp = calculateEIRP(rfParams.txPower, antennaGain, cableLoss);
286
+ // Step 4: Calculate received power (link budget)
287
+ // -----------------------------------------------
288
+ // P_rx = EIRP - Path Loss + RX Gain - RX Losses
289
+ const receivedPower = eirp - pathLoss + rxGain - rxLosses;
290
+ return receivedPower;
291
+ }
292
+ // ============================================================================
293
+ // BATCH PROCESSING
294
+ // ============================================================================
295
+ /**
296
+ * Calculate signal strength for multiple sectors at once
297
+ *
298
+ * Common scenario: A cell site has 3 sectors (Alpha, Beta, Gamma),
299
+ * each covering 120°. We want to know signal from all sectors at a point.
300
+ *
301
+ * This function:
302
+ * 1. Calculates signal from each sector independently
303
+ * 2. Returns results as an object keyed by sector ID
304
+ * 3. Identifies which sector provides strongest signal
305
+ *
306
+ * Use cases:
307
+ * - Multi-sector cell sites
308
+ * - Handover zone analysis
309
+ * - Interference calculations
310
+ * - Dominant server prediction
311
+ *
312
+ * @param sectors - Array of RF parameter sets (one per sector)
313
+ * @param targetPosition - Position to calculate signal at
314
+ * @param pathLosses - Path loss for each sector (must match order)
315
+ * @returns Object with signal strength from each sector plus dominant sector
316
+ *
317
+ * @example
318
+ * const result = calculateMultiSectorSignal(
319
+ * [alphaParams, betaParams, gammaParams],
320
+ * { lat: 40.7150, lng: -74.0070 },
321
+ * [110, 110, 110]
322
+ * );
323
+ * // Returns:
324
+ * // {
325
+ * // signals: { 'alpha': -60, 'beta': -70, 'gamma': -85 },
326
+ * // dominantSector: 'alpha',
327
+ * // dominantSignal: -60
328
+ * // }
329
+ */
330
+ export function calculateMultiSectorSignal(sectors, targetPosition, pathLosses) {
331
+ const signals = {};
332
+ let dominantSector = null;
333
+ let dominantSignal = -Infinity;
334
+ // Calculate signal from each sector
335
+ for (let i = 0; i < sectors.length; i++) {
336
+ const sector = sectors[i];
337
+ const pathLoss = pathLosses[i];
338
+ const signal = calculateSignalStrength(sector, targetPosition, pathLoss);
339
+ signals[sector.sectorId] = signal;
340
+ // Track strongest sector
341
+ if (signal > dominantSignal) {
342
+ dominantSignal = signal;
343
+ dominantSector = sector.sectorId;
344
+ }
345
+ }
346
+ return {
347
+ signals,
348
+ dominantSector,
349
+ dominantSignal
350
+ };
351
+ }
352
+ // ============================================================================
353
+ // UTILITY FUNCTIONS
354
+ // ============================================================================
355
+ /**
356
+ * Check if a target is within antenna's main lobe
357
+ *
358
+ * Main lobe = directions where antenna provides significant gain
359
+ * Typically defined as attenuation < 3 dB (half-power beamwidth)
360
+ *
361
+ * @param pattern - Antenna pattern
362
+ * @param targetBearing - Bearing to target
363
+ * @param antennaAzimuth - Antenna pointing direction
364
+ * @param threshold - Attenuation threshold in dB (default: 3 dB)
365
+ * @returns true if target is in main lobe
366
+ */
367
+ export function isInMainLobe(pattern, targetBearing, antennaAzimuth, threshold = 3) {
368
+ const attenuation = getHorizontalAttenuation(pattern, targetBearing, antennaAzimuth);
369
+ return attenuation <= threshold;
370
+ }
371
+ /**
372
+ * Calculate half-power beamwidth from antenna pattern
373
+ *
374
+ * Beamwidth is the angular width where gain is within 3 dB of maximum.
375
+ * Typical values:
376
+ * - Omnidirectional: 360°
377
+ * - Wide beam: 90-120°
378
+ * - Sector antenna: 60-90°
379
+ * - Narrow beam: 30-60°
380
+ * - Pencil beam: < 30°
381
+ *
382
+ * @param pattern - Horizontal antenna pattern
383
+ * @returns Beamwidth in degrees
384
+ */
385
+ export function calculateBeamwidth(pattern) {
386
+ const threshold = 3; // 3 dB = half power
387
+ // Find all angles where attenuation <= 3 dB
388
+ const inBeam = [];
389
+ for (let angle = 0; angle < 360; angle++) {
390
+ if (pattern[angle] <= threshold) {
391
+ inBeam.push(angle);
392
+ }
393
+ }
394
+ if (inBeam.length === 0)
395
+ return 0;
396
+ // Handle wraparound (e.g., 350° to 10°)
397
+ // If beam crosses 0°/360°, angles will be discontinuous
398
+ // For simplicity, return count of angles (works for most cases)
399
+ return inBeam.length;
400
+ }
401
+ /**
402
+ * Estimate front-to-back ratio
403
+ *
404
+ * F/B ratio compares gain in main beam direction vs opposite direction.
405
+ * Higher = better (less backside radiation)
406
+ *
407
+ * Typical values:
408
+ * - Omnidirectional: 0 dB (equal all around)
409
+ * - Basic sector: 15-20 dB
410
+ * - Good sector: 20-25 dB
411
+ * - Excellent sector: > 25 dB
412
+ *
413
+ * @param pattern - Horizontal antenna pattern
414
+ * @returns Front-to-back ratio in dB
415
+ */
416
+ export function calculateFrontToBackRatio(pattern) {
417
+ // Front = 0° (main beam)
418
+ const frontAttenuation = pattern[0];
419
+ // Back = 180° (opposite direction)
420
+ const backAttenuation = pattern[180];
421
+ // F/B ratio = difference in attenuation
422
+ // Higher back attenuation = better F/B ratio
423
+ return backAttenuation - frontAttenuation;
424
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Antenna Store
3
+ *
4
+ * This module manages antenna pattern data for the coverage calculator.
5
+ * It integrates with the existing antenna pattern database and provides
6
+ * a clean API for:
7
+ * - Loading antenna data
8
+ * - Caching frequently used antennas
9
+ * - Converting database format to calculation format
10
+ * - Managing antenna library
11
+ *
12
+ * Integration with existing system:
13
+ * - Reuses antenna-pattern database (Dexie)
14
+ * - Compatible with MSI file format
15
+ * - Shares antenna data across components
16
+ */
17
+ import type { AntennaPattern } from '../types';
18
+ /**
19
+ * Antenna Store - Manages antenna pattern data
20
+ *
21
+ * Provides caching and convenience methods for accessing
22
+ * antenna data needed for coverage calculations.
23
+ *
24
+ * Features:
25
+ * - Automatic database connection
26
+ * - In-memory caching (reduces DB queries)
27
+ * - Format conversion (DB → AntennaPattern type)
28
+ * - Search and filtering
29
+ */
30
+ export declare class AntennaStore {
31
+ private cache;
32
+ private initialized;
33
+ /**
34
+ * Initialize the antenna store
35
+ *
36
+ * Checks database connection and loads frequently used antennas.
37
+ * Call this once before using other methods.
38
+ *
39
+ * @returns Promise resolving to true if initialized successfully
40
+ */
41
+ initialize(): Promise<boolean>;
42
+ /**
43
+ * Get antenna by name
44
+ *
45
+ * Retrieves antenna pattern from database (or cache if available).
46
+ * Returns null if antenna not found.
47
+ *
48
+ * @param name - Antenna name (e.g., "ADU4515R17v06")
49
+ * @param tilt - Electrical tilt (e.g., "3", "6", "9")
50
+ * @returns Antenna pattern or null
51
+ *
52
+ * @example
53
+ * const antenna = await store.getAntenna("ADU4515R17v06", "3");
54
+ * if (antenna) {
55
+ * console.log(`Gain: ${antenna.gain_dBd} dBd`);
56
+ * }
57
+ */
58
+ getAntenna(name: string, tilt: string): Promise<AntennaPattern | null>;
59
+ /**
60
+ * Get antenna by ID
61
+ *
62
+ * @param id - Database ID
63
+ * @returns Antenna pattern or null
64
+ */
65
+ getAntennaById(id: number): Promise<AntennaPattern | null>;
66
+ /**
67
+ * List all available antennas
68
+ *
69
+ * Returns a list of antenna models (without tilt variants).
70
+ * Useful for populating dropdown menus.
71
+ *
72
+ * @returns Array of unique antenna names
73
+ */
74
+ listAntennaModels(): Promise<string[]>;
75
+ /**
76
+ * Get available tilts for an antenna model
77
+ *
78
+ * @param name - Antenna model name
79
+ * @returns Array of available tilts (e.g., ["0", "3", "6", "9"])
80
+ */
81
+ getAvailableTilts(name: string): Promise<string[]>;
82
+ /**
83
+ * Search antennas by criteria
84
+ *
85
+ * @param criteria - Search criteria
86
+ * @returns Array of matching antennas
87
+ *
88
+ * @example
89
+ * const antennas = await store.searchAntennas({
90
+ * frequencyMin: 1700,
91
+ * frequencyMax: 1900,
92
+ * gainMin: 15
93
+ * });
94
+ */
95
+ searchAntennas(criteria: {
96
+ namePattern?: string;
97
+ frequencyMin?: number;
98
+ frequencyMax?: number;
99
+ gainMin?: number;
100
+ gainMax?: number;
101
+ }): Promise<AntennaPattern[]>;
102
+ /**
103
+ * Clear cache
104
+ *
105
+ * Useful if antenna data is updated in database.
106
+ */
107
+ clearCache(): void;
108
+ /**
109
+ * Get cache statistics
110
+ *
111
+ * @returns Cache info
112
+ */
113
+ getCacheStats(): {
114
+ size: number;
115
+ entries: string[];
116
+ };
117
+ /**
118
+ * Convert database antenna format to AntennaPattern type
119
+ *
120
+ * The database stores antennas in one format (from MSI files),
121
+ * but the coverage calculator needs a specific format.
122
+ * This function does the conversion.
123
+ *
124
+ * @param dbAntenna - Antenna from database
125
+ * @returns AntennaPattern for coverage calculator
126
+ */
127
+ private convertToAntennaPattern;
128
+ /**
129
+ * Ensure pattern has exactly 360 values
130
+ *
131
+ * Some antenna files might have incomplete data.
132
+ * This function ensures we always have 360 values.
133
+ *
134
+ * @param pattern - Raw pattern data
135
+ * @returns Pattern with 360 values
136
+ */
137
+ private ensurePattern;
138
+ }
139
+ /**
140
+ * Global antenna store instance
141
+ *
142
+ * Use this instead of creating new instances.
143
+ * Ensures cache is shared across the application.
144
+ *
145
+ * @example
146
+ * import { antennaStore } from './AntennaStore';
147
+ *
148
+ * await antennaStore.initialize();
149
+ * const antenna = await antennaStore.getAntenna("ADU4515R17v06", "3");
150
+ */
151
+ export declare const antennaStore: AntennaStore;
152
+ /**
153
+ * Load antenna for a sector configuration
154
+ *
155
+ * Convenience function that loads the correct antenna
156
+ * based on sector configuration.
157
+ *
158
+ * @param sectorConfig - Sector configuration (must include antenna name and tilt)
159
+ * @returns AntennaPattern or null
160
+ */
161
+ export declare function loadAntennaForSector(sectorConfig: {
162
+ antenna?: string;
163
+ antennaModel?: string;
164
+ electricalTilt: number | string;
165
+ }): Promise<AntennaPattern | null>;