arfcn
Version:
Utilities for working with Absolute Radio Frequency Channel Numbers (ARFCN) in cellular networks
274 lines (247 loc) • 7.74 kB
text/typescript
import { LinkDirection } from './NrEutraCalculators'
import { UtraFddBands } from './UtraFddBands'
import { UtraTddBands } from './UtraTddBands'
function _fddUlXtraUarfcnToBands(uarfcn: number) {
return UtraFddBands.rows.filter((_r) => {
return _r.n_ul_xtra?.includes(uarfcn)
})
}
function _fddDlXtraUarfcnToBands(uarfcn: number) {
return UtraFddBands.rows.filter((_r) => {
return _r.n_dl_xtra?.includes(uarfcn)
})
}
function _fddUlGeneralUarfcnToBands(uarfcn: number) {
return UtraFddBands.rows.filter((_r) => {
return (
_r.n_ul_lo !== null &&
_r.n_ul_hi !== null &&
uarfcn >= _r.n_ul_lo &&
uarfcn <= _r.n_ul_hi
)
})
}
function _fddDlGeneralUarfcnToBands(uarfcn: number) {
return UtraFddBands.rows.filter((_r) => {
return (
_r.n_dl_lo !== null &&
_r.n_dl_hi !== null &&
uarfcn >= _r.n_dl_lo &&
uarfcn <= _r.n_dl_hi
)
})
}
function _tddXtraUarfcnToBands(uarfcn: number) {
return UtraTddBands.rows.filter((_r) => {
return _r.n_xtra?.includes(uarfcn)
})
}
function _tddGeneralUarfcnToBands(uarfcn: number) {
return UtraTddBands.rows.filter((_r) => {
return (
_r.n_lo !== null &&
_r.n_hi !== null &&
uarfcn >= _r.n_lo &&
uarfcn <= _r.n_hi
)
})
}
function _uarfcnBandSort(a: number | string, b: number | string) {
if (typeof a === 'number' && typeof b === 'number') {
return a - b
} else if (typeof a === 'number') {
return -1
} else if (typeof b === 'number') {
return 1
}
return a.localeCompare(b)
}
export function uarfcnToBands(
uarfcn: number,
searchOptions: {
direction?: LinkDirection
duplexMode?: 'FDD' | 'TDD' | 'All'
channelType?: 'General' | 'Additional' | 'All'
} = {
direction: LinkDirection.Unspecified,
duplexMode: 'All',
channelType: 'All',
}
): (number | string)[] {
if (!Number.isFinite(uarfcn) || uarfcn < 0) {
return []
}
const so = {
direction: searchOptions.direction ?? LinkDirection.Unspecified,
duplexMode: searchOptions.duplexMode ?? 'All',
channelType: searchOptions.channelType ?? 'All',
}
let results: (number | string)[] = []
if (so.duplexMode === 'FDD' || so.duplexMode === 'All') {
if (
so.direction === LinkDirection.Uplink ||
so.direction === LinkDirection.Unspecified
) {
if (so.channelType === 'Additional' || so.channelType === 'All') {
const bands = _fddUlXtraUarfcnToBands(uarfcn)
if (bands.length > 0) {
results.push(...bands.map((_r) => _r.band))
}
}
if (so.channelType === 'General' || so.channelType === 'All') {
const bands = _fddUlGeneralUarfcnToBands(uarfcn)
if (bands.length > 0) {
results.push(...bands.map((_r) => _r.band))
}
}
}
if (
so.direction === LinkDirection.Downlink ||
so.direction === LinkDirection.Unspecified
) {
if (so.channelType === 'Additional' || so.channelType === 'All') {
const bands = _fddDlXtraUarfcnToBands(uarfcn)
if (bands.length > 0) {
results.push(...bands.map((_r) => _r.band))
}
}
if (so.channelType === 'General' || so.channelType === 'All') {
const bands = _fddDlGeneralUarfcnToBands(uarfcn)
if (bands.length > 0) {
results.push(...bands.map((_r) => _r.band))
}
}
}
}
if (so.duplexMode === 'TDD' || so.duplexMode === 'All') {
if (so.channelType === 'Additional' || so.channelType === 'All') {
const bands = _tddXtraUarfcnToBands(uarfcn)
if (bands.length > 0) {
results.push(...bands.map((_r) => _r.band))
}
}
if (so.channelType === 'General' || so.channelType === 'All') {
const bands = _tddGeneralUarfcnToBands(uarfcn)
if (bands.length > 0) {
results.push(...bands.map((_r) => _r.band))
}
}
}
return Array.from(new Set(results)).sort(_uarfcnBandSort)
}
export function uarfcnToFrequencyFdd(
uarfcn: number,
band: number,
channelType: 'General' | 'Additional'
): number {
if (!Number.isFinite(uarfcn) || uarfcn < 0) {
return -1
}
const rows = UtraFddBands.rows.filter((_r) => {
return _r.band === band
})
if (rows.length === 1) {
const row = rows[0]
if (channelType === 'General') {
if (uarfcn >= row.n_dl_lo && uarfcn <= row.n_dl_hi) {
return uarfcn / 5 + row.f_dl_offset
}
if (
row.n_ul_lo !== null &&
row.n_ul_hi !== null &&
row.f_ul_offset !== null &&
uarfcn >= row.n_ul_lo &&
uarfcn <= row.n_ul_hi
) {
return uarfcn / 5 + row.f_ul_offset
}
} else if (channelType === 'Additional') {
if (
row.n_dl_xtra &&
row.f_dl_xtra_offset !== null &&
row.n_dl_xtra.includes(uarfcn)
) {
return uarfcn / 5 + row.f_dl_xtra_offset
} else if (
row.n_ul_xtra &&
row.f_ul_xtra_offset !== null &&
row.n_ul_xtra.includes(uarfcn)
) {
return uarfcn / 5 + row.f_ul_xtra_offset
}
}
}
return -1
}
export function uarfcnToFrequencyTdd(
uarfcn: number,
channelType: 'General' | 'Additional'
): number {
if (channelType === 'General') {
return uarfcn / 5
} else if (channelType === 'Additional') {
return uarfcn / 5 + 2150.1
}
return -1
}
export function frequencyToUarfcnFdd(
frequencyMhz: number,
band: number
): number {
if (!Number.isFinite(frequencyMhz) || frequencyMhz < 0) {
return -1
}
// For UTRA general channels, the channel raster is integer mutliples of 200 kHz.
// For UTRA additional channels, the channel raster is integer mutliples of 200 kHz shifted by 100 kHz.
// This also applies to TDD channels. The only TDD band that has channels shifted by 100 kHz is band 'd'.
// See 3GPP TS 25.101 V18.0.0 (2024-03) Section 5.4.2 Channel raster,
// and 3GPP TS 25.102 V18.0.0 (2024-03) Section 5.4.2 Channel raster
const isXtra = (frequencyMhz * 10) % 2 === 1
const rows = UtraFddBands.rows.filter((_r) => {
return _r.band === band
})
if (rows.length !== 1) {
return -1
}
const row = rows[0]
if (isXtra) {
if (
row.f_dl_xtra_offset !== null &&
row.f_dl_xtra !== null &&
row.f_dl_xtra.includes(frequencyMhz)
) {
return Math.round(5 * (frequencyMhz - row.f_dl_xtra_offset) * 10) / 10
} else if (
row.f_ul_xtra_offset !== null &&
row.f_ul_xtra !== null &&
row.f_ul_xtra.includes(frequencyMhz)
) {
return Math.round(5 * (frequencyMhz - row.f_ul_xtra_offset) * 10) / 10
}
} else {
if (frequencyMhz >= row.f_dl_lo && frequencyMhz <= row.f_dl_hi) {
return Math.round(5 * (frequencyMhz - row.f_dl_offset) * 10) / 10
}
if (
row.f_ul_lo !== null &&
row.f_ul_hi !== null &&
row.f_ul_offset !== null &&
frequencyMhz >= row.f_ul_lo &&
frequencyMhz <= row.f_ul_hi
) {
return Math.round(5 * (frequencyMhz - row.f_ul_offset) * 10) / 10
}
}
return -1
}
export function frequencyToUarfcnTdd(frequencyMhz: number): number {
if (!Number.isFinite(frequencyMhz) || frequencyMhz < 0) {
return -1
}
// See 3GPP TS 25.102 V18.0.0 (2024-03) Section 5.4.2 Channel raster
const isXtra = (frequencyMhz * 10) % 2 === 1
if (isXtra) {
return Math.round(5 * (frequencyMhz - 2150.1) * 10) / 10
}
return Math.round(5 * frequencyMhz * 10) / 10
}