soapypower/__main__.py
def setup_argument_parser():
    """Setup command line parser"""
    # Fix help formatter width
    if 'COLUMNS' not in os.environ:
        os.environ['COLUMNS'] = str(shutil.get_terminal_size().columns)

    parser = argparse.ArgumentParser(
        prog='soapy_power',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='Obtain a power spectrum from SoapySDR devices',
        add_help=False
    )

    # Fix recognition of optional argements of type float_with_multiplier
    parser._negative_number_matcher = re_float_with_multiplier_negative

    main_title = parser.add_argument_group('Main options')
    main_title.add_argument('-h', '--help', action='help',
                            help='show this help message and exit')
    main_title.add_argument('-f', '--freq', metavar='Hz|Hz:Hz', type=freq_or_freq_range, default='1420405752',
                            help='center frequency or frequency range to scan, number '
                            'can be followed by a k, M or G multiplier (default: %(default)s)')

    output_group = main_title.add_mutually_exclusive_group()
    output_group.add_argument('-O', '--output', metavar='FILE', type=argparse.FileType('w'), default=sys.stdout,
                              help='output to file (incompatible with --output-fd, default is stdout)')
    output_group.add_argument('--output-fd', metavar='NUM', type=int, default=None,
                              help='output to existing file descriptor (incompatible with -O)')

    main_title.add_argument('-F', '--format', choices=sorted(writer.formats.keys()), default='rtl_power',
                            help='output format (default: %(default)s)')
    main_title.add_argument('-q', '--quiet', action='store_true',
                            help='limit verbosity')
    main_title.add_argument('--debug', action='store_true',
                            help='detailed debugging messages')
    main_title.add_argument('--detect', action='store_true',
                            help='detect connected SoapySDR devices and exit')
    main_title.add_argument('--info', action='store_true',
                            help='show info about selected SoapySDR device and exit')
    main_title.add_argument('--version', action='version',
                            version='%(prog)s {}'.format(__version__))

    bins_title = parser.add_argument_group('FFT bins')
    bins_group = bins_title.add_mutually_exclusive_group()
    bins_group.add_argument('-b', '--bins', type=int, default=512,
                            help='number of FFT bins (incompatible with -B, default: %(default)s)')
    bins_group.add_argument('-B', '--bin-size', metavar='Hz', type=float_with_multiplier,
                            help='bin size in Hz (incompatible with -b)')

    spectra_title = parser.add_argument_group('Averaging')
    spectra_group = spectra_title.add_mutually_exclusive_group()
    spectra_group.add_argument('-n', '--repeats', type=int, default=1600,
                               help='number of spectra to average (incompatible with -t and -T, default: %(default)s)')
    spectra_group.add_argument('-t', '--time', metavar='SECONDS', type=float,
                               help='integration time (incompatible with -T and -n)')
    spectra_group.add_argument('-T', '--total-time', metavar='SECONDS', type=float,
                               help='total integration time of all hops (incompatible with -t and -n)')

    runs_title = parser.add_argument_group('Measurements')
    runs_group = runs_title.add_mutually_exclusive_group()
    runs_group.add_argument('-c', '--continue', dest='endless', action='store_true',
                            help='repeat the measurement endlessly (incompatible with -u and -e)')
    runs_group.add_argument('-u', '--runs', type=int, default=1,
                            help='number of measurements (incompatible with -c and -e, default: %(default)s)')
    runs_group.add_argument('-e', '--elapsed', metavar='SECONDS', type=float,
                            help='scan session duration (time limit in seconds, incompatible with -c and -u)')

    device_title = parser.add_argument_group('Device settings')
    device_title.add_argument('-d', '--device', default='',
                              help='SoapySDR device to use')
    device_title.add_argument('-C', '--channel', type=int, default=0,
                              help='SoapySDR RX channel (default: %(default)s)')
    device_title.add_argument('-A', '--antenna', default='',
                              help='SoapySDR selected antenna')
    device_title.add_argument('-r', '--rate', metavar='Hz', type=float_with_multiplier, default=2e6,
                              help='sample rate (default: %(default)s)')
    device_title.add_argument('-w', '--bandwidth', metavar='Hz', type=float_with_multiplier, default=0,
                              help='filter bandwidth (default: %(default)s)')
    device_title.add_argument('-p', '--ppm', type=int, default=0,
                              help='frequency correction in ppm')

    gain_group = device_title.add_mutually_exclusive_group()
    gain_group.add_argument('-g', '--gain', metavar='dB', type=float, default=37.2,
                            help='total gain (incompatible with -G and -a, default: %(default)s)')
    gain_group.add_argument('-G', '--specific-gains', metavar='STRING', type=specific_gains, default='',
                            help='specific gains of individual amplification elements '
                                 '(incompatible with -g and -a, example: LNA=28,VGA=12,AMP=0')
    gain_group.add_argument('-a', '--agc', action='store_true',
                            help='enable Automatic Gain Control (incompatible with -g and -G)')

    device_title.add_argument('--lnb-lo', metavar='Hz', type=float_with_multiplier, default=0,
                              help='LNB LO frequency, negative for upconverters (default: %(default)s)')
    device_title.add_argument('--device-settings', metavar='STRING', type=device_settings, default='',
                              help='SoapySDR device settings (example: biastee=true)')
    device_title.add_argument('--force-rate', action='store_true',
                              help='ignore list of sample rates provided by device and allow any value')
    device_title.add_argument('--force-bandwidth', action='store_true',
                              help='ignore list of filter bandwidths provided by device and allow any value')
    device_title.add_argument('--tune-delay', metavar='SECONDS', type=float, default=0,
                              help='time to delay measurement after changing frequency (to avoid artifacts)')
    device_title.add_argument('--reset-stream', action='store_true',
                              help='reset streaming after changing frequency (to avoid artifacts)')

    crop_title = parser.add_argument_group('Crop')
    crop_group = crop_title.add_mutually_exclusive_group()
    crop_group.add_argument('-o', '--overlap', metavar='PERCENT', type=float, default=0,
                            help='percent of overlap when frequency hopping (incompatible with -k)')
    crop_group.add_argument('-k', '--crop', metavar='PERCENT', type=float, default=0,
                            help='percent of crop when frequency hopping (incompatible with -o)')

    perf_title = parser.add_argument_group('Performance options')
    perf_title.add_argument('-s', '--buffer-size', type=int, default=0,
                            help='base buffer size (number of samples, 0 = auto, default: %(default)s)')
    perf_title.add_argument('-S', '--max-buffer-size', type=int, default=0,
                            help='maximum buffer size (number of samples, -1 = unlimited, 0 = auto, default: %(default)s)')

    fft_rules_group = perf_title.add_mutually_exclusive_group()
    fft_rules_group.add_argument('--even', action='store_true',
                                 help='use only even numbers of FFT bins')
    fft_rules_group.add_argument('--pow2', action='store_true',
                                 help='use only powers of 2 as number of FFT bins')

    perf_title.add_argument('--max-threads', metavar='NUM', type=int, default=0,
                            help='maximum number of PSD threads (0 = auto, default: %(default)s)')
    perf_title.add_argument('--max-queue-size', metavar='NUM', type=int, default=0,
                            help='maximum size of PSD work queue (-1 = unlimited, 0 = auto, default: %(default)s)')
    perf_title.add_argument('--no-pyfftw', action='store_true',
                            help='don\'t use pyfftw library even if it is available (use scipy.fftpack or numpy.fft)')

    other_title = parser.add_argument_group('Other options')
    other_title.add_argument('-l', '--linear', action='store_true',
                             help='linear power values instead of logarithmic')
    other_title.add_argument('-R', '--remove-dc', action='store_true',
                             help='interpolate central point to cancel DC bias (useful only with boxcar window)')
    other_title.add_argument('-D', '--detrend', choices=['none', 'constant'], default='none',
                             help='remove mean value from data to cancel DC bias (default: %(default)s)')
    other_title.add_argument('--fft-window', choices=['boxcar', 'hann', 'hamming', 'blackman', 'bartlett', 'kaiser', 'tukey'],
                             default='hann', help='Welch\'s method window function (default: %(default)s)')
    other_title.add_argument('--fft-window-param', metavar='FLOAT', type=float, default=None,
                             help='shape parameter of window function (required for kaiser and tukey windows)')
    other_title.add_argument('--fft-overlap', metavar='PERCENT', type=float, default=50,
                             help='Welch\'s method overlap between segments (default: %(default)s)')

    return parser
