Skip to content

Commit

Permalink
Fix reading RTSystems CSV files
Browse files Browse the repository at this point in the history
A number of things weren't right with this, likely for a while, since
we had no tests or examples in the tree. This adds three user-supplied
sample files and a test to hopefully keep it working. Fixes:

- Ignore blank entries
- Tolerate a missing "Channel Number" column
- Support the "Scan" (meaning not skip) column type
- Fix detection by being looser about what we look for

Fixes #11389
  • Loading branch information
kk7ds committed Jun 17, 2024
1 parent 26f7c11 commit 329ecd5
Show file tree
Hide file tree
Showing 5 changed files with 1,966 additions and 5 deletions.
43 changes: 38 additions & 5 deletions chirp/drivers/generic_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ def _load(self, f):

try:
mem = self._parse_csv_data_line(header, line)
if mem is None:
LOG.debug('Line %i did not contain a valid memory',
lineno)
continue
if mem.number is None:
raise Exception("Invalid Location field" % lineno)
except Exception as e:
Expand Down Expand Up @@ -450,6 +454,7 @@ class RTCSVRadio(CSVRadio):
"On": "S",
"P Scan": "P",
"Skip": "S",
"Scan": "",
}

TMODE_MAP = {
Expand All @@ -462,13 +467,17 @@ class RTCSVRadio(CSVRadio):
"On": True,
}

MODE_MAP = {
'FM Narrow': 'NFM',
}

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"),
"Operating Mode": (lambda v: RTCSVRadio.MODE_MAP.get(v, v), 'mode'),
"Name": (str, "name"),
"Tone Mode": (lambda v:
RTCSVRadio.TMODE_MAP.get(v, v), "tmode"),
Expand All @@ -484,6 +493,10 @@ class RTCSVRadio(CSVRadio):
"Comment": (str, "comment"),
}

def __init__(self, pipe):
self._last_loaded = 0
super().__init__(pipe)

def _clean_duplex(self, headers, line, mem):
if mem.duplex == "split":
try:
Expand Down Expand Up @@ -511,6 +524,21 @@ def _clean_ctone(self, headers, line, mem):
mem.ctone = mem.rtone
return mem

def _clean_number(self, headers, line, mem):
if 'Channel Number' not in headers and self.memories:
# Some RTSystems software generates this with an empty header name?
self._last_loaded += 1
mem.number = self._last_loaded
LOG.debug('No location column, calculated %i from %r',
mem.number, self.memories[-1])
return mem

def _parse_csv_data_line(self, headers, line):
val = get_datum_by_header(headers, line, "Receive Frequency")
if not val.strip():
return
return super()._parse_csv_data_line(headers, line)

@classmethod
def match_model(cls, filedata, filename):
"""Match files ending in .csv and using RT Systems column names."""
Expand All @@ -522,8 +550,13 @@ def match_model(cls, filedata, filename):
except UnicodeDecodeError:
# CSV files are text
return False

try:
firstline, rest = filedata.split('\n', 1)
firstline_fields = firstline.split(',')
except Exception as e:
LOG.warning('Failed to detect file as RTCSV: %s', e)
return False

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")
'Receive Frequency' in firstline_fields
Loading

0 comments on commit 329ecd5

Please sign in to comment.