Esempio n. 1
0
def tb1(epoch_output):
    tone = ToneFactory(fs=epoch_output.fs,
                       level=0,
                       frequency=100,
                       calibration=epoch_output.calibration)
    envelope = Cos2EnvelopeFactory(fs=epoch_output.fs,
                                   start_time=0,
                                   rise_time=0.5,
                                   duration=5,
                                   input_factory=tone)
    return envelope
Esempio n. 2
0
def test_token_generation():
    calibration = InterpCalibration.as_attenuation()

    fs = 100e3

    tone_factory = ToneFactory(fs=fs,
                               level=0,
                               frequency=100,
                               calibration=calibration)
    factory = Cos2EnvelopeFactory(fs=fs,
                                  start_time=0,
                                  rise_time=0,
                                  duration=1,
                                  input_factory=tone_factory)

    assert tone_factory.get_duration() == np.inf
    assert factory.get_duration() == 1

    samples = int(fs)

    # test what happens when we get more samples than are required to generate
    # the token (should be zero-padded).
    waveform = factory.next(samples * 2)
    assert tone_factory.is_complete() is False
    assert factory.is_complete() is True
    assert waveform.shape == (samples * 2, )
    rms1 = np.mean(waveform[:samples]**2)**0.5
    rms2 = np.mean(waveform[samples:]**2)**0.5
    assert rms1 == pytest.approx(1)
    assert rms2 == 0

    # test what happens when we request even more samples (should continue to
    # be zero-padded)
    waveform = factory.next(samples * 2)
    rms1 = np.mean(waveform[:samples]**2)**0.5
    rms2 = np.mean(waveform[samples:]**2)**0.5
    assert rms1 == 0
    assert rms2 == 0

    # now reset the token and start over
    factory.reset()
    assert tone_factory.is_complete() is False
    assert factory.is_complete() is False
    waveform = factory.next(samples)
    assert tone_factory.is_complete() is False
    assert factory.is_complete() is True
    assert waveform.shape == (samples, )
    rms = np.mean(waveform**2)**0.5
    assert rms == pytest.approx(1)

    t = np.arange(fs, dtype=np.float32) / fs
    expected = np.cos(2 * np.pi * t * 100) * np.sqrt(2)
    np.testing.assert_allclose(waveform, expected, atol=2e-4)
Esempio n. 3
0
def tone_pip():
    calibration = FlatCalibration.as_attenuation()
    tone = ToneFactory(100e3, 0, 250, 0, 1, calibration)
    envelope = Cos2EnvelopeFactory(100e3, 0, 0.5e-3, 5e-3, tone)
    return envelope
Esempio n. 4
0
def tone2():
    calibration = FlatCalibration.as_attenuation()
    return ToneFactory(100e3, 0, 10, 0, 1, calibration)
Esempio n. 5
0
def tone1():
    # fs, level, frequency, phase, polarity, calibration
    calibration = FlatCalibration.as_attenuation()
    return ToneFactory(100e3, 0, 5, 0, 1, calibration)
Esempio n. 6
0
def tone_power(engine, frequencies, ao_channel_name, ai_channel_names, gains=0,
               vrms=1, repetitions=2, min_snr=None, max_thd=None, thd_harmonics=3,
               duration=0.1, trim=0.01, iti=0.01, debug=False):
    '''
    Given a single output, measure response in multiple input channels.

    Parameters
    ----------
    TODO

    Returns
    -------
    result : pandas DataFrame
        Dataframe will be indexed by output channel name and frequency. Columns
        will be rms (in V), snr (in DB) and thd (in percent).
    '''
    if not isinstance(ao_channel_name, str):
        raise ValueError('Can only specify one output channel')

    from psi.controller.api import ExtractEpochs, FIFOSignalQueue

    frequencies = np.asarray(frequencies)
    calibration = FlatCalibration.as_attenuation(vrms=vrms)

    # Create a copy of the engine containing only the channels required for
    # calibration.
    channel_names = ai_channel_names + [ao_channel_name]
    cal_engine = engine.clone(channel_names)
    ao_channel = cal_engine.get_channel(ao_channel_name)
    ai_channels = [cal_engine.get_channel(name) for name in ai_channel_names]

    ao_fs = ao_channel.fs
    ai_fs = ai_channels[0].fs

    # Ensure that input channels are synced to the output channel
    device_name = ao_channel.device_name
    ao_channel.start_trigger = ''
    for channel in ai_channels:
        channel.start_trigger = f'/{device_name}/ao/StartTrigger'

    samples = int(ao_fs*duration)

    if np.isscalar(gains):
        gains = [gains] * len(frequencies)

    # Build the signal queue
    queue = FIFOSignalQueue()
    queue.set_fs(ao_fs)
    max_sf = 0
    for frequency, gain in zip(frequencies, gains):
        factory = ToneFactory(ao_fs, gain, frequency, 0, 1, calibration)
        waveform = factory.next(samples)
        md = {'gain': gain, 'frequency': frequency}
        queue.append(waveform, repetitions, iti, metadata=md)
        sf = calibration.get_sf(frequency, gain) * np.sqrt(2)
        max_sf = max(max_sf, sf)
    ao_channel.expected_range = (-max_sf*1.1, max_sf*1.1)

    factory = SilenceFactory(ao_fs, calibration)
    waveform = factory.next(samples)
    md = {'gain': -400, 'frequency': 0}
    queue.append(waveform, repetitions, iti, metadata=md)

    # Add the queue to the output channel
    output = ao_channel.add_queued_epoch_output(queue, auto_decrement=True)

    # Activate the output so it begins as soon as acquisition begins
    output.activate(0)

    # Create a dictionary of lists. Each list maps to an individual input
    # channel and will be used to accumulate the epochs for that channel.
    data = {ai_channel.name: [] for ai_channel in ai_channels}
    samples = {ai_channel.name: [] for ai_channel in ai_channels}

    def accumulate(epochs, epoch):
        epochs.extend(epoch)

    for ai_channel in ai_channels:
        cb = partial(accumulate, data[ai_channel.name])
        epoch_input = ExtractEpochs(epoch_size=duration)
        queue.connect(epoch_input.queue.append)
        epoch_input.add_callback(cb)
        ai_channel.add_input(epoch_input)
        ai_channel.add_callback(samples[ai_channel.name].append)

    cal_engine.start()
    while not epoch_input.complete:
        time.sleep(0.1)
    cal_engine.stop()

    result = []
    for ai_channel in ai_channels:
        # Process data from channel
        epochs = [epoch['signal'][np.newaxis] for epoch in data[ai_channel.name]]
        signal = np.concatenate(epochs)
        signal.shape = [-1, repetitions] + list(signal.shape[1:])

        if trim != 0:
            trim_samples = round(ai_channel.fs * trim)
            signal = signal[..., trim_samples:-trim_samples]

        # Loop through each frequency (silence will always be the last one)
        silence = signal[-1]
        signal = signal[:-1]
        channel_result = []
        for f, s in zip(frequencies, signal):
            f_result = process_tone(ai_channel.fs, s, f, min_snr, max_thd,
                                    thd_harmonics, silence)
            f_result['frequency'] = f
            if debug:
                f_result['waveform'] = s
            channel_result.append(f_result)

        df = pd.DataFrame(channel_result)
        df['channel_name'] = ai_channel.name
        result.append(df)

    return pd.concat(result).set_index(['channel_name', 'frequency'])