Skip to content

Adding DCS - #1137

Open
robotastic wants to merge 18 commits into
masterfrom
dev/dcs
Open

Adding DCS#1137
robotastic wants to merge 18 commits into
masterfrom
dev/dcs

Conversation

@robotastic

Copy link
Copy Markdown
Collaborator

No description provided.

TheGreatCodeholio and others added 11 commits May 17, 2026 20:58
Make it so you can listen to multiple CTCSS or DPL tones on a single channel.

Add search mode S in Tone -> Search for CTCSS or DPL tone on that channel

Tone = 0 -> Do nothing no gate
…s channel number instead of the transmissions tg number. Since all calls for multichannl are recorded under the top channels channel id.
…o recieved within callTimeout. This makes sense, and helps it stop flapping open/closed or being stuck open all the time and eating CPU
Tone squelch refactor & DCS

Let's get this party started.
@taclane

taclane commented May 21, 2026

Copy link
Copy Markdown
Contributor

7b85068 Tone-related functions have been removed from global_structs.h and re-homed into a new tone_manager.

This should also help with troubleshooting/compiling in the future as global_structs.h is widely included across the whole project.

Co-authored-by: Copilot <copilot@github.com>
@taclane

taclane commented May 21, 2026

Copy link
Copy Markdown
Contributor

98a5d58 The new custom squelch block residing at gr::blocks::ctcss_squelch_ff runs some risk of collision with stock gnuradio blocks that may be included by other gnuradio headers.

Even if that collision is unlikely, OOT blocks should follow gr::projectname::blockname conventions instead of using the default gnuradio blocks namespace.

There are other OOT t-r blocks to transition to gr::trunkrecorder::, but we can start with these two within the scope of the PR.

Co-authored-by: Copilot <copilot@github.com>
@taclane

taclane commented May 21, 2026

Copy link
Copy Markdown
Contributor

742550a - Remove vestigial code regarding a previous notched-reference implementation that is no longer present, as well as references to a non-existent CTCSS_DETECTOR_V2.md.

While otherwise unused, dominant_snr_db and top_three were retained to display in the CTCSS debugging output instead of being removed.

N_CTCSS is now only defined once, instead of having to match in two separate locations.

Co-authored-by: Copilot <copilot@github.com>
@taclane

taclane commented May 21, 2026

Copy link
Copy Markdown
Contributor

e864c5a - Removed mutex from CTCSS and DCS main work() loops. This should reduce some delays in squelch functionality by not having to process the whole inbound sample buffer before triggering functions to mute or unmute the audio.

Guards remain where appropriate, such as between reset() and get_verdict().

Co-authored-by: Copilot <copilot@github.com>
@taclane

taclane commented May 21, 2026

Copy link
Copy Markdown
Contributor

8b10b13 - When an explicit CTCSS or DCS tone was set, a side analysis was conducted on the unselected mode for debugging purposes. For this to have useful value, it would require simultaneous use of CTCSS and DCS tones in the same transmission, which is an improbable configuration to use as a default.

Even for debugging purposes, the non-indicated tone type would never clear the other squelch, so the additional resource cost doesn't have the ability to do thing it might purport to address (e.g. notify the user that a DCS-only channel was enabled by a specific CTCSS tone).

This does not change search mode, which will continue to analyze both tones separately and report back the highest confidence winner.

@taclane

taclane commented May 23, 2026

Copy link
Copy Markdown
Contributor

b8a0756 - CTCSS behaved largely the same regardless of whether it was gating on a single tone, or operating in an open search mode. It would lead to odd scenarios where short transmissions would successfully open squelch, but the detection algorithm would be unable to finish scoring the probable tone.

This eliminates search in single-tone/single-frequency configurations. If the received CTCSS tone opens squelch, it does not need additional scoring as it already is the correct tone. This should speed up tone recognition for short duration calls, and ensure that they send the tone data downstream upon conclusion.

@taclane

taclane commented May 27, 2026

Copy link
Copy Markdown
Contributor

02bc53d - Bring DCS to functional parity with the CTCSS decoder where it has a better split between single-code gating and open search.

When a code is input into the channel.csv, only the indicated code and its cyclic alias will be recognized (e.g. D023N and D047I have identical bitstreams). Previously, the DCS block recognized the indicated code and its inverse (e.g. D023N and D023I) as well as both cyclic aliases (D047I and D047N).

Single-code enabled channels should no longer "latch on" to incorrect, but otherwise valid DCS codes and get stuck that way.

When in search mode, the existence of a valid DCS code should always take precedence over probable CTCSS tones detected by the gnuradio block. This is for multiple reasons:

  • DCS and CTCSS should not be in concurrent use
  • the odds of a random bitstream being present and generating a DCS match is very low
  • DCS transmission at 134.4 baud unavoidably deposits some energy into the CTCSS detection bins near 67 Hz causing false positives. This is also true for the "end of call" 101010101... pattern which may cause false positives for the 131.8 and 136.5 Hz tones. Short duration DCS calls may otherwise be judged a CTCSS call based on scoring mechanics.

@taclane

taclane commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

