changeset: 1928:7dd7ce708048 tag: add_bandplans tag: qbase tag: qtip tag: tip user: Sean Burford date: Wed Mar 27 18:11:13 2013 +1100 summary: Add support for band plans (Bug 681) diff -r 443ea98c0840 -r 7dd7ce708048 chirp/bandplan.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/bandplan.py Wed Mar 27 18:11:13 2013 +1100 @@ -0,0 +1,156 @@ +# Copyright 2013 Sean Burford +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from chirp import chirp_common + + +class BandTree(object): + def __init__(self, band): + self.band = band + self.branches = [] + self.limits = None + if band: + self.limits = band.limits + + def search(self, request): + # The top level node doesn't have a specific band. + if self.band is not None: + if not self.band.contains(request): + return + + # Inherit attributes as we walk towards the most specific match. + request.mode = self.band.mode or request.mode + request.step_khz = self.band.step_khz or request.step_khz + request.offset = self.band.offset or request.offset + request.duplex = self.band.duplex or request.duplex + request.tones = self.band.tones or request.tones + if self.band.name: + request.name = '/'.join((request.name or '', self.band.name)) + + matches = [] + matches_rx = [] + for branch in self.branches: + result = branch.search(request) + if result: + # Prioritise repeater outputs. For example the North American + # 440-445MHz +5MHz range overlaps 447-450MHZ -5MHz and it's + # more appropriate to return -5MHz if the user enters 448MHz. + if result.band and result.band.duplex == "rpt RX": + matches_rx.append(result) + else: + matches.append(result) + + if matches: + return matches[0] + elif matches_rx: + return matches_rx[0] + else: + return self + + def add(self, new_bands): + for new_band in new_bands: + if self.band: + if (new_band.limits[0] == self.band.limits[0] and + new_band.limits[1] == self.band.limits[1]): + print "Discarding %s as a duplicate of %s" % ( + new_band, self.band) + continue + + # We use a new Band instance as search() modifies the instance. + best_node = self.search(Band(new_band.limits, new_band.name)) + + if best_node is self: + new_node = BandTree(new_band) + + # Move existing sub bands that belong under the new band. + relocate_branches = [] + for branch in self.branches: + if new_band.contains(branch): + relocate_branches.append(branch) + for branch in relocate_branches: + self.branches.remove(branch) + new_node.add((branch.band,)) + + self.branches.append(new_node) + elif best_node: + best_node.add((new_band,)) + else: + raise ValueError("%s does not contain %s" % (self, new_band)) + return self + + def __repr__(self): + limits = self.limits or (0,0) + return "%s-%s %s" % (limits[0], limits[1], self.band) + + +class Band(object): + def __init__(self, limits, name, mode=None, step_khz=None, + input_offset=None, output_offset=None, tones=None): + try: + # This applies semantic and chirp limitations to settings. + # memedit applies radio limitations when settings are used. + assert limits[0] <= limits[1], "Lower freq > upper freq" + if mode is not None: + assert mode in chirp_common.MODES, "Mode %s not one of %s" % ( + mode, chirp_common.MODES) + if step_khz is not None: + assert step_khz in chirp_common.TUNING_STEPS, ( + "step_khz %s not one of %s" % + (step_khz, chirp_common.TUNING_STEPS)) + if tones: + for tone in tones: + assert tone in chirp_common.TONES, ( + "tone %s not one of %s" % (tone, chirp_common.TONES)) + except AssertionError, e: + raise ValueError("%s %s: %s" % (name, limits, e)) + + self.name = name + self.mode = mode + self.step_khz = step_khz + self.tones = tones + self.limits = limits + self.offset = None + self.duplex = "simplex" + if input_offset is not None: + self.offset = input_offset + self.duplex = "rpt TX" + elif output_offset is not None: + self.offset = output_offset + self.duplex = "rpt RX" + + def contains(self, other): + return (other.limits[0] >= self.limits[0] and + other.limits[1] <= self.limits[1]) + + def inverse(self): + """Create an RX/TX shadow of this band using the offset.""" + limits = (self.limits[0] + self.offset, self.limits[1] + self.offset) + offset = -1 * self.offset + if self.duplex == "rpt RX": + return Band(limits, self.name, self.mode, self.step_khz, + input_offset=offset, tones=self.tones) + return Band(limits, self.name, self.mode, self.step_khz, + output_offset=offset, tones=self.tones) + + def __repr__(self): + desc = '%s%s%s%s' % ( + self.mode and 'mode: %s ' % (self.mode,) or '', + self.step_khz and 'step_khz: %s ' % (self.step_khz,) or '', + self.offset and 'offset: %s ' % (self.offset,) or '', + self.tones and 'tones: %s ' % (self.tones,) or '') + + return "%s-%s %s %s %s" % ( + self.limits[0], self.limits[1], self.name, self.duplex, desc) + diff -r 443ea98c0840 -r 7dd7ce708048 chirp/bandplan_au.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/bandplan_au.py Wed Mar 27 18:11:13 2013 +1100 @@ -0,0 +1,111 @@ +# Copyright 2013 Sean Burford +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +from chirp import bandplan, bandplan_iaru_r3 + + +SHORTNAME = "australia" + +DESC = { + "name": "Australian Amateur Band Plan", + "updated": "April 2010", + "url": "http://www.wia.org.au/members/bandplans/data/documents/Australian%20Band%20Plans%20100404.pdf", +} + +BANDS_10M = ( + # bandplan.Band((28000000, 29700000), "10 Meter Band"), + bandplan.Band((29520000, 29680000), "FM Simplex and Repeaters", + mode="FM", step_khz=20), + bandplan.Band((29620000, 29680000), "FM Repeaters", input_offset=-100000), +) + +BANDS_6M = ( + # bandplan.Band((50000000, 54000000), "6 Meter Band"), + bandplan.Band((52525000, 53975000), "FM Simplex and Repeaters", + mode="FM", step_khz=25), + bandplan.Band((53550000, 53975000), "FM Repeaters", input_offset=-1000000), +) + +BANDS_2M = ( + bandplan.Band((144000000, 148000000), "2 Meter Band", + tones=(91.5, 123.0, 141.3, 146.2, 85.4)), + bandplan.Band((144400000, 144600000), "Beacons", step_khz=1), + bandplan.Band((146025000, 147975000), "FM Simplex and Repeaters", + mode="FM", step_khz=25), + bandplan.Band((146625000, 147000000), "FM Repeaters Group A", + input_offset=-600000), + bandplan.Band((147025000, 147375000), "FM Repeaters Group B", + input_offset=600000), +) + +BANDS_70CM = ( + bandplan.Band((420000000, 450000000), "70cm Band", + tones=(91.5, 123.0, 141.3, 146.2, 85.4)), + bandplan.Band((432400000, 432600000), "Beacons", step_khz=1), + bandplan.Band((438025000, 439975000), "FM Simplex and Repeaters", + mode="FM", step_khz=25), + bandplan.Band((438025000, 438725000), "FM Repeaters Group A", + input_offset=-5000000), + bandplan.Band((439275000, 439975000), "FM Repeaters Group B", + input_offset=-5000000), +) + +BANDS_23CM = ( + # bandplan.Band((1240000000, 1300000000), "23cm Band"), + bandplan.Band((1273025000, 1273975000), "FM Repeaters", + mode="FM", step_khz=25, input_offset=20000000), + bandplan.Band((1296400000, 1296600000), "Beacons", step_khz=1), + bandplan.Band((1297025000, 1300400000), "General FM Simplex Data", + mode="FM", step_khz=25), +) + +BANDS_13CM = ( + bandplan.Band((2300000000, 2450000000), "13cm Band"), + bandplan.Band((2403400000, 2403600000), "Beacons", step_khz=1), + bandplan.Band((2425000000, 2428000000), "FM Simplex", + mode="FM", step_khz=25), + bandplan.Band((2428025000, 2429000000), "FM Duplex (Voice)", + mode="FM", step_khz=25, input_offset=20000000), + bandplan.Band((2429000000, 2429975000), "FM Duplex (Data)", + mode="FM", step_khz=100, input_offset=20000000), +) + +BANDS_9CM = ( + bandplan.Band((3300000000, 3600000000), "9cm Band"), + bandplan.Band((3320000000, 3340000000), "WB Channel 2: Voice/Data", + step_khz=100), + bandplan.Band((3400400000, 3400600000), "Beacons", step_khz=1), + bandplan.Band((3402000000, 3403000000), "FM Simplex (Voice)", + mode="FM", step_khz=100), + bandplan.Band((3403000000, 3405000000), "FM Simplex (Data)", + mode="FM", step_khz=100), +) + +BANDS_6CM = ( + bandplan.Band((5650000000, 5850000000), "6cm Band"), + bandplan.Band((5760400000, 5760600000), "Beacons", step_khz=1), + bandplan.Band((5700000000, 5720000000), "WB Channel 2: Data", + step_khz=100, input_offset=70000000), + bandplan.Band((5720000000, 5740000000), "WB Channel 3: Voice", + step_khz=100, input_offset=70000000), + bandplan.Band((5762000000, 5763000000), "FM Simplex (Voice)", + mode="FM", step_khz=100), + bandplan.Band((5763000000, 5765000000), "FM Simplex (Data)", + mode="FM", step_khz=100), +) + +BANDS = BANDS_10M + BANDS_6M + BANDS_2M + BANDS_70CM + BANDS_23CM +BANDS = BANDS + BANDS_13CM + BANDS_9CM + BANDS_6CM + bandplan_iaru_r3.BANDS diff -r 443ea98c0840 -r 7dd7ce708048 chirp/bandplan_iaru_r1.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/bandplan_iaru_r1.py Wed Mar 27 18:11:13 2013 +1100 @@ -0,0 +1,25 @@ +# Copyright 2013 Sean Burford +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +SHORTNAME = "iaru_r1" + +DESC = { + "name": "IARU Region 1", + "url": "http://iaru-r1.org/index.php?option=com_content&view=article&id=175&Itemid=127", + "updated": "General Conference Sun City 2011", +} + +BANDS = () + diff -r 443ea98c0840 -r 7dd7ce708048 chirp/bandplan_iaru_r2.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/bandplan_iaru_r2.py Wed Mar 27 18:11:13 2013 +1100 @@ -0,0 +1,145 @@ +# Copyright 2013 Sean Burford +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from chirp import bandplan + +SHORTNAME = "iaru_r2" + +DESC = { + "name": "IARU Region 2", + "updated": "October 8, 2010", + "url": "http://www.iaru.org/uploads/1/3/0/7/13073366/r2_band_plan.pdf" +} + +# Bands are broken up like this so that other plans can import bits. + +BANDS_160M = ( + bandplan.Band((1800000, 2000000), "160 Meter Band"), + bandplan.Band((1800000, 1810000), "Digimodes"), + bandplan.Band((1810000, 1830000), "CW", mode="CW"), + bandplan.Band((1830000, 1840000), "CW, priority for DX", mode="CW"), + bandplan.Band((1840000, 1850000), "SSB, priority for DX", mode="LSB"), + bandplan.Band((1850000, 1999000), "All modes", mode="LSB"), + bandplan.Band((1999000, 2000000), "Beacons", mode="CW"), +) + +BANDS_80M = ( + bandplan.Band((3500000, 4000000), "80 Meter Band"), + bandplan.Band((3500000, 3510000), "CW, priority for DX", mode="CW"), + bandplan.Band((3510000, 3560000), "CW, contest preferred", mode="CW"), + bandplan.Band((3560000, 3580000), "CW", mode="CW"), + bandplan.Band((3580000, 3590000), "All narrow band modes, digimodes"), + bandplan.Band((3590000, 3600000), "All modes"), + bandplan.Band((3600000, 3650000), "All modes, SSB contest preferred", + mode="LSB"), + bandplan.Band((3650000, 3700000), "All modes", mode="LSB"), + bandplan.Band((3700000, 3775000), "All modes, SSB contest preferred", + mode="LSB"), + bandplan.Band((3775000, 3800000), "All modes, SSB DX preferred", mode="LSB"), + bandplan.Band((3800000, 4000000), "All modes"), +) + +BANDS_40M = ( + bandplan.Band((7000000, 7300000), "40 Meter Band"), + bandplan.Band((7000000, 7025000), "CW, priority for DX", mode="CW"), + bandplan.Band((7025000, 7035000), "CW", mode="CW"), + bandplan.Band((7035000, 7038000), "All narrow band modes, digimodes"), + bandplan.Band((7038000, 7040000), "All narrow band modes, digimodes"), + bandplan.Band((7040000, 7043000), "All modes, digimodes"), + bandplan.Band((7043000, 7300000), "All modes"), +) + +BANDS_30M = ( + bandplan.Band((10100000, 10150000), "30 Meter Band"), + bandplan.Band((10100000, 10130000), "CW", mode="CW"), + bandplan.Band((10130000, 10140000), "All narrow band digimodes"), + bandplan.Band((10140000, 10150000), "All modes, digimodes, no phone"), +) + +BANDS_20M = ( + bandplan.Band((14000000, 14350000), "20 Meter Band"), + bandplan.Band((14000000, 14025000), "CW, priority for DX", mode="CW"), + bandplan.Band((14025000, 14060000), "CW, contest preferred", mode="CW"), + bandplan.Band((14060000, 14070000), "CW", mode="CW"), + bandplan.Band((14070000, 14089000), "All narrow band modes, digimodes"), + bandplan.Band((14089000, 14099000), "All modes, digimodes"), + bandplan.Band((14099000, 14101000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((14101000, 14112000), "All modes, digimodes"), + bandplan.Band((14112000, 14285000), "All modes, SSB contest preferred", + mode="USB"), + bandplan.Band((14285000, 14300000), "All modes", mode="AM"), + bandplan.Band((14300000, 14350000), "All modes"), +) + +BANDS_17M = ( + bandplan.Band((18068000, 18168000), "17 Meter Band"), + bandplan.Band((18068000, 18095000), "CW", mode="CW"), + bandplan.Band((18095000, 18105000), "All narrow band modes, digimodes"), + bandplan.Band((18105000, 18109000), "All narrow band modes, digimodes"), + bandplan.Band((18109000, 18111000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((18111000, 18120000), "All modes, digimodes"), + bandplan.Band((18120000, 18168000), "All modes"), +) + +BANDS_15M = ( + bandplan.Band((21000000, 21450000), "15 Meter Band"), + bandplan.Band((21000000, 21070000), "CW", mode="CW"), + bandplan.Band((21070000, 21090000), "All narrow band modes, digimodes"), + bandplan.Band((21090000, 21110000), "All narrow band modes, digimodes"), + bandplan.Band((21110000, 21120000), "All modes (exc SSB), digimodes"), + bandplan.Band((21120000, 21149000), "All narrow band modes"), + bandplan.Band((21149000, 21151000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((21151000, 21450000), "All modes", mode="USB"), +) + +BANDS_12M = ( + bandplan.Band((24890000, 24990000), "12 Meter Band"), + bandplan.Band((24890000, 24915000), "CW", mode="CW"), + bandplan.Band((24915000, 24925000), "All narrow band modes, digimodes"), + bandplan.Band((24925000, 24929000), "All narrow band modes, digimodes"), + bandplan.Band((24929000, 24931000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((24931000, 24940000), "All modes, digimodes"), + bandplan.Band((24940000, 24990000), "All modes", mode="USB"), +) + +BANDS_10M = ( + bandplan.Band((28000000, 29520000), "10 Meter Band"), + bandplan.Band((28000000, 28070000), "CW", mode="CW"), + bandplan.Band((28070000, 28120000), "All narrow band modes, digimodes"), + bandplan.Band((28120000, 28150000), "All narrow band modes, digimodes"), + bandplan.Band((28150000, 28190000), "All narrow band modes, digimodes"), + bandplan.Band((28190000, 28199000), "Regional time shared beacons", + mode="CW"), + bandplan.Band((28199000, 28201000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((28201000, 28225000), "Continuous duty beacons", + mode="CW"), + bandplan.Band((28225000, 28300000), "All modes, beacons"), + bandplan.Band((28300000, 28320000), "All modes, digimodes"), + bandplan.Band((28320000, 29000000), "All modes"), + bandplan.Band((29000000, 29200000), "All modes, AM preferred", mode="AM"), + bandplan.Band((29200000, 29300000), "All modes including FM, digimodes"), + bandplan.Band((29300000, 29510000), "Satellite downlink"), + bandplan.Band((29510000, 29520000), "Guard band, no transmission allowed"), + bandplan.Band((29520000, 29700000), "FM", step_khz=10, mode="NFM"), + bandplan.Band((29620000, 29690000), "FM Repeaters", input_offset=-100000), +) + +BANDS = BANDS_160M + BANDS_80M + BANDS_40M + BANDS_30M + BANDS_20M +BANDS = BANDS + BANDS_17M + BANDS_15M + BANDS_12M + BANDS_10M diff -r 443ea98c0840 -r 7dd7ce708048 chirp/bandplan_iaru_r3.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/bandplan_iaru_r3.py Wed Mar 27 18:11:13 2013 +1100 @@ -0,0 +1,139 @@ +# Copyright 2013 Sean Burford +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from chirp import bandplan + +SHORTNAME = "iaru_r3" + +DESC = { + "name": "IARU Region 3", + "updated": "16 October 2009", + "url": "http://www.iaru.org/uploads/1/3/0/7/13073366/r3_band_plan.pdf" +} + +# Bands are broken up like this so that other plans can import bits. + +BANDS_2100M = ( + bandplan.Band((135700, 137800), "137khz Band", mode="CW"), +) + +BANDS_160M = ( + bandplan.Band((1800000, 2000000), "160 Meter Band"), + bandplan.Band((1830000, 1840000), "Digimodes", mode="RTTY"), + bandplan.Band((1840000, 2000000), "Phone"), +) + +BANDS_80M = ( + bandplan.Band((3500000, 3900000), "80 Meter Band"), + bandplan.Band((3500000, 3510000), "CW, priority for DX", mode="CW"), + bandplan.Band((3535000, 3900000), "Phone"), + bandplan.Band((3775000, 3800000), "All modes, SSB DX preferred", mode="LSB"), +) + +BANDS_40M = ( + bandplan.Band((7000000, 7300000), "40 Meter Band"), + bandplan.Band((7000000, 7025000), "CW, priority for DX", mode="CW"), + bandplan.Band((7025000, 7035000), "All narrow band modes, cw", mode="CW"), + bandplan.Band((7035000, 7040000), "All narrow band modes, phone"), + bandplan.Band((7040000, 7300000), "All modes, digimodes"), +) + +BANDS_30M = ( + bandplan.Band((10100000, 10150000), "30 Meter Band"), + bandplan.Band((10100000, 10130000), "CW", mode="CW"), + bandplan.Band((10130000, 10150000), "All narrow band digimodes"), +) + +BANDS_20M = ( + bandplan.Band((14000000, 14350000), "20 Meter Band"), + bandplan.Band((14000000, 14070000), "CW", mode="CW"), + bandplan.Band((14070000, 14099000), "All narrow band modes, digimodes"), + bandplan.Band((14099000, 14101000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((14101000, 14112000), "All narrow band modes, digimodes"), + bandplan.Band((14101000, 14350000), "All modes, digimodes"), +) + +BANDS_17M = ( + bandplan.Band((18068000, 18168000), "17 Meter Band"), + bandplan.Band((18068000, 18100000), "CW", mode="CW"), + bandplan.Band((18100000, 18109000), "All narrow band modes, digimodes"), + bandplan.Band((18109000, 18111000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((18111000, 18168000), "All modes, digimodes"), +) + +BANDS_15M = ( + bandplan.Band((21000000, 21450000), "15 Meter Band"), + bandplan.Band((21000000, 21070000), "CW", mode="CW"), + bandplan.Band((21070000, 21125000), "All narrow band modes, digimodes"), + bandplan.Band((21125000, 21149000), "All narrow band modes, digimodes"), + bandplan.Band((21149000, 21151000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((21151000, 21450000), "All modes", mode="USB"), +) + +BANDS_12M = ( + bandplan.Band((24890000, 24990000), "12 Meter Band"), + bandplan.Band((24890000, 24920000), "CW", mode="CW"), + bandplan.Band((24920000, 24929000), "All narrow band modes, digimodes"), + bandplan.Band((24929000, 24931000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((24931000, 24990000), "All modes, digimodes", mode="USB"), +) + +BANDS_10M = ( + bandplan.Band((28000000, 29700000), "10 Meter Band"), + bandplan.Band((28000000, 28050000), "CW", mode="CW"), + bandplan.Band((28050000, 28150000), "All narrow band modes, digimodes"), + bandplan.Band((28150000, 28190000), "All narrow band modes, digimodes"), + bandplan.Band((28190000, 28199000), "Beacons", mode="CW"), + bandplan.Band((28199000, 28201000), "IBP, exclusively for beacons", + mode="CW"), + bandplan.Band((28201000, 28300000), "Beacons", mode="CW"), + bandplan.Band((28300000, 29300000), "Phone"), + bandplan.Band((29300000, 29510000), "Satellite downlink"), + bandplan.Band((29510000, 29520000), "Guard band, no transmission allowed"), + bandplan.Band((29520000, 29700000), "Wide band", step_khz=10, mode="NFM"), +) + +BANDS_6M = ( + bandplan.Band((50000000, 54000000), "6 Meter Band"), + bandplan.Band((50000000, 50100000), "Beacons", mode="CW"), + bandplan.Band((50100000, 50500000), "Phone and narrow band"), + bandplan.Band((50500000, 54000000), "Wide band"), +) + +BANDS_2M = ( + bandplan.Band((144000000, 148000000), "2 Meter Band"), + bandplan.Band((144000000, 144035000), "Earth Moon Earth"), + bandplan.Band((145800000, 146000000), "Satellite"), +) + +BANDS_70CM = ( + bandplan.Band((430000000, 450000000), "70cm Band"), + bandplan.Band((431900000, 432240000), "Earth Moon Earth"), + bandplan.Band((435000000, 438000000), "Satellite"), +) + +BANDS_23CM = ( + bandplan.Band((1240000000, 1300000000), "23cm Band"), + bandplan.Band((1260000000, 1270000000), "Satellite"), + bandplan.Band((1296000000, 1297000000), "Earth Moon Earth"), +) + +BANDS = BANDS_2100M + BANDS_160M + BANDS_80M + BANDS_40M + BANDS_30M +BANDS = BANDS + BANDS_20M + BANDS_17M + BANDS_15M + BANDS_12M + BANDS_10M +BANDS = BANDS + BANDS_6M + BANDS_2M + BANDS_70CM + BANDS_23CM diff -r 443ea98c0840 -r 7dd7ce708048 chirp/bandplan_na.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/bandplan_na.py Wed Mar 27 18:11:13 2013 +1100 @@ -0,0 +1,42 @@ +# Copyright 2013 Dan Smith +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from chirp import bandplan, bandplan_iaru_r2 + + +SHORTNAME = "north_america" + +DESC = { + "name": "North American Band Plan", +} + +BANDS = bandplan_iaru_r2.BANDS + ( + bandplan.Band((50000000, 54000000), "6 Meter Band"), + bandplan.Band((51620000, 51980000), "Repeaters A", input_offset=-500000), + bandplan.Band((52500000, 52980000), "Repeaters B", input_offset=-500000), + bandplan.Band((53500000, 53980000), "Repeaters C", input_offset=-500000), + bandplan.Band((144000000, 148000000), "2 Meter Band"), + bandplan.Band((145100000, 145500000), "Repeaters A", input_offset=-600000), + bandplan.Band((146600000, 147000000), "Repeaters B", input_offset=-600000), + bandplan.Band((147000000, 147400000), "Repeaters C", input_offset=600000), + bandplan.Band((219000000, 225000000), "220MHz Band"), + bandplan.Band((223850000, 224980000), "Repeaters", input_offset=-1600000), + bandplan.Band((420000000, 450000000), "70cm Band"), + bandplan.Band((440000000, 445000000), "Repeaters A", input_offset=5000000), + bandplan.Band((447000000, 450000000), "Repeaters B", input_offset=-5000000), + bandplan.Band((1240000000, 1300000000), "23cm Band"), + bandplan.Band((1282000000, 1288000000), "Repeaters", input_offset=-12000000), +) + diff -r 443ea98c0840 -r 7dd7ce708048 chirp/chirp_common.py --- a/chirp/chirp_common.py Tue Mar 05 09:49:47 2013 -0800 +++ b/chirp/chirp_common.py Wed Mar 27 18:11:13 2013 +1100 @@ -68,58 +68,6 @@ MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY", "DIG", "PKT", "NCW", "NCWR", "CWR", "P25", "Auto"] -STD_6M_OFFSETS = [ - (51620000, 51980000, -500000), - (52500000, 52980000, -500000), - (53500000, 53980000, -500000), - ] - -STD_2M_OFFSETS = [ - (145100000, 145500000, -600000), - (146000000, 146400000, 600000), - (146600000, 147000000, -600000), - (147000000, 147400000, 600000), - (147600000, 148000000, -600000), - ] - -STD_220_OFFSETS = [ - (223850000, 224980000, -1600000), - ] - -STD_70CM_OFFSETS = [ - (440000000, 445000000, 5000000), - (447000000, 450000000, -5000000), - ] - -STD_23CM_OFFSETS = [ - (1282000000, 1288000000, -12000000), - ] - -# Standard offsets, indexed by band (wavelength in cm) -STD_OFFSETS = { - 600 : STD_6M_OFFSETS, - 200 : STD_2M_OFFSETS, - 125 : STD_220_OFFSETS, - 70 : STD_70CM_OFFSETS, - 23 : STD_23CM_OFFSETS, - } - -BAND_TO_MHZ = { - 600 : ( 50000000, 54000000 ), - 200 : ( 144000000, 148000000 ), - 125 : ( 219000000, 225000000 ), - 70 : ( 420000000, 450000000 ), - 23 : ( 1240000000, 1300000000 ), -} - -# NB: This only works for some bands, throws an Exception otherwise -def freq_to_band(freq): - """Returns the band (in cm) for a given frequency""" - for band, (lo, hi) in BAND_TO_MHZ.items(): - if int(freq) > lo and int(freq) < hi: - return band - raise Exception("No conversion for frequency %i" % freq) - TONE_MODES = [ "", "Tone", diff -r 443ea98c0840 -r 7dd7ce708048 chirpui/bandplans.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirpui/bandplans.py Wed Mar 27 18:11:13 2013 +1100 @@ -0,0 +1,66 @@ +# Copyright 2013 Sean Burford +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from chirp import bandplan, bandplan_na, bandplan_au +from chirp import bandplan_iaru_r1, bandplan_iaru_r2, bandplan_iaru_r3 + + +def PrintBandPlan(plan, level=None): + if level is None: + level = '' + print '%s%s' % (level, plan) + for subplan in plan.branches: + PrintBandPlan(subplan, level=' %s' % level) + + +class BandPlans(object): + def __init__(self, config): + self._config = config + self.plans = {} + + for plan in (bandplan_na, bandplan_au, bandplan_iaru_r1, + bandplan_iaru_r2, bandplan_iaru_r3): + name = plan.DESC.get("name", plan.SHORTNAME) + self.plans[plan.SHORTNAME] = (name, plan) + + # Add repeater inputs as distinct bands. + rpt_inputs = [] + for band in plan.BANDS: + if band.duplex != "simplex": + rpt_inputs.append(band.inverse()) + + # Build the master band list for this bandplan. + try: + print "Building %s" % name + master = bandplan.BandTree(None) + master.add(list(plan.BANDS) + rpt_inputs) + plan.bands = master + # PrintBandPlan(master) + except ValueError, e: + print "Failed to parse bandplan %s: %s" % (name, e) + + def get_defaults_for_frequency(self, freq): + freq = int(freq) + defaults = bandplan.Band((freq, freq), repr(freq)) + + # This is limited to one band plan match for simplicity. + for shortname, plan_details in self.plans.items(): + if self._config.get_bool(shortname, "bandplan"): + if plan_details[1].bands.search(defaults): + print "Match in %s: %s" % (plan_details[0], defaults) + break + + return defaults + diff -r 443ea98c0840 -r 7dd7ce708048 chirpui/config.py --- a/chirpui/config.py Tue Mar 05 09:49:47 2013 -0800 +++ b/chirpui/config.py Wed Mar 27 18:11:13 2013 +1100 @@ -54,6 +54,12 @@ def is_defined(self, key, section): return self.__config.has_option(section, key) + def remove_option(self, section, key): + self.__config.remove_option(section, key) + + if not self.__config.items(section): + self.__config.remove_section(section) + class ChirpConfigProxy: def __init__(self, config, section="global"): self._config = config @@ -98,6 +104,10 @@ def is_defined(self, key, section=None): return self._config.is_defined(key, section or self._section) + def remove_option(self, key, section): + self._config.remove_option(section, key) + + _CONFIG = None def get(section="global"): global _CONFIG diff -r 443ea98c0840 -r 7dd7ce708048 chirpui/mainapp.py --- a/chirpui/mainapp.py Tue Mar 05 09:49:47 2013 -0800 +++ b/chirpui/mainapp.py Wed Mar 27 18:11:13 2013 +1100 @@ -40,6 +40,7 @@ from chirp import CHIRP_VERSION, chirp_common, detect, errors from chirp import icf, ic9x_icf from chirpui import editorset, clone, miscwidgets, config, reporting, fips +from chirpui import bandplans CONF = config.get() @@ -1256,8 +1257,30 @@ conf = config.get() conf.set_bool("no_report", not action.get_active()) - def do_toggle_autorpt(self, action): - CONF.set_bool("autorpt", action.get_active(), "memedit") + def do_select_bandplan(self): + plans = ["None"] + for shortname, details in self.bandplans.plans.iteritems(): + if CONF.get_bool(shortname, "bandplan"): + plans.insert(0, details[0]) + else: + plans.append(details[0]) + + d = inputdialog.ChoiceDialog(plans, parent=self, + title="Choose Defaults") + d.label.set_text(_("Band plans define default channel settings for " + "frequencies in a region. Choose a band plan " + "or None for completely manual channel " + "settings.")) + d.label.set_line_wrap(True) + r = d.run() + + if r == gtk.RESPONSE_OK: + selection = d.choice.get_active_text() + for shortname, details in self.bandplans.plans.iteritems(): + CONF.set_bool(shortname, selection == details[0], "bandplan") + if selection == details[0]: + print "Selected band plan %s: %s" % (shortname, selection) + d.destroy() def do_toggle_no_smart_tmode(self, action): CONF.set_bool("no_smart_tmode", not action.get_active(), "memedit") @@ -1351,8 +1374,8 @@ self.do_clearq() elif action == "report": self.do_toggle_report(_action) - elif action == "autorpt": - self.do_toggle_autorpt(_action) + elif action == "channel_defaults": + self.do_select_bandplan() elif action == "no_smart_tmode": self.do_toggle_no_smart_tmode(_action) elif action == "developer": @@ -1429,7 +1452,7 @@ - + @@ -1485,6 +1508,7 @@ ('export_chirp', None, _("CHIRP Native File"), None, None, self.mh), ('export_csv', None, _("CSV File"), None, None, self.mh), ('stock', None, _("Import from stock config"), None, None, self.mh), + ('channel_defaults', None, _("Channel defaults"), None, None, self.mh), ('cancelq', gtk.STOCK_STOP, None, "Escape", None, self.mh), ('help', None, _('Help'), None, None, self.mh), ('about', gtk.STOCK_ABOUT, None, None, None, self.mh), @@ -1493,15 +1517,20 @@ conf = config.get() re = not conf.get_bool("no_report"); hu = conf.get_bool("hide_unused", "memedit") - ro = conf.get_bool("autorpt", "memedit") dv = conf.get_bool("developer", "state") st = not conf.get_bool("no_smart_tmode", "memedit") + # Migrate old "automatic repeater offset" setting to + # "North American Amateur Band Plan" + ro = conf.get("autorpt", "memedit") + if ro is not None: + conf.set_bool("north_america", "bandplan", ro) + conf.remove_option("autorpt", "memedit") + toggles = [\ ('report', None, _("Report statistics"), None, None, self.mh, re), ('hide_unused', None, _("Hide Unused Fields"), None, None, self.mh, hu), ('no_smart_tmode', None, _("Smart Tone Modes"), None, None, self.mh, st), - ('autorpt', None, _("Automatic Repeater Offset"), None, None, self.mh, ro), ('developer', None, _("Enable Developer Functions"), None, None, self.mh, dv), ] @@ -1652,6 +1681,8 @@ self._recent = [] + self.bandplans = bandplans.BandPlans(CONF) + self.menu_ag = None mbar = self.make_menubar() @@ -1695,10 +1726,6 @@ d.destroy() CONF.set_bool("warned_about_reporting", True) - if not CONF.is_defined("autorpt", "memedit"): - print "autorpt not set et" - CONF.set_bool("autorpt", True, "memedit") - self.update_recent_files() self.update_stock_configs() self.setup_extra_hotkeys() diff -r 443ea98c0840 -r 7dd7ce708048 chirpui/memedit.py --- a/chirpui/memedit.py Tue Mar 05 09:49:47 2013 -0800 +++ b/chirpui/memedit.py Wed Mar 27 18:11:13 2013 +1100 @@ -32,7 +32,7 @@ import pickle import os -from chirpui import common, shiftdialog, miscwidgets, config, memdetail +from chirpui import common, shiftdialog, miscwidgets, config, memdetail, bandplans from chirp import chirp_common, errors, directory, import_logic def handle_toggle(_, path, store, col): @@ -122,10 +122,10 @@ return chirp_common.parse_freq(new) def ed_freq(self, _foo, path, new, colnum): - iter = self.store.get_iter(path) - prev, = self.store.get(iter, colnum) + path_iter = self.store.get_iter(path) + was_filled, prev = self.store.get(path_iter, self.col("_filled"), colnum) - def set_offset(path, offset): + def set_offset(offset): if offset > 0: dup = "+" elif offset == 0: @@ -134,16 +134,34 @@ dup = "-" offset *= -1 + if not dup in self.choices[_("Duplex")]: + return + if offset: - self.store.set(iter, self.col(_("Offset")), offset) + self.store.set(path_iter, self.col(_("Offset")), offset) - self.store.set(iter, self.col(_("Duplex")), dup) + self.store.set(path_iter, self.col(_("Duplex")), dup) def set_ts(ts): - self.store.set(iter, self.col(_("Tune Step")), ts) + if ts in self.choices[_("Tune Step")]: + self.store.set(path_iter, self.col(_("Tune Step")), ts) + else: + print "Tune step %s not supported by this radio" % ts def get_ts(path): - return self.store.get(iter, self.col(_("Tune Step")))[0] + return self.store.get(path_iter, self.col(_("Tune Step")))[0] + + def set_mode(mode): + if mode in self.choices[_("Mode")]: + self.store.set(path_iter, self.col(_("Mode")), mode) + else: + print "Mode %s not supported by this radio (%s)" % (mode, self.choices[_("Mode")]) + + def set_tone(tone): + if tone in self.choices[_("Tone")]: + self.store.set(path_iter, self.col(_("Tone")), tone) + else: + print "Tone %s not supported by this radio" % tone try: new = chirp_common.parse_freq(new) @@ -154,16 +172,16 @@ if not self._features.has_nostep_tuning: set_ts(chirp_common.required_step(new)) - if new and self._config.get_bool("autorpt") and new != prev: - try: - band = chirp_common.freq_to_band(new) - set_offset(path, 0) - for lo, hi, offset in chirp_common.STD_OFFSETS[band]: - if new > lo and new < hi: - set_offset(path, offset) - break - except Exception, e: - pass + is_changed = new != prev if was_filled else True + if new and is_changed: + defaults = self.bandplans.get_defaults_for_frequency(new) + set_offset(defaults.offset or 0) + if defaults.step_khz: + set_ts(defaults.step_khz) + if defaults.mode: + set_mode(defaults.mode) + if defaults.tones: + set_tone(defaults.tones[0]) return new @@ -190,11 +208,8 @@ # RX frequency as the default TX frequency self.store.set(iter, self.col("Offset"), freq) else: - band = int(freq / 100000000) - if chirp_common.STD_OFFSETS.has_key(band): - offset = chirp_common.STD_OFFSETS[band][0][2] - else: - offset = 0 + defaults = self.bandplans.get_defaults_for_frequency(freq) + offset = defaults.offset or 0 self.store.set(iter, self.col(_("Offset")), abs(offset)) return new @@ -1232,6 +1247,8 @@ self._config = config.get("memedit") + self.bandplans = bandplans.BandPlans(config.get()) + self.allowed_bands = [144, 440] self.count = 100 self.show_special = self._config.get_bool("show_special")