Merge pull request #65 from ce5rpy/develop

fix: deliver group call on both slots when TG is on TS1+TS2
pull/66/head
ce5rpy 2 months ago committed by GitHub
commit 934f4e5540
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -91,11 +91,12 @@ def normalize_ua_voice_slot(peer: dict[str, Any], wire_slot: int) -> int:
def peer_listen_slots(peer: dict[str, Any], tgid: int) -> list[int]:
"""Candidate RF slots where this peer is subscribed to ``tgid`` (OPTIONS).
"""RF slots where this peer listens for ``tgid`` (static OPTIONS or wire fallback).
When the same TG is listed on both TS1 and TS2, both slots are returned as
candidates; :func:`iter_downlink_voice_slots` collapses that to **one**
delivery slot (never duplicate DMRD).
A genuinely duplex-capable peer (see ``peer_is_simplex``) with the same TG
listed on both TS1 and TS2 gets both slots back -- it has two independent
RF timeslots and is expected to key up on both, same as a real repeater
configured that way. Simplex peers/bridges always collapse to one slot.
"""
from adn_server.application.report.payloads import parse_peer_options_static
@ -656,18 +657,14 @@ def iter_downlink_voice_slots(
wire_slot: int,
tgid: int,
) -> list[int]:
"""Exactly one voice slot for group downlink to this peer.
"""Voice slot(s) to deliver a group downlink to this peer.
Legacy ``send_peers`` sends one DMRD per peer. If OPTIONS lists the same TG
on TS1 and TS2, keep a single copy on the bridge/OBP wire slot (remap only
when the peer listens on a different unambiguous slot).
Trusts ``peer_listen_slots`` -- it already collapses simplex peers/bridges
to one slot via ``peer_is_simplex``, and only returns more than one slot
for a peer confirmed duplex-capable with the TG on both TS1 and TS2, which
should genuinely receive on both (see peer_listen_slots docstring).
"""
listen = peer_listen_slots(peer, tgid)
if len(listen) > 1:
ws = int(wire_slot)
if ws in listen:
return [ws]
return [int(peer_downlink_voice_slot(peer, wire_slot, tgid))]
if listen:
return listen
if peer_receives_group_tgid(peer, wire_slot, tgid):

@ -170,15 +170,28 @@ def test_inject_only_hangtime_blocks_static_tg_after_dynamic_tx() -> None:
assert not peer_slot_blocks_downlink(ctx, peer_id, peer, same7306, pkt_time=now + 8)
def test_same_static_tg_on_both_slots_delivers_once_on_wire_slot() -> None:
"""TG on TS1+TS2 OPTIONS: one DMRD on the bridge wire slot (not dual fan-out)."""
def test_same_static_tg_on_both_slots_delivers_twice() -> None:
"""Duplex peer: TG listed in TS1 and TS2 OPTIONS delivers on both voice slots."""
peer = {"OPTIONS": b"TS1=730444;TS2=730444;"}
assert iter_downlink_voice_slots(peer, 1, 730444) == [1]
assert iter_downlink_voice_slots(peer, 2, 730444) == [2]
# Subscription still visible on both; delivery collapses in iter_*.
assert iter_downlink_voice_slots(peer, 1, 730444) == [1, 2]
assert peer_listen_slots(peer, 730444) == [1, 2]
def test_same_static_tg_on_both_slots_bridge_delivers_once() -> None:
"""Simplex peer (bridge, e.g. ysf2dmr/adn-bridge): TG on both OPTIONS slots
still collapses to a single copy -- it has one audio pipeline, not two
independent RF timeslots, so dual-slot fan-out would double its downlink
rate (see PR #53)."""
peer = {
"OPTIONS": b"TS1=730444;TS2=730444;",
"RX_FREQ": "000000000",
"TX_FREQ": "000000000",
}
assert iter_downlink_voice_slots(peer, 1, 730444) == [2]
assert iter_downlink_voice_slots(peer, 2, 730444) == [2]
assert peer_listen_slots(peer, 730444) == [2]
def test_static_tg_on_one_slot_unchanged() -> None:
peer_ts2 = {"OPTIONS": b"TS2=9140;"}
assert iter_downlink_voice_slots(peer_ts2, 1, 9140) == [2]

Loading…
Cancel
Save

Powered by TurnKey Linux.