56b40d5 - Per ongoing reports in discord, squelch options seem to have issues with interference from adjacent channels.

Initial investigation shows that the filter used by the channelizer is likely too wide and shows overlap with neighboring channels. This small edit should establish a passband across the center +/- 5200 Hz, and establish a stop band at the boundary, not a couple kHz inside the next channel.

@photounit

photounit commented Jul 28, 2026

Copy link
Copy Markdown

I have been unable to get this branch to work after numerous attempts, and after being told it was me, my config, or my channel file... . I finally decided to get my RF Service monitor out to try to make sense of what's going on, and I spent days doing so. I'm running a BladeRF xA4 (With a modifed source block to actually use libbladeRF) with a 22Mhz bandwidth covering 54 UHF channels. With the help of Claude, we found that both the PL and DCS blocks were subjected to low frequency interference on the pipe to the PL/DCS decoders that prevented proper decodes of PL and DCS signals. A a dedicated 55Hz highpass pre-stage was added to protect the PL decode and DCS's loose lowpass+highpass combo was replaced with a properly designed, tightly-centered bandpass — 4th-order Butterworth, 100–170Hz passband filter. The cause of the interference may be unique to my BaldeRF , but adding these filters should pose no harm to other receivers and can only be a positive change. There was also a logic bug in how it computed its noise floor from the two immediately-adjacent table entries, and for 114.8. Many of my channels used PL 114.8 so this explains a lot for me. This was corrected as well. I now have this running, and everything is finally working well. I am going to try to attach all four files that were modified although I suspect it wont work.

More info on what was found:

The floor-selection algorithm bug (114.8Hz-specific) — this had nothing to do with interference at all. It was a pure logic bug: decode mode computed its noise floor from the two immediately-adjacent table entries, and for 114.8, those neighbors (110.9, 118.8) happened to sit close enough that the tone's own real signal leaked into them, poisoning the floor used to judge itself. We proved this with a controlled bench test — the correct tone consistently failed while a mismatched tone passed, against the same real signal. Fixed by searching outward for a properly-spaced floor reference instead of always using the fixed ±1 neighbors.
The broadband interference — a completely separate, unrelated issue: a strong, harmonically-structured signal (~3.9Hz and harmonics) dominating the low end of the spectrum, with no highpass filter ever protecting CTCSS's decode range from it at all.

For DCS, by contrast, the interference was the only root cause we ever found — every other layer (codeword math, parsing, class-key logic, timing, sync detection) was individually traced and confirmed correct, with nothing else wrong.

Here's the list of the files that were changed.

trunk-recorder/gr_blocks/ctcss_squelch_ff.h
Added #include
Added a new BiquadStage struct and std::array<BiquadStage, 2> d_prefilter_stages member, plus process_biquad() and init_prefilter() declarations — supports the new highpass pre-filter
Added double d_bin_resolution_hz member — supports the floor-selection fix

trunk-recorder/gr_blocks/ctcss_squelch_ff.cc
Constructor: added init_prefilter() call; added computation of d_bin_resolution_hz right after d_window_samples is set
New functions: init_prefilter() (loads the 2-stage highpass biquad coefficients) and process_biquad() (the actual per-sample biquad math)
reset(): added clearing of the new prefilter biquad state
work(): every raw sample now passes through the new highpass biquad before entering the existing decimation ring buffer (previously fed the ring buffer directly)
Floor-selection logic (inside the per-tick evaluation): replaced the fixed "always use bin ±1" neighbor selection with an outward search that skips neighbors closer than 1.5× the Goertzel bin resolution

trunk-recorder/gr_blocks/dcs_squelch_ff.h
Replaced the old d_lpf_taps/d_lpf_state/d_lpf_state_idx (FIR lowpass) and d_hpf_a/d_hpf_prev_x/d_hpf_prev_y (single-pole highpass) members with a single std::array<BiquadStage, 4> d_bandpass_stages, plus process_biquad() and init_bandpass() declarations
Replaced build_lpf() declaration with init_bandpass()
Added #include

trunk-recorder/gr_blocks/dcs_squelch_ff.cc
Constructor: replaced the old build_lpf() + manual HPF coefficient setup with a single init_bandpass() call
build_lpf() removed entirely, replaced by init_bandpass() (hardcodes the 4-stage bandpass biquad coefficients) and process_biquad()
reset(): replaced the old LPF ring-buffer/HPF state clearing with clearing all 4 biquad stages
process_one(): restructured so the full bandpass cascade runs continuously on every raw sample (previously the FIR only computed its output once every d_detect_decim samples) — the old separate FIR-convolution + single-pole-HPF computation block was removed, replaced by using the continuously-computed bandpass output directly

Summary
2 files got new signal-processing logic (ctcss_squelch_ff), 2 files got old logic fully replaced (dcs_squelch_ff) — 4 files total, all within trunk-recorder/gr_blocks/
No changes needed anywhere else — both fixes are entirely self-contained within these two decoder blocks

ctcss_squelch_ff.cc.txt
ctcss_squelch_ff.h.txt
dcs_squelch_ff.cc.txt
dcs_squelch_ff.h.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants