@smartnet360/svelte-components 0.0.101 → 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/cells/utils/cellGeoJSON.js +1 -0
- 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/demo/DemoMap.svelte +31 -5
- package/dist/map-v3/demo/demo-cells.js +51 -22
- package/dist/map-v3/features/cells/components/CellFilterControl.svelte +24 -30
- package/dist/map-v3/features/cells/layers/CellsLayer.svelte +29 -9
- package/dist/map-v3/features/cells/logic/geometry.js +3 -0
- package/dist/map-v3/features/cells/stores/cell.data.svelte.d.ts +27 -0
- package/dist/map-v3/features/cells/stores/cell.data.svelte.js +65 -0
- 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/selection/components/FeatureSelectionControl.svelte +82 -65
- package/dist/map-v3/features/selection/components/FeatureSelectionControl.svelte.d.ts +5 -9
- package/dist/map-v3/features/selection/index.d.ts +1 -2
- package/dist/map-v3/features/selection/index.js +0 -1
- package/dist/map-v3/features/selection/stores/selection.store.svelte.d.ts +44 -15
- package/dist/map-v3/features/selection/stores/selection.store.svelte.js +163 -40
- package/dist/map-v3/features/selection/types.d.ts +4 -2
- 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
- package/dist/map-v3/features/selection/layers/SelectionHighlightLayers.svelte +0 -209
- package/dist/map-v3/features/selection/layers/SelectionHighlightLayers.svelte.d.ts +0 -13
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RF Utility Functions
|
|
3
|
+
*
|
|
4
|
+
* This module provides heavily commented RF (Radio Frequency) calculations:
|
|
5
|
+
* - dB arithmetic (addition, subtraction, conversion)
|
|
6
|
+
* - EIRP (Effective Isotropic Radiated Power) calculations
|
|
7
|
+
* - Link budget calculations
|
|
8
|
+
* - Power conversions (dBm, watts, dBd, dBi)
|
|
9
|
+
*
|
|
10
|
+
* RF calculations are done in dB (decibel) domain because:
|
|
11
|
+
* 1. Multiplication becomes addition (easier math)
|
|
12
|
+
* 2. Large dynamic range (can represent 1mW to 1MW easily)
|
|
13
|
+
* 3. Industry standard
|
|
14
|
+
*/
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// CONSTANTS
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Conversion factor between dBd and dBi
|
|
20
|
+
*
|
|
21
|
+
* dBd = gain relative to dipole antenna
|
|
22
|
+
* dBi = gain relative to isotropic antenna (theoretical point source)
|
|
23
|
+
*
|
|
24
|
+
* Relationship: dBi = dBd + 2.15
|
|
25
|
+
*
|
|
26
|
+
* Example:
|
|
27
|
+
* A 10 dBd antenna = 12.15 dBi
|
|
28
|
+
* Most commercial specs use dBd, calculations often need dBi
|
|
29
|
+
*/
|
|
30
|
+
export const DBD_TO_DBI = 2.15;
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// dB CONVERSIONS AND ARITHMETIC
|
|
33
|
+
// ============================================================================
|
|
34
|
+
/**
|
|
35
|
+
* Convert dBm to watts (linear power)
|
|
36
|
+
*
|
|
37
|
+
* dBm is power relative to 1 milliwatt on a logarithmic scale:
|
|
38
|
+
* P_dBm = 10 × log₁₀(P_mW / 1mW)
|
|
39
|
+
*
|
|
40
|
+
* To convert back to watts:
|
|
41
|
+
* P_watts = 10^(P_dBm / 10) / 1000
|
|
42
|
+
*
|
|
43
|
+
* Common values to remember:
|
|
44
|
+
* 0 dBm = 1 mW = 0.001 W
|
|
45
|
+
* 10 dBm = 10 mW = 0.01 W
|
|
46
|
+
* 20 dBm = 100 mW = 0.1 W
|
|
47
|
+
* 30 dBm = 1 W
|
|
48
|
+
* 40 dBm = 10 W
|
|
49
|
+
* 43 dBm = 20 W (typical cellular base station)
|
|
50
|
+
*
|
|
51
|
+
* @param dBm - Power in dBm
|
|
52
|
+
* @returns Power in watts
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const power = dBmToWatts(43); // Typical cell site TX power
|
|
56
|
+
* // Returns: 19.95 watts (≈ 20W)
|
|
57
|
+
*/
|
|
58
|
+
export function dBmToWatts(dBm) {
|
|
59
|
+
// Step 1: Convert dBm to milliwatts using inverse log
|
|
60
|
+
const milliwatts = Math.pow(10, dBm / 10);
|
|
61
|
+
// Step 2: Convert milliwatts to watts
|
|
62
|
+
const watts = milliwatts / 1000;
|
|
63
|
+
return watts;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convert watts to dBm
|
|
67
|
+
*
|
|
68
|
+
* @param watts - Power in watts
|
|
69
|
+
* @returns Power in dBm
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* const dBm = wattsTodBm(20); // 20 watt transmitter
|
|
73
|
+
* // Returns: 43.01 dBm
|
|
74
|
+
*/
|
|
75
|
+
export function wattsTodBm(watts) {
|
|
76
|
+
// Convert watts to milliwatts
|
|
77
|
+
const milliwatts = watts * 1000;
|
|
78
|
+
// Convert to dBm using logarithm
|
|
79
|
+
const dBm = 10 * Math.log10(milliwatts);
|
|
80
|
+
return dBm;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Convert dBd to dBi
|
|
84
|
+
*
|
|
85
|
+
* dBd = gain relative to half-wave dipole antenna
|
|
86
|
+
* dBi = gain relative to isotropic radiator
|
|
87
|
+
*
|
|
88
|
+
* An isotropic antenna radiates equally in all directions (theoretical).
|
|
89
|
+
* A dipole has slight directionality, giving it 2.15 dB gain over isotropic.
|
|
90
|
+
*
|
|
91
|
+
* Therefore: dBi = dBd + 2.15
|
|
92
|
+
*
|
|
93
|
+
* @param dBd - Gain in dBd
|
|
94
|
+
* @returns Gain in dBi
|
|
95
|
+
*/
|
|
96
|
+
export function dBdTodBi(dBd) {
|
|
97
|
+
return dBd + DBD_TO_DBI;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Convert dBi to dBd
|
|
101
|
+
*
|
|
102
|
+
* @param dBi - Gain in dBi
|
|
103
|
+
* @returns Gain in dBd
|
|
104
|
+
*/
|
|
105
|
+
export function dBiTodBd(dBi) {
|
|
106
|
+
return dBi - DBD_TO_DBI;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Add two power values in dB domain
|
|
110
|
+
*
|
|
111
|
+
* IMPORTANT: You cannot just add dB values to combine powers!
|
|
112
|
+
*
|
|
113
|
+
* Correct method:
|
|
114
|
+
* 1. Convert both dB values to linear (watts)
|
|
115
|
+
* 2. Add the linear values
|
|
116
|
+
* 3. Convert back to dB
|
|
117
|
+
*
|
|
118
|
+
* Formula:
|
|
119
|
+
* P_total_dB = 10 × log₁₀(10^(P1_dB/10) + 10^(P2_dB/10))
|
|
120
|
+
*
|
|
121
|
+
* Special cases:
|
|
122
|
+
* - Two equal powers: P_total = P1 + 3 dB (doubling power = +3 dB)
|
|
123
|
+
* - Powers differ by >10 dB: P_total ≈ larger value (small contribution ignored)
|
|
124
|
+
*
|
|
125
|
+
* @param dB1 - First power in dB
|
|
126
|
+
* @param dB2 - Second power in dB
|
|
127
|
+
* @returns Combined power in dB
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* const combined = addPowersdB(20, 20); // Two 20 dBm sources
|
|
131
|
+
* // Returns: 23 dBm (not 40!)
|
|
132
|
+
*/
|
|
133
|
+
export function addPowersdB(dB1, dB2) {
|
|
134
|
+
// Convert to linear scale
|
|
135
|
+
const linear1 = Math.pow(10, dB1 / 10);
|
|
136
|
+
const linear2 = Math.pow(10, dB2 / 10);
|
|
137
|
+
// Add in linear domain
|
|
138
|
+
const totalLinear = linear1 + linear2;
|
|
139
|
+
// Convert back to dB
|
|
140
|
+
const totaldB = 10 * Math.log10(totalLinear);
|
|
141
|
+
return totaldB;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Subtract two power values in dB domain (for interference calculations)
|
|
145
|
+
*
|
|
146
|
+
* This calculates the power remaining after subtracting one power from another.
|
|
147
|
+
*
|
|
148
|
+
* Formula:
|
|
149
|
+
* P_diff_dB = 10 × log₁₀(10^(P1_dB/10) - 10^(P2_dB/10))
|
|
150
|
+
*
|
|
151
|
+
* Note: Returns -Infinity if P2 >= P1 (can't subtract more than you have)
|
|
152
|
+
*
|
|
153
|
+
* @param dB1 - First power in dB (larger)
|
|
154
|
+
* @param dB2 - Second power in dB (smaller)
|
|
155
|
+
* @returns Difference in dB
|
|
156
|
+
*/
|
|
157
|
+
export function subtractPowersdB(dB1, dB2) {
|
|
158
|
+
const linear1 = Math.pow(10, dB1 / 10);
|
|
159
|
+
const linear2 = Math.pow(10, dB2 / 10);
|
|
160
|
+
if (linear1 <= linear2) {
|
|
161
|
+
return -Infinity; // Can't subtract more than we have
|
|
162
|
+
}
|
|
163
|
+
const diffLinear = linear1 - linear2;
|
|
164
|
+
const diffdB = 10 * Math.log10(diffLinear);
|
|
165
|
+
return diffdB;
|
|
166
|
+
}
|
|
167
|
+
// ============================================================================
|
|
168
|
+
// EIRP CALCULATIONS
|
|
169
|
+
// ============================================================================
|
|
170
|
+
/**
|
|
171
|
+
* Calculate EIRP (Effective Isotropic Radiated Power)
|
|
172
|
+
*
|
|
173
|
+
* EIRP is the apparent power radiated by an antenna in the direction of peak gain.
|
|
174
|
+
* It combines:
|
|
175
|
+
* 1. Transmitter power
|
|
176
|
+
* 2. Cable/connector losses (reduces power)
|
|
177
|
+
* 3. Antenna gain (increases power in specific direction)
|
|
178
|
+
*
|
|
179
|
+
* Formula (in dB domain - simple addition!):
|
|
180
|
+
* EIRP = P_tx + G_antenna - L_cable
|
|
181
|
+
*
|
|
182
|
+
* Where:
|
|
183
|
+
* - P_tx = Transmit power (dBm)
|
|
184
|
+
* - G_antenna = Antenna gain (dBi) - must be in dBi, not dBd!
|
|
185
|
+
* - L_cable = Cable and connector losses (dB) - positive value
|
|
186
|
+
*
|
|
187
|
+
* In linear domain, this would be multiplication/division:
|
|
188
|
+
* EIRP = P_tx × G_antenna / L_cable
|
|
189
|
+
*
|
|
190
|
+
* Why EIRP matters:
|
|
191
|
+
* - Determines maximum signal strength at receiver
|
|
192
|
+
* - Regulatory limits often specified as EIRP
|
|
193
|
+
* - Used in link budget calculations
|
|
194
|
+
*
|
|
195
|
+
* Typical values:
|
|
196
|
+
* - Small cell: 30-40 dBm EIRP
|
|
197
|
+
* - Macro cell: 50-65 dBm EIRP
|
|
198
|
+
* - Microwave link: 40-50 dBm EIRP
|
|
199
|
+
*
|
|
200
|
+
* @param txPowerdBm - Transmit power in dBm
|
|
201
|
+
* @param antennaGaindBi - Antenna gain in dBi (not dBd!)
|
|
202
|
+
* @param cableLossdB - Cable and connector losses in dB (default: 0.5 dB)
|
|
203
|
+
* @returns EIRP in dBm
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* const eirp = calculateEIRP(43, 15, 1);
|
|
207
|
+
* // 43 dBm TX power (20W)
|
|
208
|
+
* // + 15 dBi antenna gain
|
|
209
|
+
* // - 1 dB cable loss
|
|
210
|
+
* // = 57 dBm EIRP (500W equivalent isotropic)
|
|
211
|
+
*/
|
|
212
|
+
export function calculateEIRP(txPowerdBm, antennaGaindBi, cableLossdB = 0.5) {
|
|
213
|
+
// In dB domain, this is simple addition/subtraction
|
|
214
|
+
const eirp = txPowerdBm + antennaGaindBi - cableLossdB;
|
|
215
|
+
return eirp;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Calculate effective EIRP in a specific direction
|
|
219
|
+
*
|
|
220
|
+
* Antennas don't radiate equally in all directions - they have patterns.
|
|
221
|
+
* This calculates the EIRP in a specific direction by:
|
|
222
|
+
* 1. Starting with maximum EIRP (boresight direction)
|
|
223
|
+
* 2. Subtracting the pattern attenuation for the given direction
|
|
224
|
+
*
|
|
225
|
+
* Formula:
|
|
226
|
+
* EIRP_direction = EIRP_max - Attenuation(angle)
|
|
227
|
+
*
|
|
228
|
+
* Where:
|
|
229
|
+
* - EIRP_max = Maximum EIRP (boresight)
|
|
230
|
+
* - Attenuation(angle) = Pattern loss at specific angle (dB)
|
|
231
|
+
*
|
|
232
|
+
* Example:
|
|
233
|
+
* - EIRP_max = 60 dBm
|
|
234
|
+
* - At 30° off boresight, pattern shows -3 dB
|
|
235
|
+
* - EIRP at 30° = 60 - 3 = 57 dBm
|
|
236
|
+
*
|
|
237
|
+
* @param maxEIRP - Maximum EIRP (dBm)
|
|
238
|
+
* @param patternAttenuation - Pattern attenuation at angle (dB, positive value)
|
|
239
|
+
* @returns Effective EIRP in direction (dBm)
|
|
240
|
+
*/
|
|
241
|
+
export function calculateEffectiveEIRP(maxEIRP, patternAttenuation) {
|
|
242
|
+
return maxEIRP - patternAttenuation;
|
|
243
|
+
}
|
|
244
|
+
// ============================================================================
|
|
245
|
+
// LINK BUDGET CALCULATIONS
|
|
246
|
+
// ============================================================================
|
|
247
|
+
/**
|
|
248
|
+
* Calculate received signal strength
|
|
249
|
+
*
|
|
250
|
+
* This is the fundamental equation for RF coverage prediction:
|
|
251
|
+
*
|
|
252
|
+
* P_received = EIRP - PathLoss + RX_Gain - RX_Losses
|
|
253
|
+
*
|
|
254
|
+
* Where (all in dB):
|
|
255
|
+
* - EIRP = Effective Isotropic Radiated Power (dBm)
|
|
256
|
+
* - PathLoss = Free space or propagation loss (dB) - positive value
|
|
257
|
+
* - RX_Gain = Receiver antenna gain (dBi) - usually small for mobiles
|
|
258
|
+
* - RX_Losses = Receiver losses, body loss, etc. (dB) - positive value
|
|
259
|
+
*
|
|
260
|
+
* For mobile devices:
|
|
261
|
+
* - RX_Gain ≈ 0 dBi (omnidirectional antenna)
|
|
262
|
+
* - RX_Losses ≈ 3 dB (body loss, hand proximity)
|
|
263
|
+
* - Net effect: -3 dB
|
|
264
|
+
*
|
|
265
|
+
* Interpretation:
|
|
266
|
+
* - P_received > -70 dBm: Excellent signal
|
|
267
|
+
* - P_received > -85 dBm: Good signal
|
|
268
|
+
* - P_received > -95 dBm: Fair signal (data services OK)
|
|
269
|
+
* - P_received > -105 dBm: Poor signal (voice calls only)
|
|
270
|
+
* - P_received < -105 dBm: No service
|
|
271
|
+
*
|
|
272
|
+
* @param eirp - Effective radiated power in direction (dBm)
|
|
273
|
+
* @param pathLoss - Path loss in dB (positive value)
|
|
274
|
+
* @param rxGain - Receiver antenna gain in dBi (default: 0)
|
|
275
|
+
* @param rxLosses - Receiver losses in dB (default: 3)
|
|
276
|
+
* @returns Received signal strength in dBm
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* const rxPower = calculateReceivedPower(57, 110, 0, 3);
|
|
280
|
+
* // 57 dBm EIRP
|
|
281
|
+
* // - 110 dB path loss (1 km at 700 MHz)
|
|
282
|
+
* // + 0 dBi receiver gain
|
|
283
|
+
* // - 3 dB receiver losses
|
|
284
|
+
* // = -56 dBm (excellent signal)
|
|
285
|
+
*/
|
|
286
|
+
export function calculateReceivedPower(eirp, pathLoss, rxGain = 0, rxLosses = 3) {
|
|
287
|
+
// Link budget equation in dB domain
|
|
288
|
+
const receivedPower = eirp - pathLoss + rxGain - rxLosses;
|
|
289
|
+
return receivedPower;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Calculate maximum range for a given signal threshold
|
|
293
|
+
*
|
|
294
|
+
* This inverts the link budget equation to find the distance where
|
|
295
|
+
* received power equals a specific threshold (e.g., -95 dBm).
|
|
296
|
+
*
|
|
297
|
+
* Starting from:
|
|
298
|
+
* P_rx = EIRP - PathLoss(d)
|
|
299
|
+
*
|
|
300
|
+
* Rearranging:
|
|
301
|
+
* PathLoss(d) = EIRP - P_rx_threshold
|
|
302
|
+
*
|
|
303
|
+
* Then using path loss model (e.g., FSPL):
|
|
304
|
+
* FSPL = 32.45 + 20log₁₀(d_km) + 20log₁₀(f_MHz)
|
|
305
|
+
*
|
|
306
|
+
* Solving for d:
|
|
307
|
+
* d_km = 10^((FSPL - 32.45 - 20log₁₀(f_MHz)) / 20)
|
|
308
|
+
*
|
|
309
|
+
* This gives us the theoretical maximum range in ideal conditions.
|
|
310
|
+
*
|
|
311
|
+
* @param eirp - Effective radiated power (dBm)
|
|
312
|
+
* @param frequencyMHz - Frequency in MHz
|
|
313
|
+
* @param threshold - Minimum received power (dBm), e.g., -95
|
|
314
|
+
* @param rxGain - Receiver gain (dBi), default: 0
|
|
315
|
+
* @param rxLosses - Receiver losses (dB), default: 3
|
|
316
|
+
* @returns Maximum range in kilometers
|
|
317
|
+
*/
|
|
318
|
+
export function calculateMaxRange(eirp, frequencyMHz, threshold, rxGain = 0, rxLosses = 3) {
|
|
319
|
+
// Calculate allowed path loss
|
|
320
|
+
// This is how much loss we can tolerate before signal drops below threshold
|
|
321
|
+
const allowedPathLoss = eirp - threshold + rxGain - rxLosses;
|
|
322
|
+
// Use FSPL model: FSPL = 32.45 + 20log₁₀(d_km) + 20log₁₀(f_MHz)
|
|
323
|
+
// Solve for d_km:
|
|
324
|
+
const frequencyTerm = 20 * Math.log10(frequencyMHz);
|
|
325
|
+
const exponent = (allowedPathLoss - 32.45 - frequencyTerm) / 20;
|
|
326
|
+
const maxRangeKm = Math.pow(10, exponent);
|
|
327
|
+
return maxRangeKm;
|
|
328
|
+
}
|
|
329
|
+
// ============================================================================
|
|
330
|
+
// ANTENNA PATTERN UTILITIES
|
|
331
|
+
// ============================================================================
|
|
332
|
+
/**
|
|
333
|
+
* Convert antenna pattern attenuation to gain
|
|
334
|
+
*
|
|
335
|
+
* Antenna pattern files typically store attenuation values:
|
|
336
|
+
* - 0 dB = maximum gain (boresight direction)
|
|
337
|
+
* - Positive values = attenuation (less gain)
|
|
338
|
+
* - Example: 3 dB attenuation = 3 dB below maximum
|
|
339
|
+
*
|
|
340
|
+
* To get actual gain in a direction:
|
|
341
|
+
* Gain(angle) = MaxGain - Attenuation(angle)
|
|
342
|
+
*
|
|
343
|
+
* @param maxGain - Maximum antenna gain (dBi or dBd)
|
|
344
|
+
* @param attenuation - Pattern attenuation at angle (dB)
|
|
345
|
+
* @returns Effective gain at angle
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* const gain = attenuationToGain(15, 3);
|
|
349
|
+
* // 15 dBi max gain
|
|
350
|
+
* // - 3 dB attenuation at 30°
|
|
351
|
+
* // = 12 dBi gain at 30°
|
|
352
|
+
*/
|
|
353
|
+
export function attenuationToGain(maxGain, attenuation) {
|
|
354
|
+
return maxGain - attenuation;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Interpolate antenna pattern value for non-integer angles
|
|
358
|
+
*
|
|
359
|
+
* Antenna patterns typically have 360 samples (one per degree).
|
|
360
|
+
* For angles between samples, use linear interpolation.
|
|
361
|
+
*
|
|
362
|
+
* @param pattern - Array of 360 pattern values
|
|
363
|
+
* @param angle - Angle in degrees (can be non-integer)
|
|
364
|
+
* @returns Interpolated pattern value
|
|
365
|
+
*/
|
|
366
|
+
export function interpolatePattern(pattern, angle) {
|
|
367
|
+
// Normalize angle to 0-360 range
|
|
368
|
+
angle = ((angle % 360) + 360) % 360;
|
|
369
|
+
// Get floor and ceiling indices
|
|
370
|
+
const idx1 = Math.floor(angle);
|
|
371
|
+
const idx2 = Math.ceil(angle) % 360;
|
|
372
|
+
// If angle is integer, return exact value
|
|
373
|
+
if (idx1 === idx2) {
|
|
374
|
+
return pattern[idx1];
|
|
375
|
+
}
|
|
376
|
+
// Linear interpolation
|
|
377
|
+
const fraction = angle - idx1;
|
|
378
|
+
const value1 = pattern[idx1];
|
|
379
|
+
const value2 = pattern[idx2];
|
|
380
|
+
return value1 + (value2 - value1) * fraction;
|
|
381
|
+
}
|
|
382
|
+
// ============================================================================
|
|
383
|
+
// UTILITY FUNCTIONS
|
|
384
|
+
// ============================================================================
|
|
385
|
+
/**
|
|
386
|
+
* Classify signal quality based on received power
|
|
387
|
+
*
|
|
388
|
+
* @param rssi - Received signal strength in dBm
|
|
389
|
+
* @returns Quality category
|
|
390
|
+
*/
|
|
391
|
+
export function classifySignalQuality(rssi) {
|
|
392
|
+
if (rssi >= -70)
|
|
393
|
+
return 'excellent';
|
|
394
|
+
if (rssi >= -85)
|
|
395
|
+
return 'good';
|
|
396
|
+
if (rssi >= -95)
|
|
397
|
+
return 'fair';
|
|
398
|
+
if (rssi >= -105)
|
|
399
|
+
return 'poor';
|
|
400
|
+
return 'no-signal';
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Calculate signal-to-noise ratio (SNR)
|
|
404
|
+
*
|
|
405
|
+
* SNR = Signal Power - Noise Power (both in dBm)
|
|
406
|
+
*
|
|
407
|
+
* Typical noise floor: -100 to -120 dBm depending on bandwidth
|
|
408
|
+
*
|
|
409
|
+
* @param signalPower - Signal power in dBm
|
|
410
|
+
* @param noisePower - Noise power in dBm
|
|
411
|
+
* @returns SNR in dB
|
|
412
|
+
*/
|
|
413
|
+
export function calculateSNR(signalPower, noisePower) {
|
|
414
|
+
return signalPower - noisePower;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Calculate thermal noise floor
|
|
418
|
+
*
|
|
419
|
+
* Formula: N = -174 + 10log₁₀(BW) + NF
|
|
420
|
+
*
|
|
421
|
+
* Where:
|
|
422
|
+
* - -174 dBm/Hz is thermal noise density at room temperature
|
|
423
|
+
* - BW is bandwidth in Hz
|
|
424
|
+
* - NF is receiver noise figure in dB (typically 5-9 dB)
|
|
425
|
+
*
|
|
426
|
+
* @param bandwidthHz - Signal bandwidth in Hz
|
|
427
|
+
* @param noiseFigure - Receiver noise figure in dB (default: 7)
|
|
428
|
+
* @returns Noise floor in dBm
|
|
429
|
+
*/
|
|
430
|
+
export function calculateNoiseFloor(bandwidthHz, noiseFigure = 7) {
|
|
431
|
+
const thermalNoise = -174; // dBm/Hz
|
|
432
|
+
const bandwidthTerm = 10 * Math.log10(bandwidthHz);
|
|
433
|
+
return thermalNoise + bandwidthTerm + noiseFigure;
|
|
434
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Schemes
|
|
3
|
+
*
|
|
4
|
+
* This module defines color schemes for coverage visualization.
|
|
5
|
+
* Supports multiple visualization styles:
|
|
6
|
+
* - Heatmap: Gradient from strong to weak signal
|
|
7
|
+
* - Categorical: Discrete quality levels
|
|
8
|
+
* - Single color: Opacity-based (for sector identification)
|
|
9
|
+
*
|
|
10
|
+
* All colors designed to be:
|
|
11
|
+
* - Colorblind-friendly (where possible)
|
|
12
|
+
* - Print-friendly
|
|
13
|
+
* - Intuitive (red = strong, blue = weak for heatmap)
|
|
14
|
+
*/
|
|
15
|
+
import type { SignalQuality, SignalThresholds } from '../types';
|
|
16
|
+
export interface ColorStop {
|
|
17
|
+
value: number;
|
|
18
|
+
color: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ColorScheme {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
stops: ColorStop[];
|
|
24
|
+
noSignalColor: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Heatmap - Red to Blue gradient
|
|
28
|
+
*
|
|
29
|
+
* Classic heatmap where:
|
|
30
|
+
* - Red = Excellent signal (hot)
|
|
31
|
+
* - Orange/Yellow = Good signal (warm)
|
|
32
|
+
* - Green = Fair signal (cool)
|
|
33
|
+
* - Blue = Poor signal (cold)
|
|
34
|
+
* - Gray = No signal
|
|
35
|
+
*
|
|
36
|
+
* This is intuitive for most users.
|
|
37
|
+
*/
|
|
38
|
+
export declare const HEATMAP_RED_BLUE: ColorScheme;
|
|
39
|
+
/**
|
|
40
|
+
* Heatmap - Green to Red (inverted)
|
|
41
|
+
*
|
|
42
|
+
* Alternative where:
|
|
43
|
+
* - Green = Excellent (good status)
|
|
44
|
+
* - Yellow = Fair (warning)
|
|
45
|
+
* - Red = Poor (problem)
|
|
46
|
+
*
|
|
47
|
+
* More intuitive for traffic light metaphor.
|
|
48
|
+
*/
|
|
49
|
+
export declare const HEATMAP_GREEN_RED: ColorScheme;
|
|
50
|
+
/**
|
|
51
|
+
* Categorical - Discrete quality levels
|
|
52
|
+
*
|
|
53
|
+
* Uses distinct colors for each quality level.
|
|
54
|
+
* No gradients - clear boundaries.
|
|
55
|
+
* Good for presentations and reports.
|
|
56
|
+
*/
|
|
57
|
+
export declare const CATEGORICAL: ColorScheme;
|
|
58
|
+
/**
|
|
59
|
+
* Viridis-inspired (colorblind-friendly)
|
|
60
|
+
*
|
|
61
|
+
* Based on matplotlib's viridis colormap.
|
|
62
|
+
* Optimized for colorblind users and grayscale printing.
|
|
63
|
+
*/
|
|
64
|
+
export declare const VIRIDIS: ColorScheme;
|
|
65
|
+
/**
|
|
66
|
+
* Plasma-inspired (high contrast)
|
|
67
|
+
*
|
|
68
|
+
* High contrast colormap for visibility.
|
|
69
|
+
*/
|
|
70
|
+
export declare const PLASMA: ColorScheme;
|
|
71
|
+
/**
|
|
72
|
+
* All available color schemes
|
|
73
|
+
*/
|
|
74
|
+
export declare const COLOR_SCHEMES: Record<string, ColorScheme>;
|
|
75
|
+
/**
|
|
76
|
+
* Default color scheme
|
|
77
|
+
*/
|
|
78
|
+
export declare const DEFAULT_COLOR_SCHEME: ColorScheme;
|
|
79
|
+
/**
|
|
80
|
+
* Interpolate color for a specific signal strength
|
|
81
|
+
*
|
|
82
|
+
* Uses linear interpolation between color stops to create
|
|
83
|
+
* smooth gradients.
|
|
84
|
+
*
|
|
85
|
+
* Process:
|
|
86
|
+
* 1. Find two nearest color stops
|
|
87
|
+
* 2. Calculate interpolation factor
|
|
88
|
+
* 3. Interpolate RGB components
|
|
89
|
+
* 4. Convert back to hex
|
|
90
|
+
*
|
|
91
|
+
* @param signalDbm - Signal strength in dBm
|
|
92
|
+
* @param scheme - Color scheme to use
|
|
93
|
+
* @returns Hex color string
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* const color = interpolateColor(-75, HEATMAP_RED_BLUE);
|
|
97
|
+
* // Returns color between -70 (orange-red) and -80 (orange)
|
|
98
|
+
* // e.g., "#FF7400"
|
|
99
|
+
*/
|
|
100
|
+
export declare function interpolateColor(signalDbm: number, scheme: ColorScheme): string;
|
|
101
|
+
/**
|
|
102
|
+
* Get color for categorical quality level
|
|
103
|
+
*
|
|
104
|
+
* Returns discrete color based on quality level without interpolation.
|
|
105
|
+
*
|
|
106
|
+
* @param quality - Signal quality level
|
|
107
|
+
* @param scheme - Color scheme (optional, defaults to categorical)
|
|
108
|
+
* @returns Hex color string
|
|
109
|
+
*/
|
|
110
|
+
export declare function getCategoricalColor(quality: SignalQuality, scheme?: ColorScheme): string;
|
|
111
|
+
/**
|
|
112
|
+
* Get color based on signal thresholds
|
|
113
|
+
*
|
|
114
|
+
* Convenience function that combines threshold checking and coloring.
|
|
115
|
+
*
|
|
116
|
+
* @param signalDbm - Signal strength in dBm
|
|
117
|
+
* @param thresholds - Signal quality thresholds
|
|
118
|
+
* @param scheme - Color scheme to use
|
|
119
|
+
* @returns Hex color string
|
|
120
|
+
*/
|
|
121
|
+
export declare function getColorForSignal(signalDbm: number, thresholds: SignalThresholds, scheme?: ColorScheme): string;
|
|
122
|
+
/**
|
|
123
|
+
* Add opacity to hex color
|
|
124
|
+
*
|
|
125
|
+
* Converts hex to rgba with specified opacity.
|
|
126
|
+
*
|
|
127
|
+
* @param hex - Hex color
|
|
128
|
+
* @param opacity - Opacity (0-1)
|
|
129
|
+
* @returns RGBA color string
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* const semi = addOpacity("#FF0000", 0.5);
|
|
133
|
+
* // Returns: "rgba(255, 0, 0, 0.5)"
|
|
134
|
+
*/
|
|
135
|
+
export declare function addOpacity(hex: string, opacity: number): string;
|
|
136
|
+
/**
|
|
137
|
+
* Create legend entries for a color scheme
|
|
138
|
+
*
|
|
139
|
+
* Generates legend data for UI display.
|
|
140
|
+
*
|
|
141
|
+
* @param scheme - Color scheme
|
|
142
|
+
* @param thresholds - Signal thresholds
|
|
143
|
+
* @returns Array of legend entries
|
|
144
|
+
*/
|
|
145
|
+
export declare function createLegend(scheme: ColorScheme, thresholds: SignalThresholds): Array<{
|
|
146
|
+
label: string;
|
|
147
|
+
color: string;
|
|
148
|
+
range: string;
|
|
149
|
+
}>;
|