Ejemplo n.º 1
0
def test_TCXOPoly_compute0():
  '''
  Unit test for empty TCXOPoly object
  '''
  tcxo = TCXOPoly(())
  time = tcxo.computeTcxoTime(0, 10, NormalRateConfig)
  assert time is None
Ejemplo n.º 2
0
def test_TCXOPoly_compute1():
  '''
  Unit test for TCXOPoly with linear time shift (10e-6)
  '''
  tcxo = TCXOPoly((1.,))
  time = tcxo.computeTcxoTime(0, 10, NormalRateConfig)
  test_vector = numpy.linspace(0.,
                               10. * 1e-6 / NormalRateConfig.SAMPLE_RATE_HZ,
                               10.,
                               endpoint=False)
  assert (time == test_vector).all()
Ejemplo n.º 3
0
def test_TCXOPoly_compute2():
  '''
  Unit test for TCXOPoly with linear time shift (10e-6)
  '''
  tcxo = TCXOPoly((1., 1.))
  time = tcxo.computeTcxoTime(0, 10, NormalRateConfig)
  test_vector = numpy.linspace(0.,
                               10. * 1e-6 / NormalRateConfig.SAMPLE_RATE_HZ,
                               10.,
                               endpoint=False)
  test_vector = test_vector * test_vector / 2. + test_vector
  assert (numpy.abs(time - test_vector) < EPSILON).all()
Ejemplo n.º 4
0
def test_Task_generate0():
    '''
  Task object generation test
  '''
    outputConfig = HighRateConfig
    sv0 = GPSSatellite(1)
    sv0.setL1CAEnabled(True)
    signalSources = [sv0]
    noiseParams = NoiseParameters(outputConfig.SAMPLE_RATE_HZ, 1.)
    tcxo = TCXOPoly((1., ))
    signalFilters = [
        LowPassFilter(outputConfig,
                      outputConfig.GPS.L1.INTERMEDIATE_FREQUENCY_HZ), None,
        None, None
    ]
    groupDelays = True
    bands = [outputConfig.GPS.L1]
    generateDebug = False

    task = Task(outputConfig, signalSources, noiseParams, tcxo, signalFilters,
                groupDelays, bands, generateDebug)
    nSamples = 1024
    userTime0_s = 0.
    firstSampleIndex = 1
    task.update(userTime0_s, nSamples, firstSampleIndex)
    inputParams, sigs, debugData = task.perform()
    assert inputParams[0] == userTime0_s
    assert inputParams[1] == nSamples
    assert inputParams[2] == firstSampleIndex
    assert debugData is None
    assert sigs.shape == (outputConfig.N_GROUPS, nSamples)
Ejemplo n.º 5
0
def test_Task_update1():
    '''
  Task object parameter update test
  '''
    outputConfig = NormalRateConfig
    sv0 = GPSSatellite(1)
    sv0.setL1CAEnabled(True)
    signalSources = [sv0]
    noiseParams = NoiseParameters(outputConfig.SAMPLE_RATE_HZ, 1.)
    tcxo = TCXOPoly(())
    signalFilters = [None] * 4
    groupDelays = False
    bands = [outputConfig.GPS.L1]
    generateDebug = False

    task = Task(outputConfig, signalSources, noiseParams, tcxo, signalFilters,
                groupDelays, bands, generateDebug)

    userTime0_s = 123.
    nSamples = 1024
    firstSampleIndex = 1
    task.update(userTime0_s, nSamples, firstSampleIndex)
    assert task.nSamples == nSamples
    assert task.firstSampleIndex == firstSampleIndex
    assert task.userTime0_s == userTime0_s
    assert task.noise.shape == (outputConfig.N_GROUPS, nSamples)
    assert isinstance(task.signals, numpy.ndarray)
    assert task.signals.shape == (outputConfig.N_GROUPS, nSamples)
