Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # -*- coding: utf-8 -*- |
| 3 | # @file |
Piotr Krysik | a6268a5 | 2017-08-23 16:02:19 +0200 | [diff] [blame] | 4 | # @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com> |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 5 | # @section LICENSE |
| 6 | # |
| 7 | # Gr-gsm is free software; you can redistribute it and/or modify |
| 8 | # it under the terms of the GNU General Public License as published by |
| 9 | # the Free Software Foundation; either version 3, or (at your option) |
| 10 | # any later version. |
| 11 | # |
| 12 | # Gr-gsm is distributed in the hope that it will be useful, |
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | # GNU General Public License for more details. |
| 16 | # |
| 17 | # You should have received a copy of the GNU General Public License |
| 18 | # along with gr-gsm; see the file COPYING. If not, write to |
| 19 | # the Free Software Foundation, Inc., 51 Franklin Street, |
| 20 | # Boston, MA 02110-1301, USA. |
| 21 | # |
| 22 | # |
| 23 | |
Roman Khassraf | e7e75f0 | 2015-08-22 19:26:12 +0200 | [diff] [blame] | 24 | import collections |
| 25 | |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 26 | # first uplink freq, distance between uplink/downlink frequency, list of range tuple |
| 27 | # each tuple in a range tuple contains: first arfcn of the range, last arfcn of the range, offset of the range |
Roman Khassraf | e7e75f0 | 2015-08-22 19:26:12 +0200 | [diff] [blame] | 28 | # entries are ordered by relevance |
| 29 | __band_conf = collections.OrderedDict([ |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 30 | ('P-GSM', {'f_start': 890.0e6, 'distance': 45e6, 'ranges': [(1, 124, 0)]}), |
| 31 | ('DCS1800', {'f_start': 1710.2e6, 'distance': 95e6, 'ranges': [(512, 885, 512)]}), |
| 32 | ('PCS1900', {'f_start': 1850.2e6, 'distance': 80e6, 'ranges': [(512, 810, 512)]}), |
| 33 | ('E-GSM', {'f_start': 890.0e6, 'distance': 45e6, 'ranges': [(0, 124, 0), (975, 1023, 1024)]}), |
| 34 | ('R-GSM', {'f_start': 890.0e6, 'distance': 45e6, 'ranges': [(0, 124, 0), (955, 1023, 1024)]}), |
| 35 | ('GSM450', {'f_start': 450.6e6, 'distance': 10e6, 'ranges': [(259, 293, 259)]}), |
| 36 | ('GSM480', {'f_start': 479e6, 'distance': 10e6, 'ranges': [(306, 340, 306)]}), |
| 37 | ('GSM850', {'f_start': 824.2e6, 'distance': 45e6, 'ranges': [(128, 251, 128)]}), |
| 38 | ]) |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 39 | |
| 40 | __chan_spacing = 2e5 |
| 41 | |
| 42 | |
| 43 | def get_bands(): |
| 44 | return __band_conf.keys() |
| 45 | |
| 46 | |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 47 | def is_valid_arfcn(arfcn, band): |
| 48 | """ |
| 49 | Returns True if arfcn is valid in the given band, else False |
| 50 | """ |
| 51 | if band in __band_conf: |
| 52 | conf = __band_conf.get(band) |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 53 | for arfcn_range in conf['ranges']: |
| 54 | arfcn_start = arfcn_range[0] |
| 55 | arfcn_end = arfcn_range[1] |
| 56 | if arfcn_start <= arfcn <= arfcn_end: |
| 57 | return True |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 58 | return False |
| 59 | |
| 60 | |
| 61 | def is_valid_uplink(freq, band): |
| 62 | """ |
| 63 | Returns True if the given frequency is a valid uplink frequency in the given band |
| 64 | """ |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 65 | result = None |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 66 | if band in __band_conf: |
| 67 | conf = __band_conf.get(band) |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 68 | result = False |
| 69 | for arfcn_range in conf['ranges']: |
| 70 | arfcn_start = arfcn_range[0] |
| 71 | arfcn_end = arfcn_range[1] |
| 72 | first_freq = arfcn2uplink(arfcn_start, band) |
| 73 | last_freq = arfcn2uplink(arfcn_end, band) |
| 74 | if first_freq is None or last_freq is None: |
| 75 | result = False |
| 76 | elif first_freq <= freq <= last_freq: |
| 77 | result = True |
| 78 | return result |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 79 | |
| 80 | |
| 81 | def is_valid_downlink(freq, band): |
| 82 | """ |
| 83 | Returns True if the given frequency is a valid downlink frequency in the given band |
| 84 | """ |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 85 | result = None |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 86 | if band in __band_conf: |
| 87 | conf = __band_conf.get(band) |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 88 | result = False |
| 89 | for arfcn_range in conf['ranges']: |
| 90 | arfcn_start = arfcn_range[0] |
| 91 | arfcn_end = arfcn_range[1] |
| 92 | first_freq = arfcn2downlink(arfcn_start, band) |
| 93 | last_freq = arfcn2downlink(arfcn_end, band) |
| 94 | if first_freq is None or last_freq is None: |
| 95 | result = False |
| 96 | elif first_freq <= freq <= last_freq: |
| 97 | result = True |
| 98 | return result |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 99 | |
| 100 | |
| 101 | def arfcn2uplink(arfcn, band): |
| 102 | if band in __band_conf and is_valid_arfcn(arfcn, band): |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 103 | conf = __band_conf.get(band) |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 104 | f_start = conf['f_start'] |
| 105 | offset = None |
| 106 | for arfcn_range in conf['ranges']: |
| 107 | arfcn_start = arfcn_range[0] |
| 108 | arfcn_end = arfcn_range[1] |
| 109 | if arfcn_start <= arfcn <= arfcn_end: |
| 110 | offset = arfcn_range[2] |
| 111 | |
| 112 | if offset is not None: |
| 113 | f = f_start + (__chan_spacing * (arfcn - offset)) |
| 114 | return round(f, 1) |
Piotr Krysik | aed9f5c | 2017-08-26 11:25:53 +0200 | [diff] [blame] | 115 | return -1 |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 116 | |
| 117 | |
| 118 | def arfcn2downlink(arfcn, band): |
| 119 | if band in __band_conf and is_valid_arfcn(arfcn, band): |
| 120 | conf = __band_conf.get(band) |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 121 | distance = conf['distance'] |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 122 | return round(arfcn2uplink(arfcn, band) + distance, 1) |
Piotr Krysik | aed9f5c | 2017-08-26 11:25:53 +0200 | [diff] [blame] | 123 | return -1 |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 124 | |
| 125 | |
| 126 | def uplink2arfcn(freq, band): |
| 127 | if band in __band_conf and is_valid_uplink(freq, band): |
| 128 | conf = __band_conf.get(band) |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 129 | f_start = conf['f_start'] |
| 130 | offset = None |
| 131 | for arfcn_range in conf['ranges']: |
| 132 | arfcn_start = arfcn_range[0] |
| 133 | arfcn_end = arfcn_range[1] |
| 134 | offset = arfcn_range[2] |
| 135 | arfcn = int(round(offset + ((freq - f_start) / __chan_spacing), 0)) |
| 136 | if arfcn_start <= arfcn <= arfcn_end: |
| 137 | return arfcn |
Piotr Krysik | aed9f5c | 2017-08-26 11:25:53 +0200 | [diff] [blame] | 138 | return -1 |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 139 | |
| 140 | |
| 141 | def downlink2arfcn(freq, band): |
| 142 | if band in __band_conf and is_valid_downlink(freq, band): |
| 143 | conf = __band_conf.get(band) |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 144 | distance = conf['distance'] |
Roman Khassraf | 9144861 | 2015-08-21 11:14:51 +0200 | [diff] [blame] | 145 | freq_uplink = freq - distance |
| 146 | return int(round(uplink2arfcn(freq_uplink, band), 0)) |
Piotr Krysik | aed9f5c | 2017-08-26 11:25:53 +0200 | [diff] [blame] | 147 | return -1 |
Roman Khassraf | fb772f8 | 2016-09-23 07:23:05 +0200 | [diff] [blame] | 148 | |
| 149 | |
| 150 | def get_arfcn_ranges(band): |
| 151 | """ |
| 152 | Returns a list of arfcn tuples, each with first and last arfcn of the range. |
| 153 | """ |
| 154 | result = [] |
| 155 | if band in __band_conf: |
| 156 | conf = __band_conf.get(band) |
| 157 | for arfcn_range in conf['ranges']: |
| 158 | arfcn_tuple = (arfcn_range[0], arfcn_range[1]) |
| 159 | result.append(arfcn_tuple) |
| 160 | return result |