17 'EDGE-POWER-1': 1<<11,
18 'EDGE-POWER-2': 2<<11,
19 'EDGE-POWER-3': 3<<11,
22 class FreqBand(object):
23 def __init__(self, start, end, bw, comments=None):
27 self.comments = comments or []
29 def __cmp__(self, other):
32 if not isinstance(o, FreqBand):
34 return cmp((s.start, s.end, s.maxbw), (o.start, o.end, o.maxbw))
38 return hash((s.start, s.end, s.maxbw))
41 return '<FreqBand %.3f - %.3f @ %.3f>' % (
42 self.start, self.end, self.maxbw)
44 class PowerRestriction(object):
45 def __init__(self, max_ant_gain, max_eirp, comments = None):
46 self.max_ant_gain = max_ant_gain
47 self.max_eirp = max_eirp
48 self.comments = comments or []
50 def __cmp__(self, other):
53 if not isinstance(o, PowerRestriction):
55 return cmp((s.max_ant_gain, s.max_eirp),
56 (o.max_ant_gain, o.max_eirp))
59 return '<PowerRestriction ...>'
63 return hash((s.max_ant_gain, s.max_eirp))
65 class FlagError(Exception):
66 def __init__(self, flag):
69 class DuplicateEdgePowerError(Exception):
72 class Permission(object):
73 def __init__(self, freqband, power, flags):
74 assert isinstance(freqband, FreqBand)
75 assert isinstance(power, PowerRestriction)
76 self.freqband = freqband
79 f = [e for e in flags if e.startswith('EDGE-POWER-')]
81 raise DuplicateEdgePowerError()
83 if not flag in flag_definitions:
85 self.flags |= flag_definitions[flag]
86 self.textflags = flags
89 return (self.freqband, self.power, self.flags)
91 def __cmp__(self, other):
92 if not isinstance(other, Permission):
94 return cmp(self._as_tuple(), other._as_tuple())
97 return hash(self._as_tuple())
99 class Country(object):
100 def __init__(self, permissions=None, comments=None):
101 self._permissions = permissions or []
102 self.comments = comments or []
105 assert isinstance(perm, Permission)
106 self._permissions.append(perm)
107 self._permissions.sort()
109 def __contains__(self, perm):
110 assert isinstance(perm, Permission)
111 return perm in self._permissions
114 r = ['(%s, %s)' % (str(b), str(p)) for b, p in self._permissions]
115 return '<Country (%s)>' % (', '.join(r))
117 def _get_permissions_tuple(self):
118 return tuple(self._permissions)
119 permissions = property(_get_permissions_tuple)
121 class SyntaxError(Exception):
124 class DBParser(object):
125 def __init__(self, warn=None):
126 self._warn_callout = warn or sys.stderr.write
128 def _syntax_error(self, txt=None):
129 txt = txt and ' (%s)' % txt or ''
130 raise SyntaxError("Syntax error in line %d%s" % (self._lineno, txt))
132 def _warn(self, txt):
133 self._warn_callout("Warning (line %d): %s\n" % (self._lineno, txt))
135 def _parse_band_def(self, bname, banddef, dupwarn=True):
137 freqs, bw = banddef.split('@')
143 start, end = freqs.split('-')
147 self._syntax_error("band must have frequency range")
149 b = FreqBand(start, end, bw, comments=self._comments)
151 self._banddup[bname] = bname
152 if b in self._bandrev:
154 self._warn('Duplicate band definition ("%s" and "%s")' % (
155 bname, self._bandrev[b]))
156 self._banddup[bname] = self._bandrev[b]
157 self._bands[bname] = b
158 self._bandrev[b] = bname
159 self._bandline[bname] = self._lineno
161 def _parse_band(self, line):
163 bname, line = line.split(':', 1)
165 self._syntax_error("'band' keyword must be followed by name")
167 self._syntax_error("band name must be followed by colon")
169 if bname in flag_definitions:
170 self._syntax_error("Invalid band name")
172 self._parse_band_def(bname, line)
174 def _parse_power(self, line):
176 pname, line = line.split(':', 1)
178 self._syntax_error("'power' keyword must be followed by name")
180 self._syntax_error("power name must be followed by colon")
182 if pname in flag_definitions:
183 self._syntax_error("Invalid power name")
185 self._parse_power_def(pname, line)
187 def _parse_power_def(self, pname, line, dupwarn=True):
190 max_eirp) = line.split(',')
191 if max_ant_gain == 'N/A':
193 if max_eirp == 'N/A':
195 max_ant_gain = float(max_ant_gain)
197 if pwr.endswith('mW'):
198 pwr = float(pwr[:-2])
199 return 10.0 * math.log10(pwr)
202 max_eirp = conv_pwr(max_eirp)
204 self._syntax_error("invalid power data")
206 p = PowerRestriction(max_ant_gain, max_eirp,
207 comments=self._comments)
209 self._powerdup[pname] = pname
210 if p in self._powerrev:
212 self._warn('Duplicate power definition ("%s" and "%s")' % (
213 pname, self._powerrev[p]))
214 self._powerdup[pname] = self._powerrev[p]
215 self._power[pname] = p
216 self._powerrev[p] = pname
217 self._powerline[pname] = self._lineno
219 def _parse_country(self, line):
221 cname, line = line.split(':', 1)
223 self._syntax_error("'country' keyword must be followed by name")
225 self._syntax_error("extra data at end of country line")
227 self._syntax_error("country name must be followed by colon")
229 if not cname in self._countries:
230 self._countries[cname] = Country(comments=self._comments)
232 self._current_country = self._countries[cname]
233 self._current_country_name = cname
235 def _parse_country_item(self, line):
238 band, line = line[1:].split('),', 1)
239 bname = 'UNNAMED %d' % self._lineno
240 self._parse_band_def(bname, band, dupwarn=False)
242 self._syntax_error("Badly parenthesised band definition")
245 bname, line = line.split(',', 1)
247 self._syntax_error("country definition must have band")
249 self._syntax_error("country definition must have power")
251 self._syntax_error("country definition must have band and power")
254 items = line.split('),', 1)
258 if not pname[-1] == ')':
259 self._syntax_error("Badly parenthesised power definition")
264 flags = items[1].split(',')
266 pname = 'UNNAMED %d' % self._lineno
267 self._parse_power_def(pname, power, dupwarn=False)
269 line = line.split(',')
273 if not bname in self._bands:
274 self._syntax_error("band does not exist")
275 if not pname in self._power:
276 self._syntax_error("power does not exist")
277 self._bands_used[bname] = True
278 self._power_used[pname] = True
279 # de-duplicate so binary database is more compact
280 bname = self._banddup[bname]
281 pname = self._powerdup[pname]
282 b = self._bands[bname]
283 p = self._power[pname]
285 perm = Permission(b, p, flags)
287 self._syntax_error("Invalid flag '%s'" % e.flag)
288 except DuplicateEdgePowerError:
289 self._syntax_error("More than one edge power value given!")
290 if perm in self._current_country:
291 self._warn('Rule "%s, %s" added to "%s" twice' % (
292 bname, pname, self._current_country_name))
294 self._current_country.add(perm)
297 self._current_country = None
301 self._bands_used = {}
302 self._power_used = {}
317 self._comments.append(line[1:].strip())
318 line = line.replace(' ', '').replace('\t', '')
321 line = line.split('#')[0]
324 if line[0:4] == 'band':
325 self._parse_band(line[4:])
326 self._current_country = None
328 elif line[0:5] == 'power':
329 self._parse_power(line[5:])
330 self._current_country = None
332 elif line[0:7] == 'country':
333 self._parse_country(line[7:])
335 elif self._current_country is not None:
336 self._parse_country_item(line)
339 self._syntax_error("Expected band, power or country definition")
342 for k, v in self._countries.iteritems():
343 for k in k.split(','):
346 for k, v in self._bands.iteritems():
347 if k in self._bands_used:
348 bands[self._banddup[k]] = v
350 # we de-duplicated, but don't warn again about the dupes
351 if self._banddup[k] == k:
352 self._lineno = self._bandline[k]
353 self._warn('Unused band definition "%s"' % k)
355 for k, v in self._power.iteritems():
356 if k in self._power_used:
357 power[self._powerdup[k]] = v
359 # we de-duplicated, but don't warn again about the dupes
360 if self._powerdup[k] == k:
361 self._lineno = self._powerline[k]
362 self._warn('Unused power definition "%s"' % k)