Ejemplo n.º 6
0
def test_Task_init():
    '''
  Task object initialization test
  '''
    outputConfig = NormalRateConfig
    sv0 = GPSSatellite(1)
    sv0.setL1CAEnabled(True)
    signalSources = [sv0]
    noiseParams = NoiseParameters(outputConfig.SAMPLE_RATE_HZ, 1.)
    tcxo = TCXOPoly(())
    signalFilters = [None] * 4
    groupDelays = False
    bands = [outputConfig.GPS.L1]
    generateDebug = False

    task = Task(outputConfig, signalSources, noiseParams, tcxo, signalFilters,
                groupDelays, bands, generateDebug)

    assert task.outputConfig == outputConfig
    assert task.bands == bands
    assert task.generateDebug == generateDebug
    assert task.groupDelays == groupDelays
    assert task.noiseParams == noiseParams
    assert task.signalFilters == signalFilters
    assert task.signalSources == signalSources
    assert task.tcxo == tcxo
    assert isinstance(task.noise, numpy.ndarray)
    assert task.noise.shape == (outputConfig.N_GROUPS,
                                outputConfig.SAMPLE_BATCH_SIZE)
    assert isinstance(task.signals, numpy.ndarray)
    assert task.signals.shape == (outputConfig.N_GROUPS,
                                  outputConfig.SAMPLE_BATCH_SIZE)
Ejemplo n.º 7
0
def test_Task_computeGroupTimeVectors1():
    '''
  Task object group time vector test
  '''
    outputConfig = NormalRateConfig
    sv0 = GPSSatellite(1)
    sv0.setL1CAEnabled(True)
    signalSources = [sv0]
    noiseParams = NoiseParameters(outputConfig.SAMPLE_RATE_HZ, 1.)
    tcxo = TCXOPoly((1., ))
    signalFilters = [None] * 4
    groupDelays = True
    bands = [outputConfig.GPS.L1]
    generateDebug = False

    task = Task(outputConfig, signalSources, noiseParams, tcxo, signalFilters,
                groupDelays, bands, generateDebug)
    nSamples = 1024
    userTime0_s = 0.
    firstSampleIndex = 1
    task.update(userTime0_s, nSamples, firstSampleIndex)
    userTimeAll_s = task.computeTimeVector()
    result = task.computeGroupTimeVectors(userTimeAll_s)
    assert isinstance(result, list)
    for i in range(outputConfig.N_GROUPS):
        assert (result[i] == userTimeAll_s +
                outputConfig.GROUP_DELAYS[i]).all()
Ejemplo n.º 8
0
def test_Task_createNoise():
    '''
  Task object noise helper test
  '''
    outputConfig = NormalRateConfig
    sv0 = GPSSatellite(1)
    sv0.setL1CAEnabled(True)
    signalSources = [sv0]
    noiseParams = NoiseParameters(outputConfig.SAMPLE_RATE_HZ, 1.)
    tcxo = TCXOPoly((1., ))
    signalFilters = [None] * 4
    groupDelays = False
    bands = [outputConfig.GPS.L1]
    generateDebug = False

    task = Task(outputConfig, signalSources, noiseParams, tcxo, signalFilters,
                groupDelays, bands, generateDebug)
    userTime0_s = 123.
    nSamples = outputConfig.SAMPLE_BATCH_SIZE
    firstSampleIndex = 1
    task.update(userTime0_s, nSamples, firstSampleIndex)

    noiseMatrix = task.createNoise()
    assert isinstance(noiseMatrix, numpy.ndarray)
    assert noiseMatrix.shape == (outputConfig.N_GROUPS, nSamples)
    assert numpy.mean(noiseMatrix) < 0.1
    assert (noiseMatrix != 0.).sum() > 1000
Ejemplo n.º 9
0
def test_Task_computeTcxoVector2():
    '''
  Task object TCXO helper test
  '''
    outputConfig = NormalRateConfig
    sv0 = GPSSatellite(1)
    sv0.setL1CAEnabled(True)
    signalSources = [sv0]
    noiseParams = NoiseParameters(outputConfig.SAMPLE_RATE_HZ, 1.)
    tcxo = TCXOPoly((1., ))
    signalFilters = [None] * 4
    groupDelays = False
    bands = [outputConfig.GPS.L1]
    generateDebug = False

    task = Task(outputConfig, signalSources, noiseParams, tcxo, signalFilters,
                groupDelays, bands, generateDebug)
    userTime0_s = 123.
    nSamples = 1024
    firstSampleIndex = 1
    task.update(userTime0_s, nSamples, firstSampleIndex)

    tcxoVector = task.computeTcxoVector()
    assert isinstance(tcxoVector, numpy.ndarray)
    assert tcxoVector.shape == (1024, )
    assert (tcxoVector != 0.).all()
