@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.
- 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 +13 -81
- package/dist/apps/site-check/SiteCheckControls.svelte +0 -7
- package/dist/apps/site-check/helper.js +0 -33
- package/dist/apps/site-check/transforms.js +15 -65
- package/dist/core/CellTable/CellTable.svelte +456 -0
- package/dist/core/CellTable/CellTable.svelte.d.ts +27 -0
- package/dist/core/CellTable/CellTablePanel.svelte +211 -0
- package/dist/core/CellTable/CellTablePanel.svelte.d.ts +49 -0
- package/dist/core/CellTable/CellTableToolbar.svelte +218 -0
- package/dist/core/CellTable/CellTableToolbar.svelte.d.ts +32 -0
- package/dist/core/CellTable/column-config.d.ts +63 -0
- package/dist/core/CellTable/column-config.js +465 -0
- package/dist/core/CellTable/index.d.ts +10 -0
- package/dist/core/CellTable/index.js +11 -0
- package/dist/core/CellTable/types.d.ts +166 -0
- package/dist/core/CellTable/types.js +6 -0
- package/dist/core/Charts/ChartCard.svelte +118 -31
- package/dist/core/Charts/ChartCard.svelte.d.ts +2 -0
- package/dist/core/Charts/ChartComponent.svelte +8 -31
- package/dist/core/Charts/data-processor.js +1 -19
- 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/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -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 +4 -3
- package/dist/apps/site-check/transforms-old.d.ts +0 -56
- package/dist/apps/site-check/transforms-old.js +0 -273
- package/dist/core/TreeView/tree.store.d.ts +0 -10
- 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>;
|