diff --git a/routes/dmr.py b/routes/dmr.py index 8d0d725..c795e0b 100644 --- a/routes/dmr.py +++ b/routes/dmr.py @@ -52,6 +52,7 @@ dmr_active_device: Optional[int] = None _active_ffmpeg_stdin: Optional[object] = None # set by stream endpoint VALID_PROTOCOLS = ['auto', 'dmr', 'p25', 'p25p2', 'nxdn', 'dstar', 'provoice'] +VALID_DEMODS = ['nfm', 'fm'] # Classic dsd flags _DSD_PROTOCOL_FLAGS = { @@ -489,11 +490,14 @@ def start_dmr() -> Response: device = validate_device_index(data.get('device', 0)) protocol = str(data.get('protocol', 'auto')).lower() ppm = validate_ppm(data.get('ppm', 0)) + demod = str(data.get('demod', 'nfm')).lower() except (ValueError, TypeError) as e: return jsonify({'status': 'error', 'message': f'Invalid parameter: {e}'}), 400 if protocol not in VALID_PROTOCOLS: return jsonify({'status': 'error', 'message': f'Invalid protocol. Use: {", ".join(VALID_PROTOCOLS)}'}), 400 + if demod not in VALID_DEMODS: + return jsonify({'status': 'error', 'message': f'Invalid demod. Use: {", ".join(VALID_DEMODS)}'}), 400 if protocol == 'p25p2' and not is_fme: return jsonify({'status': 'error', 'message': 'P25 Phase 2 requires dsd-fme.'}), 400 @@ -521,7 +525,7 @@ def start_dmr() -> Response: # internally via its own frame-sync detection. rtl_cmd = [ rtl_fm_path, - '-M', 'fm', + '-M', demod, '-f', str(freq_hz), '-s', '48000', '-g', str(gain), diff --git a/static/js/modes/dmr.js b/static/js/modes/dmr.js index f0e63c0..342e973 100644 --- a/static/js/modes/dmr.js +++ b/static/js/modes/dmr.js @@ -70,6 +70,7 @@ function startDmr() { const gain = parseInt(document.getElementById('dmrGain')?.value || 40); const ppm = parseInt(document.getElementById('dmrPPM')?.value || 0); const relaxCrc = document.getElementById('dmrRelaxCrc')?.checked || false; + const demod = document.getElementById('dmrDemod')?.value || 'nfm'; const device = typeof getSelectedDevice === 'function' ? getSelectedDevice() : 0; // Use protocol name for device reservation so panel shows "D-STAR", "P25", etc. @@ -83,14 +84,14 @@ function startDmr() { // Save settings to localStorage for persistence try { localStorage.setItem(DMR_SETTINGS_KEY, JSON.stringify({ - frequency, protocol, gain, ppm, relaxCrc + frequency, protocol, gain, ppm, relaxCrc, demod })); } catch (e) { /* localStorage unavailable */ } fetch('/dmr/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ frequency, protocol, gain, device, ppm, relaxCrc }) + body: JSON.stringify({ frequency, protocol, gain, device, ppm, relaxCrc, demod }) }) .then(r => r.json()) .then(data => { @@ -617,11 +618,13 @@ function restoreDmrSettings() { const gainEl = document.getElementById('dmrGain'); const ppmEl = document.getElementById('dmrPPM'); const crcEl = document.getElementById('dmrRelaxCrc'); + const demodEl = document.getElementById('dmrDemod'); if (freqEl && s.frequency != null) freqEl.value = s.frequency; if (protoEl && s.protocol) protoEl.value = s.protocol; if (gainEl && s.gain != null) gainEl.value = s.gain; if (ppmEl && s.ppm != null) ppmEl.value = s.ppm; if (crcEl && s.relaxCrc != null) crcEl.checked = s.relaxCrc; + if (demodEl && s.demod) demodEl.value = s.demod; } catch (e) { /* localStorage unavailable */ } } diff --git a/templates/partials/modes/dmr.html b/templates/partials/modes/dmr.html index 730345e..7c8fa1f 100644 --- a/templates/partials/modes/dmr.html +++ b/templates/partials/modes/dmr.html @@ -29,6 +29,17 @@ +