Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sharc): imt spectral mask #108

Draft
wants to merge 2 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions sharc/mask/spectral_mask_imt.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ class SpectralMaskImt(SpectralMask):
alternative_mask_used (bool): represents whether the alternative mask should be used or not
ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE (int): A hardcoded value that specifies how many samples of a diagonal
line may be taken. Is needed because oob_power is calculated expecting rectangles, so we approximate
the diagonal with 'SAMPLESIZE' rectangles
the diagonal with 'SAMPLESIZE' rectangles. Its value is 50 because the related documents specify
density per 100KHz through 5MHz bandwidth (5M/100K = 50).
"""
ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE: int = 40
ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE: int = 50

def __init__(
self,
Expand Down Expand Up @@ -161,8 +162,8 @@ def set_mask(self, p_tx=0):

def get_alternative_mask_delta_f_lim(self, freq_mhz: float, band_mhz: float) -> np.array:
"""
Implements spectral masks for IMT-2020 outdoor BS's when freq < 26GHz,
according to Documents ITU-R SM.1541-6, ITU-R SM.1539-1 and ETSI TS 138 104 V16.6.0.
Implements spectral masks for IMT-2020 outdoor BS's when freq < 24.25GHz,
according to Documents ITU-R SM.1541-6, ITU-R SM.1539-1 and ETSI TS 138 104 V16.6.0.
Reference tables are:
- Table 1 in ITU-R SM.1541-6
- Table 2 in ITU-R SM.1539-1
Expand Down Expand Up @@ -190,7 +191,7 @@ def get_alternative_mask_delta_f_lim(self, freq_mhz: float, band_mhz: float) ->
elif (freq_mhz > 10000 and freq_mhz <= 15000):
B_L = 0.3
B_U = 250
elif (freq_mhz > 15000 and freq_mhz <= 26000):
elif (freq_mhz > 15000 and freq_mhz <= 24500):
B_L = 0.5
B_U = 500
else:
Expand All @@ -209,22 +210,20 @@ def get_alternative_mask_delta_f_lim(self, freq_mhz: float, band_mhz: float) ->
delta_f_spurious = B_N_separation

diagonal = np.array([
i * 5 / self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE for i in range(
self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE,
0.05 + i * 5 / self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE for i in range(
self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE
)
])

# band/2 is subtracted from delta_f_spurious beacuse that specific interval is from frequency center
rest_of_oob_and_spurious = np.array(
[5, 10.0, delta_f_spurious - band_mhz / 2],
)
rest_of_oob_and_spurious = np.array([5.05, 10.05, delta_f_spurious - band_mhz/2])

return np.concatenate((diagonal, rest_of_oob_and_spurious))

def get_alternative_mask_mask_dbm(self, power: float = 0) -> np.array:
"""
Implements spectral masks for IMT-2020 outdoor BS's when freq < 26GHz,
according to Documents ITU-R SM.1541-6, ITU-R SM.1539-1 and ETSI TS 138 104 V16.6.0.
Implements spectral masks for IMT-2020 outdoor BS's when freq < 24.25GHz,
according to Documents ITU-R SM.1541-6, ITU-R SM.1539-1 and ETSI TS 138 104 V16.6.0.
Reference tables are:
- Table 1 in ITU-R SM.1541-6
- Table 2 in ITU-R SM.1539-1
Expand All @@ -235,32 +234,33 @@ def get_alternative_mask_mask_dbm(self, power: float = 0) -> np.array:
"""
self.p_tx = power - 10 * np.log10(self.band_mhz)

# @important we added 10dB to the spectral densities that were x/100KHz
# this way we have the value as x/1MHz.
# @important When analyzing the mask this conversion should be EXPLICITLY mentioned.
# However, numerically, after computing the oob_power, both give the same result
if self.spurious_emissions == -13:
# Category A, so
# use document ETSI TS 138 104 V16.6.0 Table 6.6.4.2.1-2
diagonal_samples = np.array([
-7 - 7 / 5 *
(i * 5 / self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE)
for i in range(self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE)
10 -7 -7/5 * (0.05 + i * 5 / self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE)
for i in range(self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE)
])
rest_of_oob_and_spurious = np.array([
-14, -13, self.spurious_emissions,
10 -14, -13, self.spurious_emissions
])
mask_dbm = np.concatenate(
(diagonal_samples, rest_of_oob_and_spurious),
)
elif self.spurious_emissions == -30:
# Category B, so
# use document ETSI TS 138 104 V16.6.0 Table 6.6.4.2.2.1-2
diagonal_samples = np.array([
-7 - 7 / 5 *
(i * 5 / self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE)
10 -7 -7/5 *
(0.05 + i * 5 / self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE)
for i in range(self.ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE)
])
rest_of_oob_and_spurious = np.array(
[-14, -15, self.spurious_emissions],
)
mask_dbm = np.concatenate(
(diagonal_samples, rest_of_oob_and_spurious),
)
rest_of_oob_and_spurious = np.array([10 -14, -15, self.spurious_emissions])
mask_dbm = np.concatenate((diagonal_samples, rest_of_oob_and_spurious))
else:
raise ValueError(
"Alternative mask should only be used for spurious emissions -13 and -30",
Expand Down
25 changes: 11 additions & 14 deletions tests/test_spectral_mask_imt.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,21 +140,19 @@ def test_power_calc(self):
fc = 9200
band = 200
poob = self.mask_bs_9GHz.power_calc(fc, band)
# for this test to pass, alternative mask sample size needed to be at
# least approx. 30 for OOB start
self.assertAlmostEqual(poob, 10.09, delta=1e-2)
self.assertAlmostEqual(poob, 12.17, delta=1e-2)

# Test 2 - BS
fc = 8800
band = 200
poob = self.mask_bs_9GHz.power_calc(fc, band)
self.assertAlmostEqual(poob, 10.09, delta=1e-2)
self.assertAlmostEqual(poob, 12.17, delta=1e-2)

# Test 3 - BS
fc = 9400
band = 400
fc = 9205.05
band = 200
poob = self.mask_bs_9GHz.power_calc(fc, band)
self.assertAlmostEqual(poob, 13.02, delta=1e-2)
self.assertAlmostEqual(poob, 10.71, delta=1e-2)

#######################################################################
# Testing mask for 9 GHz and -30dBm/MHz spurious emissions (alternative mask, for BS only)
Expand All @@ -164,21 +162,20 @@ def test_power_calc(self):
fc = 9200
band = 200
poob = self.mask_bs_9GHz_30_spurious.power_calc(fc, band)
# for this test to pass, 'ALTERNATIVE_MASK_DIAGONAL_SAMPLESIZE' needed
# to be at least approx. 40
self.assertAlmostEqual(poob, 8.26, delta=1e-2)
self.assertAlmostEqual(poob, 11.12, delta=1e-2)


# Test 2 - BS
fc = 8800
band = 200
poob = self.mask_bs_9GHz_30_spurious.power_calc(fc, band)
self.assertAlmostEqual(poob, 8.26, delta=1e-2)
self.assertAlmostEqual(poob, 11.12, delta=1e-2)

# Test 3 - BS
fc = 9400
band = 400
fc = 9205.05
band = 200
poob = self.mask_bs_9GHz_30_spurious.power_calc(fc, band)
self.assertAlmostEqual(poob, 8.14, delta=1e-2)
self.assertAlmostEqual(poob, 9.12, delta=1e-2)


if __name__ == '__main__':
Expand Down
Loading