ka9q-python 3.26.1__py3-none-any.whl → 3.26.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ka9q/control.py CHANGED
@@ -910,7 +910,11 @@ class RadiodControl:
910
910
  Sends TLV-encoded commands to radiod's control socket to create
911
911
  and configure channels.
912
912
  """
913
-
913
+
914
+ # ensure_channel(): pause between status polls while waiting for radiod
915
+ # to report the requested OUTPUT_ENCODING.
916
+ _ENCODING_SETTLE_POLL_S = 0.1
917
+
914
918
  def __init__(self, status_address: str, max_commands_per_sec: int = 100,
915
919
  interface: Optional[str] = None,
916
920
  client_id: Optional[str] = None):
@@ -2072,9 +2076,40 @@ class RadiodControl:
2072
2076
  f"Channel SSRC {ssrc} not verified within {timeout}s. "
2073
2077
  f"Requested: {frequency_hz/1e6:.3f} MHz, {preset}, {sample_rate} Hz"
2074
2078
  )
2075
- # radiod may grant a different encoding (e.g. F32->S16 for some IQ
2076
- # configs); the returned ChannelInfo carries the granted value, which
2077
- # consumers use authoritatively. A rate/preset divergence is logged
2079
+ # create_channel() sends OUTPUT_ENCODING as a separate command after
2080
+ # the create, so the first status can predate it and report the
2081
+ # preset's encoding. Streams pick their decoder from
2082
+ # ChannelInfo.encoding; a stale S16 on an F32 stream decodes 4-byte
2083
+ # floats as 2-byte ints — noise-like garbage at "100% completeness".
2084
+ # Keep polling until radiod reports the requested encoding. If it
2085
+ # still differs at the deadline, return what radiod reports (the
2086
+ # stream must decode what is on the wire) and say so loudly.
2087
+ if encoding != 0 and channel.encoding != encoding:
2088
+ deadline = time.monotonic() + timeout
2089
+ stale = channel.encoding
2090
+ while time.monotonic() < deadline:
2091
+ time.sleep(self._ENCODING_SETTLE_POLL_S)
2092
+ fresh = self.poll_channel(
2093
+ ssrc, expected_freq=frequency_hz,
2094
+ timeout=max(0.1, min(1.0, deadline - time.monotonic())),
2095
+ frequency_tolerance=frequency_tolerance,
2096
+ )
2097
+ if fresh is not None:
2098
+ channel = fresh
2099
+ if channel.encoding == encoding:
2100
+ break
2101
+ if channel.encoding == encoding:
2102
+ logger.info(
2103
+ "ensure_channel: SSRC %s encoding settled %s -> %s",
2104
+ ssrc, stale, channel.encoding,
2105
+ )
2106
+ else:
2107
+ logger.error(
2108
+ "ensure_channel: SSRC %s still reports encoding %s after "
2109
+ "%.1fs (requested %s); returning radiod's value",
2110
+ ssrc, channel.encoding, timeout, encoding,
2111
+ )
2112
+ # A rate/preset divergence is logged
2078
2113
  # but not fatal — the channel exists at the requested frequency.
2079
2114
  # An ABSENT preset is not a divergence. radiod may decline to echo
2080
2115
  # PRESET at all (write-only from the nopreset branch on), and a label
ka9q/stream.py CHANGED
@@ -129,11 +129,12 @@ def parse_rtp_samples(
129
129
  if floats is None:
130
130
  return None
131
131
  if is_iq:
132
- # radiod silently downgrades F32 → S16 for some IQ-channel
133
- # configurations (high sample rate + wide filter); confirmed on
134
- # bee1 2026-05-15 for T6/TSL3 BPSK PPS channel (encoding=4
135
- # requested, encoding=2 granted, decoded as F32LE → NaN-poisoned
136
- # input, TSL3 dark). Honor the granted encoding above.
132
+ # Decode with the encoding the channel reports. A wrong value
133
+ # here corrupts silently: the bee1 2026-05-15 T6/TSL3 "F32→S16
134
+ # downgrade" and codar-sounder's 2026-11-01 garbage IQ were both
135
+ # a stale encoding from ensure_channel (status read before the
136
+ # separate OUTPUT_ENCODING command landed), not radiod changing
137
+ # the encoding. ensure_channel now waits for it to settle.
137
138
  if len(floats) % 2 != 0:
138
139
  logger.warning(f"Odd number of samples in IQ payload: {len(floats)}")
139
140
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ka9q-python
3
- Version: 3.26.1
3
+ Version: 3.26.2
4
4
  Summary: Python interface for ka9q-radio control and monitoring
5
5
  Author-email: Michael Hauan AC0G <ac0g@hauan.org>
6
6
  License: MIT
@@ -3,7 +3,7 @@ ka9q/_multicast.py,sha256=U6pCiH1ruy3i5yv7B_rRhkakokwnetu1Nd3009nAI5w,3343
3
3
  ka9q/addressing.py,sha256=GaHLeTwRpsmJawRAROoj-AqALcVkqO6Jq4_th9u7fjE,2681
4
4
  ka9q/cli.py,sha256=nfU4hqScqjJtuWBBraPxUb-37cvjY_qtZkNIxr9GzRU,15040
5
5
  ka9q/compat.py,sha256=uiU-wkQ1p88fcfPYRdqU1QxHiunmQPIhM5CXkW_5GMs,436
6
- ka9q/control.py,sha256=twKMbLjXmkAp2P2Pi51QKCj1W8zrv4wE-4RYSSeL7Zk,156426
6
+ ka9q/control.py,sha256=RbiK9Wwn6E9tNQZX6rbJyPRM3YJXwDSpDaBd3QkXyKw,158132
7
7
  ka9q/discovery.py,sha256=-jGcDczuwEQmmJRogc2hR22PUKT2qxWkPgy_ffd9Cdk,28615
8
8
  ka9q/exceptions.py,sha256=_P8rok_tTmRMu-lLRhS95kv8FRK5PEY80_6jIux2mWM,474
9
9
  ka9q/managed_stream.py,sha256=gHvGcQgJF5FCOzVp7V6vcIp75NLGnOHgClGR24i9_No,18842
@@ -17,14 +17,14 @@ ka9q/slot_clock.py,sha256=9OuE1qMHJ5YBHfNPtQSguj_KHgkU9iAXfhOLEk-1B78,13468
17
17
  ka9q/spectrum_stream.py,sha256=utMhpH8hkkwOSD1WpKb4r0YHm3isDF0TvYMq-jJJFwY,11458
18
18
  ka9q/status.py,sha256=ufrw-8pPvOs9YjRlR3W8cWWsfA23utdR-eMddyuokKU,28421
19
19
  ka9q/status_listener.py,sha256=20l9aHii263myYlO8mmSgI_tUcchUwLwgh69Dx-1UQY,20045
20
- ka9q/stream.py,sha256=s1Xl4Njia9DwXxsSF2XSon1plFjJoDXXXCGeLZdF734,33111
20
+ ka9q/stream.py,sha256=TvEcQorlRWZ9_MoyN0apXlrZaLtlheU7V2RP0yR-hIk,33197
21
21
  ka9q/stream_quality.py,sha256=YrKa07PzDi4xeF5tubwpDvSTs5eHu0G7mOa12iIINrs,8222
22
22
  ka9q/tui.py,sha256=ryL5CxfYkVzXhsCy1hw9GP3ya8hDbZTccfkc_uJtrDQ,27182
23
23
  ka9q/types.py,sha256=woWL_ShotZnYUYsR4RBL2wu13S1MTkkJC9_-9IVh14g,7514
24
24
  ka9q/utils.py,sha256=fFR_sYIpeDyPaFlXzN6jCiGhYlssvrMMVx4z6vyhqcc,7107
25
- ka9q_python-3.26.1.dist-info/licenses/LICENSE,sha256=mjz-l5TVsO9j7Jqt1gfLvTWtZK_HL9sZRRpsHaq4Qv8,1074
26
- ka9q_python-3.26.1.dist-info/METADATA,sha256=74ee0echpb96cFl7hzAeHZ7QD42Wk3Y4bUUhJWCaWI0,14637
27
- ka9q_python-3.26.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
28
- ka9q_python-3.26.1.dist-info/entry_points.txt,sha256=Wt5Q4OMUKHaf4KdsZXWsjSBpbHVuTtt3pypVIHi7eRk,39
29
- ka9q_python-3.26.1.dist-info/top_level.txt,sha256=FUTLZDtQpJGenGPPlN34E5QpQi_oaiLB6LuzgHblBNE,5
30
- ka9q_python-3.26.1.dist-info/RECORD,,
25
+ ka9q_python-3.26.2.dist-info/licenses/LICENSE,sha256=mjz-l5TVsO9j7Jqt1gfLvTWtZK_HL9sZRRpsHaq4Qv8,1074
26
+ ka9q_python-3.26.2.dist-info/METADATA,sha256=s80doU296SSsQAQg8UBa2rYHI5nBGwGOzIYhW561_Qo,14637
27
+ ka9q_python-3.26.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
28
+ ka9q_python-3.26.2.dist-info/entry_points.txt,sha256=Wt5Q4OMUKHaf4KdsZXWsjSBpbHVuTtt3pypVIHi7eRk,39
29
+ ka9q_python-3.26.2.dist-info/top_level.txt,sha256=FUTLZDtQpJGenGPPlN34E5QpQi_oaiLB6LuzgHblBNE,5
30
+ ka9q_python-3.26.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (84.0.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5