19 class FreqBand(object):
20 def __init__(self, start, end, bw, comments=None):
24 self.comments = comments or []
26 def __cmp__(self, other):
29 if not isinstance(o, FreqBand):
31 return cmp((s.start, s.end, s.maxbw), (o.start, o.end, o.maxbw))
35 return hash((s.start, s.end, s.maxbw))
38 return '<FreqBand %.3f - %.3f @ %.3f>' % (
39 self.start, self.end, self.maxbw)
41 class PowerRestriction(object):
42 def __init__(self, max_ant_gain, max_eirp, comments = None):
43 self.max_ant_gain = max_ant_gain
44 self.max_eirp = max_eirp
45 self.comments = comments or []
47 def __cmp__(self, other):
50 if not isinstance(o, PowerRestriction):
52 return cmp((s.max_ant_gain, s.max_eirp),
53 (o.max_ant_gain, o.max_eirp))
56 return '<PowerRestriction ...>'
60 return hash((s.max_ant_gain, s.max_eirp))
62 class FlagError(Exception):
63 def __init__(self, flag):
66 class Permission(object):
67 def __init__(self, freqband, power, flags):
68 assert isinstance(freqband, FreqBand)
69 assert isinstance(power, PowerRestriction)
70 self.freqband = freqband
74 if not flag in flag_definitions:
76 self.flags |= flag_definitions[flag]
77 self.textflags = flags
80 return (self.freqband, self.power, self.flags)
82 def __cmp__(self, other):
83 if not isinstance(other, Permission):
85 return cmp(self._as_tuple(), other._as_tuple())
88 return hash(self._as_tuple())
90 class Country(object):
91 def __init__(self, permissions=None, comments=None):
92 self._permissions = permissions or []
93 self.comments = comments or []
96 assert isinstance(perm, Permission)
97 self._permissions.append(perm)
98 self._permissions.sort()
100 def __contains__(self, perm):
101 assert isinstance(perm, Permission)
102 return perm in self._permissions
105 r = ['(%s, %s)' % (str(b), str(p)) for b, p in self._permissions]
106 return '<Country (%s)>' % (', '.join(r))
108 def _get_permissions_tuple(self):
109 return tuple(self._permissions)
110 permissions = property(_get_permissions_tuple)
112 class SyntaxError(Exception):
115 class DBParser(object):
116 def __init__(self, warn=None):
117 self._warn_callout = warn or sys.stderr.write
119 def _syntax_error(self, txt=None):
120 txt = txt and ' (%s)' % txt or ''
121 raise SyntaxError("Syntax error in line %d%s" % (self._lineno, txt))
123 def _warn(self, txt):
124 self._warn_callout("Warning (line %d): %s\n" % (self._lineno, txt))
126 def _parse_band_def(self, bname, banddef, dupwarn=True):
128 freqs, bw = banddef.split('@')
134 start, end = freqs.split('-')
138 self._syntax_error("band must have frequency range")
140 b = FreqBand(start, end, bw, comments=self._comments)
142 self._banddup[bname] = bname
143 if b in self._bandrev:
145 self._warn('Duplicate band definition ("%s" and "%s")' % (
146 bname, self._bandrev[b]))
147 self._banddup[bname] = self._bandrev[b]
148 self._bands[bname] = b
149 self._bandrev[b] = bname
150 self._bandline[bname] = self._lineno
152 def _parse_band(self, line):
154 bname, line = line.split(':', 1)
156 self._syntax_error("'band' keyword must be followed by name")
158 self._syntax_error("band name must be followed by colon")
160 if bname in flag_definitions:
161 self._syntax_error("Invalid band name")
163 self._parse_band_def(bname, line)
165 def _parse_power(self, line):
167 pname, line = line.split(':', 1)
169 self._syntax_error("'power' keyword must be followed by name")
171 self._syntax_error("power name must be followed by colon")
173 if pname in flag_definitions:
174 self._syntax_error("Invalid power name")
176 self._parse_power_def(pname, line)
178 def _parse_power_def(self, pname, line, dupwarn=True):
181 max_eirp) = line.split(',')
182 if max_ant_gain == 'N/A':
184 if max_eirp == 'N/A':
186 max_ant_gain = float(max_ant_gain)
188 if pwr.endswith('mW'):
189 pwr = float(pwr[:-2])
190 return 10.0 * math.log10(pwr)
193 max_eirp = conv_pwr(max_eirp)
195 self._syntax_error("invalid power data")
197 p = PowerRestriction(max_ant_gain, max_eirp,
198 comments=self._comments)
200 self._powerdup[pname] = pname
201 if p in self._powerrev:
203 self._warn('Duplicate power definition ("%s" and "%s")' % (
204 pname, self._powerrev[p]))
205 self._powerdup[pname] = self._powerrev[p]
206 self._power[pname] = p
207 self._powerrev[p] = pname
208 self._powerline[pname] = self._lineno
210 def _parse_country(self, line):
212 cname, line = line.split(':', 1)
214 self._syntax_error("'country' keyword must be followed by name")
216 self._syntax_error("extra data at end of country line")
218 self._syntax_error("country name must be followed by colon")
220 cnames = cname.split(',')
222 self._current_countries = {}
225 self._warn("country '%s' not alpha2" % cname)
226 if not cname in self._countries:
227 self._countries[cname] = Country(comments=self._comments)
228 self._current_countries[cname] = self._countries[cname]
231 def _parse_country_item(self, line):
234 band, line = line[1:].split('),', 1)
235 bname = 'UNNAMED %d' % self._lineno
236 self._parse_band_def(bname, band, dupwarn=False)
238 self._syntax_error("Badly parenthesised band definition")
241 bname, line = line.split(',', 1)
243 self._syntax_error("country definition must have band")
245 self._syntax_error("country definition must have power")
247 self._syntax_error("country definition must have band and power")
250 items = line.split('),', 1)
254 if not pname[-1] == ')':
255 self._syntax_error("Badly parenthesised power definition")
260 flags = items[1].split(',')
262 pname = 'UNNAMED %d' % self._lineno
263 self._parse_power_def(pname, power, dupwarn=False)
265 line = line.split(',')
269 if not bname in self._bands:
270 self._syntax_error("band does not exist")
271 if not pname in self._power:
272 self._syntax_error("power does not exist")
273 self._bands_used[bname] = True
274 self._power_used[pname] = True
275 # de-duplicate so binary database is more compact
276 bname = self._banddup[bname]
277 pname = self._powerdup[pname]
278 b = self._bands[bname]
279 p = self._power[pname]
281 perm = Permission(b, p, flags)
283 self._syntax_error("Invalid flag '%s'" % e.flag)
284 for cname, c in self._current_countries.iteritems():
286 self._warn('Rule "%s, %s" added to "%s" twice' % (
287 bname, pname, cname))
292 self._current_countries = None
296 self._bands_used = {}
297 self._power_used = {}
312 self._comments.append(line[1:].strip())
313 line = line.replace(' ', '').replace('\t', '')
316 line = line.split('#')[0]
319 if line[0:4] == 'band':
320 self._parse_band(line[4:])
321 self._current_countries = None
323 elif line[0:5] == 'power':
324 self._parse_power(line[5:])
325 self._current_countries = None
327 elif line[0:7] == 'country':
328 self._parse_country(line[7:])
330 elif self._current_countries is not None:
331 self._parse_country_item(line)
334 self._syntax_error("Expected band, power or country definition")
336 countries = self._countries
338 for k, v in self._bands.iteritems():
339 if k in self._bands_used:
340 bands[self._banddup[k]] = v
342 # we de-duplicated, but don't warn again about the dupes
343 if self._banddup[k] == k:
344 self._lineno = self._bandline[k]
345 self._warn('Unused band definition "%s"' % k)
347 for k, v in self._power.iteritems():
348 if k in self._power_used:
349 power[self._powerdup[k]] = v
351 # we de-duplicated, but don't warn again about the dupes
352 if self._powerdup[k] == k:
353 self._lineno = self._powerline[k]
354 self._warn('Unused power definition "%s"' % k)