Cell towers were being displayed at incorrect physical locations on the map. They were using a simplistic circular distribution around the user's location rather than realistic position estimation based on signal strength.
File: MapViewModel.kt (lines 149-153)
The old code was spreading towers in a simple circle:
val angle = (index * 60.0) * (Math.PI / 180.0)
val radius = 0.0004 // About 40 meters (static distance)
val towerLat = latitude + (radius * kotlin.math.cos(angle))
val towerLon = longitude + (radius * kotlin.math.sin(angle))Problems:
- Used only the tower index for angle calculation, making placement arbitrary
- Fixed radius of ~40 meters for all towers regardless of signal strength
- Ignored the sophisticated
CellTowerLocationEstimatorutility that was already implemented - Towers didn't reflect their actual distance from the user based on signal strength
File: CellTowerLocationEstimator.kt
The codebase already had a complete location estimation system that:
- Calculates tower distance using path loss models (free-space, urban, indoor)
- Uses different reference signals for each network type (2G/3G/4G/5G)
- Applies network-specific path loss exponents for accurate distance estimation
- Uses cell ID to determine consistent angular direction (so same tower always appears in same spot)
- Converts distance to latitude/longitude offsets with proper Earth radius calculations
But it was completely ignored by the MapViewModel.
Added import statement to MapViewModel:
import com.stingrayshield.util.CellTowerLocationEstimatorChanged from hardcoded circular placement to intelligent position estimation for each cell type:
LTE (4G):
val (towerLat, towerLon) = CellTowerLocationEstimator.estimateTowerPosition(
latitude, longitude, sig.dbm, "LTE", cellId
)WCDMA (3G):
val (towerLat, towerLon) = CellTowerLocationEstimator.estimateTowerPosition(
latitude, longitude, sig.dbm, "3G", cellId
)GSM (2G):
val (towerLat, towerLon) = CellTowerLocationEstimator.estimateTowerPosition(
latitude, longitude, sig.dbm, "2G", cellId
)CDMA:
val (towerLat, towerLon) = CellTowerLocationEstimator.estimateTowerPosition(
latitude, longitude, sig.dbm, "CDMA", id.basestationId
)5G NR:
val (towerLat, towerLon) = CellTowerLocationEstimator.estimateTowerPosition(
latitude, longitude, sig.dbm, "5G", nci.toInt()
)The estimator uses the path loss formula:
signalStrength = referenceSignal - 10 * n * log10(d/d0)
Solving for d: d = d0 * 10^((referenceSignal - signalStrength) / (10 * n))
Where:
- referenceSignal: Reference signal at 1 meter (varies by network type)
- n: Path loss exponent (2.0 for free space, 2.7-3.5 for urban, 3.0-5.0 for indoor)
- d: Calculated distance in meters
- Clamped range: 50 meters (min) to 35 km (max) for realistic tower distances
val angle = (cellId % 360) * (PI / 180.0)Uses the cell ID modulo 360 to generate a consistent angle. This means:
- Same tower ID always appears in the same direction
- Different towers get different angular positions
- Much more realistic distribution
val latOffset = (distance * cos(angle)) / 111320.0
val lngOffset = (distance * sin(angle)) / (111320.0 * cos(userLat * PI / 180.0))Converts distance in meters to geographic offsets using:
- 1 degree latitude ≈ 111,320 meters
- 1 degree longitude ≈ 111,320 * cos(latitude) meters
The estimator uses different parameters for each network type to account for their propagation characteristics:
| Network | Reference Signal @ 1m | Path Loss Exponent | Frequency Range |
|---|---|---|---|
| 5G NR | -40 dBm | 2.5 | Sub-6 GHz / mmWave |
| 4G LTE | -45 dBm | 3.0 | 700-2600 MHz |
| 3G UMTS | -50 dBm | 3.2 | 850-2100 MHz |
| 2G GSM | -55 dBm | 3.5 | 850-1900 MHz |
| CDMA | -50 dBm | 3.0 | 800-1900 MHz |
✅ Stronger signal towers appear closer to the user's location ✅ Weaker signal towers appear further away ✅ Same tower ID always appears in the same direction ✅ Realistic distances based on cellular propagation models ✅ Network-aware positioning (5G behaves differently than 2G) ✅ Better visual distribution on the map for detection analysis
- Move to different locations and verify tower positions adjust accordingly
- Compare signal strength with displayed distance (stronger = closer)
- Check consistency - same towers should always be in similar positions relative to you
- Test different network types (LTE, 5G, etc.) to see different distance characteristics
- Verify map patterns - primary/serving towers should typically be closer than neighbor cells
app/src/main/java/com/stingrayshield/ui/screens/map/MapViewModel.kt- Added import for
CellTowerLocationEstimator - Updated
createCellTower()function for all network types (LTE, WCDMA, GSM, CDMA, NR)
- Added import for
✅ Build successful with no compilation errors ✅ All deprecation warnings are pre-existing (unrelated to these changes)