diff --git a/scope.py b/scope.py index a13e564..9e85a29 100755 --- a/scope.py +++ b/scope.py @@ -35,7 +35,7 @@ class Scope(vm.VirtualMachine): @staticmethod def _analog_map_func(ks, low, high): - return ks[0] + ks[1]*low + ks[2]*high + return ks[0] + ks[1]*low + ks[2]*high async def setup(self): Log.info("Resetting scope") @@ -57,7 +57,7 @@ class Scope(vm.VirtualMachine): self.trigger_timeout_tick = 6.4e-6 self.trigger_low = -7.517 self.trigger_high = 10.816 - #await self.load_params() XXX switch this off until I understand EEPROM better + # await self.load_params() XXX switch this off until I understand EEPROM better self._generator_running = False Log.info("Initialised scope, revision: {}".format(revision)) @@ -75,14 +75,13 @@ class Scope(vm.VirtualMachine): params.append(await self.read_eeprom(i+70)) params = struct.unpack('= 20 and ticks < 65536: - sample_width = 2 - buffer_width = 6*1024 - dump_mode = vm.DumpMode.Native - if 'A' in channels and 'B' in channels: - trace_mode = vm.TraceMode.MacroChop - buffer_mode = vm.BufferMode.MacroChop - else: - trace_mode = vm.TraceMode.Macro - buffer_mode = vm.BufferMode.Macro - elif ticks >= 15 and ticks < 40: - sample_width = 1 - buffer_width = 12*1024 - dump_mode = vm.DumpMode.Raw - if 'A' in channels and 'B' in channels: - trace_mode = vm.TraceMode.AnalogChop - buffer_mode = vm.BufferMode.Chop - else: - trace_mode = vm.TraceMode.Analog - buffer_mode = vm.BufferMode.Single - elif ticks >= 8 and ticks < 15: - sample_width = 1 - buffer_width = 12*1024 - dump_mode = vm.DumpMode.Raw - if 'A' in channels and 'B' in channels: - trace_mode = vm.TraceMode.AnalogFastChop - buffer_mode = vm.BufferMode.Chop - else: - trace_mode = vm.TraceMode.AnalogFast - buffer_mode = vm.BufferMode.Single - elif ticks >= 2 and ticks < 8: - if ticks > 5: - ticks = 5 - sample_width = 1 - buffer_width = 12*1024 - dump_mode = vm.DumpMode.Raw - if 'A' in channels and 'B' in channels: - trace_mode = vm.TraceMode.AnalogShotChop - buffer_mode = vm.BufferMode.Chop - else: - trace_mode = vm.TraceMode.AnalogShot - buffer_mode = vm.BufferMode.Single + for clock_mode in vm.ClockModes: + if clock_mode.dual == dual and ticks in range(clock_mode.clock_low, clock_mode.clock_high + 1): + break else: raise RuntimeError("Unsupported clock period: {}".format(ticks)) + if clock_mode.clock_max is not None and ticks > clock_mode.clock_max: + ticks = clock_mode.clock_max nsamples = int(round(period / ticks / nsamples_multiplier / self.capture_clock_period)) total_samples = nsamples * nsamples_multiplier + buffer_width = self.capture_buffer_size // clock_mode.sample_width assert total_samples <= buffer_width - + if raw: lo, hi = low, high else: @@ -188,7 +153,8 @@ class Scope(vm.VirtualMachine): analog_enable |= 2 async with self.transaction(): - await self.set_registers(TraceMode=trace_mode, BufferMode=buffer_mode, SampleAddress=0, ClockTicks=ticks, ClockScale=1, + await self.set_registers(TraceMode=clock_mode.TraceMode, BufferMode=clock_mode.BufferMode, + SampleAddress=0, ClockTicks=ticks, ClockScale=1, TraceIntro=total_samples//2, TraceOutro=total_samples//2, TraceDelay=0, Timeout=int(round((period*5 if timeout is None else timeout) / self.trigger_timeout_tick)), TriggerMask=0x7f, TriggerLogic=0x80, TriggerLevel=trigger_level, SpockOption=spock_option, @@ -206,13 +172,13 @@ class Scope(vm.VirtualMachine): traces = {} for dump_channel, channel in enumerate(sorted(channels)): async with self.transaction(): - await self.set_registers(SampleAddress=(address - nsamples) * nsamples_multiplier % buffer_width, - DumpMode=dump_mode, DumpChan=dump_channel, + await self.set_registers(SampleAddress=(address - nsamples) * nsamples_multiplier % buffer_width, + DumpMode=clock_mode.DumpMode, DumpChan=dump_channel, DumpCount=nsamples, DumpRepeat=1, DumpSend=1, DumpSkip=0) await self.issue_program_spock_registers() await self.issue_analog_dump_binary() - data = await self._reader.readexactly(nsamples * sample_width) - if sample_width == 2: + data = await self._reader.readexactly(nsamples * clock_mode.sample_width) + if clock_mode.sample_width == 2: if raw: trace = [(value / 65536 + 0.5) for value in struct.unpack('>{}h'.format(nsamples), data)] else: @@ -227,7 +193,7 @@ class Scope(vm.VirtualMachine): return traces async def start_generator(self, frequency, waveform='sine', wavetable=None, ratio=0.5, vpp=None, offset=0, - min_samples=50, max_error=1e-4): + min_samples=50, max_error=1e-4): if vpp is None: vpp = self.awg_maximum_voltage possible_params = [] @@ -260,7 +226,7 @@ class Scope(vm.VirtualMachine): Ratio=nwaves * self.awg_wavetable_size / size, Index=0, Address=0, Size=size) await self.issue_translate_wavetable() - await self.set_registers(Cmd=2, Mode=0, Clock=clock, Modulo=size, + await self.set_registers(Cmd=2, Mode=0, Clock=clock, Modulo=size, Mark=10, Space=1, Rest=0x7f00, Option=0x8004) await self.issue_control_waveform_generator() await self.set_registers(KitchenSinkB=vm.KitchenSinkB.WaveformGeneratorEnable) @@ -297,7 +263,6 @@ class Scope(vm.VirtualMachine): raise RuntimeError("Error writing EEPROM byte") async def calibrate(self, n=33): - global data import numpy as np from scipy.optimize import leastsq, least_squares items = [] @@ -325,7 +290,7 @@ class Scope(vm.VirtualMachine): result = least_squares(f, self.analog_params, args=items.T[:4], bounds=([0, -np.inf, 250, 0, 0], [np.inf, np.inf, 350, np.inf, np.inf])) if result.success in range(1, 5): self.analog_params = tuple(result.x) - offset = items[:,4].mean() + offset = items[:, 4].mean() self.analog_offsets = {'A': -offset, 'B': +offset} else: Log.warning("Calibration failed: {}".format(result.message)) diff --git a/streams.py b/streams.py index e6833a0..17e95bd 100644 --- a/streams.py +++ b/streams.py @@ -15,6 +15,8 @@ class SerialStream: self._connection = serial.Serial(self._device, timeout=0, write_timeout=0, **kwargs) self._loop = loop if loop is not None else asyncio.get_event_loop() self._input_buffer = bytes() + self._output_buffer = bytes() + self._output_wait = None def __repr__(self): return '<{}:{}>'.format(self.__class__.__name__, self._device) @@ -23,22 +25,25 @@ class SerialStream: self._connection.close() self._connection = None - async def write(self, data): - while data: - n = await self._write(data) - data = data[n:] - - def _write(self, data): - future = asyncio.Future() - self._loop.add_writer(self._connection, self._feed_data, data, future) - return future - - def _feed_data(self, data, future): - n = self._connection.write(data) - Log.debug('Write {}'.format(repr(data[:n]))) - future.set_result(n) - self._loop.remove_writer(self._connection) + def write(self, data): + self._output_buffer += data + if self._output_wait is None: + self._output_wait = asyncio.Future() + self._loop.add_writer(self._connection, self._feed_data) + + async def drain(self): + if self._output_wait is not None: + await self._output_wait + def _feed_data(self): + n = self._connection.write(self._output_buffer) + Log.debug('Write {}'.format(repr(self._output_buffer[:n]))) + self._output_buffer = self._output_buffer[n:] + if not self._output_buffer: + self._loop.remove_writer(self._connection) + self._output_wait.set_result(None) + self._output_wait = None + async def read(self, n=None): while True: if self._input_buffer: diff --git a/vm.py b/vm.py index 6f6f33f..684e1ed 100644 --- a/vm.py +++ b/vm.py @@ -1,5 +1,6 @@ import asyncio +from collections import namedtuple from enum import IntEnum import logging import struct @@ -136,6 +137,19 @@ class KitchenSinkB(IntEnum): AnalogFilterEnable = 0x80 WaveformGeneratorEnable = 0x40 +ClockMode = namedtuple('ClockMode', ('clock_low', 'clock_high', 'clock_max', 'dual', 'sample_width', + 'TraceMode', 'BufferMode', 'DumpMode')) + +ClockModes = [ + ClockMode(40, 65536, None, False, 2, TraceMode.Macro, BufferMode.Macro, DumpMode.Native), + ClockMode(40, 65536, None, True, 2, TraceMode.MacroChop, BufferMode.MacroChop, DumpMode.Native), + ClockMode(15, 40, None, False, 1, TraceMode.Analog, BufferMode.Single, DumpMode.Raw), + ClockMode(13, 40, None, True, 1, TraceMode.AnalogChop, BufferMode.Chop, DumpMode.Raw), + ClockMode( 8, 14, None, False, 1, TraceMode.AnalogFast, BufferMode.Single, DumpMode.Raw), + ClockMode( 8, 40, None, True, 1, TraceMode.AnalogFastChop, BufferMode.Chop, DumpMode.Raw), + ClockMode( 2, 8, 5, False, 1, TraceMode.AnalogShot, BufferMode.Single, DumpMode.Raw), + ClockMode( 4, 8, 5, True, 1, TraceMode.AnalogShotChop, BufferMode.Chop, DumpMode.Raw), +] def encode(value, dtype): sign = dtype[0]