Ejemplo n.º 10
0
def test_Task_computeTcxoVector0():
    '''
  Task object TCXO helper test
  '''
    outputConfig = NormalRateConfig
    sv0 = GPSSatellite(1)
    sv0.setL1CAEnabled(True)
    signalSources = [sv0]
    noiseParams = NoiseParameters(outputConfig.SAMPLE_RATE_HZ, 1.)
    tcxo = TCXOPoly(())
    signalFilters = [None] * 4
    groupDelays = False
    bands = [outputConfig.GPS.L1]
    generateDebug = False

    task = Task(outputConfig, signalSources, noiseParams, tcxo, signalFilters,
                groupDelays, bands, generateDebug)
    userTime0_s = 123.
    nSamples = 1024
    firstSampleIndex = 1
    task.update(userTime0_s, nSamples, firstSampleIndex)

    tcxo = task.computeTcxoVector()
    # TCXO object is None because TCXO polynomial is empty
    assert tcxo is None
Ejemplo n.º 11
0
        def __call__(self, parser, namespace, values, option_string=None):
            setattr(namespace, self.dest, values)

            if namespace.tcxo_type == "poly":
                coeffs = []
                hasHighOrder = False

                srcA = [
                    namespace.tcxo_a3, namespace.tcxo_a2, namespace.tcxo_a1,
                    namespace.tcxo_a0
                ]
                for a in srcA:
                    if a is not None:
                        coeffs.append(a)
                        hasHighOrder = True
                    elif hasHighOrder:
                        coeffs.append(0.)
                tcxo = TCXOPoly(coeffs)
            elif namespace.tcxo_type == "sine":
                initial = 0.
                ampl = 0.5
                period_s = 1.
                if namespace.tcxo_a0 is not None:
                    ampl = namespace.tcxo_a0
                if namespace.tcxo_a1 is not None:
                    ampl = namespace.tcxo_a1
                if namespace.tcxo_period is not None:
                    period_s = namespace.tcxo_period

                tcxo = TCXOSine(initial, ampl, period_s)
            else:
                raise ValueError("Unsupported TCXO type")
            namespace.tcxo = tcxo
