ka9q-python 3.4.1__tar.gz → 3.4.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.
Files changed (71) hide show
  1. {ka9q_python-3.4.1/ka9q_python.egg-info → ka9q_python-3.4.2}/PKG-INFO +2 -1
  2. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/README.md +1 -0
  3. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/stream_example.py +4 -6
  4. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/__init__.py +1 -1
  5. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/control.py +2 -0
  6. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/discovery.py +3 -1
  7. {ka9q_python-3.4.1 → ka9q_python-3.4.2/ka9q_python.egg-info}/PKG-INFO +2 -1
  8. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q_python.egg-info/SOURCES.txt +1 -0
  9. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/pyproject.toml +1 -1
  10. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/setup.py +1 -1
  11. ka9q_python-3.4.2/tests/test_decode_description.py +46 -0
  12. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/LICENSE +0 -0
  13. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/MANIFEST.in +0 -0
  14. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/advanced_features_demo.py +0 -0
  15. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/channel_cleanup_example.py +0 -0
  16. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/codar_oceanography.py +0 -0
  17. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/diagnostics/diagnose_packets.py +0 -0
  18. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/diagnostics/repro_utc_bug.py +0 -0
  19. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/discover_example.py +0 -0
  20. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/grape_integration_example.py +0 -0
  21. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/hf_band_scanner.py +0 -0
  22. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/rtp_recorder_example.py +0 -0
  23. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/simple_am_radio.py +0 -0
  24. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/superdarn_recorder.py +0 -0
  25. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/test_channel_operations.py +0 -0
  26. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/test_improvements.py +0 -0
  27. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/test_timing_fields.py +0 -0
  28. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/tune.py +0 -0
  29. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/examples/tune_example.py +0 -0
  30. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/addressing.py +0 -0
  31. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/exceptions.py +0 -0
  32. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/managed_stream.py +0 -0
  33. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/monitor.py +0 -0
  34. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/resequencer.py +0 -0
  35. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/rtp_recorder.py +0 -0
  36. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/stream.py +0 -0
  37. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/stream_quality.py +0 -0
  38. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/types.py +0 -0
  39. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q/utils.py +0 -0
  40. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q_python.egg-info/dependency_links.txt +0 -0
  41. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q_python.egg-info/requires.txt +0 -0
  42. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/ka9q_python.egg-info/top_level.txt +0 -0
  43. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/setup.cfg +0 -0
  44. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/__init__.py +0 -0
  45. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/conftest.py +0 -0
  46. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_addressing.py +0 -0
  47. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_channel_verification.py +0 -0
  48. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_create_split_encoding.py +0 -0
  49. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_decode_functions.py +0 -0
  50. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_encode_functions.py +0 -0
  51. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_encode_socket.py +0 -0
  52. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_ensure_channel_encoding.py +0 -0
  53. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_integration.py +0 -0
  54. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_iq_20khz_f32.py +0 -0
  55. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_listen_multicast.py +0 -0
  56. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_managed_stream_recovery.py +0 -0
  57. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_monitor.py +0 -0
  58. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_multihomed.py +0 -0
  59. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_native_discovery.py +0 -0
  60. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_performance_fixes.py +0 -0
  61. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_remove_channel.py +0 -0
  62. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_rtp_recorder.py +0 -0
  63. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_security_features.py +0 -0
  64. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_ssrc_dest_unit.py +0 -0
  65. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_ssrc_encoding_unit.py +0 -0
  66. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_ttl_warning.py +0 -0
  67. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_tune.py +0 -0
  68. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_tune_cli.py +0 -0
  69. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_tune_debug.py +0 -0
  70. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_tune_live.py +0 -0
  71. {ka9q_python-3.4.1 → ka9q_python-3.4.2}/tests/test_tune_method.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ka9q-python
3
- Version: 3.4.1
3
+ Version: 3.4.2
4
4
  Summary: Python interface for ka9q-radio control and monitoring
5
5
  Home-page: https://github.com/mijahauan/ka9q-python
6
6
  Author: Michael Hauan AC0G
@@ -48,6 +48,7 @@ Control radiod channels for any application: AM/FM/SSB radio, WSPR monitoring, S
48
48
 
