# HG changeset patch # User Tom Hayward tom@tomh.us # Date 1360440250 28800 # Node ID 26774d27b348c139a013114d3588173617d3ad1d # Parent f501ac229f9b1be1c24dab889b75ac40f27efd16 [csv] Add support for reading CSV generated by RT Systems software. #477
diff -r f501ac229f9b -r 26774d27b348 chirp/chirp_common.py --- a/chirp/chirp_common.py Sat Feb 09 11:45:20 2013 -0800 +++ b/chirp/chirp_common.py Sat Feb 09 12:04:10 2013 -0800 @@ -185,6 +185,13 @@
def parse_freq(freqstr): """Parse a frequency string and return the value in integral Hz""" + if freqstr == " ": + return 0 + elif freqstr.endswith(" MHz"): + return parse_freq(freqstr.split(" ")[0]) + elif freqstr.endswith(" kHz"): + return int(freqstr.split(" ")[0]) * 1000 + if "." in freqstr: mhz, khz = freqstr.split(".") else: diff -r f501ac229f9b -r 26774d27b348 chirp/generic_csv.py --- a/chirp/generic_csv.py Sat Feb 09 11:45:20 2013 -0800 +++ b/chirp/generic_csv.py Sat Feb 09 12:04:10 2013 -0800 @@ -255,6 +255,93 @@ ",".join(self.memories[number].to_csv())
@classmethod - def match_model(cls, _filedata, filename): + def match_model(cls, filedata, filename): """Match files ending in .CSV""" - return filename.lower().endswith("." + cls.FILE_EXTENSION) + return filename.lower().endswith("." + cls.FILE_EXTENSION) and \ + (filedata.startswith("Location,") or filedata == "") + + +@directory.register +class RTCSVRadio(CSVRadio): + """A driver for reading CSV files generated by RT Systems software""" + VENDOR = "RT Systems" + MODEL = "CSV" + FILE_EXTENSION = "csv" + + DUPLEX_MAP = { + "Minus": "-", + "Plus": "+", + "Simplex": "", + "Split": "split", + } + + SKIP_MAP = { + "Off": "", + "On": "S", + "P Scan": "P", + "Skip": "S", + } + + TMODE_MAP = { + "None": "", + "T Sql": "TSQL", + } + + BOOL_MAP = { + "Off": False, + "On": True, + } + + ATTR_MAP = { + "Channel Number": (int, "number"), + "Receive Frequency":(chirp_common.parse_freq, "freq"), + "Offset Frequency": (chirp_common.parse_freq, "offset"), + "Offset Direction": (lambda v: RTCSVRadio.DUPLEX_MAP.get(v, v), "duplex"), + "Operating Mode": (str, "mode"), + "Name": (str, "name"), + "Tone Mode": (lambda v: RTCSVRadio.TMODE_MAP.get(v, v), "tmode"), + "CTCSS": (lambda v: float(v.split(" ")[0]), "rtone"), + "DCS": (int, "dtcs"), + "Skip": (lambda v: RTCSVRadio.SKIP_MAP.get(v, v), "skip"), + "Step": (lambda v: float(v.split(" ")[0]), "tuning_step"), + "Mask": (lambda v: RTCSVRadio.BOOL_MAP.get(v, v), "empty",), + "Comment": (str, "comment"), + } + + def _clean_duplex(self, headers, line, mem): + if mem.duplex == "split": + try: + val = get_datum_by_header(headers, line, "Transmit Frequency") + val = chirp_common.parse_freq(val) + mem.offset = val + except OmittedHeaderError: + pass + + return mem + + def _clean_mode(self, headers, line, mem): + if mem.mode == "FM": + try: + val = get_datum_by_header(headers, line, "Half Dev") + if self.BOOL_MAP[val]: + mem.mode = "FMN" + except OmittedHeaderError: + pass + + return mem + + def _clean_ctone(self, headers, line, mem): + # RT Systems only stores a single tone value + mem.ctone = mem.rtone + return mem + + @classmethod + def match_model(cls, filedata, filename): + """Match files ending in .csv and using RT Systems column names.""" + # RT Systems provides a different set of columns for each radio. + # We attempt to match only the first few columns, hoping they are + # consistent across radio models. + return filename.lower().endswith("." + cls.FILE_EXTENSION) and \ + filedata.startswith("Channel Number,Receive Frequency," + "Transmit Frequency,Offset Frequency,Offset Direction," + "Operating Mode,Name,Tone Mode,CTCSS,DCS")