Skip to content

Commit

Permalink
Merge pull request #824 from helium/mj/mobile-coverage-points-calculator
Browse files Browse the repository at this point in the history
Mobile Coverage points calculator
  • Loading branch information
michaeldjeffrey authored Jun 12, 2024
2 parents 315cb85 + 0399213 commit aecb7bc
Show file tree
Hide file tree
Showing 8 changed files with 1,950 additions and 0 deletions.
54 changes: 54 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ debug = true
members = [
"boost_manager",
"coverage_map",
"coverage_point_calculator",
"custom_tracing",
"db_store",
"denylist",
Expand Down
20 changes: 20 additions & 0 deletions coverage_point_calculator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "coverage-point-calculator"
version = "0.1.0"
description = "Calculate Coverage Points for hotspots in the Mobile Network"
authors.workspace = true
license.workspace = true
edition.workspace = true

[dependencies]
chrono = { workspace = true }
hextree = { workspace = true }
rust_decimal = { workspace = true }
rust_decimal_macros = { workspace = true }
thiserror = { workspace = true }
hex-assignments = { path = "../hex_assignments" }
coverage-map = { path = "../coverage_map" }

[dev-dependencies]
helium-crypto = { workspace = true }
rstest = { version = "0.21.0", default-features = false }
133 changes: 133 additions & 0 deletions coverage_point_calculator/src/hexes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use coverage_map::RankedCoverage;
use hex_assignments::assignment::HexAssignments;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;

use crate::{BoostedHexStatus, RadioType, Result};

#[derive(Debug, Clone)]
pub struct CoveredHex {
pub hex: hextree::Cell,
/// Default points received from (RadioType, SignalLevel) pair.
pub base_coverage_points: Decimal,
/// Coverage points including assignment, rank, and boosted hex multipliers.
pub calculated_coverage_points: Decimal,
/// Oracle boosted Assignments
pub assignments: HexAssignments,
pub assignment_multiplier: Decimal,
/// [RankedCoverage::rank] 1-based
pub rank: usize,
pub rank_multiplier: Decimal,
/// Provider boosted multiplier. Will be None if the Radio does not qualify
/// for boosted rewards.
pub boosted_multiplier: Option<Decimal>,
}

pub(crate) fn clean_covered_hexes(
radio_type: RadioType,
boosted_hex_status: BoostedHexStatus,
ranked_coverage: Vec<RankedCoverage>,
) -> Result<Vec<CoveredHex>> {
// verify all hexes can obtain a base coverage point
let covered_hexes = ranked_coverage
.into_iter()
.map(|ranked| {
let base_coverage_points = radio_type.base_coverage_points(&ranked.signal_level)?;
let rank_multiplier = radio_type.rank_multiplier(ranked.rank);

let boosted_multiplier = if boosted_hex_status.is_eligible() {
ranked.boosted.map(|boost| boost.get()).map(Decimal::from)
} else {
None
};

// hip-103: if a hex is boosted by a service provider >=1x, the oracle
// multiplier will automatically be 1x, regardless of boosted_hex_status.
let assignment_multiplier = if ranked.boosted.is_some() {
dec!(1)
} else {
ranked.assignments.boosting_multiplier()
};

let calculated_coverage_points = base_coverage_points
* assignment_multiplier
* rank_multiplier
* boosted_multiplier.unwrap_or(dec!(1));

Ok(CoveredHex {
hex: ranked.hex,
base_coverage_points,
calculated_coverage_points,
assignments: ranked.assignments,
assignment_multiplier,
rank: ranked.rank,
rank_multiplier,
boosted_multiplier,
})
})
.collect::<Result<Vec<_>>>()?;

Ok(covered_hexes)
}

pub(crate) fn calculated_coverage_points(covered_hexes: &[CoveredHex]) -> Decimal {
covered_hexes
.iter()
.map(|hex| hex.calculated_coverage_points)
.sum()
}

#[cfg(test)]
mod tests {
use rstest::rstest;
use std::num::NonZeroU32;

use coverage_map::SignalLevel;
use hex_assignments::Assignment;

use super::*;

#[rstest]
#[case(BoostedHexStatus::Eligible)]
#[case(BoostedHexStatus::WifiLocationScoreBelowThreshold(dec!(999)))]
#[case(BoostedHexStatus::RadioThresholdNotMet)]
fn hip_103_provider_boosted_hex_receives_maximum_oracle_boost(
#[case] boost_status: BoostedHexStatus,
) {
// Regardless of the radio's eligibility to receive provider boosted
// rewards, a boosted hex increases the oracle assignment.
let unboosted_coverage = RankedCoverage {
hotspot_key: vec![1],
cbsd_id: None,
hex: hextree::Cell::from_raw(0x8c2681a3064edff).unwrap(),
rank: 1,
signal_level: SignalLevel::High,
assignments: HexAssignments {
footfall: Assignment::C,
landtype: Assignment::C,
urbanized: Assignment::C,
},
boosted: NonZeroU32::new(0),
};
let boosted_coverage = RankedCoverage {
boosted: NonZeroU32::new(5),
..unboosted_coverage.clone()
};

let covered_hexes = clean_covered_hexes(
RadioType::IndoorWifi,
boost_status,
vec![unboosted_coverage, boosted_coverage],
)
.unwrap();

let unboosted = &covered_hexes[0];
let boosted = &covered_hexes[1];

// unboosted receives original multiplier
assert_eq!(dec!(0), unboosted.assignment_multiplier);

// provider boosted gets oracle assignment bumped to 1x
assert_eq!(dec!(1), boosted.assignment_multiplier);
}
}
Loading

0 comments on commit aecb7bc

Please sign in to comment.