10 class FreqBand(object):
11 def __init__(self, start, end, bw, flags, comments=None):
16 self.comments = comments or []
18 def __cmp__(self, other):
21 if not isinstance(o, FreqBand):
23 return cmp((s.start, s.end, s.maxbw, s.flags), (o.start, o.end, o.maxbw, o.flags))
27 return hash((s.start, s.end, s.maxbw, s.flags))
31 for f, v in band_flags.iteritems():
34 return '<FreqBand %.3f - %.3f @ %.3f%s>' % (
35 self.start, self.end, self.maxbw, ', '.join(flags))
37 class PowerRestriction(object):
38 def __init__(self, environment, max_ant_gain, max_ir_ptmp,
39 max_ir_ptp, max_eirp_ptmp, max_eirp_ptp,
41 self.environment = environment
42 self.max_ant_gain = max_ant_gain
43 self.max_ir_ptmp = max_ir_ptmp
44 self.max_ir_ptp = max_ir_ptp
45 self.max_eirp_ptmp = max_eirp_ptmp
46 self.max_eirp_ptp = max_eirp_ptp
47 self.comments = comments or []
49 def __cmp__(self, other):
52 if not isinstance(o, PowerRestriction):
54 return cmp((s.environment, s.max_ant_gain, s.max_ir_ptmp,
55 s.max_ir_ptp, s.max_eirp_ptmp, s.max_eirp_ptp),
56 (o.environment, o.max_ant_gain, o.max_ir_ptmp,
57 o.max_ir_ptp, o.max_eirp_ptmp, o.max_eirp_ptp))
60 return '<PowerRestriction ...>'
64 return hash((s.environment, s.max_ant_gain, s.max_ir_ptmp,
65 s.max_ir_ptp, s.max_eirp_ptmp, s.max_eirp_ptp))
67 class Country(object):
68 def __init__(self, restrictions=None, comments=None):
69 # tuple of (freqband, powerrestriction)
70 self._restrictions = restrictions or []
71 self.comments = comments or []
73 def add(self, band, power):
74 assert isinstance(band, FreqBand)
75 assert isinstance(power, PowerRestriction)
76 self._restrictions.append((band, power))
77 self._restrictions.sort()
79 def __contains__(self, tup):
80 return tup in self._restrictions
83 r = ['(%s, %s)' % (str(b), str(p)) for b, p in self._restrictions]
84 return '<Country (%s)>' % (', '.join(r))
86 def _get_restrictions_tuple(self):
87 return tuple(self._restrictions)
88 restrictions = property(_get_restrictions_tuple)
90 class SyntaxError(Exception):
93 class DBParser(object):
97 def _syntax_error(self, txt=None):
98 txt = txt and ' (%s)' % txt or ''
99 raise SyntaxError("Syntax error in line %d%s" % (self._lineno, txt))
101 def _warn(self, txt):
102 sys.stderr.write("Warning (line %d): %s\n" % (self._lineno, txt))
104 def _parse_band_def(self, bname, banddef, dupwarn=True):
107 freqs, line = line.split(',', 1)
112 flags = [f.upper() for f in line.split(',') if f]
115 freqs, bw = freqs.split('@')
121 start, end = freqs.split('-')
125 self._syntax_error("band must have frequency range")
129 if not f in band_flags:
130 self._syntax_error("Invalid band flag")
133 b = FreqBand(start, end, bw, fl, comments=self._comments)
135 self._banddup[bname] = bname
136 if b in self._bandrev:
138 self._warn('Duplicate band definition ("%s" and "%s")' % (
139 bname, self._bandrev[b]))
140 self._banddup[bname] = self._bandrev[b]
141 self._bands[bname] = b
142 self._bandrev[b] = bname
143 self._bandline[bname] = self._lineno
145 def _parse_band(self, line):
147 bname, line = line.split(':', 1)
149 self._syntax_error("'band' keyword must be followed by name")
151 self._syntax_error("band name must be followed by colon")
153 self._parse_band_def(bname, line)
155 def _parse_power(self, line):
157 pname, line = line.split(':', 1)
159 self._syntax_error("'power' keyword must be followed by name")
161 self._syntax_error("power name must be followed by colon")
163 self._parse_power_def(pname, line)
165 def _parse_power_def(self, pname, line, dupwarn=True):
172 max_eirp_ptp) = line.split(',')
173 max_ant_gain = float(max_ant_gain)
174 max_ir_ptmp = float(max_ir_ptmp)
175 max_ir_ptp = float(max_ir_ptp)
176 max_eirp_ptmp = float(max_eirp_ptmp)
177 max_eirp_ptp = float(max_eirp_ptp)
179 self._syntax_error("invalid power data")
183 if not environ in ('I', 'O', ' '):
184 self._syntax_error("Invalid environment specifier")
186 p = PowerRestriction(environ, max_ant_gain, max_ir_ptmp,
187 max_ir_ptp, max_eirp_ptmp, max_eirp_ptp,
188 comments=self._comments)
190 self._powerdup[pname] = pname
191 if p in self._powerrev:
193 self._warn('Duplicate power definition ("%s" and "%s")' % (
194 pname, self._powerrev[p]))
195 self._powerdup[pname] = self._powerrev[p]
196 self._power[pname] = p
197 self._powerrev[p] = pname
198 self._powerline[pname] = self._lineno
200 def _parse_country(self, line):
202 cname, line = line.split(':', 1)
204 self._syntax_error("'country' keyword must be followed by name")
206 self._syntax_error("extra data at end of country line")
208 self._syntax_error("country name must be followed by colon")
210 if not cname in self._countries:
211 self._countries[cname] = Country(comments=self._comments)
213 self._current_country = self._countries[cname]
214 self._current_country_name = cname
216 def _parse_country_item(self, line):
219 band, pname = line[1:].split('),')
220 bname = 'UNNAMED %d' % self._lineno
221 self._parse_band_def(bname, band, dupwarn=False)
223 self._syntax_error("Badly parenthesised band definition")
226 bname, pname = line.split(',', 1)
228 self._syntax_error("country definition must have band")
230 self._syntax_error("country definition must have power")
232 self._syntax_error("country definition must have band and power")
235 if not pname[-1] == ')':
236 self._syntax_error("Badly parenthesised power definition")
238 pname = 'UNNAMED %d' % self._lineno
239 self._parse_power_def(pname, power, dupwarn=False)
241 if not bname in self._bands:
242 self._syntax_error("band does not exist")
243 if not pname in self._power:
244 self._syntax_error("power does not exist")
245 self._bands_used[bname] = True
246 self._power_used[pname] = True
247 # de-duplicate so binary database is more compact
248 bname = self._banddup[bname]
249 pname = self._powerdup[pname]
250 b = self._bands[bname]
251 p = self._power[pname]
252 if (b, p) in self._current_country:
253 self._warn('Rule "%s, %s" added to "%s" twice' % (
254 bname, pname, self._current_country_name))
256 self._current_country.add(b, p)
259 self._current_country = None
263 self._bands_used = {}
264 self._power_used = {}
279 self._comments.append(line[1:].strip())
280 line = line.replace(' ', '').replace('\t', '')
283 line = line.split('#')[0]
286 if line[0:4] == 'band':
287 self._parse_band(line[4:])
288 self._current_country = None
290 elif line[0:5] == 'power':
291 self._parse_power(line[5:])
292 self._current_country = None
294 elif line[0:7] == 'country':
295 self._parse_country(line[7:])
297 elif self._current_country is not None:
298 self._parse_country_item(line)
301 self._syntax_error("Expected band, power or country definition")
304 for k, v in self._countries.iteritems():
305 for k in k.split(','):
308 for k, v in self._bands.iteritems():
309 if k in self._bands_used:
310 bands[self._banddup[k]] = v
312 # we de-duplicated, but don't warn again about the dupes
313 if self._banddup[k] == k:
314 self._lineno = self._bandline[k]
315 self._warn('Unused band definition "%s"' % k)
317 for k, v in self._power.iteritems():
318 if k in self._power_used:
319 power[self._powerdup[k]] = v
321 # we de-duplicated, but don't warn again about the dupes
322 if self._powerdup[k] == k:
323 self._lineno = self._powerline[k]
324 self._warn('Unused power definition "%s"' % k)