diff --git a/src/convenience/convenience.c b/src/convenience/convenience.c index 1a0fbfb..cfddc9f 100644 --- a/src/convenience/convenience.c +++ b/src/convenience/convenience.c @@ -42,22 +42,22 @@ #ifdef _MSC_VER struct tm *localtime_r (const time_t *timer, struct tm *result) { - struct tm *local_result = localtime (timer); - if (local_result == NULL || result == NULL) return NULL; - memcpy (result, local_result, sizeof (struct tm)); - return result; + struct tm *local_result = localtime (timer); + if (local_result == NULL || result == NULL) return NULL; + memcpy (result, local_result, sizeof (struct tm)); + return result; } //http://unixpapa.com/incnote/string.html char * strsep(char **sp, char *sep) { - char *p, *s; - if (sp == NULL || *sp == NULL || **sp == '\0') return(NULL); - s = *sp; - p = s + strcspn(s, sep); - if (*p != '\0') *p++ = '\0'; - *sp = p; - return(s); + char *p, *s; + if (sp == NULL || *sp == NULL || **sp == '\0') return(NULL); + s = *sp; + p = s + strcspn(s, sep); + if (*p != '\0') *p++ = '\0'; + *sp = p; + return(s); } #endif @@ -134,47 +134,74 @@ double atofp(char *s) return atof(s); } -int verbose_set_frequency(SoapySDRDevice *dev, uint32_t frequency) +int verbose_set_frequency(SoapySDRDevice *dev, uint32_t frequency, size_t chan) { int r; - + SoapySDRKwargs args = {0}; - r = (int)SoapySDRDevice_setFrequency(dev, SOAPY_SDR_RX, 0, (double)frequency, &args); + r = (int)SoapySDRDevice_setFrequency(dev, SOAPY_SDR_RX, chan, (double)frequency, &args); + if (r != 0) { + fprintf(stderr, "ERROR: Failed to set center freq. for chan %zu\n", chan); + exit(10); + } else { + fprintf(stderr, "Tuned to %u Hz on channel %zu.\n", frequency, chan); + } + + return r; +} + +int verbose_set_antenna(SoapySDRDevice *dev, const char * ant, size_t chan) +{ + int r; + if (ant != NULL) { + r = (int)SoapySDRDevice_setAntenna(dev, SOAPY_SDR_RX, chan, ant); + } else { + r = 0; + } + if (r != 0) { - fprintf(stderr, "WARNING: Failed to set center freq.\n"); + fprintf(stderr, "ERROR: Failed to set antenna for chan %zu.\n", chan); + exit(11); } else { - fprintf(stderr, "Tuned to %u Hz.\n", frequency); + char * antval = SoapySDRDevice_getAntenna(dev, SOAPY_SDR_RX, chan); + fprintf(stderr, "****Antenna set to %s on chan %zu\n", antval, chan); } + return r; } -int verbose_set_sample_rate(SoapySDRDevice *dev, uint32_t samp_rate) +int verbose_set_sample_rate(SoapySDRDevice *dev, uint32_t samp_rate, size_t chan) { int r; - r = (int)SoapySDRDevice_setSampleRate(dev, SOAPY_SDR_RX, 0, (double)samp_rate); + r = (int)SoapySDRDevice_setSampleRate(dev, SOAPY_SDR_RX, chan, (double)samp_rate); if (r != 0) { - fprintf(stderr, "WARNING: Failed to set sample rate.\n"); + fprintf(stderr, "ERROR: Failed to set sample rate for chan %zu.\n", chan); + exit(12); } else { - fprintf(stderr, "Sampling at %u S/s.\n", samp_rate); + fprintf(stderr, "Sampling at %u S/s on chan %zu.\n", samp_rate, chan); } + return r; } -int verbose_set_bandwidth(SoapySDRDevice *dev, uint32_t bandwidth) +int verbose_set_bandwidth(SoapySDRDevice *dev, uint32_t bandwidth, size_t chan) { int r; - r = (int)SoapySDRDevice_setBandwidth(dev, SOAPY_SDR_RX, 0, (double)bandwidth); + r = (int)SoapySDRDevice_setBandwidth(dev, SOAPY_SDR_RX, chan, (double)bandwidth); uint32_t applied_bw = 0; if (r != 0) { - fprintf(stderr, "WARNING: Failed to set bandwidth.\n"); + fprintf(stderr, "ERROR: Failed to set bandwidth on chan %zu.\n", chan); + exit(13); } else if (bandwidth > 0) { - applied_bw = (uint32_t)SoapySDRDevice_getBandwidth(dev, SOAPY_SDR_RX, 0); + applied_bw = (uint32_t)SoapySDRDevice_getBandwidth(dev, SOAPY_SDR_RX, chan); if (applied_bw) - fprintf(stderr, "Bandwidth parameter %u Hz resulted in %u Hz.\n", bandwidth, applied_bw); + fprintf(stderr, "Bandwidth parameter %u Hz resulted in %u Hz on chan %zu.\n", + bandwidth, applied_bw, chan); else - fprintf(stderr, "Set bandwidth parameter %u Hz.\n", bandwidth); + fprintf(stderr, "Set bandwidth parameter %u Hz on chan %zu.\n", bandwidth, chan); } else { - fprintf(stderr, "Bandwidth set to automatic resulted in %u Hz.\n", applied_bw); + fprintf(stderr, "Bandwidth set to automatic resulted in %u Hz on chan %zu.\n", + applied_bw, chan); } return r; } @@ -195,8 +222,8 @@ int verbose_direct_sampling(SoapySDRDevice *dev, int on) set_value = SoapySDRDevice_readSetting(dev, "direct_samp"); if (set_value == NULL) { - fprintf(stderr, "WARNING: Failed to set direct sampling mode.\n"); - return r; + fprintf(stderr, "ERROR: Failed to set direct sampling mode.\n"); + exit(14); } if (atoi(set_value) == 0) { fprintf(stderr, "Direct sampling mode disabled.\n");} @@ -230,7 +257,7 @@ int verbose_offset_tuning(SoapySDRDevice *dev) return r; } -int verbose_auto_gain(SoapySDRDevice *dev) +int verbose_auto_gain(SoapySDRDevice *dev, size_t chan) { int r; r = 0; @@ -249,7 +276,7 @@ int verbose_auto_gain(SoapySDRDevice *dev) // For now, set 40.0 dB, high // Note: 26.5 dB in https://github.com/librtlsdr/librtlsdr/blob/master/src/tuner_r82xx.c#L1067 - but it's not the same // TODO: remove or change after auto-gain? https://github.com/pothosware/SoapyRTLSDR/issues/21 rtlsdr_set_tuner_gain_mode(dev, 0); - r = (int)SoapySDRDevice_setGain(dev, SOAPY_SDR_RX, 0, 40.); + r = (int)SoapySDRDevice_setGain(dev, SOAPY_SDR_RX, chan, 40.); if (r != 0) { fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); } else { @@ -260,15 +287,15 @@ int verbose_auto_gain(SoapySDRDevice *dev) // even though it logs HACKRF_ERROR_INVALID_PARAM? https://github.com/rxseger/rx_tools/issues/9 // Total gain is distributed amongst all gains, 116 = 37,65,1; the LNA is OK (<40) but VGA is out of range (65 > 62) // TODO: generic means to set all gains, of any SDR? string parsing LNA=#,VGA=#,AMP=#? - r = (int)SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, "LNA", 40.); // max 40 + r = (int)SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, chan, "LNA", 40.); // max 40 if (r != 0) { fprintf(stderr, "WARNING: Failed to set LNA tuner gain.\n"); } - r = (int)SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, "VGA", 20.); // max 65 + r = (int)SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, chan, "VGA", 20.); // max 65 if (r != 0) { fprintf(stderr, "WARNING: Failed to set VGA tuner gain.\n"); } - r = (int)SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, "AMP", 0.); // on or off + r = (int)SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, chan, "AMP", 0.); // on or off if (r != 0) { fprintf(stderr, "WARNING: Failed to set AMP tuner gain.\n"); } @@ -279,7 +306,7 @@ int verbose_auto_gain(SoapySDRDevice *dev) return r; } -int verbose_gain_str_set(SoapySDRDevice *dev, char *gain_str) +int verbose_gain_str_set(SoapySDRDevice *dev, char *gain_str, size_t chan) { SoapySDRKwargs args = {0}; size_t i; @@ -301,37 +328,40 @@ int verbose_gain_str_set(SoapySDRDevice *dev, char *gain_str) char *name = args.keys[i]; double value = atof(args.vals[i]); - fprintf(stderr, "Setting gain element %s: %f dB\n", name, value); - r = SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, name, value); - if (r != 0) { - fprintf(stderr, "WARNING: setGainElement(%s, %f) failed: %d\n", name, value, r); - } + fprintf(stderr, "Setting gain element %s: %f dB on chan %zu\n", name, value, chan); + r = SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, chan, name, value); + if (r != 0) { + fprintf(stderr, "WARNING: setGainElement(%s, %f, %zu) failed: %d\n", + name, value, chan, r); + } + } } else { // Set overall gain and let SoapySDR distribute amongst components double value = atof(gain_str); - r = SoapySDRDevice_setGain(dev, SOAPY_SDR_RX, 0, value); + r = SoapySDRDevice_setGain(dev, SOAPY_SDR_RX, chan, value); if (r != 0) { - fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); + fprintf(stderr, "WARNING: Failed to set tuner gain for chan %zu.\n", chan); } else { - fprintf(stderr, "Tuner gain set to %0.2f dB.\n", value); + fprintf(stderr, "Tuner gain set to %0.2f dB on chan %zu.\n", value, chan); } + // TODO: read back and print each individual getGainElement()s } return r; } -int verbose_ppm_set(SoapySDRDevice *dev, int ppm_error) +int verbose_ppm_set(SoapySDRDevice *dev, int ppm_error, size_t chan) { int r; if (ppm_error == 0) { return 0;} - r = (int)SoapySDRDevice_setFrequencyComponent(dev, SOAPY_SDR_RX, 0, "CORR", (double)ppm_error, NULL); + r = (int)SoapySDRDevice_setFrequencyComponent(dev, SOAPY_SDR_RX, chan, "CORR", (double)ppm_error, NULL); if (r != 0) { - fprintf(stderr, "WARNING: Failed to set ppm error.\n"); + fprintf(stderr, "WARNING: Failed to set ppm error for chan %zu.\n", chan); } else { - fprintf(stderr, "Tuner error set to %i ppm.\n", ppm_error); + fprintf(stderr, "Tuner error set to %i ppm for chan %zu.\n", ppm_error, chan); } return r; } @@ -406,6 +436,14 @@ static void show_device_info(SoapySDRDevice *dev) fprintf(stderr, "%.0f ", bandwidths[i]); } fprintf(stderr, "\n"); + + args = SoapySDRDevice_getChannelInfo(dev, direction, channel); + fprintf(stderr, "Channel info: "); + for (i = 0; i < args.size; ++i) { + fprintf(stderr, "%s=%s ", args.keys[i], args.vals[i]); + } + fprintf(stderr, "\n"); + } int suppress_stdout_start(void) { @@ -430,7 +468,8 @@ void suppress_stdout_stop(int tmp_stdout) { } -int verbose_device_search(char *s, SoapySDRDevice **devOut, SoapySDRStream **streamOut, const char *format) +int verbose_device_search(char *s, SoapySDRDevice **devOut, SoapySDRStream **streamOut, const char *format, + const size_t * channels, size_t nchannels) { size_t device_count = 0; size_t i = 0; @@ -448,8 +487,14 @@ int verbose_device_search(char *s, SoapySDRDevice **devOut, SoapySDRStream **str } show_device_info(dev); - - if (SoapySDRDevice_setupStream(dev, streamOut, SOAPY_SDR_RX, format, NULL, 0, &stream_args) != 0) { + if (nchannels > SoapySDRDevice_getNumChannels(dev, SOAPY_SDR_RX)) { + fprintf(stderr, "User asked for more channels (%d) than device supports (%d)\n", + nchannels, SoapySDRDevice_getNumChannels(dev, SOAPY_SDR_RX)); + return -5; + } + + if (SoapySDRDevice_setupStream(dev, streamOut, SOAPY_SDR_RX, format, channels, nchannels, + &stream_args) != 0) { fprintf(stderr, "SoapySDRDevice_setupStream failed\n"); return -3; } diff --git a/src/convenience/convenience.h b/src/convenience/convenience.h index 90ead49..93b30cb 100644 --- a/src/convenience/convenience.h +++ b/src/convenience/convenience.h @@ -70,27 +70,29 @@ double atofp(char *s); * \return 0 on success */ -int verbose_set_frequency(SoapySDRDevice *dev, uint32_t frequency); +int verbose_set_frequency(SoapySDRDevice *dev, uint32_t frequency, size_t chan); /*! * Set device sample rate and report status on stderr * * \param dev the device handle * \param samp_rate in samples/second + * \param chan the channel humber * \return 0 on success */ -int verbose_set_sample_rate(SoapySDRDevice *dev, uint32_t samp_rate); +int verbose_set_sample_rate(SoapySDRDevice *dev, uint32_t samp_rate, size_t chan); /*! * Set device bandwidth and report status on stderr * * \param dev the device handle * \param frequency in Hz + * \param chan the channel humber * \return 0 on success */ -int verbose_set_bandwidth(SoapySDRDevice *dev, uint32_t bandwidth); +int verbose_set_bandwidth(SoapySDRDevice *dev, uint32_t bandwidth, size_t chan); /*! @@ -116,39 +118,54 @@ int verbose_offset_tuning(SoapySDRDevice *dev); * Enable auto gain and report status on stderr * * \param dev the device handle + * \param chan the channel humber * \return 0 on success */ -int verbose_auto_gain(SoapySDRDevice *dev); +int verbose_auto_gain(SoapySDRDevice *dev, size_t chan); /*! * Set tuner gain and report status on stderr * * \param dev the device handle * \param gain in tenths of a dB + * \param chan the channel humber * \return 0 on success */ -int verbose_gain_set(SoapySDRDevice *dev, int gain); +int verbose_gain_set(SoapySDRDevice *dev, int gain, size_t chan); /*! * Set tuner gain elements by a key/value string * * \param dev the device handle * \param gain_str string of gain element pairs (example LNA=40,VGA=20,AMP=0), or string of overall gain, in dB + * \param chan the channel humber * \return 0 on success */ -int verbose_gain_str_set(SoapySDRDevice *dev, char *gain_str); +int verbose_gain_str_set(SoapySDRDevice *dev, char *gain_str, size_t chan); /*! * Set the frequency correction value for the device and report status on stderr. * * \param dev the device handle * \param ppm_error correction value in parts per million (ppm) + * \param chan the channel humber * \return 0 on success */ -int verbose_ppm_set(SoapySDRDevice *dev, int ppm_error); +int verbose_ppm_set(SoapySDRDevice *dev, int ppm_error, size_t chan); + +/*! + * Set the antenna value for the device and report status on stderr. + * + * \param dev the device handle + * \param ant string name of the antenna to use + * \param chan the channel humber + * \return 0 on success + */ + +int verbose_set_antenna(SoapySDRDevice *dev, const char * ant, size_t chan); /*! * Reset buffer @@ -169,7 +186,8 @@ int verbose_reset_buffer(SoapySDRDevice *dev); * \return dev 0 if successful */ -int verbose_device_search(char *s, SoapySDRDevice **devOut, SoapySDRStream **streamOut, const char *format); +int verbose_device_search(char *s, SoapySDRDevice **devOut, SoapySDRStream **streamOut, const char *format, + const size_t * channels, size_t nchannels); /*! * Start redirecting stdout to stderr to avoid unwanted stdout emissions. diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 0a70e87..0b7b69e 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -1016,7 +1016,7 @@ static void *controller_thread_fn(void *arg) if (!dongle.offset_tuning) fprintf(stderr, " frequency is away from parametrized one, to avoid negative impact from dc\n"); } - verbose_set_frequency(dongle.dev, dongle.freq); + verbose_set_frequency(dongle.dev, dongle.freq, 0); fprintf(stderr, "Oversampling input by: %ix.\n", demod.downsample); fprintf(stderr, "Oversampling output by: %ix.\n", demod.post_downsample); fprintf(stderr, "Buffer size: %0.2fms\n", @@ -1025,7 +1025,7 @@ static void *controller_thread_fn(void *arg) /* Set the sample rate */ if (verbosity) fprintf(stderr, "verbose_set_sample_rate(%.0f Hz)\n", (double)dongle.rate); - verbose_set_sample_rate(dongle.dev, dongle.rate); + verbose_set_sample_rate(dongle.dev, dongle.rate, 0); fprintf(stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample); SoapySDRKwargs args = {0}; @@ -1369,7 +1369,8 @@ int main(int argc, char **argv) ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH; tmp_stdout = suppress_stdout_start(); - verbose_device_search(dongle.dev_query, &dongle.dev, &dongle.stream, SOAPY_SDR_CS16); + verbose_device_search(dongle.dev_query, &dongle.dev, &dongle.stream, SOAPY_SDR_CS16, + NULL, 0); if (!dongle.dev) { fprintf(stderr, "Failed to open rtlsdr device matching %s.\n", dongle.dev_query); @@ -1397,16 +1398,16 @@ int main(int argc, char **argv) /* Set the tuner gain */ if (dongle.gain_str == NULL) { - verbose_auto_gain(dongle.dev); + verbose_auto_gain(dongle.dev, 0); } else { - verbose_gain_str_set(dongle.dev, dongle.gain_str); + verbose_gain_str_set(dongle.dev, dongle.gain_str, 0); } SoapySDRDevice_setGainMode(dongle.dev, SOAPY_SDR_RX, 0, rtlagc); - verbose_ppm_set(dongle.dev, dongle.ppm_error); + verbose_ppm_set(dongle.dev, dongle.ppm_error, 0); - verbose_set_bandwidth(dongle.dev, dongle.bandwidth); + verbose_set_bandwidth(dongle.dev, dongle.bandwidth, 0); if (verbosity && dongle.bandwidth) { diff --git a/src/rtl_power.c b/src/rtl_power.c index ad7fee4..23373b8 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -946,7 +946,7 @@ int main(int argc, char **argv) fprintf(stderr, "Reporting every %i seconds\n", interval); - r = verbose_device_search(dev_query, &dev, &stream, SOAPY_SDR_CS16); + r = verbose_device_search(dev_query, &dev, &stream, SOAPY_SDR_CS16, NULL, 0); if (r != 0) { fprintf(stderr, "Failed to open rtlsdr device matching %s.\n", dev_query); @@ -978,12 +978,12 @@ int main(int argc, char **argv) /* Set the tuner gain */ if (gain_str == NULL) { - verbose_auto_gain(dev); + verbose_auto_gain(dev, 0); } else { - verbose_gain_str_set(dev, gain_str); + verbose_gain_str_set(dev, gain_str, 0); } - verbose_ppm_set(dev, ppm_error); + verbose_ppm_set(dev, ppm_error, 0); if (strcmp(filename, "-") == 0) { /* Write log to stdout */ file = stdout; diff --git a/src/rtl_sdr.c b/src/rtl_sdr.c index 266ce93..8378f3c 100644 --- a/src/rtl_sdr.c +++ b/src/rtl_sdr.c @@ -25,6 +25,7 @@ #ifndef _WIN32 #include +#include #else #include #include @@ -40,9 +41,10 @@ #define DEFAULT_BUF_LENGTH (16 * 16384) #define MINIMAL_BUF_LENGTH 512 #define MAXIMAL_BUF_LENGTH (256 * 16384) +#define MAX_NUM_CHANNELS (8) static int do_exit = 0; -static uint32_t samples_to_read = 0; +static uint64_t samples_to_read = 0; static SoapySDRDevice *dev = NULL; static SoapySDRStream *stream = NULL; @@ -50,17 +52,25 @@ void usage(void) { fprintf(stderr, "rx_sdr (based on rtl_sdr), an I/Q recorder for RTL2832 based DVB-T receivers\n\n" - "Usage:\t -f frequency_to_tune_to [Hz]\n" - "\t[-s samplerate (default: 2048000 Hz)]\n" + "Usage:\t -f/--freq frequency_to_tune_to [Hz]\n" + "\t[-s/--srate samplerate (default: 2048000 Hz)]\n" "\t[-d device key/value query (ex: 0, 1, driver=rtlsdr, driver=hackrf)]\n" - "\t[-g tuner gain(s) (ex: 20, 40, LNA=40,VGA=20,AMP=0)]\n" + "\t[-g/--gain tuner gain(s) (ex: 20, 40, LNA=40,VGA=20,AMP=0)]\n" "\t[-p ppm_error (default: 0)]\n" "\t[-b output_block_size (default: 16 * 16384)]\n" "\t[-n number of samples to read (default: 0, infinite)]\n" "\t[-F output format, CU8|CS8|CS16|CF32 (default: CU8)]\n" "\t[-S force sync output (default: async)]\n" "\t[-D direct_sampling_mode, 0 (default/off), 1 (I), 2 (Q), 3 (no-mod)]\n" - "\tfilename (a '-' dumps samples to stdout)\n\n"); + "\t[-A/--Antenna ant Name of antenna to use]\n" + "\t[-B/--Bandwidth bw Receiver bandwidth to use]\n" + "\t[-N Number of receive channels to use]\n" + "\tfilename0 [filename1.....] (a '-' dumps samples to stdout)\n" + "\n" + "\tAll long options have optional channel number, so --gain3 10 sets the\n" + "\tgain of channel 3 (starting from 0) to be '10'. -g/--gain would set the gain\n" + "\tof all the channels, for example.\n\n" + ); exit(1); } @@ -88,36 +98,129 @@ int main(int argc, char **argv) #ifndef _WIN32 struct sigaction sigact; #endif - char *filename = NULL; - int n_read; + char *filename[MAX_NUM_CHANNELS]; + int64_t n_read; + int r, opt; - char *gain_str = NULL; + char *gain_str[MAX_NUM_CHANNELS]; int ppm_error = 0; int sync_mode = 0; int direct_sampling = 0; - FILE *file; - int16_t *buffer; + FILE *file_ch[MAX_NUM_CHANNELS]; + int16_t *buffer_ch[MAX_NUM_CHANNELS]; + double bw[MAX_NUM_CHANNELS]; + uint8_t *buf8 = NULL; float *fbuf = NULL; // assumed 32-bit char *dev_query = NULL; - uint32_t frequency = 100000000; - uint32_t samp_rate = DEFAULT_SAMPLE_RATE; + uint32_t frequency[MAX_NUM_CHANNELS]; + uint32_t samp_rate[MAX_NUM_CHANNELS]; uint32_t out_block_size = DEFAULT_BUF_LENGTH; char *output_format = SOAPY_SDR_CU8; - - while ((opt = getopt(argc, argv, "d:f:g:s:b:n:p:D:SF:")) != -1) { + char * ant[MAX_NUM_CHANNELS]; + size_t * channels; + size_t nchan = 0; + size_t ch; + struct option *lg_opts = NULL; + char * base_opt[] = {"freq", "gain", "srate", "Ant", "Bandwidth"}; + int o, count; + int option_idx; + + lg_opts = malloc(sizeof(struct option) * (MAX_NUM_CHANNELS * (sizeof(base_opt) / sizeof(char *) + 2))); + if (lg_opts == NULL) { + fprintf(stderr, "Failed to malloc data for lg_opts!!\n"); + exit(10); + } + + for (ch = 0; ch < MAX_NUM_CHANNELS; ch++) { + filename[ch] = NULL; + gain_str[ch] = NULL; + file_ch[ch] = NULL; + buffer_ch[ch] = NULL; + bw[ch] = -1; + frequency[ch] = 40000000; + samp_rate[ch] = DEFAULT_SAMPLE_RATE; + ant[ch] = NULL; + } + + count = 0; + for (o = 0; o < sizeof(base_opt) / sizeof(char *); o++) { + char * tmp_name; + + lg_opts[count].name = base_opt[o]; + lg_opts[count].has_arg = 1; + lg_opts[count].flag = NULL; + lg_opts[count].val = base_opt[0][0]; + count++; + for(ch = 0; ch < MAX_NUM_CHANNELS; ch++) { + tmp_name = malloc(strlen(base_opt[o]) + 10); + if (tmp_name == NULL) { + fprintf(stderr, "Failed to malloc data for tmp_name!!\n"); + exit(10); + } + + if (MAX_NUM_CHANNELS > 9) { + snprintf(tmp_name, strlen(base_opt[o]) + 10, "%s%02d", base_opt[o], ch); + } else { + snprintf(tmp_name, strlen(base_opt[o]) + 10, "%s%d", base_opt[o], ch); + } + lg_opts[count].name = tmp_name; + lg_opts[count].has_arg = 1; + lg_opts[count].flag = NULL; + lg_opts[count].val = 0; + count++; + } + + } + lg_opts[count].name = NULL; + lg_opts[count].has_arg = 0; + lg_opts[count].flag = NULL; + lg_opts[count].val = 0; + + while ((opt = getopt_long(argc, argv, "d:f:g:s:b:n:p:D:SF:A:N:B:", lg_opts, &option_idx)) != -1) { switch (opt) { + case 0: + for (o = 0; o < sizeof(base_opt) / sizeof(char *); o++) { + if (strncmp(base_opt[o], lg_opts[option_idx].name, strlen(base_opt[o])) == 0) { + sscanf(&lg_opts[option_idx].name[strlen(base_opt[o])], "%d", &ch); + + switch (base_opt[o][0]) { + case 'f': + frequency[ch] = (uint32_t)atofs(optarg); + break; + case 'g': + gain_str[ch] = optarg; + break; + case 's': + samp_rate[ch] = (uint32_t)atofs(optarg); + break; + case 'A': + ant[ch] = optarg; + break; + case 'B': + bw[ch] = atofs(optarg); + break; + } + } + } + break; case 'd': dev_query = optarg; break; case 'f': - frequency = (uint32_t)atofs(optarg); + for (ch = 0; ch < MAX_NUM_CHANNELS; ch++) { + frequency[ch] = (uint32_t)atofs(optarg); + } break; case 'g': - gain_str = optarg; + for (ch = 0; ch < MAX_NUM_CHANNELS; ch++) { + gain_str[ch] = optarg; + } break; case 's': - samp_rate = (uint32_t)atofs(optarg); + for (ch = 0; ch < MAX_NUM_CHANNELS; ch++) { + samp_rate[ch] = (uint32_t)atofs(optarg); + } break; case 'p': ppm_error = atoi(optarg); @@ -127,11 +230,25 @@ int main(int argc, char **argv) break; case 'n': // half of I/Q pair count (double for one each of I and Q) - samples_to_read = (uint32_t)atofs(optarg) * 2; + samples_to_read = (uint64_t)atofs(optarg) * 2; break; case 'S': sync_mode = 1; break; + case 'A': + for (ch = 0; ch < MAX_NUM_CHANNELS; ch++) { + ant[ch] = optarg; + } + printf("Setting antenna to %s\n", optarg); + break; + case 'B': + for (ch = 0; ch < MAX_NUM_CHANNELS; ch++) { + bw[ch] = atofs(optarg); + } + break; + case 'N': + nchan = atoi(optarg); + break; case 'F': if (strcasecmp(optarg, "CU8") == 0) { output_format = SOAPY_SDR_CU8; @@ -151,17 +268,21 @@ int main(int argc, char **argv) direct_sampling = atoi(optarg); break; default: + printf("unknown option! - %d\n", opt); usage(); break; } } - if (argc <= optind) { + if (argc != optind + nchan) { + printf("\nNot enough args for channel count\n\n"); usage(); } else { - filename = argv[optind]; + for (ch = 0; ch < nchan; ch++) { + filename[ch] = argv[optind + ch]; + } } - + if(out_block_size < MINIMAL_BUF_LENGTH || out_block_size > MAXIMAL_BUF_LENGTH ){ fprintf(stderr, @@ -173,24 +294,55 @@ int main(int argc, char **argv) out_block_size = DEFAULT_BUF_LENGTH; } - buffer = malloc(out_block_size * SoapySDR_formatToSize(SOAPY_SDR_CS16)); + for(ch = 0; ch < nchan; ch++) { + buffer_ch[ch] = malloc(out_block_size * SoapySDR_formatToSize(SOAPY_SDR_CS16)); + if (buffer_ch[ch] == NULL) { + fprintf(stderr, "Failed to malloc data for buffer_ch[%d]!!\n", ch); + exit(10); + } + } + if (output_format == SOAPY_SDR_CS8 || output_format == SOAPY_SDR_CU8) { buf8 = malloc(out_block_size * SoapySDR_formatToSize(SOAPY_SDR_CS8)); + if (buf8 == NULL) { + fprintf(stderr, "Failed to malloc data for buf8!!\n"); + exit(10); + } } else if (output_format == SOAPY_SDR_CF32) { fbuf = malloc(out_block_size * SoapySDR_formatToSize(SOAPY_SDR_CF32)); + if (fbuf == NULL) { + fprintf(stderr, "Failed to malloc data for fbuf!!\n"); + exit(10); + } } - + int tmp_stdout = suppress_stdout_start(); // TODO: allow choosing input format, see https://www.reddit.com/r/RTLSDR/comments/4tpxv7/rx_tools_commandline_sdr_tools_for_rtlsdr_bladerf/d5ohfse?context=3 - r = verbose_device_search(dev_query, &dev, &stream, SOAPY_SDR_CS16); - + + if (nchan > 0) { + channels = calloc(nchan, sizeof(size_t)); + if (channels == NULL) { + fprintf(stderr, "Failed to malloc data for channels!!\n"); + exit(10); + } + for(ch = 0; ch < nchan; ch++) { + channels[ch] = ch; + } + } else { + channels = NULL; + } + + r = verbose_device_search(dev_query, &dev, &stream, SOAPY_SDR_CS16, channels, nchan); + if (r != 0) { - fprintf(stderr, "Failed to open rtlsdr device matching %s.\n", dev_query); + fprintf(stderr, "Failed to open SDR device matching %s.\n", dev_query); exit(1); } - + fprintf(stderr, "Using output format: %s (input format %s)\n", output_format, SOAPY_SDR_CS16); - + + printf("****Number of channels: %d\n", SoapySDRDevice_getNumChannels(dev, SOAPY_SDR_RX)); + #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); @@ -202,43 +354,70 @@ int main(int argc, char **argv) #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif - + if (direct_sampling) { verbose_direct_sampling(dev, direct_sampling); } - + + for(ch = 0; ch < nchan; ch++) { + if (bw[ch] < 0) { + bw[ch] = 0.9 * samp_rate[ch]; + } + } + /* Set the sample rate */ - verbose_set_sample_rate(dev, samp_rate); - /* Set the frequency */ - verbose_set_frequency(dev, frequency); - - if (NULL == gain_str) { - /* Enable automatic gain */ - verbose_auto_gain(dev); + /* Set the antenna */ + /* Set the bandwidth */ + if (channels == NULL) { + verbose_set_sample_rate(dev, samp_rate[0], 0); + verbose_set_frequency(dev, frequency[0], 0); + verbose_set_antenna(dev, ant[0], 0); + verbose_set_bandwidth(dev, bw[0], 0); + if (NULL == gain_str[0]) { + /* Enable automatic gain */ + verbose_auto_gain(dev, 0); + } else { + /* Enable manual gain */ + verbose_gain_str_set(dev, gain_str[0], 0); + } + verbose_ppm_set(dev, ppm_error, 0); } else { - /* Enable manual gain */ - verbose_gain_str_set(dev, gain_str); + for(ch = 0; ch < nchan; ch++) { + verbose_set_sample_rate(dev, samp_rate[ch], channels[ch]); + verbose_set_frequency(dev, frequency[ch], channels[ch]); + verbose_set_antenna(dev, ant[ch], channels[ch]); + verbose_set_bandwidth(dev, bw[ch], channels[ch]); + if (NULL == gain_str[ch]) { + /* Enable automatic gain */ + verbose_auto_gain(dev, channels[ch]); + } else { + /* Enable manual gain */ + verbose_gain_str_set(dev, gain_str[ch], channels[ch]); + } + verbose_ppm_set(dev, ppm_error, channels[ch]); + + } } - - verbose_ppm_set(dev, ppm_error); - - if(strcmp(filename, "-") == 0) { /* Write samples to stdout */ - file = stdout; + + + for(ch = 0; ch < nchan; ch++) { + if(strcmp(filename[ch], "-") == 0) { /* Write samples to stdout */ + file_ch[ch] = stdout; #ifdef _WIN32 - _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdin), _O_BINARY); #endif - } else { - file = fopen(filename, "wb"); - if (!file) { - fprintf(stderr, "Failed to open %s\n", filename); - goto out; + } else { + file_ch[ch] = fopen(filename[ch], "wb"); + if (!file_ch[ch]) { + fprintf(stderr, "Failed to open %s\n", filename[ch]); + goto out; + } } } - /* Reset endpoint before we start reading from it (mandatory) */ verbose_reset_buffer(dev); - + if (true || sync_mode) { fprintf(stderr, "Reading samples in sync mode...\n"); SoapySDRKwargs args = {0}; @@ -248,14 +427,14 @@ int main(int argc, char **argv) } suppress_stdout_stop(tmp_stdout); while (!do_exit) { - void *buffs[] = {buffer}; int flags = 0; long long timeNs = 0; long timeoutNs = 1000000; - int n_read = 0, r, i; - - r = SoapySDRDevice_readStream(dev, stream, buffs, out_block_size, &flags, &timeNs, timeoutNs); - + int64_t n_read = 0, r, i; + + r = SoapySDRDevice_readStream(dev, stream, (void *) buffer_ch, out_block_size, + &flags, &timeNs, timeoutNs); + //fprintf(stderr, "readStream ret=%d, flags=%d, timeNs=%lld\n", r, flags, timeNs); if (r >= 0) { // r is number of elements read, elements=complex pairs of 8-bits, so buffer length in bytes is twice @@ -268,72 +447,88 @@ int main(int argc, char **argv) } fprintf(stderr, "WARNING: sync read failed. %d\n", r); } - - if ((samples_to_read > 0) && (samples_to_read < (uint32_t)n_read)) { + + if ((samples_to_read > 0) && (samples_to_read < (uint64_t)n_read)) { n_read = samples_to_read; do_exit = 1; } - + // TODO: read these formats natively from SoapySDR (setupStream) instead of converting ourselves? - if (output_format == SOAPY_SDR_CS16) { - // The "native" format we read in, write out no conversion needed - // (Always reading in CS16 to support >8-bit devices) - if (fwrite(buffer, sizeof(int16_t), n_read, file) != (size_t)n_read) { - fprintf(stderr, "Short write, samples lost, exiting!\n"); - break; - } - } else if (output_format == SOAPY_SDR_CS8) { - for (i = 0; i < n_read; ++i) { - buf8[i] = ( (int16_t)buffer[i] / 32767.0 * 128.0 + 0.4); - } - if (fwrite(buf8, sizeof(uint8_t), n_read, file) != (size_t)n_read) { - fprintf(stderr, "Short write, samples lost, exiting!\n"); - break; - } - } else if (output_format == SOAPY_SDR_CU8) { - for (i = 0; i < n_read; ++i) { - buf8[i] = ( (int16_t)buffer[i] / 32767.0 * 128.0 + 127.4); - } - if (fwrite(buf8, sizeof(uint8_t), n_read, file) != (size_t)n_read) { - fprintf(stderr, "Short write, samples lost, exiting!\n"); - break; - } - } else if (output_format == SOAPY_SDR_CF32) { - for (i = 0; i < n_read; ++i) { - fbuf[i] = buffer[i] * 1.0f / SHRT_MAX; - } - if (fwrite(fbuf, sizeof(float), n_read, file) != (size_t)n_read) { - fprintf(stderr, "Short write, samples lost, exiting!\n"); - break; + for(ch = 0; ch < nchan; ch++) { + if (output_format == SOAPY_SDR_CS16) { + // The "native" format we read in, write out no conversion needed + // (Always reading in CS16 to support >8-bit devices) + if (fwrite(buffer_ch[ch], sizeof(int16_t), n_read, file_ch[ch]) != (size_t)n_read) { + fprintf(stderr, "Short write, samples lost, exiting!\n"); + do_exit = 1; + } + + } else if (output_format == SOAPY_SDR_CS8) { + for (i = 0; i < n_read; ++i) { + buf8[i] = ( (int16_t)buffer_ch[ch][i] / 32767.0 * 128.0 + 0.4); + } + if (fwrite(buf8, sizeof(uint8_t), n_read, file_ch[ch]) != (size_t)n_read) { + fprintf(stderr, "Short write, samples lost, exiting!\n"); + do_exit = 1; + } + } else if (output_format == SOAPY_SDR_CU8) { + for (i = 0; i < n_read; ++i) { + buf8[i] = ( (int16_t)buffer_ch[ch][i] / 32767.0 * 128.0 + 127.4); + } + if (fwrite(buf8, sizeof(uint8_t), n_read, file_ch[ch]) != (size_t)n_read) { + fprintf(stderr, "Short write, samples lost, exiting!\n"); + do_exit = 1; + } + } else if (output_format == SOAPY_SDR_CF32) { + for (i = 0; i < n_read; ++i) { + fbuf[i] = buffer_ch[ch][i] * 1.0f / SHRT_MAX; + } + if (fwrite(fbuf, sizeof(float), n_read, file_ch[ch]) != (size_t)n_read) { + fprintf(stderr, "Short write, samples lost, exiting!\n"); + do_exit = 1; + } } } - - + // TODO: hmm.. n_read 8192, but out_block_size (16 * 16384) is much larger TODO: loop? or accept 8192? rtl_fm ok with it /* - if ((uint32_t)n_read < out_block_size) { - fprintf(stderr, "Short read, samples lost, exiting! (%d < %d)\n", n_read, out_block_size); - break; - } + if ((uint32_t)n_read < out_block_size) { + fprintf(stderr, "Short read, samples lost, exiting! (%d < %d)\n", n_read, out_block_size); + break; + } */ - + if (samples_to_read > 0) samples_to_read -= n_read; } } - + if (do_exit) fprintf(stderr, "\nUser cancel, exiting...\n"); else fprintf(stderr, "\nLibrary error %d, exiting...\n", r); - - if (file != stdout) - fclose(file); - + + for(ch = 0; ch < nchan; ch++) { + if (file_ch[ch] != stdout) { + fclose(file_ch[ch]); + } + } + SoapySDRDevice_deactivateStream(dev, stream, 0, 0); SoapySDRDevice_closeStream(dev, stream); SoapySDRDevice_unmake(dev); - free (buffer); -out: + + for(ch = 0; ch < nchan; ch++) { + free(buffer_ch[ch]); + } + + if (buf8 != NULL) { + free(buf8); + } + + if (fbuf != NULL) { + free(fbuf); + } + out: return r >= 0 ? r : -r; }