Skip to content

Commit

Permalink
Merge pull request #66 from nocarryr/device-addressing
Browse files Browse the repository at this point in the history
Support addressing devices by serial number

Closes #61
  • Loading branch information
nocarryr committed Sep 30, 2017
2 parents 6002a38 + 9183c5e commit 9d9bfc0
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 8 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ show()

See the files 'demo_waterfall.py' and 'test.py' for more examples.

### Handling multiple devices:
*(added in v2.5.x)*
```python
from rtlsdr import RtlSdr

# Get a list of detected device serial numbers (str)
serial_numbers = RtlSdr.get_device_serial_addresses()

# Find the device index for a given serial number
device_index = RtlSdr.get_device_index_by_serial('00000001')

sdr = RtlSdr(device_index)


# Or pass the serial number directly:
sdr = RtlSdr(serial_number='00000001')
```

#### Note
Most devices by default have the same serial number: '0000001'. This can be set
to a custom value by using the [rtl_eeprom][rtl_eeprom] utility packaged with `librtlsdr`.

[rtl_eeprom]: http://manpages.ubuntu.com/manpages/trusty/man1/rtl_eeprom.1.html

## Experimental features

Two new submodules are available for testing: **rtlsdraio**, which adds native Python 3 asynchronous support (asyncio module), and **rtlsdrtcp** which adds a TCP server/client for accessing a device over the network. See the respective modules in the rtlsdr folder for more details and feel free to test and report any bugs!
Expand Down
4 changes: 4 additions & 0 deletions rtlsdr/librtlsdr.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ def load_librtlsdr():
POINTER(c_ubyte),
POINTER(c_ubyte)]

# int rtlsdr_get_index_by_serial(const char *serial);
f = librtlsdr.rtlsdr_get_index_by_serial
f.restype, f.argtypes = c_int, [c_char_p]

# int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index);
f = librtlsdr.rtlsdr_open
f.restype, f.argtypes = c_int, [POINTER(p_rtlsdr_dev), c_uint]
Expand Down
40 changes: 36 additions & 4 deletions rtlsdr/rtlsdr.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
except ImportError: izip = zip
import sys

if sys.version_info.major >= 3:
PY3 = sys.version_info.major >= 3
if PY3:
basestring = str

# see if NumPy is available
Expand All @@ -54,15 +55,46 @@ class BaseRtlSdr(object):
num_bytes_read = c_int32(0)
device_opened = False

def __init__(self, device_index=0, test_mode_enabled=False):
self.open(device_index, test_mode_enabled)
@staticmethod
def get_device_index_by_serial(serial):
if PY3 and isinstance(serial, str):
serial = bytes(serial, 'UTF-8')

def open(self, device_index=0, test_mode_enabled=False):
result = librtlsdr.rtlsdr_get_index_by_serial(serial)
if result < 0:
raise IOError('Error code %d when searching device by serial' % (result))

return result

@staticmethod
def get_device_serial_addresses():
def get_serial(device_index):
bfr = (c_ubyte * 256)()
r = librtlsdr.rtlsdr_get_device_usb_strings(device_index, None, None, bfr)
if r != 0:
raise IOError(
'Error code %d when reading USB strings (device %d)' % (r, device_index)
)
return ''.join((chr(b) for b in bfr if b > 0))

num_devices = librtlsdr.rtlsdr_get_device_count()
return [get_serial(i) for i in range(num_devices)]

def __init__(self, device_index=0, test_mode_enabled=False, serial_number=None):
self.open(device_index, test_mode_enabled, serial_number)

def open(self, device_index=0, test_mode_enabled=False, serial_number=None):
''' Initialize RtlSdr object.
The test_mode_enabled parameter can be used to enable a special test mode, which will return the value of an
internal RTL2832 8-bit counter with calls to read_bytes()
If provided, serial_number parameter (str) will be used to search for the
device instead of the device_index.
'''

if serial_number is not None:
device_index = self.get_device_index_by_serial(serial_number)

# this is the pointer to the device structure used by all librtlsdr
# functions
self.dev_p = p_rtlsdr_dev(None)
Expand Down
8 changes: 4 additions & 4 deletions rtlsdr/rtlsdrtcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ class RtlSdrTcpServer(RtlSdr, RtlSdrTcpBase):
"""Server that connects to a physical dongle to allow client connections.
"""

def __init__(self, device_index=0, test_mode_enabled=False,
def __init__(self, device_index=0, test_mode_enabled=False, serial_number=None,
hostname='127.0.0.1', port=None):

RtlSdrTcpBase.__init__(self, device_index, test_mode_enabled,
hostname, port)
RtlSdr.__init__(self, device_index, test_mode_enabled)
RtlSdr.__init__(self, device_index, test_mode_enabled, serial_number)

def open(self, device_index=0, test_mode_enabled=False):
def open(self, device_index=0, test_mode_enabled=False, serial_number=None):
if not self.device_ready:
return
super(RtlSdrTcpServer, self).open(device_index, test_mode_enabled)
super(RtlSdrTcpServer, self).open(device_index, test_mode_enabled, serial_number)

def run(self):
"""Runs the server thread and returns. Use this only if you are
Expand Down
6 changes: 6 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ def test(sdr_cls, use_numpy):
sdr = sdr_cls()
generic_test(sdr, use_numpy=use_numpy)
sdr.close()

def test_serial_addressing(sdr_cls, use_numpy):
for i, serial in enumerate(sdr_cls.get_device_serial_addresses()):
assert sdr_cls.get_device_index_by_serial(serial) == i
sdr = sdr_cls(serial_number=serial)
sdr.close()
17 changes: 17 additions & 0 deletions tests/testlibrtlsdr.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def __init__(self, *args):
class LibRtlSdr(object):
async_callback = None
async_generator = None
NUM_FAKE_DEVICES = 32
def __init__(self):
self.fc = 1e6
self.rs = 2e6
Expand All @@ -20,6 +21,22 @@ def __init__(self):
self.gain_mode = 0
self.agc_mode = 0
self.direct_sampling = 0
def rtlsdr_get_device_count(self):
return self.NUM_FAKE_DEVICES
def rtlsdr_get_device_usb_strings(self, device_index, manufact, product, serial):
if device_index >= self.NUM_FAKE_DEVICES:
return -1
ser_string = '%08d' % (device_index)
for i, c in enumerate(ser_string):
serial[i] = ord(c)
return 0
def rtlsdr_get_index_by_serial(self, serial):
if not serial.isdigit():
return -1
i = int(serial)
if i >= self.NUM_FAKE_DEVICES:
return -3
return i
def rtlsdr_open(self, *args):
return 0
def rtlsdr_set_testmode(self, *args):
Expand Down

0 comments on commit 9d9bfc0

Please sign in to comment.