From ac0d199b25e06affb86b10d33e708d1887cfa547 Mon Sep 17 00:00:00 2001 From: Yuuta Liang Date: Wed, 2 Oct 2024 00:55:19 -0700 Subject: [PATCH] SoapySDR: Support multiple gains SoapySDR has a function called setGainElement() that allows users to specify gains for different hardware components (e.g., for HackRF, the preamp, the LNA, and the VGA. Currently acarsdec calls setGain(double) which asks the driver to distribute the gain number to different components, which sometimes may not be desired. This patch enables the new cmd syntax of `-g name:gain -g name:gain ...` that allows users to set different gains for different components. This patch preserves compatibility with the previous syntax `-g gain`. It conflicts with the previous syntax. The user has to choose between `-g gain` (use setGain(), supports AGC) or `-g name:gain -g name:gain ...` (use setGainElement(), does not support AGC). This patch is for SoapySDR only. --- acarsdec.c | 49 +++++++++++++++++++++++++++++++++++++++++++++---- acarsdec.h | 7 ++++++- soapy.c | 42 ++++++++++++++++++++++++++++++++---------- 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/acarsdec.c b/acarsdec.c index e96581d..094d9d0 100644 --- a/acarsdec.c +++ b/acarsdec.c @@ -68,7 +68,8 @@ int ppm = 0; #endif #ifdef WITH_SOAPY char *antenna=NULL; -double gain = -10.0; +struct soapy_gain *gains = NULL; +size_t gains_len = 0; int ppm = 0; int rateMult = 160; int freq = 0; @@ -121,7 +122,7 @@ static void usage(void) fprintf (stderr, " [-L lnaState] [-G GRdB] [-p ppm] -s f1 [f2] .. [fN]"); #endif #ifdef WITH_SOAPY - fprintf (stderr, " [--antenna antenna] [-g gain] [-p ppm] [-c freq] -d devicestring f1 [f2] .. [fN]"); + fprintf (stderr, " [--antenna antenna] [-g [name1:]gain1] [-g [name2:]gain2] .. [-p ppm] [-c freq] -d devicestring f1 [f2] .. [fN]"); #endif fprintf(stderr, "\n\n"); #ifdef HAVE_LIBACARS @@ -192,7 +193,7 @@ static void usage(void) fprintf(stderr, " --antenna antenna\t: set antenna port to use\n"); fprintf(stderr, - " -g gain\t\t: set gain in db (-10 will result in AGC; default is AGC)\n"); + " -g [name:]gain\t\t: set gain of specific name (optional, e.g. \"LNA\" \"VGA\") in db (-10 will result in AGC; default is AGC; set the default gain if no name is set, only one default gain or AGC permitted per device, or use multiple gains with names)\n"); fprintf(stderr, " -p ppm\t\t\t: set ppm frequency correction\n"); fprintf(stderr, " c freq\t\t\t: set center frequency to tune to\n"); fprintf(stderr, " -m rateMult\t\t\t: set sample rate multiplier: 160 for 2 MS/s or 192 for 2.4 MS/s (default: 160)\n"); @@ -319,7 +320,40 @@ int main(int argc, char **argv) freq = atoi(optarg); break; case 'g': - gain = atof(optarg); + if (verbose) { + fprintf(stderr, "Got gain argument: '%s'\n", optarg); + } + char *gaini = strchr(optarg, ':'); + if (gaini) { + for (int i = 0; i < gains_len; i ++) { + if (!gains[i].name) { + fprintf(stderr, "You can only specify either one default gain (no name), like `-g 10`, or a set of gain with names, e.g. `-g LNA:10 -g VGA:20 -g AMP:30`.\n"); + exit(1); + } + } + } else if (gains_len) { + fprintf(stderr, "You can only specify either one default gain (no name), like `-g 10`, or a set of gain with names, e.g. `-g LNA:10 -g VGA:20 -g AMP:30`.\n"); + exit(1); + } + if (!(gains = realloc(gains, (++gains_len) * sizeof(struct soapy_gain)))) { + fprintf(stderr, "Cannot allocate memory.\n"); + exit(1); + } + struct soapy_gain *g = &gains[gains_len - 1]; + memset(g, 0, sizeof(struct soapy_gain)); + if (gaini) { + char *dup = strdup(optarg); + *strchr(dup, ':') = 0; + g->gain = atof(gaini + 1); + g->name = dup; + } else { + g->gain = atof(optarg); + g->name = NULL; + } + if (gaini && g->gain == -10.0) { + fprintf(stderr, "Setting gain to AGC is allowed only for the default gain. I.e., you can only specify one `-g -10` if you want to use AGC.\n"); + exit(1); + } break; case 'm': rateMult = atoi(optarg); @@ -508,6 +542,13 @@ int main(int argc, char **argv) fprintf(stderr, "exiting ...\n"); deinitAcars(); +#ifdef WITH_SOAPY + for (int i = 0; i < gains_len; i ++) { + if (gains[i].name) + free(gains[i].name); + } + free(gains); +#endif #ifdef WITH_MQTT MQTTend(); diff --git a/acarsdec.h b/acarsdec.h index e0b6428..d85f890 100644 --- a/acarsdec.h +++ b/acarsdec.h @@ -175,7 +175,12 @@ extern int runSoapySample(void); extern int runSoapyClose(void); extern int rateMult; extern int freq; -extern double gain; +struct soapy_gain { + char *name; + double gain; +}; +extern struct soapy_gain *gains; +extern size_t gains_len; #else extern int gain; #endif diff --git a/soapy.c b/soapy.c index 3c8a149..85be1e4 100644 --- a/soapy.c +++ b/soapy.c @@ -90,21 +90,43 @@ int initSoapy(char **argv, int optind) soapyInBuf = malloc(sizeof(int16_t) * soapyInBufSize); - if (gain == -10.0) { + if (!gains_len) { if (verbose) fprintf(stderr, "Tuner gain: AGC\n"); r = SoapySDRDevice_setGainMode(dev, SOAPY_SDR_RX, 0, 1); if (r != 0) fprintf(stderr, "WARNING: Failed to turn on AGC: %s\n", SoapySDRDevice_lastError()); - } else { - r = SoapySDRDevice_setGainMode(dev, SOAPY_SDR_RX, 0, 0); - if (r != 0) - fprintf(stderr, "WARNING: Failed to turn off AGC: %s\n", SoapySDRDevice_lastError()); - if (verbose) - fprintf(stderr, "Setting gain to: %f\n", gain); - r = SoapySDRDevice_setGain(dev, SOAPY_SDR_RX, 0, gain); - if (r != 0) - fprintf(stderr, "WARNING: Failed to set gain: %s\n", SoapySDRDevice_lastError()); + } + for (int i = 0; i < gains_len; i ++) { + const struct soapy_gain *g = &gains[i]; + if (!g->name) { + if (g->gain == -10.0) { + if (verbose) + fprintf(stderr, "Tuner gain: AGC\n"); + r = SoapySDRDevice_setGainMode(dev, SOAPY_SDR_RX, 0, 1); + if (r != 0) + fprintf(stderr, "WARNING: Failed to turn on AGC: %s\n", SoapySDRDevice_lastError()); + } else { + r = SoapySDRDevice_setGainMode(dev, SOAPY_SDR_RX, 0, 0); + if (r != 0) + fprintf(stderr, "WARNING: Failed to turn off AGC: %s\n", SoapySDRDevice_lastError()); + if (verbose) + fprintf(stderr, "Setting gain to: %f\n", g->gain); + r = SoapySDRDevice_setGain(dev, SOAPY_SDR_RX, 0, g->gain); + if (r != 0) + fprintf(stderr, "WARNING: Failed to set gain: %s\n", SoapySDRDevice_lastError()); + } + break; + } else { + r = SoapySDRDevice_setGainMode(dev, SOAPY_SDR_RX, 0, 0); + if (r != 0) + fprintf(stderr, "WARNING: Failed to turn off AGC: %s\n", SoapySDRDevice_lastError()); + if (verbose) + fprintf(stderr, "Setting %s gain to: %f\n", g->name, g->gain); + r = SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, g->name, g->gain); + if (r != 0) + fprintf(stderr, "WARNING: Failed to set %s gain: %s\n", g->name, SoapySDRDevice_lastError()); + } } if (ppm != 0) {