def test_superposition(wave: WaveformGenerator, scope: Oscilloscope): frequency = 1000 amplitude1 = 2 amplitude2 = 1 def super_sine(x): return amplitude1 * np.sin(x) + amplitude2 * np.sin(5 * x) wave.load_function("SI1", super_sine, [0, 2 * np.pi]) wave.generate("SI1", frequency) time.sleep(0.1) x, y = scope.capture(1, 10000, 1, trigger=0) def expected_f(x, amplitude1, amplitude2, frequency, phase): return amplitude1 * np.sin(2 * np.pi * frequency * x + phase) + amplitude2 * np.sin( 5 * (2 * np.pi * frequency * x + phase)) amplitude1 = 2 amplitude2 = 1 guess = [amplitude1, amplitude2, frequency, 0] [amplitude1_est, amplitude2_est, frequency_est, phase_est], _ = curve_fit(expected_f, x * MICROSECONDS, y, guess) assert amplitude1_est == pytest.approx(amplitude1, rel=RELTOL) assert amplitude2_est == pytest.approx(amplitude2, rel=RELTOL) assert frequency_est == pytest.approx(frequency, rel=RELTOL) coeff_of_det = r_squared( y, expected_f(x * MICROSECONDS, amplitude1_est, amplitude2_est, frequency_est, phase_est), ) assert coeff_of_det >= GOOD_FIT
def scope(handler): """Return an Oscilloscope instance. In integration test mode, this function also enables the analog output. """ if not isinstance(handler, MockHandler): wave = WaveformGenerator(handler) wave.generate(["SI1", "SI2"], FREQUENCY) handler._logging = True return Oscilloscope(handler)
def test_wave_load_table(wave, mocker): wavegen = WaveformGenerator(mocker.Mock()) wavegen.load_function("SI1", "tria") def tria(x): return AnalogOutput.RANGE[1] * (abs(x % 4 - 2) - 1) span = [-1, 3] x = np.arange(span[0], span[1], (span[1] - span[0]) / 512) table = json.dumps(tria(x).tolist()) cli.cmdline(["wave", "load", "SI2", "--table", table]) assert AnalogOutput("SI1").waveform_table == AnalogOutput( "SI2").waveform_table
def test_wave_load_tablefile(wave, mocker, tmp_path): wavegen = WaveformGenerator(mocker.Mock()) wavegen.load_function("SI1", "tria") def tria(x): return AnalogOutput.RANGE[1] * (abs(x % 4 - 2) - 1) span = [-1, 3] x = np.arange(span[0], span[1], (span[1] - span[0]) / 512) table_tmp_json = str(tmp_path / "table.json") with open(table_tmp_json, "w") as json_file: json.dump(tria(x).tolist(), json_file) cli.cmdline(["wave", "load", "SI2", "--table-file", table_tmp_json]) assert AnalogOutput("SI1").waveform_table == AnalogOutput( "SI2").waveform_table
def test_sine_phase(wave: WaveformGenerator, scope: Oscilloscope): frequency = 500 phase = 90 wave.load_function("SI1", "sine") wave.load_function("SI2", "sine") wave.generate(["SI1", "SI2"], frequency, phase) time.sleep(0.1) x, y1, y2 = scope.capture(2, 5000, 2, trigger=0) def expected_f(x, amplitude, frequency, phase): return amplitude * np.sin(2 * np.pi * frequency * x + phase) guess1 = [3.3, frequency, 0] [_, _, phase1_est], _ = curve_fit(expected_f, x * MICROSECONDS, y1, guess1) guess2 = [3.3, frequency, phase * np.pi / 180] [_, _, phase2_est], _ = curve_fit(expected_f, x * MICROSECONDS, y2, guess2) assert phase2_est - phase1_est == pytest.approx(phase * np.pi / 180, rel=RELTOL)
def wave(handler: SerialHandler, args: argparse.Namespace): """Generate or load wave. Parameters ---------- handler : :class:`Handler` Serial interface for communicating with the PSLab device. args : :class:`argparse.Namespace` Parsed arguments. """ waveform_generator = WaveformGenerator(handler) if args.wave_function == "gen": waveform_generator.generate( channels=args.channel, frequency=args.frequency, phase=args.phase, ) elif args.wave_function == "load": if args.table is not None: table = args.table elif args.table_file is not None: with open(args.table_file) as table_file: table = json.load(table_file) x = np.arange(0, len(table), len(table) / 512) y = [table[int(i)] for i in x] waveform_generator.load_table(channel=args.channel, points=y)
def __init__(self): super().__init__() self.logic_analyzer = LogicAnalyzer(device=self) self.oscilloscope = Oscilloscope(device=self) self.waveform_generator = WaveformGenerator(device=self) self.pwm_generator = PWMGenerator(device=self) self.multimeter = Multimeter(device=self) self.power_supply = PowerSupply(device=self) self.i2c = I2CMaster(device=self) self.nrf = NRF24L01(device=self) if "V6" in self.version: # Set the built-in WS2812B to green :) self.rgb_led([0, 20, 0])
def test_sine_wave(wave: WaveformGenerator, scope: Oscilloscope): frequency = 500 wave.load_function("SI1", "sine") wave.generate("SI1", frequency) time.sleep(0.1) x, y = scope.capture(1, 10000, 1, trigger=0) def expected_f(x, amplitude, frequency, phase): return amplitude * np.sin(2 * np.pi * frequency * x + phase) amplitude = 3.3 guess = [amplitude, frequency, 0] [amplitude_est, frequency_est, phase_est], _ = curve_fit(expected_f, x * MICROSECONDS, y, guess) assert amplitude_est == pytest.approx(amplitude, rel=RELTOL) assert frequency_est == pytest.approx(frequency, rel=RELTOL) coeff_of_det = r_squared( y, expected_f(x * MICROSECONDS, amplitude_est, frequency_est, phase_est)) assert coeff_of_det >= GOOD_FIT
def __init__( self, port: str = None, baudrate: int = 1000000, timeout: float = 1.0, ): super().__init__(port, baudrate, timeout) self.logic_analyzer = LogicAnalyzer(device=self) self.oscilloscope = Oscilloscope(device=self) self.waveform_generator = WaveformGenerator(device=self) self.pwm_generator = PWMGenerator(device=self) self.multimeter = Multimeter(device=self) self.power_supply = PowerSupply(device=self) self.i2c = I2CMaster(device=self) self.nrf = NRF24L01(device=self) if "V6" in self.version: # Set the built-in WS2812B to green :) self.rgb_led([0, 20, 0])
def wave(handler: SerialHandler) -> WaveformGenerator: handler._logging = True return WaveformGenerator(handler)
def test_dimension_mismatch(wave: WaveformGenerator): with pytest.raises(ValueError): wave.generate("SI2", [500, 1000])
def test_high_frequency_warning(caplog: LogCaptureFixture, wave: WaveformGenerator): wave.generate("SI1", 1e4) assert "Frequencies above"
def test_low_frequency_error(wave: WaveformGenerator): with pytest.raises(ValueError): wave.generate("SI1", 0.05)
def test_low_frequency_warning(caplog: LogCaptureFixture, wave: WaveformGenerator): wave.generate("SI1", 1) assert "AC coupling" in caplog.text