def setup_argument_parser():
    """Setup command line parser"""
    # Fix help formatter width
    if 'COLUMNS' not in os.environ:
        os.environ['COLUMNS'] = str(shutil.get_terminal_size().columns)

    parser = argparse.ArgumentParser(
        prog='soapy_power',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='Obtain a power spectrum from SoapySDR devices',
        add_help=False
    )

    # Fix recognition of optional argements of type float_with_multiplier
    parser._negative_number_matcher = re_float_with_multiplier_negative

    main_title = parser.add_argument_group('Main options')
    main_title.add_argument('-h', '--help', action='help',
                            help='show this help message and exit')
    main_title.add_argument('-f', '--freq', metavar='Hz|Hz:Hz', type=freq_or_freq_range, default='1420405752',
                            help='center frequency or frequency range to scan, number '
                            'can be followed by a k, M or G multiplier (default: %(default)s)')

    output_group = main_title.add_mutually_exclusive_group()
    output_group.add_argument('-O', '--output', metavar='FILE', type=argparse.FileType('w'), default=sys.stdout,
                              help='output to file (incompatible with --output-fd, default is stdout)')
    output_group.add_argument('--output-fd', metavar='NUM', type=int, default=None,
                              help='output to existing file descriptor (incompatible with -O)')

    main_title.add_argument('-F', '--format', choices=sorted(writer.formats.keys()), default='rtl_power',
                            help='output format (default: %(default)s)')
    main_title.add_argument('-q', '--quiet', action='store_true',
                            help='limit verbosity')
    main_title.add_argument('--debug', action='store_true',
                            help='detailed debugging messages')
    main_title.add_argument('--detect', action='store_true',
                            help='detect connected SoapySDR devices and exit')
    main_title.add_argument('--info', action='store_true',
                            help='show info about selected SoapySDR device and exit')
    main_title.add_argument('--version', action='version',
                            version='%(prog)s {}'.format(__version__))

    bins_title = parser.add_argument_group('FFT bins')
    bins_group = bins_title.add_mutually_exclusive_group()
    bins_group.add_argument('-b', '--bins', type=int, default=512,
                            help='number of FFT bins (incompatible with -B, default: %(default)s)')
    bins_group.add_argument('-B', '--bin-size', metavar='Hz', type=float_with_multiplier,
                            help='bin size in Hz (incompatible with -b)')

    spectra_title = parser.add_argument_group('Averaging')
    spectra_group = spectra_title.add_mutually_exclusive_group()
    spectra_group.add_argument('-n', '--repeats', type=int, default=1600,
                               help='number of spectra to average (incompatible with -t and -T, default: %(default)s)')
    spectra_group.add_argument('-t', '--time', metavar='SECONDS', type=float,
                               help='integration time (incompatible with -T and -n)')
    spectra_group.add_argument('-T', '--total-time', metavar='SECONDS', type=float,
                               help='total integration time of all hops (incompatible with -t and -n)')

    runs_title = parser.add_argument_group('Measurements')
    runs_group = runs_title.add_mutually_exclusive_group()
    runs_group.add_argument('-c', '--continue', dest='endless', action='store_true',
                            help='repeat the measurement endlessly (incompatible with -u and -e)')
    runs_group.add_argument('-u', '--runs', type=int, default=1,
                            help='number of measurements (incompatible with -c and -e, default: %(default)s)')
    runs_group.add_argument('-e', '--elapsed', metavar='SECONDS', type=float,
                            help='scan session duration (time limit in seconds, incompatible with -c and -u)')

    device_title = parser.add_argument_group('Device settings')
    device_title.add_argument('-d', '--device', default='',
                              help='SoapySDR device to use')
    device_title.add_argument('-C', '--channel', type=int, default=0,
                              help='SoapySDR RX channel (default: %(default)s)')
    device_title.add_argument('-A', '--antenna', default='',
                              help='SoapySDR selected antenna')
    device_title.add_argument('-r', '--rate', metavar='Hz', type=float_with_multiplier, default=2e6,
                              help='sample rate (default: %(default)s)')
    device_title.add_argument('-w', '--bandwidth', metavar='Hz', type=float_with_multiplier, default=0,
                              help='filter bandwidth (default: %(default)s)')
    device_title.add_argument('-p', '--ppm', type=int, default=0,
                              help='frequency correction in ppm')

    gain_group = device_title.add_mutually_exclusive_group()
    gain_group.add_argument('-g', '--gain', metavar='dB', type=float, default=37.2,
                            help='total gain (incompatible with -G and -a, default: %(default)s)')
    gain_group.add_argument('-G', '--specific-gains', metavar='STRING', type=specific_gains, default='',
                            help='specific gains of individual amplification elements '
                                 '(incompatible with -g and -a, example: LNA=28,VGA=12,AMP=0')
    gain_group.add_argument('-a', '--agc', action='store_true',
                            help='enable Automatic Gain Control (incompatible with -g and -G)')

    device_title.add_argument('--lnb-lo', metavar='Hz', type=float_with_multiplier, default=0,
                              help='LNB LO frequency, negative for upconverters (default: %(default)s)')
    device_title.add_argument('--device-settings', metavar='STRING', type=device_settings, default='',
                              help='SoapySDR device settings (example: biastee=true)')
    device_title.add_argument('--force-rate', action='store_true',
                              help='ignore list of sample rates provided by device and allow any value')
    device_title.add_argument('--force-bandwidth', action='store_true',
                              help='ignore list of filter bandwidths provided by device and allow any value')
    device_title.add_argument('--tune-delay', metavar='SECONDS', type=float, default=0,
                              help='time to delay measurement after changing frequency (to avoid artifacts)')
    device_title.add_argument('--reset-stream', action='store_true',
                              help='reset streaming after changing frequency (to avoid artifacts)')

    crop_title = parser.add_argument_group('Crop')
    crop_group = crop_title.add_mutually_exclusive_group()
    crop_group.add_argument('-o', '--overlap', metavar='PERCENT', type=float, default=0,
                            help='percent of overlap when frequency hopping (incompatible with -k)')
    crop_group.add_argument('-k', '--crop', metavar='PERCENT', type=float, default=0,
                            help='percent of crop when frequency hopping (incompatible with -o)')

    perf_title = parser.add_argument_group('Performance options')
    perf_title.add_argument('-s', '--buffer-size', type=int, default=0,
                            help='base buffer size (number of samples, 0 = auto, default: %(default)s)')
    perf_title.add_argument('-S', '--max-buffer-size', type=int, default=0,
                            help='maximum buffer size (number of samples, -1 = unlimited, 0 = auto, default: %(default)s)')

    fft_rules_group = perf_title.add_mutually_exclusive_group()
    fft_rules_group.add_argument('--even', action='store_true',
                                 help='use only even numbers of FFT bins')
    fft_rules_group.add_argument('--pow2', action='store_true',
                                 help='use only powers of 2 as number of FFT bins')

    perf_title.add_argument('--max-threads', metavar='NUM', type=int, default=0,
                            help='maximum number of PSD threads (0 = auto, default: %(default)s)')
    perf_title.add_argument('--max-queue-size', metavar='NUM', type=int, default=0,
                            help='maximum size of PSD work queue (-1 = unlimited, 0 = auto, default: %(default)s)')
    perf_title.add_argument('--no-pyfftw', action='store_true',
                            help='don\'t use pyfftw library even if it is available (use scipy.fftpack or numpy.fft)')

    other_title = parser.add_argument_group('Other options')
    other_title.add_argument('-l', '--linear', action='store_true',
                             help='linear power values instead of logarithmic')
    other_title.add_argument('-R', '--remove-dc', action='store_true',
                             help='interpolate central point to cancel DC bias (useful only with boxcar window)')
    other_title.add_argument('-D', '--detrend', choices=['none', 'constant'], default='none',
                             help='remove mean value from data to cancel DC bias (default: %(default)s)')
    other_title.add_argument('--fft-window', choices=['boxcar', 'hann', 'hamming', 'blackman', 'bartlett', 'kaiser', 'tukey'],
                             default='hann', help='Welch\'s method window function (default: %(default)s)')
    other_title.add_argument('--fft-window-param', metavar='FLOAT', type=float, default=None,
                             help='shape parameter of window function (required for kaiser and tukey windows)')
    other_title.add_argument('--fft-overlap', metavar='PERCENT', type=float, default=50,
                             help='Welch\'s method overlap between segments (default: %(default)s)')

    return parser
Setup command line parser