Ejemplo n.º 12
0
def prepareArgsParser():
    '''
  Constructs command line argument parser.

  Returns
  -------
  object
    Command line argument parser object.
  '''
    class AddSv(argparse.Action):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(AddSv, self).__init__(option_strings, dest, **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):
            # Initialize SV list if not yet done
            if namespace.gps_sv is None:
                namespace.gps_sv = []

            # Add SV to the tail of the list.
            if option_string == '--gps-sv':
                sv = GPSSatellite(int(values))
            elif option_string == '--glo-sv':
                sv = GLOSatellite(int(values))
            else:
                raise ValueError("Option value is not supported: %s" %
                                 option_string)
            namespace.gps_sv.append(sv)

            # Reset all configuration parameters
            namespace.l2cl_code_type = '01'
            namespace.ignore_code_doppler = False

            # Doppler
            namespace.doppler_type = "zero"
            namespace.doppler_value = 0.
            namespace.doppler_speed = 0.
            namespace.distance = None
            namespace.tec = 50.
            namespace.doppler_amplitude = 0.
            namespace.doppler_period = 1.
            namespace.symbol_delay = None
            namespace.chip_delay = None

            # Source message data
            namespace.message_type = "zero"
            namespace.message_file = None

            # Amplitude parameters
            namespace.amplitude_type = "poly"
            namespace.amplitude_unis = "snr-db"
            namespace.amplitude_a0 = None
            namespace.amplitude_a1 = None
            namespace.amplitude_a2 = None
            namespace.amplitude_a3 = None
            namespace.amplitude_period = None

    class UpdateSv(argparse.Action):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(UpdateSv, self).__init__(option_strings, dest, **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):
            sv_list = getattr(namespace, "gps_sv")
            if sv_list is None:
                raise ValueError("No SV specified")
            setattr(namespace, self.dest, values)
            # super(UpdateSv, self).__call__(parser, namespace, values, option_string)
            self.doUpdate(sv_list[len(sv_list) - 1], parser, namespace, values,
                          option_string)

        def doUpdate(self, sv, parser, namespace, values, option_string):
            pass

    class UpdateBands(UpdateSv):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(UpdateBands, self).__init__(option_strings, dest, **kwargs)

        def doUpdate(self, sv, parser, namespace, values, option_string):
            l1caEnabled = False
            l2cEnabled = False
            if namespace.bands == "l1ca" or namespace.bands == 'l1':
                l1caEnabled = True
            elif namespace.bands == "l2c" or namespace.bands == 'l2':
                l2cEnabled = True
            elif namespace.bands == "l1ca+l2c" or namespace.bands == 'l1+l2':
                l1caEnabled = True
                l2cEnabled = True
            else:
                raise ValueError()
            if isinstance(sv, GPSSatellite):
                sv.setL2CLCodeType(namespace.l2cl_code_type)
                sv.setL1CAEnabled(l1caEnabled)
                sv.setL2CEnabled(l2cEnabled)
            elif isinstance(sv, GLOSatellite):
                sv.setL1Enabled(l1caEnabled)
                sv.setL2Enabled(l2cEnabled)
            else:
                raise ValueError("Unsupported object type in SV list")

    class UpdateDopplerType(UpdateSv):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(UpdateDopplerType, self).__init__(option_strings, dest,
                                                    **kwargs)

        def doUpdate(self, sv, parser, namespace, values, option_string):
            if isinstance(sv, GPSSatellite):
                if sv.l1caEnabled:
                    signal = signals.GPS.L1CA
                elif sv.l2cEnabled:
                    signal = signals.GPS.L2C
                else:
                    raise ValueError(
                        "Signal band must be specified before doppler")
            elif isinstance(sv, GLOSatellite):
                if sv.isL1Enabled():
                    frequency_hz = signals.GLONASS.L1S[
                        sv.prn].CENTER_FREQUENCY_HZ
                elif sv.isL2Enabled():
                    frequency_hz = signals.GLONASS.L2S[
                        sv.prn].CENTER_FREQUENCY_HZ
                else:
                    raise ValueError(
                        "Signal band must be specified before doppler")
            else:
                raise ValueError(
                    "Signal band must be specified before doppler")

            frequency_hz = signal.CENTER_FREQUENCY_HZ

            # Select distance: either from a distance parameter or from delays
            if namespace.symbol_delay is not None or namespace.chip_delay is not None:
                distance = computeDistanceDelay(namespace.symbol_delay,
                                                namespace.chip_delay, signal)
            else:
                distance = namespace.distance if namespace.distance is not None else 0.

            if namespace.doppler_type == "zero":
                doppler = zeroDoppler(distance, namespace.tec, frequency_hz)
            elif namespace.doppler_type == "const":
                doppler = constDoppler(distance, namespace.tec, frequency_hz,
                                       namespace.doppler_value)
            elif namespace.doppler_type == "linear":
                doppler = linearDoppler(distance, namespace.tec, frequency_hz,
                                        namespace.doppler_value,
                                        namespace.doppler_speed)
            elif namespace.doppler_type == "sine":
                doppler = sineDoppler(distance, namespace.tec, frequency_hz,
                                      namespace.doppler_value,
                                      namespace.doppler_amplitude,
                                      namespace.doppler_period)
            else:
                raise ValueError("Unsupported doppler type")
            sv.doppler = doppler

    class DisableCodeDoppler(UpdateSv):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(DisableCodeDoppler, self).__init__(option_strings, dest,
                                                     **kwargs)

        def doUpdate(self, sv, parser, namespace, values, option_string):
            print "CD=", namespace.ignore_code_doppler, values, option_string
            sv.setCodeDopplerIgnored(namespace.ignore_code_doppler)

    class UpdateAmplitudeType(UpdateSv):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(UpdateAmplitudeType, self).__init__(option_strings, dest,
                                                      **kwargs)

        def doUpdate(self, sv, parser, namespace, values, option_string):
            amplitude_units = AMP_MAP[namespace.amplitude_units]

            if namespace.amplitude_type == "poly":
                coeffs = []
                hasHighOrder = False

                srcA = [
                    namespace.amplitude_a3, namespace.amplitude_a2,
                    namespace.amplitude_a1, namespace.amplitude_a0
                ]
                for a in srcA:
                    if a is not None:
                        coeffs.append(a)
                        hasHighOrder = True
                    elif hasHighOrder:
                        coeffs.append(0.)
                amplitude = AmplitudePoly(amplitude_units, tuple(coeffs))
            elif namespace.amplitude_type == "sine":
                initial = 1.
                ampl = 0.5
                period_s = 1.
                if namespace.amplitude_a0 is not None:
                    initial = namespace.amplitude_a0
                if namespace.amplitude_a1 is not None:
                    ampl = namespace.amplitude_a1
                if namespace.amplitude_period is not None:
                    period_s = namespace.amplitude_period

                amplitude = AmplitudeSine(amplitude_units, initial, ampl,
                                          period_s)
            else:
                raise ValueError("Unsupported amplitude type")
            sv.setAmplitude(amplitude)

    class UpdateTcxoType(argparse.Action):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(UpdateTcxoType, self).__init__(option_strings, dest,
                                                 **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):
            setattr(namespace, self.dest, values)

            if namespace.tcxo_type == "poly":
                coeffs = []
                hasHighOrder = False

                srcA = [
                    namespace.tcxo_a3, namespace.tcxo_a2, namespace.tcxo_a1,
                    namespace.tcxo_a0
                ]
                for a in srcA:
                    if a is not None:
                        coeffs.append(a)
                        hasHighOrder = True
                    elif hasHighOrder:
                        coeffs.append(0.)
                tcxo = TCXOPoly(coeffs)
            elif namespace.tcxo_type == "sine":
                initial = 0.
                ampl = 0.5
                period_s = 1.
                if namespace.tcxo_a0 is not None:
                    ampl = namespace.tcxo_a0
                if namespace.tcxo_a1 is not None:
                    ampl = namespace.tcxo_a1
                if namespace.tcxo_period is not None:
                    period_s = namespace.tcxo_period

                tcxo = TCXOSine(initial, ampl, period_s)
            else:
                raise ValueError("Unsupported TCXO type")
            namespace.tcxo = tcxo

    class UpdateMessageType(UpdateSv):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(UpdateMessageType, self).__init__(option_strings, dest,
                                                    **kwargs)

        def doUpdate(self, sv, parser, namespace, values, option_string):
            if namespace.message_type == "zero":
                messageL1 = ConstMessage(1)
                messageL2 = messageL1
            elif namespace.message_type == "one":
                messageL1 = ConstMessage(-1)
                messageL2 = messageL1
            elif namespace.message_type == "zero+one":
                messageL1 = ZeroOneMessage()
                messageL2 = messageL1
            elif namespace.message_type == "crc":
                if isinstance(sv, GPSSatellite):
                    messageL1 = LNavMessage(sv.prn)
                    messageL2 = CNavMessage(sv.prn)
                elif isinstance(sv, GLOSatellite):
                    messageL1 = GLOMessage(sv.prn)
                    messageL2 = GLOMessage(sv.prn)
                else:
                    raise ValueError(
                        "Message type is not supported for a satellite type")
            else:
                raise ValueError("Unsupported message type")
            if isinstance(sv, GPSSatellite):
                sv.setL1CAMessage(messageL1)
                sv.setL2CMessage(messageL2)
            elif isinstance(sv, GLOSatellite):
                sv.setL1Message(messageL1)
                sv.setL2Message(messageL2)
            else:
                raise ValueError("Unsupported object type in SV list")

    class UpdateMessageFile(UpdateSv):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(UpdateMessageFile, self).__init__(option_strings, dest,
                                                    **kwargs)

        def doUpdate(self, sv, parser, namespace, values, option_string):
            data = numpy.fromfile(namespace.message_file, dtype=numpy.uint8)
            namespace.message_file.close()
            data = numpy.unpackbits(data)
            data = numpy.asarray(data, dtype=numpy.int8)
            data <<= 1
            data -= 1
            numpy.negative(data, out=data)
            message = BlockMessage(data)

            if isinstance(sv, GPSSatellite):
                sv.setL1CAMessage(message)
                sv.setL2CMessage(message)
            elif isinstance(sv, GLOSatellite):
                sv.setL1Message(message)
                sv.setL2Message(message)
            else:
                raise ValueError("Unsupported object type in SV list")

    class SaveConfigAction(argparse.Action):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(SaveConfigAction, self).__init__(option_strings, dest,
                                                   **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):

            gps_sv = namespace.gps_sv

            encoded_gps_sv = [satelliteFO.toMapForm(sv) for sv in gps_sv]

            data = {
                'type': 'Namespace',
                'gps_sv': encoded_gps_sv,
                'profile': namespace.profile,
                'encoder': namespace.encoder,
                'generate': namespace.generate,
                'noise_sigma': namespace.noise_sigma,
                'filter_type': namespace.filter_type,
                'tcxo': tcxoFO.toMapForm(namespace.tcxo),
                'group_delays': namespace.group_delays
            }
            json.dump(data, values, indent=2)
            values.close()
            namespace.no_run = True

    class LoadConfigAction(argparse.Action):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            super(LoadConfigAction, self).__init__(option_strings, dest,
                                                   **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):
            loaded = json.load(values)
            namespace.profile = loaded['profile']
            namespace.encoder = loaded['encoder']
            namespace.generate = loaded['generate']
            namespace.noise_sigma = loaded['noise_sigma']
            namespace.filter_type = loaded['filter_type']
            namespace.tcxo = tcxoFO.fromMapForm(loaded['tcxo'])
            namespace.gps_sv = [
                satelliteFO.fromMapForm(sv) for sv in loaded['gps_sv']
            ]
            namespace.group_delays = loaded['group_delays']
            values.close()

    parser = argparse.ArgumentParser(description="Signal generator",
                                     usage='%(prog)s [options]')
    parser.add_argument('--gps-sv',
                        default=[],
                        help='Enable GPS satellite',
                        action=AddSv)
    parser.add_argument('--glo-sv',
                        default=[],
                        help='Enable GLONASS satellite',
                        action=AddSv)
    parser.add_argument(
        '--bands',
        default="l1ca",
        choices=["l1ca", "l2c", "l1ca+l2c", "l1", "l2", "l1+l2"],
        help="Signal bands to enable",
        action=UpdateBands)
    dopplerGrp = parser.add_argument_group("Doppler Control",
                                           "Doppler control parameters")
    dopplerGrp.add_argument('--doppler-type',
                            default="zero",
                            choices=["zero", "const", "linear", "sine"],
                            help="Configure doppler type",
                            action=UpdateDopplerType)
    dopplerGrp.add_argument('--doppler-value',
                            type=float,
                            help="Doppler shift in hertz (initial)",
                            action=UpdateDopplerType)
    dopplerGrp.add_argument('--doppler-speed',
                            type=float,
                            help="Doppler shift change in hertz/second",
                            action=UpdateDopplerType)

    delayGrp = parser.add_argument_group("Signal Delay Control",
                                         "Signal delay control parameters")
    # Common delays: ionosphere
    delayGrp.add_argument('--tec',
                          type=float,
                          help="Ionosphere TEC for signal delay"
                          " (electrons per meter^2)",
                          action=UpdateDopplerType)
    # Distance control over direct parameter
    delayGrp.add_argument('--distance',
                          type=float,
                          help="Distance in meters for signal delay (initial)",
                          action=UpdateDopplerType)
    # Distance control over delays
    delayGrp.add_argument('--symbol-delay',
                          type=float,
                          help="Initial symbol index",
                          action=UpdateDopplerType)
    delayGrp.add_argument('--chip-delay',
                          type=float,
                          help="Initial chip index",
                          action=UpdateDopplerType)
    dopplerGrp.add_argument('--doppler-amplitude',
                            type=float,
                            help="Doppler change amplitude (hertz)",
                            action=UpdateDopplerType)
    dopplerGrp.add_argument('--doppler-period',
                            type=float,
                            help="Doppler change period (seconds)",
                            action=UpdateDopplerType)
    dopplerGrp.add_argument(
        '--ignore-code-doppler',
        type=bool,
        help="Disable doppler for code and data processing",
        action=DisableCodeDoppler)
    amplitudeGrp = parser.add_argument_group("Amplitude Control",
                                             "Amplitude control parameters")
    amplitudeGrp.add_argument(
        '--amplitude-type',
        default="poly",
        choices=["poly", "sine"],
        help="Configure amplitude type: polynomial or sine.",
        action=UpdateAmplitudeType)
    amplitudeGrp.add_argument('--amplitude-units',
                              default="snr-db",
                              choices=["snr-db", "snr", "amplitude", "power"],
                              help="Configure amplitude units: SNR in dB; SNR;"
                              " amplitude; power.",
                              action=UpdateAmplitudeType)
    amplitudeGrp.add_argument('--amplitude-a0',
                              type=float,
                              help="Amplitude coefficient (a0 for polynomial;"
                              " offset for sine)",
                              action=UpdateAmplitudeType)
    amplitudeGrp.add_argument('--amplitude-a1',
                              type=float,
                              help="Amplitude coefficient (a1 for polynomial,"
                              " amplitude for size)",
                              action=UpdateAmplitudeType)
    amplitudeGrp.add_argument('--amplitude-a2',
                              type=float,
                              help="Amplitude coefficient (a2 for polynomial)",
                              action=UpdateAmplitudeType)
    amplitudeGrp.add_argument('--amplitude-a3',
                              type=float,
                              help="Amplitude coefficient (a3 for polynomial)",
                              action=UpdateAmplitudeType)
    amplitudeGrp.add_argument('--amplitude-period',
                              type=float,
                              help="Amplitude period in seconds for sine",
                              action=UpdateAmplitudeType)
    messageGrp = parser.add_argument_group("Message Data Control",
                                           "Message data control parameters")
    messageGrp.add_argument('--message-type',
                            default="zero",
                            choices=["zero", "one", "zero+one", "crc"],
                            help="Message type",
                            action=UpdateMessageType)
    messageGrp.add_argument('--message-file',
                            type=argparse.FileType('rb'),
                            help="Source file for message contents.",
                            action=UpdateMessageFile)
    messageGrp.add_argument('--l2cl-code-type',
                            default='01',
                            choices=['01', '1', '0'],
                            help="GPS L2 CL code type",
                            action=UpdateBands)
    parser.add_argument('--filter-type',
                        default='none',
                        choices=['none', 'lowpass', 'bandpass'],
                        help="Enable filter")
    parser.add_argument('--noise-sigma',
                        type=float,
                        default=1.,
                        help="Noise sigma for noise generation")
    tcxoGrp = parser.add_argument_group("TCXO Control",
                                        "TCXO control parameters")

    tcxoGrp.add_argument('--tcxo-type',
                         choices=["poly", "sine"],
                         help="TCXO drift type",
                         action=UpdateTcxoType)
    tcxoGrp.add_argument('--tcxo-a0',
                         type=float,
                         help="TCXO a0 coefficient for polynomial TCXO drift"
                         " or initial shift for sine TCXO drift",
                         action=UpdateTcxoType)
    tcxoGrp.add_argument('--tcxo-a1',
                         type=float,
                         help="TCXO a1 coefficient for polynomial TCXO drift"
                         " or amplitude for sine TCXO drift",
                         action=UpdateTcxoType)
    tcxoGrp.add_argument('--tcxo-a2',
                         type=float,
                         help="TCXO a2 coefficient for polynomial TCXO drift",
                         action=UpdateTcxoType)
    tcxoGrp.add_argument('--tcxo-a3',
                         type=float,
                         help="TCXO a3 coefficient for polynomial TCXO drift",
                         action=UpdateTcxoType)
    tcxoGrp.add_argument('--tcxo-period',
                         type=float,
                         help="TCXO period in seconds for sine TCXO drift",
                         action=UpdateTcxoType)
    parser.add_argument(
        '--group-delays',
        type=bool,
        help="Enable/disable group delays simulation between bands")
    parser.add_argument('--debug',
                        type=argparse.FileType('wb'),
                        help="Debug output file")
    parser.add_argument('--generate',
                        type=float,
                        default=3.,
                        help="Amount of data to generate, in seconds")
    parser.add_argument('--encoder',
                        default="2bits",
                        choices=["1bit", "2bits"],
                        help="Output data format")
    parser.add_argument('--output',
                        type=argparse.FileType('wb'),
                        help="Output file name")
    parser.add_argument(
        '--profile',
        default="normal_rate",
        choices=["low_rate", "normal_rate", "high_rate", "custom_rate"],
        help="Output profile configuration")
    parser.add_argument('-j',
                        '--jobs',
                        type=int,
                        default=0,
                        help="Use parallel threads")

    parser.add_argument(
        '--save-config',
        type=argparse.FileType('wt'),
        help="Store configuration into file (implies --no-run)",
        action=SaveConfigAction)

    parser.add_argument('--load-config',
                        type=argparse.FileType('rt'),
                        help="Restore configuration from file",
                        action=LoadConfigAction)

    parser.add_argument('--no-run',
                        action="store_true",
                        default=False,
                        help="Do not generate output.")

    if sys.stdout.isatty():
        progress_bar_default = 'stdout'
    elif sys.stderr.isatty():
        progress_bar_default = 'stderr'
    else:
        progress_bar_default = 'none'
    parser.add_argument("--progress-bar",
                        metavar='FLAG',
                        choices=['stdout', 'stderr', 'none'],
                        default=progress_bar_default,
                        help="Show progress bar. Default is '%s'" %
                        progress_bar_default)

    parser.set_defaults(tcxo=TCXOPoly(()))

    return parser