49
49
  - [Features](#features)
50
50
  - [Installation](#installation)
51
+ - [Getting Started](docs/GETTING_STARTED.md)
51
52
  - [Quick Start](#quick-start)
52
53
  - [Documentation](#documentation)
53
54
  - [Examples](#examples)
@@ -13,6 +13,7 @@ Control radiod channels for any application: AM/FM/SSB radio, WSPR monitoring, S
13
13
 
14
14
  - [Features](#features)
15
15
  - [Installation](#installation)
16
+ - [Getting Started](docs/GETTING_STARTED.md)
16
17
  - [Quick Start](#quick-start)
17
18
  - [Documentation](#documentation)
18
19
  - [Examples](#examples)
@@ -123,15 +123,13 @@ def main():
123
123
  if args.discover:
124
124
  print("Discovering channels...")
125
125
  channels = discover_channels(timeout=3.0)
126
- channel = None
127
- for ch in channels:
128
- if ch.ssrc == args.ssrc:
129
- channel = ch
130
- break
126
+
127
+ # FIX: Properly access the channel from the dictionary
128
+ channel = channels.get(args.ssrc)
131
129
 
132
130
  if channel is None:
133
131
  print(f"SSRC {args.ssrc} not found in discovered channels")
134
- print(f"Available SSRCs: {[ch.ssrc for ch in channels]}")
132
+ print(f"Available SSRCs: {list(channels.keys())}")
135
133
  return 1
136
134
 
137
135
  print(f"Found channel: {channel.frequency/1e6:.3f} MHz, {channel.sample_rate} Hz")
@@ -56,7 +56,7 @@ Lower-level usage (explicit control):
56
56
  )
57
57
  print(f"Created channel with SSRC: {ssrc}")
58
58
  """
59
- __version__ = '3.4.1'
59
+ __version__ = '3.4.2'
60
60
  __author__ = 'Michael Hauan AC0G'
61
61
 
62
62
  from .control import RadiodControl, allocate_ssrc
@@ -1972,6 +1972,8 @@ class RadiodControl:
1972
1972
  status['rf_atten'] = decode_float(data, optlen)
1973
1973
  elif type_val == StatusType.RF_AGC:
1974
1974
  status['rf_agc'] = decode_int(data, optlen)
1975
+ elif type_val == StatusType.DESCRIPTION:
1976
+ status['description'] = decode_string(data, optlen)
1975
1977
  elif type_val == StatusType.PRESET:
1976
1978
  status['preset'] = decode_string(data, optlen)
1977
1979
  elif type_val == StatusType.LOW_EDGE:
@@ -33,6 +33,7 @@ class ChannelInfo:
33
33
  gps_time: Optional[int] = None # GPS nanoseconds when RTP_TIMESNAP was captured
34
34
  rtp_timesnap: Optional[int] = None # RTP timestamp at GPS_TIME
35
35
  encoding: int = 0 # stream encoding (0=none, 4=F32, etc)
36
+ description: Optional[str] = None # SDR hardware description
36
37
 
37
38
 
38
39
  def _create_status_listener_socket(multicast_addr: str, interface: Optional[str] = None) -> socket.socket:
@@ -208,7 +209,8 @@ def discover_channels_native(status_address: str, listen_duration: float = 2.0,
208
209
  port=port,
209
210
  gps_time=status.get('gps_time'),
210
211
  rtp_timesnap=status.get('rtp_timesnap'),
211
- encoding=status.get('encoding', 0)
212
+ encoding=status.get('encoding', 0),
213
+ description=status.get('description')
212
214
  )
213
215
 
214
216
  # Store or update channel info
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ka9q-python
3
- Version: 3.4.1
3
+ Version: 3.4.2
4
4
  Summary: Python interface for ka9q-radio control and monitoring
5
5
  Home-page: https://github.com/mijahauan/ka9q-python
6
6
  Author: Michael Hauan AC0G
@@ -48,6 +48,7 @@ Control radiod channels for any application: AM/FM/SSB radio, WSPR monitoring, S
48
48
 
49
49
  - [Features](#features)
50
50
  - [Installation](#installation)
51
+ - [Getting Started](docs/GETTING_STARTED.md)
51
52
  - [Quick Start](#quick-start)
52
53
  - [Documentation](#documentation)
53
54
  - [Examples](#examples)
@@ -43,6 +43,7 @@ tests/conftest.py
43
43
  tests/test_addressing.py
44
44
  tests/test_channel_verification.py
45
45
  tests/test_create_split_encoding.py
46
+ tests/test_decode_description.py
46
47
  tests/test_decode_functions.py
47
48
  tests/test_encode_functions.py
48
49
  tests/test_encode_socket.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ka9q-python"
7
- version = "3.4.1"
7
+ version = "3.4.2"
8
8
  description = "Python interface for ka9q-radio control and monitoring"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -12,7 +12,7 @@ long_description = readme.read_text() if readme.exists() else ''
12
12
 
13
13
  setup(
14
14
  name='ka9q-python',
15
- version='3.4.1',
15
+ version='3.4.2',
16
16
  description='Python interface for ka9q-radio control and monitoring',
17
17
  long_description=long_description,
18
18
  long_description_content_type='text/markdown',
@@ -0,0 +1,46 @@
1
+ import sys
2
+ import unittest
3
+ from pathlib import Path
4
+ from unittest.mock import patch, MagicMock
5
+
6
+ # Add parent directory to path
7
+ sys.path.insert(0, str(Path(__file__).parent.parent))
8
+
9
+ from ka9q.control import RadiodControl
10
+ from ka9q.types import StatusType
11
+
12
+ class TestDescriptionDecoding(unittest.TestCase):
13
+ """Test SDR description decoding"""
14
+
15
+ @patch('ka9q.control.RadiodControl._connect', return_value=None)
16
+ def test_decode_description(self, mock_connect):
17
+ """Verify that _decode_status_response decodes StatusType.DESCRIPTION correctly"""
18
+ # Create a RadiodControl instance (now safe due to mock)
19
+ control = RadiodControl("radiod.local")
20
+
21
+ # Manually set attributes that __init__ might not have set due to mocked _connect
22
+ control.status_mcast_addr = "239.1.1.1"
23
+ control.metrics = MagicMock()
24
+ # Create a dummy status packet with a description
25
+ # Format: Packet Type (0), Tag (4), Length (N), Data (SDR Name)
26
+ description = "AirspyHF+"
27
+ desc_bytes = description.encode('utf-8')
28
+ desc_len = len(desc_bytes)
29
+
30
+ # Construct buffer
31
+ # 0x00: Status packet type
32
+ # 0x04: StatusType.DESCRIPTION
33
+ # desc_len: Length
34
+ # desc_bytes: SDR Name
35
+ # 0xff: StatusType.EOL
36
+ buffer = bytes([0, StatusType.DESCRIPTION, desc_len]) + desc_bytes + bytes([StatusType.EOL])
37
+
38
+ # Decode
39
+ status = control._decode_status_response(buffer)
40
+
41
+ # Verify
42
+ self.assertIn('description', status)
43
+ self.assertEqual(status['description'], description)
44
+
45
+ if __name__ == "__main__":
46
+ unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes