Fixed choppy audio from partial buffer reads, updated README to be more helpful.
This commit is contained in:
parent
f9480cd3c1
commit
53ffbb8afe
64
README.md
64
README.md
@ -3,7 +3,7 @@
|
|||||||
This repository is a demo - it isn't a fully fleshed out system and isn't intended to be. Things might break. It also makes use of some 3rd-party code that we don't have a commercial licence for, so legally this can only be used for demonstration purposes, and only interally.
|
This repository is a demo - it isn't a fully fleshed out system and isn't intended to be. Things might break. It also makes use of some 3rd-party code that we don't have a commercial licence for, so legally this can only be used for demonstration purposes, and only interally.
|
||||||
|
|
||||||
# Getting started
|
# Getting started
|
||||||
|
- Use a debian-based 64-bit system. No way around it; this project is not cross-platform.
|
||||||
- Install the SDRPlay device driver & install the project dependencies.
|
- Install the SDRPlay device driver & install the project dependencies.
|
||||||
- `./SDRplay_RSP_API-Linux-3.07.1.run`
|
- `./SDRplay_RSP_API-Linux-3.07.1.run`
|
||||||
- Follow the prompts, there is some audience participation getting the driver installed.
|
- Follow the prompts, there is some audience participation getting the driver installed.
|
||||||
@ -23,17 +23,67 @@ The NESDR Smart is a good starting place as it has a command line utility alread
|
|||||||
## SDRPlay radio
|
## SDRPlay radio
|
||||||
The script `./sdrplay_sample` was built to mimic the interface provided by `rtl_sdr`, and is nearly a slot in replacement but for the SDRPlay hardware. It is used for producing raw I/Q samples from the radio device, which (assuming you tune to a frequency where an FM radio station is available) can be piped into the FM demodulator `fm_demod.py`, which itself outputs raw PCM audio.
|
The script `./sdrplay_sample` was built to mimic the interface provided by `rtl_sdr`, and is nearly a slot in replacement but for the SDRPlay hardware. It is used for producing raw I/Q samples from the radio device, which (assuming you tune to a frequency where an FM radio station is available) can be piped into the FM demodulator `fm_demod.py`, which itself outputs raw PCM audio.
|
||||||
|
|
||||||
To run the whole chain at once, tuning to a radio station frequency, demodulating the IQ samples to PCM and rendering to the system audio sink, run the following:
|
To capture raw IQ samples from the device, follow the below example (omitting the comments).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# FM broadcasting is in the VHF Band II space -
|
# FM broadcasting is in the VHF Band II space - in Australia this is 87.5 MHz to 108 MHz.
|
||||||
# in Australia this is 87.5 MHz to 108 MHz. You can specify the
|
# You can specify the frequency as either a plain integer (87500000) or using a suffix:
|
||||||
# frequency as either a plan integer (87500000) or using a suffix:
|
station="87.5M"
|
||||||
export station="87.5M"
|
|
||||||
|
|
||||||
# You'll want to replace that number with an actual radio station.
|
# You'll want to replace that number with an actual radio station.
|
||||||
|
|
||||||
./sdrplay_sample -d sdrplay --format CS16 -f ${station} -s 384k | \
|
./sdrplay_sample \
|
||||||
|
-v \ # Enable verbose output.
|
||||||
|
-d sdrplay \ # Connect to first radio of sdrplay driver;
|
||||||
|
--format CS16 \ # Sample format of complex signed 16-bit int;
|
||||||
|
-f ${station} \ # Tune to FM station frequency;
|
||||||
|
-s 384k \ # Sampling rate 384 kHz;
|
||||||
|
-n 4M \ # Collect 4 million samples (~10 seconds);
|
||||||
|
-o output.iq # Save IQ samples to file.
|
||||||
|
|
||||||
|
# To find the formats, frequencies, sampling rates etc that are supported by the
|
||||||
|
# radio device, run:
|
||||||
|
./sdrplay_sample -v --list-devices
|
||||||
|
```
|
||||||
|
|
||||||
|
To demodulate the received IQ samples into raw PCM audio, do:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./fm_demod.py \
|
||||||
|
-f CS16 \ # Sample format must match IQ samples;
|
||||||
|
-s 384k \ # Sampling rate must match IQ samples;
|
||||||
|
-d 32k \ # Output rate - see notes.
|
||||||
|
< test.iq \ # IQ sample input file,
|
||||||
|
> test.raw # Write PCM to file.
|
||||||
|
|
||||||
|
# The output rate is a bit finicky as it needs to satisfy multiple requirements.
|
||||||
|
# - must be an integer divisor of the sampling rate;
|
||||||
|
# - must be a supported input format for the system sound card.
|
||||||
|
#
|
||||||
|
# A good ratio to aim for is for sampling-rate / output-rate = 10. This might not
|
||||||
|
# always be possible, but near enough will work. Higher and you're wasting samples
|
||||||
|
# and too low you will face audio problems.
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, to play the PCM file through the system audio out, use the `sox` utility. Most of the parameters are required to be fixed values, so only the modifiable ones are explained.
|
||||||
|
```bash
|
||||||
|
sox \
|
||||||
|
-t raw \
|
||||||
|
-r 32000 \ # Sample rate (must match demodulator output rate)
|
||||||
|
-b 16 \
|
||||||
|
-c 2 \
|
||||||
|
-L \
|
||||||
|
-e signed-integer \
|
||||||
|
test.raw \ # Input audio file
|
||||||
|
-d # Output to default audio device
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the whole chain in realtime without using intermediary files, tuning to a radio station frequency, demodulating the IQ samples to PCM and rendering to the system audio sink, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./sdrplay_sample -d sdrplay --format CS16 -f ${station} -s 384k -o - | \
|
||||||
./fm_demod.py -f CS16 -s 384k -d 32k | \
|
./fm_demod.py -f CS16 -s 384k -d 32k | \
|
||||||
sox -t raw -r 32000 -b 16 -c 2 -L -e signed-integer - -d
|
sox -t raw -r 32000 -b 16 -c 2 -L -e signed-integer - -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There's an outstanding bug where running live causes a buffer under-run in alsa - still working on that.
|
||||||
@ -164,15 +164,14 @@ if args.d:
|
|||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print('Rx:', result.ret, result.flags, result.timeNs, file=sys.stderr)
|
print('Rx:', result.ret, result.flags, result.timeNs, file=sys.stderr)
|
||||||
if result.ret != samples:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if result.ret < 1:
|
if result.ret < 1:
|
||||||
print('Stream read failed, exiting.', file=sys.stderr)
|
print('Stream read failed, exiting.', file=sys.stderr)
|
||||||
keep_going = False
|
keep_going = False
|
||||||
|
|
||||||
if outfile:
|
if outfile:
|
||||||
outfile.write(struct.pack(PACKINGS[format] % len(buffer), *buffer))
|
received = int(result.ret * 2)
|
||||||
|
outfile.write(struct.pack(PACKINGS[format] % received, *buffer[:received]))
|
||||||
|
|
||||||
if sample_count > 0:
|
if sample_count > 0:
|
||||||
total_samples += result.ret
|
total_samples += result.ret
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user