mirror of
https://github.com/smittix/intercept.git
synced 2026-05-18 05:44:49 -07:00
Fix grgsm_scanner output parser to match real output format
Parser expected pipe-delimited table rows but grgsm_scanner outputs comma-separated key-value pairs like: ARFCN: 975, Freq: 925.2M, CID: 13522, LAC: 38722, MCC: 262, MNC: 1, Pwr: -58 This was the root cause of no data appearing in GSM Spy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1096,43 +1096,40 @@ def traffic_correlation():
|
|||||||
def parse_grgsm_scanner_output(line: str) -> dict[str, Any] | None:
|
def parse_grgsm_scanner_output(line: str) -> dict[str, Any] | None:
|
||||||
"""Parse grgsm_scanner output line.
|
"""Parse grgsm_scanner output line.
|
||||||
|
|
||||||
Actual output format is a table:
|
Actual output format (comma-separated key-value pairs):
|
||||||
ARFCN | Freq (MHz) | CID | LAC | MCC | MNC | Power (dB)
|
ARFCN: 975, Freq: 925.2M, CID: 13522, LAC: 38722, MCC: 262, MNC: 1, Pwr: -58
|
||||||
--------------------------------------------------------------------
|
|
||||||
23 | 940.6 | 31245 | 1234 | 214 | 01 | -48
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Skip progress, header, and separator lines
|
line = line.strip()
|
||||||
if 'Scanning:' in line or 'ARFCN' in line or '---' in line or 'Found' in line:
|
|
||||||
|
# Skip non-data lines (progress, config, neighbour info, blank)
|
||||||
|
if not line or 'ARFCN:' not in line:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Parse table row: " 23 | 940.6 | 31245 | 1234 | 214 | 01 | -48"
|
# Parse "ARFCN: 975, Freq: 925.2M, CID: 13522, LAC: 38722, MCC: 262, MNC: 1, Pwr: -58"
|
||||||
# Split by pipe and clean whitespace
|
fields = {}
|
||||||
parts = [p.strip() for p in line.split('|')]
|
for part in line.split(','):
|
||||||
|
part = part.strip()
|
||||||
|
if ':' in part:
|
||||||
|
key, _, value = part.partition(':')
|
||||||
|
fields[key.strip()] = value.strip()
|
||||||
|
|
||||||
if len(parts) >= 7:
|
if 'ARFCN' in fields and 'CID' in fields:
|
||||||
arfcn = parts[0]
|
# Freq may have 'M' suffix (e.g. "925.2M")
|
||||||
freq = parts[1]
|
freq_str = fields.get('Freq', '0').rstrip('Mm')
|
||||||
cid = parts[2]
|
|
||||||
lac = parts[3]
|
|
||||||
mcc = parts[4]
|
|
||||||
mnc = parts[5]
|
|
||||||
power = parts[6]
|
|
||||||
|
|
||||||
# Validate that we have numeric data (not header line)
|
data = {
|
||||||
if arfcn.isdigit():
|
'type': 'tower',
|
||||||
data = {
|
'arfcn': int(fields['ARFCN']),
|
||||||
'type': 'tower',
|
'frequency': float(freq_str),
|
||||||
'arfcn': int(arfcn),
|
'cid': int(fields.get('CID', 0)),
|
||||||
'frequency': float(freq),
|
'lac': int(fields.get('LAC', 0)),
|
||||||
'cid': int(cid),
|
'mcc': int(fields.get('MCC', 0)),
|
||||||
'lac': int(lac),
|
'mnc': int(fields.get('MNC', 0)),
|
||||||
'mcc': int(mcc),
|
'signal_strength': float(fields.get('Pwr', -999)),
|
||||||
'mnc': int(mnc),
|
'timestamp': datetime.now().isoformat()
|
||||||
'signal_strength': float(power),
|
}
|
||||||
'timestamp': datetime.now().isoformat()
|
return data
|
||||||
}
|
|
||||||
return data
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Failed to parse scanner line: {line} - {e}")
|
logger.debug(f"Failed to parse scanner line: {line} - {e}")
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ from routes.gsm_spy import (
|
|||||||
class TestParseGrgsmScannerOutput:
|
class TestParseGrgsmScannerOutput:
|
||||||
"""Tests for parse_grgsm_scanner_output()."""
|
"""Tests for parse_grgsm_scanner_output()."""
|
||||||
|
|
||||||
def test_valid_table_row(self):
|
def test_valid_output_line(self):
|
||||||
"""Test parsing a valid scanner output table row."""
|
"""Test parsing a valid grgsm_scanner output line."""
|
||||||
line = " 23 | 940.6 | 31245 | 1234 | 214 | 01 | -48"
|
line = "ARFCN: 23, Freq: 940.6M, CID: 31245, LAC: 1234, MCC: 214, MNC: 01, Pwr: -48"
|
||||||
result = parse_grgsm_scanner_output(line)
|
result = parse_grgsm_scanner_output(line)
|
||||||
|
|
||||||
assert result is not None
|
assert result is not None
|
||||||
@@ -29,33 +29,34 @@ class TestParseGrgsmScannerOutput:
|
|||||||
assert result['signal_strength'] == -48.0
|
assert result['signal_strength'] == -48.0
|
||||||
assert 'timestamp' in result
|
assert 'timestamp' in result
|
||||||
|
|
||||||
def test_header_line(self):
|
def test_freq_without_suffix(self):
|
||||||
"""Test that header lines are skipped."""
|
"""Test parsing frequency without M suffix."""
|
||||||
line = "ARFCN | Freq (MHz) | CID | LAC | MCC | MNC | Power (dB)"
|
line = "ARFCN: 975, Freq: 925.2, CID: 13522, LAC: 38722, MCC: 262, MNC: 1, Pwr: -58"
|
||||||
|
result = parse_grgsm_scanner_output(line)
|
||||||
|
assert result is not None
|
||||||
|
assert result['frequency'] == 925.2
|
||||||
|
|
||||||
|
def test_config_line(self):
|
||||||
|
"""Test that configuration lines are skipped."""
|
||||||
|
line = " Configuration: 1 CCCH, not combined"
|
||||||
result = parse_grgsm_scanner_output(line)
|
result = parse_grgsm_scanner_output(line)
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
def test_separator_line(self):
|
def test_neighbour_line(self):
|
||||||
"""Test that separator lines are skipped."""
|
"""Test that neighbour cell lines are skipped."""
|
||||||
line = "--------------------------------------------------------------------"
|
line = " Neighbour Cells: 57, 61, 70, 71, 72, 86"
|
||||||
|
result = parse_grgsm_scanner_output(line)
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
def test_cell_arfcn_line(self):
|
||||||
|
"""Test that cell ARFCN lines are skipped."""
|
||||||
|
line = " Cell ARFCNs: 63, 76"
|
||||||
result = parse_grgsm_scanner_output(line)
|
result = parse_grgsm_scanner_output(line)
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
def test_progress_line(self):
|
def test_progress_line(self):
|
||||||
"""Test that progress lines are skipped."""
|
"""Test that progress/status lines are skipped."""
|
||||||
line = "Scanning: 50% complete"
|
line = "Scanning GSM900 band..."
|
||||||
result = parse_grgsm_scanner_output(line)
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
def test_found_line(self):
|
|
||||||
"""Test that 'Found X towers' lines are skipped."""
|
|
||||||
line = "Found 5 towers"
|
|
||||||
result = parse_grgsm_scanner_output(line)
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
def test_invalid_data(self):
|
|
||||||
"""Test handling of invalid data."""
|
|
||||||
line = " abc | xyz | invalid | data | bad | bad | bad"
|
|
||||||
result = parse_grgsm_scanner_output(line)
|
result = parse_grgsm_scanner_output(line)
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
@@ -64,9 +65,9 @@ class TestParseGrgsmScannerOutput:
|
|||||||
result = parse_grgsm_scanner_output("")
|
result = parse_grgsm_scanner_output("")
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
def test_partial_data(self):
|
def test_invalid_data(self):
|
||||||
"""Test handling of incomplete table rows."""
|
"""Test handling of non-numeric values."""
|
||||||
line = " 23 | 940.6 | 31245" # Missing fields
|
line = "ARFCN: abc, Freq: xyz, CID: bad, LAC: data, MCC: bad, MNC: bad, Pwr: bad"
|
||||||
result = parse_grgsm_scanner_output(line)
|
result = parse_grgsm_scanner_output(line)
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user