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