UNPKG

arfcn

Version:

Utilities for working with Absolute Radio Frequency Channel Numbers (ARFCN) in cellular networks

274 lines (247 loc) 7.74 kB
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 }