ka9q-python 3.14.1__tar.gz → 3.14.2__tar.gz
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_python-3.14.1 → ka9q_python-3.14.2}/CHANGELOG.md +21 -0
- {ka9q_python-3.14.1/ka9q_python.egg-info → ka9q_python-3.14.2}/PKG-INFO +1 -1
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/API_REFERENCE.md +16 -1
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/__init__.py +1 -1
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/rtp_recorder.py +22 -6
- {ka9q_python-3.14.1 → ka9q_python-3.14.2/ka9q_python.egg-info}/PKG-INFO +1 -1
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/pyproject.toml +1 -1
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_rtp_recorder.py +64 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/LICENSE +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/MANIFEST.in +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/README.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/ARCHITECTURE.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/CLI_GUIDE.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/GETTING_STARTED.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/INSTALLATION.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/MULTI_STREAM.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/RECIPES.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/RTP_TIMING_SUPPORT.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/SECURITY.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/TESTING_GUIDE.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/docs/TUI_GUIDE.md +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/advanced_features_demo.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/channel_cleanup_example.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/codar_oceanography.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/diagnostics/diagnose_packets.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/diagnostics/repro_utc_bug.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/discover_example.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/grape_integration_example.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/hf_band_scanner.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/multi_stream_smoke.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/rtp_recorder_example.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/simple_am_radio.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/spectrum_example.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/stream_example.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/superdarn_recorder.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/test_channel_operations.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/test_improvements.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/test_timing_fields.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/tune.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/examples/tune_example.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/_multicast.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/addressing.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/cli.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/compat.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/control.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/discovery.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/exceptions.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/managed_stream.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/monitor.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/multi_stream.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/pps_calibrator.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/resequencer.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/spectrum_stream.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/status.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/stream.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/stream_quality.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/tui.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/types.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q/utils.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q_python.egg-info/SOURCES.txt +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q_python.egg-info/dependency_links.txt +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q_python.egg-info/entry_points.txt +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q_python.egg-info/requires.txt +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q_python.egg-info/top_level.txt +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/ka9q_radio_compat +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/scripts/check_upstream_drift.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/scripts/sync_types.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/setup.cfg +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/setup.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/__init__.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/conftest.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_addressing.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_channel_verification.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_client_id_destination.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_create_split_encoding.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_decode_description.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_decode_functions.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_encode_functions.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_encode_socket.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_ensure_channel_encoding.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_filter_edges.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_integration.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_iq_20khz_f32.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_lifetime.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_listen_multicast.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_managed_stream_recovery.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_monitor.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_multicast_helpers.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_multihomed.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_native_discovery.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_performance_fixes.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_protocol_compat.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_remove_channel.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_security_features.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_spectrum.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_ssrc_dest_unit.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_ssrc_encoding_unit.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_ssrc_radiod_host_unit.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_status_decoder.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_ttl_warning.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_tune.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_tune_cli.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_tune_debug.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_tune_live.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_tune_method.py +0 -0
- {ka9q_python-3.14.1 → ka9q_python-3.14.2}/tests/test_upstream_drift.py +0 -0
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.14.2] - 2026-05-14
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`rtp_to_wallclock()` gains optional `wallclock_hint_sec` parameter.**
|
|
8
|
+
When supplied, the function uses the hint to disambiguate the 32-bit
|
|
9
|
+
RTP wrap epoch instead of calling `time.time()`. Authority-aware
|
|
10
|
+
callers (those with access to an hf-timestd `rtp_to_utc_offset_ns`)
|
|
11
|
+
can now keep the labeling path off the chrony-disciplined system
|
|
12
|
+
clock, per the METROLOGY.md §4.5 RTP-reference invariant. The hint
|
|
13
|
+
only needs ±period/2 accuracy (≥6 hours at typical sample rates).
|
|
14
|
+
When omitted, the function falls back to `time.time()` for backward
|
|
15
|
+
compatibility — existing callers are unaffected.
|
|
16
|
+
|
|
17
|
+
### Tests
|
|
18
|
+
|
|
19
|
+
- 3 new in `tests/test_rtp_recorder.py`: hint bypasses `time.time()`
|
|
20
|
+
entirely; hint at a different wrap epoch correctly overrides system
|
|
21
|
+
clock; default path still consults `time.time()` when no hint is
|
|
22
|
+
given.
|
|
23
|
+
|
|
3
24
|
## [3.14.1] - 2026-05-14
|
|
4
25
|
|
|
5
26
|
### Fixed
|
|
@@ -727,7 +727,11 @@ Dataclass: `packets_received`, `packets_dropped`,
|
|
|
727
727
|
|
|
728
728
|
```python
|
|
729
729
|
parse_rtp_header(data: bytes) -> Optional[RTPHeader]
|
|
730
|
-
rtp_to_wallclock(
|
|
730
|
+
rtp_to_wallclock(
|
|
731
|
+
rtp_timestamp: int,
|
|
732
|
+
channel: ChannelInfo,
|
|
733
|
+
wallclock_hint_sec: Optional[float] = None,
|
|
734
|
+
) -> Optional[float]
|
|
731
735
|
```
|
|
732
736
|
|
|
733
737
|
`rtp_to_wallclock()` returns `None` unless `channel.gps_time` and
|
|
@@ -735,6 +739,17 @@ rtp_to_wallclock(rtp_timestamp: int, channel: ChannelInfo) -> Optional[float]
|
|
|
735
739
|
`channel.chain_delay_correction_ns` is set (see below), it is
|
|
736
740
|
subtracted from the computed wallclock.
|
|
737
741
|
|
|
742
|
+
The `wallclock_hint_sec` parameter (v3.14.2+) lets callers supply an
|
|
743
|
+
approximate UTC reference for 32-bit RTP wrap-epoch disambiguation
|
|
744
|
+
without falling back to `time.time()`. Authority-aware callers
|
|
745
|
+
(e.g. those reading `rtp_to_utc_offset_ns` from an `hf-timestd`
|
|
746
|
+
authority.json) should pass this hint to keep the labeling path off
|
|
747
|
+
the chrony-disciplined system clock (METROLOGY.md §4.5 RTP-reference
|
|
748
|
+
invariant). The hint only needs ±period/2 accuracy (≥6 hours at
|
|
749
|
+
typical sample rates), so even a coarse value is sufficient. When
|
|
750
|
+
omitted, the function falls back to `time.time()` for backward
|
|
751
|
+
compatibility.
|
|
752
|
+
|
|
738
753
|
---
|
|
739
754
|
|
|
740
755
|
## L6 BPSK PPS Calibration
|
|
@@ -124,7 +124,11 @@ def parse_rtp_header(data: bytes) -> Optional[RTPHeader]:
|
|
|
124
124
|
)
|
|
125
125
|
|
|
126
126
|
|
|
127
|
-
def rtp_to_wallclock(
|
|
127
|
+
def rtp_to_wallclock(
|
|
128
|
+
rtp_timestamp: int,
|
|
129
|
+
channel: ChannelInfo,
|
|
130
|
+
wallclock_hint_sec: Optional[float] = None,
|
|
131
|
+
) -> Optional[float]:
|
|
128
132
|
"""
|
|
129
133
|
Convert RTP timestamp to Unix wall-clock time
|
|
130
134
|
|
|
@@ -133,6 +137,15 @@ def rtp_to_wallclock(rtp_timestamp: int, channel: ChannelInfo) -> Optional[float
|
|
|
133
137
|
Args:
|
|
134
138
|
rtp_timestamp: RTP timestamp from packet header
|
|
135
139
|
channel: ChannelInfo with gps_time, rtp_timesnap, sample_rate
|
|
140
|
+
wallclock_hint_sec: Approximate UTC seconds, used solely to
|
|
141
|
+
disambiguate the 32-bit RTP wrap epoch (see below). Must be
|
|
142
|
+
within ±period/2 of true UTC (period = 2**32 / sample_rate
|
|
143
|
+
seconds, ≥6 hours for typical sample rates). When omitted,
|
|
144
|
+
falls back to ``time.time()`` — convenient but couples the
|
|
145
|
+
result to the host system clock. Callers that have an
|
|
146
|
+
hf-timestd authority offset available should pass it
|
|
147
|
+
explicitly to keep the labeling path off the chrony-disciplined
|
|
148
|
+
system clock (METROLOGY.md §4.5 RTP-reference invariant).
|
|
136
149
|
|
|
137
150
|
Returns:
|
|
138
151
|
Unix timestamp (seconds) or None if timing info unavailable
|
|
@@ -149,10 +162,10 @@ def rtp_to_wallclock(rtp_timestamp: int, channel: ChannelInfo) -> Optional[float
|
|
|
149
162
|
|
|
150
163
|
We disambiguate by picking the wrap-epoch count ``k`` (full
|
|
151
164
|
2**32-sample periods elapsed since the snapshot) that places
|
|
152
|
-
the resulting wall-clock time closest to
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
the resulting wall-clock time closest to ``wallclock_hint_sec``
|
|
166
|
+
(or ``time.time()`` if no hint was given). Either source needs
|
|
167
|
+
only ±period/2 accuracy, so this stays robust even when the
|
|
168
|
+
hinting reference is loose.
|
|
156
169
|
|
|
157
170
|
Observed on bee1 2026-05-08: long-running SSRCs caused TSL3
|
|
158
171
|
SHM samples stuck at the snapshot's wall-clock time, ~12.4 h
|
|
@@ -181,7 +194,10 @@ def rtp_to_wallclock(rtp_timestamp: int, channel: ChannelInfo) -> Optional[float
|
|
|
181
194
|
# value closest to the system clock. Exact when sys clock is
|
|
182
195
|
# within ±period/2 of true UTC.
|
|
183
196
|
period_ns = BILLION * 0x100000000 // channel.sample_rate
|
|
184
|
-
|
|
197
|
+
if wallclock_hint_sec is not None:
|
|
198
|
+
sys_now_ns = int(wallclock_hint_sec * BILLION)
|
|
199
|
+
else:
|
|
200
|
+
sys_now_ns = int(time.time() * BILLION)
|
|
185
201
|
diff_ns = sys_now_ns - base_wall_ns
|
|
186
202
|
if period_ns > 0:
|
|
187
203
|
# Round-to-nearest of diff_ns / period_ns (Python `//` is
|
|
@@ -118,3 +118,67 @@ def test_rtp_to_wallclock_returns_none_when_timing_missing():
|
|
|
118
118
|
assert rtp_to_wallclock(1000, channel) is None
|
|
119
119
|
channel = _channel(48000, None, 1000)
|
|
120
120
|
assert rtp_to_wallclock(1000, channel) is None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_rtp_to_wallclock_hint_bypasses_system_clock():
|
|
124
|
+
"""When `wallclock_hint_sec` is provided, time.time() must NOT be
|
|
125
|
+
consulted. This is the RTP-reference invariant: authority-aware
|
|
126
|
+
callers can disambiguate the wrap epoch without coupling to the
|
|
127
|
+
host system clock."""
|
|
128
|
+
sample_rate = 96000
|
|
129
|
+
gps_time_ns = 1234567890000000000
|
|
130
|
+
channel = _channel(sample_rate, gps_time_ns, 1000)
|
|
131
|
+
snapshot_wall = (gps_time_ns + BILLION * (GPS_UTC_OFFSET - 18)) / BILLION
|
|
132
|
+
|
|
133
|
+
period_sec = 0x100000000 / sample_rate
|
|
134
|
+
target_rtp = (1000 + 0x100000000) & 0xFFFFFFFF
|
|
135
|
+
expected = snapshot_wall + period_sec
|
|
136
|
+
|
|
137
|
+
# Pin time.time() to something WAY OFF (1 year earlier) — if the
|
|
138
|
+
# function ignores the hint and consults the system clock anyway,
|
|
139
|
+
# the wrap-epoch math will pick k=0 and the result will be wrong.
|
|
140
|
+
bogus_now = expected - 365 * 86400.0
|
|
141
|
+
with patch("ka9q.rtp_recorder.time.time", return_value=bogus_now) as mock_time:
|
|
142
|
+
result = rtp_to_wallclock(target_rtp, channel, wallclock_hint_sec=expected)
|
|
143
|
+
assert result == pytest.approx(expected, abs=1e-3)
|
|
144
|
+
# Strict: hint path must not call time.time() at all.
|
|
145
|
+
mock_time.assert_not_called()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_rtp_to_wallclock_hint_picks_correct_epoch():
|
|
149
|
+
"""Hint at a different wrap epoch from the system clock → result
|
|
150
|
+
follows the hint, proving the function uses the hint for k-selection."""
|
|
151
|
+
sample_rate = 12000 # WSPR-band rate; period ≈ 99 hours
|
|
152
|
+
gps_time_ns = 1234567890000000000
|
|
153
|
+
channel = _channel(sample_rate, gps_time_ns, 1000)
|
|
154
|
+
snapshot_wall = (gps_time_ns + BILLION * (GPS_UTC_OFFSET - 18)) / BILLION
|
|
155
|
+
period_sec = 0x100000000 / sample_rate
|
|
156
|
+
|
|
157
|
+
# RTP value aliased to ~snapshot, but actually 2 wraps later.
|
|
158
|
+
target_rtp = (1000 + 2 * 0x100000000) & 0xFFFFFFFF
|
|
159
|
+
expected = snapshot_wall + 2 * period_sec
|
|
160
|
+
|
|
161
|
+
# System clock at the snapshot wall (would pick k=0); hint says
|
|
162
|
+
# ~2 periods later (correct k=2).
|
|
163
|
+
with patch("ka9q.rtp_recorder.time.time", return_value=snapshot_wall):
|
|
164
|
+
result = rtp_to_wallclock(target_rtp, channel, wallclock_hint_sec=expected)
|
|
165
|
+
assert result == pytest.approx(expected, abs=1e-3)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_rtp_to_wallclock_hint_omitted_falls_back_to_system_clock():
|
|
169
|
+
"""Default path (no hint) preserves legacy behavior for
|
|
170
|
+
backwards compatibility — the hint parameter is purely additive."""
|
|
171
|
+
sample_rate = 48000
|
|
172
|
+
gps_time_ns = 1234567890000000000
|
|
173
|
+
channel = _channel(sample_rate, gps_time_ns, 1000)
|
|
174
|
+
snapshot_wall = (gps_time_ns + BILLION * (GPS_UTC_OFFSET - 18)) / BILLION
|
|
175
|
+
period_sec = 0x100000000 / sample_rate
|
|
176
|
+
|
|
177
|
+
target_rtp = (1000 + 0x100000000) & 0xFFFFFFFF
|
|
178
|
+
expected = snapshot_wall + period_sec
|
|
179
|
+
|
|
180
|
+
# No hint → must consult time.time() to land on k=1.
|
|
181
|
+
with patch("ka9q.rtp_recorder.time.time", return_value=expected) as mock_time:
|
|
182
|
+
result = rtp_to_wallclock(target_rtp, channel)
|
|
183
|
+
assert result == pytest.approx(expected, abs=1e-3)
|
|
184
|
+
mock_time.assert_called()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|