├── doc
└── img
│ ├── openwebrxcfg.png
│ └── baudlinecfg_1.png
├── CMakeLists.txt
├── README.md
└── play_sdr.c
/doc/img/openwebrxcfg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hb9fxq/SDRPlayPorts/HEAD/doc/img/openwebrxcfg.png
--------------------------------------------------------------------------------
/doc/img/baudlinecfg_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hb9fxq/SDRPlayPorts/HEAD/doc/img/baudlinecfg_1.png
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 2.8)
2 | project(SDRPlayPorts)
3 |
4 | link_directories(/usr/local/lib)
5 |
6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
7 |
8 | add_executable(play_sdr play_sdr.c)
9 |
10 | target_link_libraries (play_sdr pthread m mirsdrapi-rsp)
11 |
12 | install (TARGETS play_sdr DESTINATION /usr/local/bin)
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SDRPlayPorts
2 | Ports of some parts of git://git.osmocom.org/rtl-sdr.git / http://sdr.osmocom.org/trac/wiki/rtl-sdr for the SDRPlay (http://www.sdrplay.com/)
3 |
4 |
5 | Incomplete!!! Actually not working yet ... Work in progress
6 | Prototype demo:
7 | * https://youtu.be/rDvxwpe5HT8 rtl_tcp first steps
8 | * https://youtu.be/aYnz0Auqwho play_sdr first steps
9 |
10 | # Installation
11 |
12 | 1. SDRPlay API
13 |
Download the SDRPlay linux api.
14 | * x86 32/64bit: http://www.sdrplay.com/linux.html
15 | * RaspberryPI: http://www.sdrplay.com/raspberrypi2.html
16 |
17 | and follow the instructions.
18 |
19 | 2. Build SDRPlayPorts:
20 |
21 | git clone https://github.com/krippendorf/SDRPlayPorts.git
22 | cd SDRPlayPorts
23 | mkdir build && cd build
24 | cmake ..
25 | make
26 | sudo make install
27 |
28 |
29 | # Todo
30 | * Test, refactor and enhance ;-)
31 |
32 | ## Samples
33 | * Use with OpenWebRx / Config file:
34 |
35 | Very unstable with play_sdr so far...
36 |
37 | I've tried the 16 bit (-x 16 switch and csdr convert_s16_f) and 8bit variant, but no luck so far, maybe you?
38 | Check for aliasing, mirrors, before reporting success ;)
39 |
40 |
41 | ```python
42 | # ==== DSP/RX settings ====
43 | dsp_plugin="csdr"
44 | fft_fps=9
45 | fft_size=4096
46 | samp_rate = 1024000
47 | center_freq = 3600000
48 | rf_gain = 35 #in dB. For an RTL-SDR, rf_gain=0 will set the tuner to auto gain mode, else it will be in manual gain mode.
49 | ppm = 0
50 |
51 | audio_compression="adpcm" #valid values: "adpcm", "none"
52 | fft_compression="adpcm" #valid values: "adpcm", "none"
53 |
54 | start_rtl_thread=True
55 |
56 | # ==== I/Q sources (uncomment the appropriate) ====
57 |
58 | # >> RTL-SDR via rtl_sdr
59 |
60 | start_rtl_command="play_sdr -b 600 -s {samp_rate} -f {center_freq} -x 16 -g {rf_gain} -y 0 -".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm)
61 | format_conversion="csdr convert_s16_f"
62 |
63 | ```
64 | 
65 | * Use with Baudline
66 |
67 |
68 | ```bash
69 | timeout 15s play_sdr -s 8000000 -b 600 -f 3.6M -g 35 -l 0 -x 16 8000000_16bit.raw
70 | ```
71 | 
72 |
73 |
74 |
75 | # License
76 |
77 | ##SDRPlayPorts Licence
78 |
79 |
80 | SDRPlayPorts
81 | Ports of some parts of rtl-sdr for the SDRPlay (git://git.osmocom.org/rtl-sdr.git /)
82 | Fork by HB9FXQ (Frank Werner-Krippendorf, mail@hb9fxq.ch)
83 |
84 | This program is free software: you can redistribute it and/or modify
85 | it under the terms of the GNU General Public License as published by
86 | the Free Software Foundation, either version 2 of the License, or
87 | (at your option) any later version.
88 |
89 | This program is distributed in the hope that it will be useful,
90 | but WITHOUT ANY WARRANTY; without even the implied warranty of
91 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
92 | GNU General Public License for more details.
93 |
94 | You should have received a copy of the GNU General Public License
95 | along with this program. If not, see .
96 |
97 |
98 |
99 | ##rtl-sdr Licence
100 |
101 |
102 | rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
103 | Copyright (C) 2012 by Steve Markgraf
104 | Copyright (C) 2012-2013 by Hoernchen
105 |
106 | This program is free software: you can redistribute it and/or modify
107 | it under the terms of the GNU General Public License as published by
108 | the Free Software Foundation, either version 2 of the License, or
109 | (at your option) any later version.
110 |
111 | This program is distributed in the hope that it will be useful,
112 | but WITHOUT ANY WARRANTY; without even the implied warranty of
113 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
114 | GNU General Public License for more details.
115 |
116 | You should have received a copy of the GNU General Public License
117 | along with this program. If not, see .
118 |
119 |
120 |
--------------------------------------------------------------------------------
/play_sdr.c:
--------------------------------------------------------------------------------
1 | /*
2 | * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
3 | * Copyright (C) 2012 by Steve Markgraf
4 | * Copyright (C) 2012-2013 by Hoernchen
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 2 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | *
19 | * ************************************** THIS IS A FORK ******************* ORIGINAL COPYRIGHT SEE ABOVE.
20 | *
21 | *
22 |
23 |
24 | __/\\\________/\\\__/\\\\\\\\\\\\\________/\\\\\\\\\_____/\\\\\\\\\\\\\\\__/\\\_______/\\\________/\\\_______
25 | _\/\\\_______\/\\\_\/\\\/////////\\\____/\\\///////\\\__\/\\\///////////__\///\\\___/\\\/______/\\\\/\\\\____
26 | _\/\\\_______\/\\\_\/\\\_______\/\\\___/\\\______\//\\\_\/\\\_______________\///\\\\\\/______/\\\//\////\\\__
27 | _\/\\\\\\\\\\\\\\\_\/\\\\\\\\\\\\\\___\//\\\_____/\\\\\_\/\\\\\\\\\\\_________\//\\\\_______/\\\______\//\\\_
28 | _\/\\\/////////\\\_\/\\\/////////\\\___\///\\\\\\\\/\\\_\/\\\///////___________\/\\\\______\//\\\______/\\\__
29 | _\/\\\_______\/\\\_\/\\\_______\/\\\_____\////////\/\\\_\/\\\__________________/\\\\\\______\///\\\\/\\\\/___
30 | _\/\\\_______\/\\\_\/\\\_______\/\\\___/\\________/\\\__\/\\\________________/\\\////\\\______\////\\\//_____
31 | _\/\\\_______\/\\\_\/\\\\\\\\\\\\\/___\//\\\\\\\\\\\/___\/\\\______________/\\\/___\///\\\_______\///\\\\\\__
32 | _\///________\///__\/////////////______\///////////_____\///______________\///_______\///__________\//////___
33 |
34 | *
35 | *
36 | *
37 | *
38 | * SDRPlayPorts
39 | * Ports of some parts of rtl-sdr for the SDRPlay (original: git://git.osmocom.org/rtl-sdr.git /)
40 | * 2016: Fork by HB9FXQ (Frank Werner-Krippendorf, mail@hb9fxq.ch)
41 | *
42 | * Code changes I've done:
43 | * - removed rtl_sdr related calls and replaced them with mir_* to work with the SDRPlay
44 | * - removed various local variables
45 | *
46 | * This program is free software: you can redistribute it and/or modify
47 | * it under the terms of the GNU General Public License as published by
48 | * the Free Software Foundation, either version 2 of the License, or
49 | * (at your option) any later version.
50 | *
51 | * This program is distributed in the hope that it will be useful,
52 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
53 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54 | * GNU General Public License for more details.
55 | *
56 | * You should have received a copy of the GNU General Public License
57 | * along with this program. If not, see .
58 | *
59 | *
60 | *
61 | */
62 |
63 | #include
64 | #include
65 | #include
66 | #include
67 | #include
68 | #include
69 |
70 | #include
71 | #include "mirsdrapi-rsp.h"
72 |
73 |
74 | #define DEFAULT_SAMPLE_RATE 2048000
75 | #define DEFAULT_LNA 0;
76 | #define DEFAULT_AGC_SETPOINT -30;
77 | #define DEFAULT_FREQUENCY 100000000;
78 | #define DEFAULT_RESULT_BITS 8; // more compatible with RTL_SDR
79 |
80 | static int do_exit = 0;
81 |
82 | int samplesPerPacket;
83 | int resultBits = DEFAULT_RESULT_BITS;
84 | int flipComplex = 0;
85 | uint8_t *buffer8;
86 | short *buffer16;
87 | FILE *file;
88 |
89 | void* context = NULL; /* currently unused - let the API the thread stuff */
90 |
91 |
92 | void adjust_bw(int bwHz, mir_sdr_Bw_MHzT *ptr);
93 | void adjust_if(int ifFreq, mir_sdr_If_kHzT *ptr);
94 | void adjust_result_bits(int bits, int *ptrResultBits);
95 |
96 |
97 | double atofs(char *s)
98 | /* standard suffixes */
99 | {
100 | char last;
101 | int len;
102 | double suff = 1.0;
103 | len = strlen(s);
104 | last = s[len - 1];
105 | s[len - 1] = '\0';
106 | switch (last) {
107 | case 'g':
108 | case 'G':
109 | suff *= 1e3;
110 | case 'm':
111 | case 'M':
112 | suff *= 1e3;
113 | case 'k':
114 | case 'K':
115 | suff *= 1e3;
116 | suff *= atof(s);
117 | s[len - 1] = last;
118 | return suff;
119 | }
120 | s[len - 1] = last;
121 | return atof(s);
122 | }
123 |
124 |
125 | void usage(void) {
126 | fprintf(stderr,
127 | "play_sdr, an I/Q recorder for SDRplay RSP receivers\n\n"
128 | "Usage:\t -f frequency_to_tune_to (Hz)\n"
129 | "\t[-s samplerate (default: 2048000 Hz)]\n"
130 | "\t[-b Band width in kHz (default: 1536) possible values: 200 300 600 1536 5000 6000 7000 8000]\n"
131 | "\t[-i IF in kHz (default: 0 (=Zero IF)) possible values: 0 450 1620 2048]\n"
132 | "\t[-r ADC set point is in dBfs\n"
133 | "(decibels relative to full scale) and will normally lie in the range 0dBfs (full scale) to -50dBfs (50dB below\n"
134 | "full scale). Default: -30)]\n\n"
135 | "\t[-l RSP LNA enable (default: 0, disabled)]\n"
136 | "\t[-y Flipcomplex I-Q => Q-I (default: 0, disabled) 1 = enabled\n"
137 | "\t[-x Result I/Q bit resolution (uint8 / short) (default: 8, possible values: 8 16)]\n"
138 | "\t[-v Verbose mode, prints debug information. Default 0, 1 = enabled\n"
139 | "\tfilename (a '-' dumps samples to stdout)\n\n");
140 | exit(1);
141 | }
142 |
143 | void callbackGC(unsigned int gRdB, unsigned int lnaGRdB, void *cbContext)
144 | {
145 | return;
146 | }
147 |
148 | unsigned int firstBufferCallback = 1;
149 |
150 | void streamCallback (short *xi, short *xq, unsigned int firstSampleNum, int grChanged, int rfChanged, int fsChanged, unsigned int numSamples, unsigned int reset, void *cbContext){
151 |
152 |
153 | if(firstBufferCallback == 1 || reset == 1){
154 |
155 | if (resultBits == 8) {
156 | buffer8 = malloc(numSamples * 2 * sizeof(uint8_t));
157 | }
158 | else {
159 | buffer16 = malloc(numSamples * 2 * sizeof(short));
160 | }
161 |
162 | firstBufferCallback = 0;
163 | }
164 |
165 | int j = 0;
166 | int i = 0;
167 |
168 | for (i = 0; i < numSamples; i++) {
169 | if (resultBits == 8) {
170 | if (flipComplex == 0) {
171 | buffer8[j++] = (unsigned char) (xi[i] >> 8);
172 | buffer8[j++] = (unsigned char) (xq[i] >> 8);
173 | } else {
174 | buffer8[j++] = (unsigned char) (xq[i] >> 8);
175 | buffer8[j++] = (unsigned char) (xi[i] >> 8);
176 | }
177 | }
178 | else {
179 | if (flipComplex == 0) {
180 | buffer16[j++] = (xi[i]);
181 | buffer16[j++] = (xq[i]);
182 | } else {
183 | buffer16[j++] = (xq[i]);
184 | buffer16[j++] = (xi[i]);
185 | }
186 | }
187 | }
188 |
189 | if (resultBits == 8) {
190 | if (fwrite(buffer8, sizeof(uint8_t), numSamples*2, file) != (size_t) numSamples*2) {
191 | fprintf(stderr, "Short write, samples lost, exiting 8!\n"); // TODO: take firstSampleNum into account
192 | return;
193 | }
194 | }
195 | else {
196 | if (fwrite(buffer16, sizeof(short), numSamples * 2, file) != (size_t) numSamples*2) {
197 | fprintf(stderr, "Short write, samples lost, exiting 16!\n"); // TODO: take firstSampleNum into account
198 | return;
199 | }
200 | }
201 | }
202 |
203 | static void sighandler(int signum) {
204 | fprintf(stderr, "Signal (%d) caught, exiting!\n", signum);
205 | do_exit = 1;
206 | }
207 |
208 | int main(int argc, char **argv) {
209 |
210 | struct sigaction sigact;
211 | char *filename = NULL;
212 | mir_sdr_ErrT r;
213 | int opt;
214 | int agcSetPoint = DEFAULT_AGC_SETPOINT;
215 | int verbose = 0;
216 |
217 | uint32_t frequency = DEFAULT_FREQUENCY;
218 | uint32_t samp_rate = DEFAULT_SAMPLE_RATE;
219 | int rspLNA = DEFAULT_LNA;
220 |
221 | int i, j;
222 |
223 | mir_sdr_Bw_MHzT bandwidth = mir_sdr_BW_1_536;
224 | mir_sdr_If_kHzT ifKhz = mir_sdr_IF_Zero;
225 |
226 | while ((opt = getopt(argc, argv, "f:g:s:n:l:b:i:x:y:v:")) != -1) {
227 | switch (opt) {
228 | case 'f':
229 | frequency = (uint32_t) atofs(optarg);
230 | break;
231 | case 'g':
232 | agcSetPoint = (int) atof(optarg);
233 | break;
234 | case 's':
235 | samp_rate = (uint32_t) atofs(optarg);
236 | break;
237 | case 'l':
238 | rspLNA = atoi(optarg);
239 | break;
240 | case 'b':
241 | adjust_bw(atoi(optarg), &bandwidth);
242 | break;
243 | case 'i':
244 | adjust_if(atoi(optarg), &ifKhz);
245 | break;
246 | case 'x':
247 | adjust_result_bits(atoi(optarg), &resultBits);
248 | break;
249 | case 'y':
250 | flipComplex = atoi(optarg);
251 | break;
252 | case 'v':
253 | verbose = atoi(optarg);
254 | break;
255 | default:
256 | usage();
257 | break;
258 | }
259 | }
260 |
261 | if (argc <= optind) {
262 | usage();
263 | } else {
264 | filename = argv[optind];
265 | }
266 |
267 | if (verbose == 1) {
268 | fprintf(stderr, "[DEBUG] *************** play_sdr init summary *********************\n");
269 | fprintf(stderr, "[DEBUG] LNA: %d\n", rspLNA);
270 | fprintf(stderr, "[DEBUG] samp_rate: %d\n", samp_rate);
271 | fprintf(stderr, "[DEBUG] agcSetPoint: %d\n", agcSetPoint);
272 | fprintf(stderr, "[DEBUG] frequency: [Hz] %d / [MHz] %f\n", frequency, frequency / 1e6);
273 | fprintf(stderr, "[DEBUG] bandwidth: [kHz] %d\n", bandwidth);
274 | fprintf(stderr, "[DEBUG] IF: %d\n", ifKhz);
275 | fprintf(stderr, "[DEBUG] Result I/Q bit resolution (bit): %d\n", resultBits);
276 | fprintf(stderr, "[DEBUG] *************************************************************\n");
277 | }
278 |
279 |
280 | sigact.sa_handler = sighandler;
281 | sigemptyset(&sigact.sa_mask);
282 | sigact.sa_flags = 0;
283 | sigaction(SIGINT, &sigact, NULL);
284 | sigaction(SIGTERM, &sigact, NULL);
285 | sigaction(SIGQUIT, &sigact, NULL);
286 | sigaction(SIGPIPE, &sigact, NULL);
287 |
288 |
289 | if (strcmp(filename, "-") == 0) { /* Write samples to stdout */
290 | file = stdout;
291 | } else {
292 | file = fopen(filename, "wb");
293 | if (!file) {
294 | fprintf(stderr, "Failed to open %s\n", filename);
295 | goto out;
296 | }
297 | }
298 |
299 |
300 | mir_sdr_AgcControl(1, agcSetPoint, 0, 0, 0, 0, rspLNA);
301 |
302 | int infoOverallGr;
303 | r = mir_sdr_StreamInit(&agcSetPoint, (samp_rate/1e6), (frequency/1e6), mir_sdr_BW_1_536, mir_sdr_IF_Zero, rspLNA, &infoOverallGr, 1 /* use internal gr tables acording to band */, &samplesPerPacket, streamCallback,
304 | callbackGC, &context);
305 |
306 |
307 | if (r != mir_sdr_Success) {
308 | fprintf(stderr, "Failed to start SDRplay RSP device.\n");
309 | exit(r);
310 | }
311 |
312 |
313 | mir_sdr_SetDcMode(4, 0);
314 | mir_sdr_SetDcTrackTime(63);
315 |
316 |
317 | fprintf(stderr, "Writing samples...\n");
318 | while (!do_exit) {
319 | sleep(1);
320 | }
321 |
322 | if (do_exit)
323 | fprintf(stderr, "\nUser cancel, exiting...\n");
324 | else
325 | fprintf(stderr, "\nLibrary error %d, exiting...\n", r);
326 |
327 | if (file != stdout)
328 | fclose(file);
329 |
330 |
331 | if (resultBits == 8) {
332 | free(buffer8);
333 | }
334 | else {
335 | free(buffer16);
336 | }
337 |
338 | out:
339 | return r >= 0 ? r : -r;
340 | }
341 |
342 | void adjust_result_bits(int bits, int *ptrResultBits) {
343 | switch (bits) {
344 | case 8:
345 | case 16:
346 | *ptrResultBits = bits;
347 | return;
348 | }
349 |
350 | fprintf(stderr, "Invalid result I/Q resolution (-x) !\n");
351 | usage();
352 | }
353 |
354 | void adjust_if(int ifFreq, mir_sdr_If_kHzT *ptrIFKhz) {
355 | switch (ifFreq) {
356 | case 0:
357 | case 450:
358 | case 1620:
359 | case 2048:
360 | *ptrIFKhz = ifFreq;
361 | return;
362 | }
363 |
364 | fprintf(stderr, "Invalid IF frequency (-i) !\n");
365 | usage();
366 |
367 | }
368 |
369 | void adjust_bw(int bwHz, mir_sdr_Bw_MHzT *ptrBw) {
370 |
371 | switch (bwHz) {
372 | case 200:
373 | case 300:
374 | case 600:
375 | case 1536:
376 | case 5000:
377 | case 6000:
378 | case 8000:
379 | *ptrBw = bwHz;
380 | return;
381 | }
382 |
383 | fprintf(stderr, "Invalid Band Width (-b) !\n");
384 | usage();
385 | }
--------------------------------------------------------------------------------