class MachineTuner(object): def __init__(self): """Serial Setup""" self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False self.state = self.s_find_device """Data exposed through API""" self.data = { "device": { "connected": False, "model": None }, "accepted_models": ["BS0005", "BS0010"] } """API Functions""" """States""" def s_find_device(self): self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.flushInput() #Try to get anything in the buffer. self.s_t.clear_waiting() #Force the counter to reset. self.s_t.issue_wait("?") model = (self.ser.read(20)[1:7]) self.data['device']['model'] = model self.dirty = True if model in self.data['accepted_models']: self.state = self.s_setup_bs print self.data['device']['model'] + " Connected." else: self.state = self.s_check_model def s_setup_bs(self): pass """Data Processing Functions""" """Update Functions""" def update(self): try: self.state() except serial.SerialException: print "Device disconnected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconnected | Error: STE" self.state = self.s_find_device
class MachineSimpleMeter(object): def __init__(self): """ Serial """ self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False """ Trace and dump settings """ self.trace_size = 512 # Size in samples (2 bytes) self.extra_trace = 16 self.whole_trace = self.trace_size + self.extra_trace self.dump_compression = 16 # The BitScope will compress 16 samples to 1 self.dump_size = (self.trace_size / self.dump_compression) * 2 # Size in bytes self.extra_dump = (self.extra_trace / self.dump_compression) * 2 self.whole_dump = (self.whole_trace / self.dump_compression) * 2 """ API Data """ self.data = { "device": { "connected": False, "model": None }, "accepted_models": ["BS0005", "BS0010"], "dump": [], "voltage": 0.0, "voltage_text": "", "dump_ready": False, "channel": "a", "ch_a_zero": 0.0, "ch_b_zero": 0.0, } self.data['ranges'] = { "BS0005": (-5.28, 8.11), # <1% error "BS0010": (-4.84, 10.8) # Not sure on this one! } self.command = "" self.state = self.s_find_device self.range_in_use = None """ API Functions """ def zero_current_channel(self): ch_zero_string = "ch_" + self.data['channel'] + "_zero" self.data[ch_zero_string] += self.data['voltage'] def change_channel(self): if self.data['channel'] == 'a': self.data['channel'] = 'b' num = 2 else: self.data['channel'] = 'a' num = 1 self.s_t.issue("[30]@[0" + str(num - 1) + "]s") self.s_t.issue("[37]@[0" + str(num) + "]s") self.s_t.issue_wait(">") """ Helpers """ def ch_char_to_number(self, ch): return (1 if ch == 'a' else 2) """States""" def s_find_device(self): self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.flushInput() #Try to get anything in the buffer. self.s_t.clear_waiting() #Force the counter to reset. self.s_t.issue_wait("?") model = (self.ser.read(20)[1:7]) self.data['device']['model'] = model self.dirty = True if model in self.data['accepted_models']: self.state = self.s_setup_bs print self.data['device']['model'] + " Connected." else: self.state = self.s_check_model def s_setup_bs(self): d = self.data si = self.s_t.issue siw = self.s_t.issue_wait leh = l_endian_hexify ch_num = self.ch_char_to_number(d['channel']) self.range_in_use = self.data['ranges'][self.data['device']['model']] siw("!") si("[1c]@[%s]sn[%s]s" % leh(self.whole_dump / 2) # Dump size (samples) + "[2a]@[%s]sn[%s]s" % leh(self.whole_trace) # Post trig (samples) + "[26]@[%s]sn[%s]s" % leh(self.extra_trace) # Pre trig (samples) + "[1e]@[06]s[21]@[12]s" # Dump/Trace = Filter/Macro + "[16]@[01]sn[00]s" # Iterations = 1 + "[31]@[04]s" # Buffer mode = macro + "[08]@[00]sn[00]sn[00]s" # Spock address = 0 + "[30]@[0" + str(ch_num - 1) + "]s" # Dump channel + "[37]@[0" + str(ch_num) + "]s" # Analogue channel enable + "[2c]@[00]sn[0a]s" # Time out = 10 + "[2e]@[90]sn[01]s" # Clock ticks = 400 + "[14]@[01]sn[00]s" # Clock scale = 1 # Filter mode + "[18]@[10]sn[00]s" # Dump send = 16 + "[1a]@[00]sn[00]s" # Skip = 0 # Range + "[66]@[ff]sn[ff]s" # High + "[64]@[00]sn[00]s" # Low ) siw(">") siw("U") self.state = self.s_idle def s_idle(self): self.s_t.clear_waiting() self.s_t.issue_wait("?") self.ser.flushInput() self.state = self.s_init_req def s_init_req(self): self.s_t.clear_waiting() self.ser.flushInput() self.s_t.issue_wait(">") self.s_t.issue("D") # Trace selected channel self.state = self.s_dump def s_dump(self): self.s_t.clear_waiting() self.ser.read(33) # Dispose of the trace's aux information self.s_t.issue_wait(">") self.s_t.issue("A") # Dump selected channel self.data['dump_ready'] = False self.state = self.s_process_and_req def s_process_and_req(self): self.s_t.clear_waiting() self.ser.read(self.extra_dump) self.data['dump'] = convert_12bit_bin_dump( self.ser.read(self.dump_size)) self.data['dump_ready'] = True self.s_t.issue_wait(">") self.s_t.issue("D") self.state = self.s_dump """ Data Processing Functions """ def derive_voltage(self): d = self.data avg = sum(d['dump']) / len(d['dump']) # Map 16 bit range to voltage range d['voltage'] = to_range(avg, (-32768, 32767), self.range_in_use) d['voltage'] -= d['ch_' + d['channel'] + '_zero'] print d def set_voltage_text(self): # Move this stuff to view somehow! d = self.data reduce_to = "%." + '3' + "f" # Max length of voltage is 8 chars d['voltage_text'] = (reduce_to % d['voltage'] + "v").rjust(8) """Update Functions""" def update(self): try: self.state() except serial.SerialException: print "Device disconnected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconnected | Error: STE" self.state = self.s_find_device if self.data['dump_ready']: self.derive_voltage() self.set_voltage_text()
class MachineTriggeredLogic(object): def __init__(self): """Serial Setup""" self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False self.state = self.s_find_device self.buffer_size = 12288 self.trace_size = 12000 self.trace_received = 0 self.trace_intro = 16 self.trace_outro = self.trace_size self.command = "" """Data exposed through API""" self.data = { "device":{"connected":False, "model":None}, "accepted_models":["BS0005", "BS0010"], "status":"stopped", "ba":bytearray(), "ba_pos":0, "ba_range":1000, "ba_zoom":(0,1000), "range_high":20000, "range_low":100, "deep_scan":False, "data_ready":False, "trigger_byte":0, "mask_byte":255, # Any bit marked as ? will be a "don't care" bit "trigger":[2,2,2,2,2,2,2,2], "rate":5000, # kHz (up to 5mHz on tmLogic) "running":False } """API Functions""" def inc_trigger(self, ch, inc): trig = self.data["trigger"] trig[ch] += inc if trig[ch] == 3: trig[ch] = 0 self.data["trigger_byte"] = self.to_trigger_byte(trig) self.data["mask_byte"] = self.to_mask_byte(trig) tb = l_endian_hexify(self.data["trigger_byte"], 1) mb = l_endian_hexify(self.data["mask_byte"], 1) self.command += "[05]@[%s]s[06]@[%s]s" % (tb[0], mb[0]) # Trigger logic and mask def stop_start(self): if self.data['running']: self.data['running'] = False self.state = self.s_idle self.s_t.issue_wait(".") elif not self.data['running']: self.data['running'] = True self.state = self.s_start_capture def set_ba_pos(self, val): zr = self.data['ba_zoom'] pos = to_range(val, [0,100], zr) pos = self.check_position(pos - self.data['ba_range'] / 2) self.data['ba_pos'] = int(pos) def inc_ba_range(self, inc): # Make some data local and get centre sd = self.data bar = sd['ba_range'] bap = sd['ba_pos'] old_centre = bap + (bar / 2) # Increment sd['ba_range'] = int(inc_125_pattern(bar, inc)) # Get new, unaltered centre, and adjust the view pos new_centre = bap + (sd['ba_range'] / 2) sd['ba_pos'] -= int(new_centre - old_centre) # Refresh locals bar = sd['ba_range'] bap = sd['ba_pos'] # Check if ((bar + bap) > len(sd['ba']) or bar > sd['range_high'] or bar < sd['range_low'] or bap < 0): # Reverse self.inc_ba_range(-inc) """ Utilities """ def to_trigger_byte(self, bit_ls): to_bit = lambda x : str(int(x > 0)) bit_str = "".join(map(to_bit, bit_ls)) return int(bit_str, 2) def to_mask_byte(self, bit_ls): to_mask = lambda x : str(int(x == 2)) bit_str = "".join(map(to_mask, bit_ls)) return int(bit_str, 2) def check_position(self, pos): ba_range = self.data['ba_range'] ba_len = len(self.data['ba']) if (pos + ba_range) > ba_len: return int(ba_len - ba_range) elif pos < 0: return 0 else: return int(pos) """States""" def s_find_device(self): self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.flushInput() #Try to get anything in the buffer. self.s_t.clear_waiting() #Force the counter to reset. self.s_t.issue_wait("?") model = (self.ser.read(20)[1:7]) self.data['device']['model'] = model self.dirty = True if model in self.data['accepted_models']: self.state = self.s_setup_bs print self.data['device']['model'] + " Connected." else: self.state = self.s_check_model def s_setup_bs(self): si = self.s_t.issue siw = self.s_t.issue_wait self.command = "" siw("!") si( "[21]@[0e]s" # Logic trace mode + "[1e]@[00]s" # Dump mode + "[1c]@[%s]sn[%s]s" % l_endian_hexify(self.trace_size) # Dump + "[31]@[00]s" # Buffer mode + "[14]@[01]sn[00]s" # Clock scale + "[2a]@[%s]sn[%s]s" % l_endian_hexify(self.trace_size) # Post trig cap + "[30]@[80]s" # Dump channel + "[37]@[00]s" # Analogue chan + "[38]@[00]s" # Digital + "[18]@[00]sn[00]s" # Dump send + "[1a]@[00]sn[00]s" # Dump skip ) ticks = int(freq_to_ns(self.data["rate"]) / 25) si("[2e]@[%s]sn[%s]s" % l_endian_hexify(ticks)) # Trigger si( "[2c]@[00]sn[00]s" # Trigger timeout (NEVER!) "[07]@[01]s" # Trigger mode (spock opt) (hardware/rising edge) "[32]@[01]sn[00]sn[01]sn[00]s" # Trigger intro and outro ) tb = hex(self.data["trigger_byte"])[2:] mb = hex(self.data["mask_byte"])[2:] si("[05]@[%s]s[06]@[%s]s" % (tb, mb)) siw(">") siw("U") self.state = self.s_idle def s_idle(self): self.s_t.issue_wait("?") self.ser.flushInput() if self.command and self.ser: self.s_t.issue_wait(self.command) self.command = "" def s_start_capture(self): self.data['data_ready'] = False self.data['ba'] = bytearray() self.trace_received = 0 self.s_t.clear_waiting() self.ser.flushInput() if self.command and self.ser: self.s_t.issue_wait(self.command) self.command = "" self.s_t.issue_wait(">") self.s_t.issue("D") self.state = self.s_await_trigger def s_await_trigger(self): self.s_t.clear_waiting() returned = self.ser.inWaiting() if returned >= 12: self.ser.read(12) # Read out the first part of the timing info self.state = self.s_await_complete else: self.state = self.s_await_trigger def s_await_complete(self): returned = self.ser.inWaiting() if returned >= 21: # Trace has finished! self.ser.read(12) # Pesky other information end_address = unhexify(self.ser.read(8)) self.ser.read(1) # Last byte # Set up spock stuff start_address = ((end_address + self.buffer_size) - self.trace_size) % self.buffer_size self.s_t.issue("[08]@[%s]sn[%s]sn[%s]s" % l_endian_hexify(start_address, 3)) self.s_t.issue_wait(">") # Dump! self.s_t.issue("A") self.state = self.s_acquire else: self.state = self.s_await_complete def s_acquire(self): self.s_t.clear_waiting() # Bye 'A' to_get = self.ser.inWaiting() self.trace_received += to_get self.data['ba'] += self.ser.read(to_get) if self.trace_received == self.trace_size: self.data['data_ready'] = True self.data['running'] = False self.data['ba_range'] = self.trace_size self.state = self.s_idle """Data Processing Functions""" """Update Functions""" def update(self): try: self.state() except serial.SerialException: print "Device disconnected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconnected | Error: STE" self.state = self.s_find_device
class MachineMeter(object): def __init__(self): """Serial and other""" self.model = "" self.device_connected = False self.ser = None self.s_t = Serial_Tools(self.ser) # Tool to help issuing without waiting. self.s_t.show_messages = False scom = Scope_Commander(self.ser) """TRACE AND DUMP SETTINGS""" self.trace_size = 512 # Captured trace size in samples. self.extra_trace = 16 # ADC warmup junk self.whole_trace = self.trace_size + self.extra_trace self.d_dec = 16 self.d_size = (self.trace_size / self.d_dec) * 2 # Dump size in bytes self.ex_size = (self.extra_trace / self.d_dec) * 2 # Extra samples for ADC warmup in bytes self.whole_dump = ((self.trace_size + self.extra_trace) / self.d_dec) * 2 self.div = float(self.d_size) """ STATE. IT'S HERE """ self.state = self.s_find_device self.data = {} self.data['ranges'] = { "BS0005":(-5.28, 8.11), # <1% error "BS0010":(-4.84, 10.8) # Not sure on this one! } """ Measurement data """ self.data['ch'] = {} self.data['ch']['a'] = { 'raw_avg': 0, 'clip': {'top':False, 'bot':False}, 'ana': 0.0, 'display': 0.0, 'ready': {'data':False, 'display':False}, 'ena': False, 'was_ena': False, 'text_out': "" } """ Mult """ self.data['mult'] = {'values':[1,2,5,10]} self.data['ch']['a']['mult_index'] = 0 """ Logic """ self.data['logic'] = { 'step': 0.100, 'rounding': 3, 'min': -6.000, 'max': 6.000 } self.data['ch']['a']['logic'] = {'top':1.5,'bot':0.0,'ena':False,'val':0} """ OTHER """ self.data['ch']['a']['zero'] = 0.00 self.data['diff'] = { 'ena': False, 'zero':0.00, 'display':0.00, 'text_out':"" } self.data['frame_toggle'] = 0 """ Interpolation for Enhanced """ self.data['enhanced'] = { 'ena': False, 'frame': {'count': 0, 'max': 24} } self.data['ch']['a']['interp'] = { 'queue': [0,0,0,0,0,0,0,0,0], 'value': 0.0 } self.data['device'] = { 'model':None, 'connected':False } self.data['mode'] = { 'single':True, 'dual':False, 'diff':False, 'logic':False } self.data['ch']['b'] = {} copy_dict(self.data['ch']['a'], self.data['ch']['b']) self.ch = self.data['ch'] self.a = self.data['ch']['a'] self.b = self.data['ch']['b'] self.diff = self.data['diff'] # Start the engine.... In the key of a. self.force_ch('a', True) self.range_in_use = self.data['ranges']['BS0005'] """ API Functions """ def step_limit(self, ch, top_bot, inc): if self.ch[ch]['logic']['ena']: lim = self.ch[ch]['logic'] lim[top_bot] = round(lim[top_bot] + inc, 1) if lim[top_bot] < self.data["logic"]['min']: lim[top_bot] = self.data["logic"]['min'] elif lim[top_bot] > self.data["logic"]['max']: lim[top_bot] = self.data["logic"]['max'] def toggle_limits(self, ch): self.ch[ch]['logic']['ena'] = not self.ch[ch]['logic']['ena'] def step_mult(self, ch, inc): if self.ch[ch]['ena']: ch = self.ch[ch] ch['mult_index'] += inc if ch['mult_index'] < 0: ch['mult_index'] = 0 elif ch['mult_index'] >= len(self.data['mult']['values']): ch['mult_index'] = len(self.data['mult']['values']) - 1 def toggle_ch(self, ch): self.ch[ch]['ena'] = not self.ch[ch]['ena'] def force_ch(self, ch, state): self.ch[ch]['ena'] = state def zero_ch(self, ch): if self.ch[ch]['ena']: if self.data['diff']['ena'] and ch == 'a': self.data['diff']['zero'] = self.ch[ch]['display'] + self.data['diff']['zero'] else: self.ch[ch]['zero'] = self.ch[ch]['display'] + self.ch[ch]['zero'] def toggle_diff(self): self.data['diff']['ena'] = not self.data['diff']['ena'] def force_diff(self, state): self.data['diff']['ena'] = state def toggle_sound(self): self.sound_enabled = not self.sound_enabled def toggle_enhanced(self): self.data['enhanced']['ena'] = not self.data['enhanced']['ena'] def force_enhanced(self, state): self.data['enhanced']['ena'] = state """ STATES """ def s_find_device(self): self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.device_connected: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.flushInput() #Try to get anything in the buffer. self.s_t.clear_waiting() #Force the counter to reset. self.s_t.issue_wait("?") self.data['device']['model'] = (self.ser.read(20)[1:7]) print self.data['device']['model'] + " Connected." self.dirty = True if self.data['device']['model'] in self.data['ranges']: self.state = self.s_setup_bs else: self.state = self.s_check_model def s_setup_bs(self): """ Put the BS into a workable state """ si = self.s_t.issue siw = self.s_t.issue_wait self.range_in_use = self.data['ranges'][self.data['device']['model']] siw("!") si( "[1c]@[%s]sn[%s]s" % l_endian_hexify(self.whole_dump / 2) # Dump size + "[1e]@[06]s[21]@[12]s" # Dump, trace mode = filter, macro + "[08]@[00]sn[00]sn[00]s" # Spock address + "[16]@[01]sn[00]s" # Iterations = 1 + "[26]@[%s]sn[%s]s" % l_endian_hexify(16) # Pre trig cap + "[2a]@[%s]sn[%s]s" % l_endian_hexify(self.whole_trace) # Post trig cap + "[30]@[00]s" # Dump channel + "[31]@[04]s" # Buffer mode = macro + "[37]@[01]s" # Analogue channel enable + "[2c]@[00]sn[0a]s" # Time out (REALLY IMPORTANT) + "[2e]@[90]sn[01]s" # Clock ticks + "[14]@[01]sn[00]s" # Clock scale ### Filter mode ### + "[18]@[10]sn[00]s" # Dump send = 16 + "[1a]@[00]sn[00]s" # Skip = 0 ### Range ### + "[66]@[ff]sn[ff]s" # High + "[64]@[00]sn[00]s" # Low ) self.ser.flushInput() siw("U") siw(">") self.state = self.s_change_to_chop def s_change_to_chop(self): self.ser.flushInput() self.s_t.clear_waiting() self.s_t.issue( "[1e]@[06]s" # Set dump mode stays as filter "[21]@[13]s" # Set trace mode (macro chop) "[31]@[05]s" # Buffer mode "[37]@[03]s" # Ana ch enable ) self.s_t.issue_wait(">") self.s_t.issue("U") self.state = self.s_pre_idle def s_pre_idle(self): self.s_t.clear_waiting() self.ser.flushInput() self.state = self.s_idle def s_idle(self): # Idle self.s_t.issue_wait("?") self.ser.flushInput() if self.a['ena'] or self.b['ena']: self.state = self.s_init_a_b_req_chop """ Dual chop """ def s_init_a_b_req_chop(self): self.s_t.clear_waiting() self.ser.flushInput() self.s_t.issue_wait(">") self.s_t.issue("[37]@[03]sD") self.state = self.s_a_dump_chop def s_a_dump_chop(self): self.s_t.clear_waiting() self.ser.read(33) # Get rid of unused timing junk self.s_t.issue_wait(">") self.s_t.issue("[30]@[00]sA") # Dump chA if self.a['ena'] or self.b['ena']: self.state = self.s_a_proc_b_dump_chop else: self.state = self.s_pre_idle def s_a_proc_b_dump_chop(self): self.s_t.clear_waiting() if self.ch['a']['ena']: self.ser.read(self.ex_size) dump = convert_12bit_bin_dump(self.ser.read(self.d_size)) self.a['ana'] = self.derive_voltage(dump) self.a['clip']['top'], self.a['clip']['bot'] = self.check_clipping(dump) self.a['ready']['data'] = True else: self.ser.read(self.d_size + self.ex_size) # Read off dump as junk. self.b['ready']['data'] = False self.s_t.issue_wait(">") self.s_t.issue("[30]@[01]sA") # Dump chB self.state = self.s_b_proc_a_b_req_chop def s_b_proc_a_b_req_chop(self): self.s_t.clear_waiting() if self.b['ena']: self.ser.read(self.ex_size) dump = convert_12bit_bin_dump(self.ser.read(self.d_size)) self.b['ana'] = self.derive_voltage(dump) self.b['clip']['top'], self.b['clip']['bot'] = self.check_clipping(dump) self.b['ready']['data'] = True else: self.ser.read(self.d_size + self.ex_size) # Read off dump as junk. self.a['ready']['data'] = False self.s_t.issue_wait(">") self.s_t.issue("[37]@[03]sD") # Request both channels. self.state = self.s_a_dump_chop """ Utility functions """ def volt_interp(self, ls, new_v): ls.pop(0) ls.append(new_v) return round((sum(ls) / len(ls)), 4) def derive_voltage(self, dump): avg = sum(dump) / len(dump) # Map 16 bit range to voltage range return to_range(avg, (-32768, 32767), self.range_in_use) def check_clipping(self, dump): clip_top, clip_bot = False, False if (max(dump) > (32767 - 72)): # Clipping top WHY 72?!?!?! clip_top = True if (min(dump) < (-32768 + 32)): # Clipping bottom clip_bot = True return (clip_top, clip_bot) def update(self): """ Do state! """ try: self.state() except serial.SerialException: print "Device disconected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconected | Error: STE" self.state = self.s_find_device a = self.a b = self.b a_ready = a['ready'] b_ready = b['ready'] a_interp = a['interp'] b_interp = b['interp'] diff = self.diff enhanced_frame = self.data['enhanced']['frame'] """ Data post-process """ if self.data['device']['connected']: for ch, ch_ready in (a, a_ready), (b, b_ready): if ch['ena'] and ch_ready['data']: ch['ana'] = round((ch['ana'] - ch['zero']), 6) # Zero ch['display'] = ch['ana'] * self.data['mult']['values'][ch['mult_index']] # Apply multiplier if ch['logic']['ena']: if ch['display'] >= ch['logic']['top']: log = 1 elif ch['display'] <= ch['logic']['bot']: log = 2 else: log = 0 else: log = 0 ch['logic']['val'] = log """ INTERP MODE """ if self.data['enhanced']['ena']: for ch, ch_interp in (a, a_interp), (b, b_interp): if ch['ena'] and ch['ready']['data']: ch_interp['value'] = self.volt_interp(ch_interp['queue'], ch['display']) ch['display'] = ch_interp['value'] """ DIFF MODE """ if diff['ena']: a['display'] = (a['ana'] - b['ana']) - diff['zero'] """ SLOW DOWN INTERP """ if self.data['enhanced']['ena']: for ch, count in (a, 0), (b, 12): if ch['ena'] and enhanced_frame['count'] == count: ch['ready']['display'] = True enhanced_frame['count'] += 1 if enhanced_frame['count'] >= enhanced_frame['max']-1: enhanced_frame['count'] = 0 else: a_ready['display'] = True b_ready['display'] = True if self.data['enhanced']['ena']: # Check interp, set rounding round_to = "4" else: round_to = "3" """ Set the text """ # This should really be done by the widget reduce_to = "%." + round_to + "f" # Max length of voltage is 8 chars for ch, ch_ready in (a, a_ready), (b, b_ready): if ch['ena'] and ch_ready['display']: ch['text_out'] = (reduce_to % ch['display'] + "v").rjust(8) ch_ready['display'] = False
class MachineLogic(object): def __init__(self, name, cfg): self.name = name self.cfg = cfg self.mode_cfg = cfg['modes'][name] """Serial Setup""" self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False self.command = "" self.state = self.s_find_device self.position = 0 self.max = 10000000 """Data exposed through API""" self.data = { "device":{"connected":False, "model":None}, "accepted_models":["BS0005", "BS0010"], "status":"stopped", "streaming":False, "stream_mode":0, # Analogue A, or logic "trace":[], "ba":bytearray(1024*1024*10), "ba":bytearray(100000), "ba_pos":0, "ba_range":1000, "data_ready":False, "deep_scan":False, "range_high":200000, "range_low":100, "ba_zoom":(0,0), "frequency":100, # khz "interval":0, # us "file_mode":0 } self.data['ba_zoom'] = (0, len(self.data['ba'])) self.data['interval'] = int(freq_to_ns(self.data['frequency']) / 1000) """API Functions""" def start_capture(self): self.command += "T" self.state = self.s_capture def pause_capture(self): self.command += "." self.state = self.s_idle self.data['ba_zoom'] = (0,len(self.data['ba'])) def stop_capture(self): self.command += "." self.state = self.s_idle self.data['ba_zoom'] = (0,len(self.data['ba'])) def stop_start(self): status = self.data['status'] if self.state == self.s_idle or self.state == self.s_capture: if status == "stopped": self.data['data_ready'] = False self.data['status'] = "capturing" self.data['streaming'] = True # self.state = self.start_capture self.start_capture() elif status == "paused": self.data['data_ready'] = False self.data['status'] = "capturing" self.data['streaming'] = True # self.state = self.start_capture self.start_capture() elif status == "capturing": self.data['data_ready'] = True self.data['status'] = "stopped" self.data['streaming'] = False # self.state = self.stop_capture self.stop_capture() def set_sample_frequency(self, freq): self.data['frequency'] = freq self.data['interval'] = int(freq_to_ns(freq)) self.command = ("[2e]@[%s]sn[%s]s" % l_endian_hexify(self.data['interval'] / 25)) self.data['interval'] /= 1000 # Convert to us def inc_sample_frequency(self, inc): freq = self.data['frequency'] freq = int(inc_125_pattern(freq, inc)) if (1 <= freq <= 500): # If the value is okay self.set_sample_frequency(freq) # Set it! def clear_data(self): self.data['ba'] = bytearray(100000) self.data['ba_zoom'] = (0, len(self.data['ba'])) self.data['ba_range'] = 1000 self.data['ba_pos'] = 0 self.data['data_ready'] = 100 # To ensure redraw no matter true or false self.position = 0 def check_position(self, pos): ba_range = self.data['ba_range'] ba_len = len(self.data['ba']) if (pos + ba_range) > ba_len: return int(ba_len - ba_range) elif pos < 0: return 0 else: return int(pos) def inc_ba_pos(self, inc): self.data['ba_pos'] += int(inc) self.data['ba_pos'] = self.check_position(self.data['ba_pos']) def set_ba_pos(self, val): zr = self.data['ba_zoom'] pos = to_range(val, [0,100], zr) pos = self.check_position(pos - self.data['ba_range'] / 2) self.data['ba_pos'] = int(pos) def inc_ba_range(self, inc): # Make some data local and get centre sd = self.data bar = sd['ba_range'] bap = sd['ba_pos'] old_centre = bap + (bar / 2) # Increment sd['ba_range'] = int(inc_125_pattern(bar, inc)) # Get new, unaltered centre, and adjust the view pos new_centre = bap + (sd['ba_range'] / 2) sd['ba_pos'] -= int(new_centre - old_centre) # Refresh locals bar = sd['ba_range'] bap = sd['ba_pos'] # Check if ((bar + bap) > len(sd['ba']) or bar > sd['range_high'] or bar < sd['range_low'] or bap < 0): # Reverse self.inc_ba_range(-inc) def zoom_ba(self): sd = self.data zoom = sd['ba_pos'], sd['ba_pos'] + sd['ba_range'] if zoom[1] - zoom[0] >= 1000: sd['ba_zoom'] = zoom def reset_zoom_ba(self): sd = self.data sd['ba_zoom'] = 0, len(sd['ba']) def do_deep_scan(self): self.data['deep_scan'] = not self.data['deep_scan'] def inc_file_format(self, inc): last_mode = 3 new_mode = self.data['file_mode'] + inc if new_mode > last_mode: new_mode = 0 elif new_mode < 0: new_mode = last_mode self.data['file_mode'] = new_mode def dump_to_file(self): file_name = time.strftime("%Y-%m-%d_%H:%M:%S") mode = self.data['file_mode'] extension = "txt" replace_chars = [] if mode == 0: # Raw file_name = "bin_" + file_name to_write = self.data['ba'][:self.position] elif mode == 1: # CSV decimal file_name = "dec_" + file_name to_write = str([int(b) for b in self.data['ba'][:self.position]]) replace_chars = ["[", "]"] elif mode == 2: # CSV hex file_name = "hex_" + file_name to_write = str([hex(b)[2:] for b in self.data['ba'][:self.position]]) replace_chars = ["'", "[", "]"] elif mode == 3: # Sigrok session file_name = "sr_" + file_name extension = "sr" data = self.data['ba'][:self.position] sr = sigrok_session.SigrokSession() sr.set_rate(self.data['frequency'] * 1000) sr.set_data(data) to_write = sr.get_session_file().getvalue() for ch in replace_chars: to_write = to_write.replace(ch, "") directory = os.path.dirname(self.cfg['globals']['output_dir']) if not os.path.exists(directory): os.makedirs(directory) with open(directory + "/" + file_name + '.' + extension, 'wb') as out: out.write(to_write) def toggle_stream_mode(self): sm = self.data['stream_mode'] if sm == 0: self.set_stream_mode(1) elif sm == 1: self.set_stream_mode(0) def set_stream_mode(self, mode): self.data['stream_mode'] = mode if mode == 1: self.command = "[37]@[01]sn[00]s>" elif mode == 0: self.command = "[37]@[00]sn[ff]s>" """States""" def s_find_device(self): self.data['streaming'] = False self.data['status'] = "stopped" self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.read(10000) #Try to get anything in the buffer. self.s_t.clear_waiting() #Force the counter to reset. self.s_t.issue_wait("?") model = (self.ser.read(20)[1:7]) self.data['device']['model'] = model self.dirty = True if model in self.data['accepted_models']: self.state = self.s_setup_bs print self.data['device']['model'] + " Connected." else: self.state = self.s_check_model def s_setup_bs(self): si = self.s_t.issue siw = self.s_t.issue_wait self.command = "" # Clear command buffer siw("!") # Reset si( "[21]@[02]s" # Trace mode (change to logic!) "[14]@[01]sn[00]s" # Clock scale # "[74]@[00]s" # Logic pins to inputs ) self.set_stream_mode(self.data['stream_mode']) mch_cfg = self.mode_cfg['machine_cfg'] high, low = to_span(mch_cfg['offset'], mch_cfg['range']) si("[66]@[%s]sn[%s]s" % l_endian_hexify(high)) si("[64]@[%s]sn[%s]s" % l_endian_hexify(low)) intervals = int(freq_to_ns(self.data['frequency']) / 25) si("[2e]@[%s]sn[%s]s" % l_endian_hexify(intervals)) # Clock ticks siw("U") siw(">") self.state = self.s_idle def s_idle(self): self.s_t.issue_wait("?") self.ser.flushInput() if self.command and self.ser: self.s_t.issue_wait(self.command) self.command = "" def s_capture(self): if self.command and self.ser: self.s_t.issue_wait(self.command) self.command = "" in_count = self.ser.inWaiting() if in_count: new_ba = self.ser.read(in_count) self.alter_bytearray(new_ba) if self.position >= self.max: self.stop_start() """Data Processing Functions""" def alter_bytearray(self, new_data): # No for-loop version self.data['ba'][self.position:self.position + len(new_data)] = new_data self.position += len(new_data) """Update Functions""" def update(self): try: self.state() except serial.SerialException: print "Device disconnected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconnected | Error: STE" self.state = self.s_find_device except IOError: print "Some other IO fault" self.state = self.s_find_device
class MachineScope(object): def __init__(self): """Serial Setup""" self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False self.state = self.s_find_device self.trace_size = 1024 # Samples self.extra_trace = 4 self.whole_trace = self.trace_size + self.extra_trace self.dump_size = self.trace_size self.extra_dump = self.extra_trace self.whole_dump = self.dump_size + self.extra_dump self.write_buffer = "" self.buffer_size = 3 * 4096 """Data exposed through API""" self.data = { "device":{"connected":False, "model":None}, "accepted_models":["BS0005", "BS0010"], "ch":{"active":{}}, "trigger_level":32000, "trigger_display":0, "range_data":{"top":32000, "bot":0}, "range":{"min":-5.8, "max":5.8, "high":4.0, "low":-1.0, "offset":1.5, "span":5.0}, "timebase":{"min":15, "max":535, "value":40, "display":""}, "current_channel":"a", "waveform":0, "symetry":32768, "symetry_percentage":50, "frequency":4, "on_time":0, "off_time":0 } self.data['ch']['active'] = { "trace":[], "display_trace":[] } self.data['spock_option'] = [0,0,0,0,0,0,0,0] # This is BEian (7 -> 0) self.active = self.data['ch']['active'] self.device = self.data['device'] self.compute_times() self.ticks_to_timebase() """ Helper Functions """ def compute_times(self): """ Works out the on and off times, based on the frequency and symetry. """ d = self.data freq = d['frequency'] * 1000 p_length = freq ** -1 d['on_time'] = (p_length) * (d['symetry_percentage'] / 100.0) d['off_time'] = (p_length) * ((100.0 - d['symetry_percentage']) / 100.0) d['on_time'] = d['on_time'] * 1000000 d['off_time'] = d['off_time'] * 1000000 def restart_awg(self): """ Resends all AWG commands that can be altered in this mode """ d = self.data leh = l_endian_hexify ### Synthesise ### out = ( "[46]@[00]s" # CV + "[47]@[" + str(d['waveform']) + "]s" + "[5a]@[%s]sn[%s]s" % leh(d['symetry'], 2) # Phase Ratio + "Y" ### Translate ### + "[47]@[00]s" + "[5a]@[26]sn[31]sn[08]sn[00]s" # Phase Ratio + "X" ### Generate ### + "[46]@[02]s" + "[50]@[%s]sn[%s]s" % leh(self.freq_to_ticks(), 2) # Clock ticks + "Z" ) self.write_buffer += out def freq_to_ticks(self): """ Frequency relies on a constant phase ratio at the translate command. With 0x00083126 we get 8 periods per 1000 point table. clock_ticks = period / points_per_period frequency is in Hz, clock_ticks are 25ns long. """ d = self.data points_per_period = 125.0 period = (d['frequency'] * 1000.0) ** -1 ticks_per_period = period / 0.000000025 return int(round(ticks_per_period / points_per_period)) def ticks_to_timebase(self): tb = self.data['timebase'] tb['display'] = self.trace_size * (tb['value'] * 25) tb['display'] *= 0.000001 """ API Functions """ def step_waveform(self, inc): d = self.data d['waveform'], changed = increment(d['waveform'], inc, 0, 3) if changed: self.restart_awg() def reset_symetry(self): sym = 50 self.data['symetry_percentage'] = sym self.data['symetry'] = int(to_range(sym, (0, 100),(0, 65535))) self.compute_times() self.restart_awg() def reset_frequency(self): self.data['frequency'] = 4 self.compute_times() self.restart_awg() def snap_symetry(self): d = self.data sym = round_to_n(d['symetry_percentage'], 2) d['symetry_percentage'] = sym d['symetry'] = int(to_range(sym, (0, 100),(0, 65535))) self.compute_times() self.restart_awg() def snap_frequency(self): d = self.data d['frequency'] = round_to_n(d['frequency'], 2) self.compute_times() self.restart_awg() def snap_on_off_time(self, select): d = self.data on = d['on_time'] off = d['off_time'] if select == "on": on = round_to_n(on, 2) elif select == "off": off = round_to_n(off, 2) on_frac = on / 1000000.0 off_frac = off / 1000000.0 newFreq = (((on_frac + off_frac) ** -1) / 1000.0) newSym = to_range(on_frac, (0, on_frac + off_frac), (0, 100)) if 0.06 <= newFreq <= 8.0 and 0.0 <= newSym <= 100.0: d['on_time'] = on d['off_time'] = off d['frequency'] = newFreq d['symetry_percentage'] = newSym d['symetry'] = int(to_range(newSym, (0,100), (0,65535))) self.restart_awg() def adjust_symetry(self, inc): d = self.data d['symetry_percentage'], changed = increment(d['symetry_percentage'], inc, 0, 100) if changed: d['symetry'] = int(to_range(d['symetry_percentage'], (0,100), (0,65535))) self.restart_awg() self.compute_times() def adjust_frequency(self, inc): d = self.data d['frequency'], changed = increment(d['frequency'], inc, 0.6, 8.0) if changed: self.restart_awg() self.compute_times() def adjust_on_off_time(self, select, inc): d = self.data on_inc, off_inc = 0, 0 if select == "on": on_inc = inc elif select == "off": off_inc = inc on = (d['on_time'] + on_inc) / 1000000.0 off = (d['off_time'] + off_inc) / 1000000.0 newFreq = (((on + off) ** -1) / 1000.0) newSym = to_range(on, (0, on + off), (0, 100)) if 0.06 <= newFreq <= 8.0 and 0.0 <= newSym <= 100.0: d['on_time'] = on * 1000000 d['off_time'] = off * 1000000 d['frequency'] = newFreq d['symetry_percentage'] = newSym d['symetry'] = int(to_range(newSym, (0,100), (0,65535))) self.restart_awg() def adjust_timebase(self, inc): tb = self.data['timebase'] tb['value'], changed = increment(tb['value'], inc, tb['min'], tb['max']) if changed: self.write_buffer += ("[2e]@[%s]sn[%s]s" % l_endian_hexify(tb['value'])) self.ticks_to_timebase() def change_channel(self): """ Changes the capture channel, and the trigger channel """ if self.data['current_channel'] == "a": self.data['current_channel'] = "b" code = "2" self.data['spock_option'][5] = True # Source bit (true = b, false = a) else: self.data['current_channel'] = "a" code = "1" self.data['spock_option'][5] = False self.write_buffer += "[37]@[0" + code + "]s" s_op = from_bin_array(self.data['spock_option']) self.write_buffer += "[07]@[%s]s" % l_endian_hexify(s_op,1) def move_range(self, inc): r = self.data['range'] if (r['high'] + inc) <= r['max'] and (r['low'] + inc) >= r['min']: self.adjust_range('high', inc) self.adjust_range('low', inc) def adjust_range(self, hl, inc): r = self.data['range'] r[hl] += inc if (r[hl] > r['max'] or r[hl] < r['min'] or r['low'] >= r['high']): r[hl] -= inc else: r[hl] = round(r[hl], 1) # Get scale scale = r['high'] - r['low'] r['span'] = scale # Get offset r['offset'] = r['low'] + (scale / 2) # Compute register values high, low = to_span(r['offset'], scale, self.data['device']['model']) # Figure out trigger trig_voltage = r['low'] + ((r['high'] - r['low']) / 2) self.data['trigger_display'] = trig_voltage self.data['trigger_level'] = int(to_range(trig_voltage, [-7.517, 10.816], [0,65535])) # Submit high, then low self.write_buffer += "[68]@[%s]sn[%s]s" % l_endian_hexify(self.data['trigger_level'], 2) self.write_buffer += "[66]@[%s]sn[%s]s" % l_endian_hexify(high) self.write_buffer += "[64]@[%s]sn[%s]s" % l_endian_hexify(low) def adjust_span(self, inc): r = self.data['range'] if (r['high'] + inc) <= r['max'] and (r['low'] - inc) >= r['min']: self.adjust_range('high', inc) self.adjust_range('low', -inc) """ Utility States """ def s_find_device(self): self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.read(10000) # Try to get anything in the buffer. self.s_t.clear_waiting() # Force the counter to reset. self.s_t.issue_wait("?") model = (self.ser.read(20)[1:7]) self.data['device']['model'] = model self.dirty = True if model in self.data['accepted_models']: self.state = self.s_setup_bs print self.data['device']['model'] + " Connected." else: self.state = self.s_check_model """ States """ def s_setup_bs(self): siw = self.s_t.issue_wait si = self.s_t.issue leh = l_endian_hexify d = self.data ### General ### siw("!") # Reset! si( "[1c]@[%s]sn[%s]s" % leh(self.whole_dump) # Dump size + "[1e]@[00]s" # Dump mode + "[21]@[00]s" # Trace mode + "[08]@[00]sn[00]sn[00]s" # Default spock address + "[16]@[01]sn[00]s" # Iterations to 1 + "[2a]@[%s]sn[%s]s" % leh(self.whole_trace) # Post trig cap + "[30]@[00]s" # Dump channel + "[31]@[00]s" # Buffer mode + "[37]@[01]s" # Analogue chan enable + "[26]@[01]sn[00]s" # Pre trig capture + "[22]@[00]sn[00]sn[00]sn[00]s" # Trigger checking delay period. + "[2c]@[00]sn[0a]s" # Timeout + "[2e]@[%s]sn[%s]s" % leh(d['timebase']['value']) # Set clock ticks + "[14]@[01]sn[00]s" # Clock scale ### Trigger ### + "[06]@[7f]s" # TriggerMask + "[05]@[80]s" # TriggerLogic + "[32]@[04]sn[00]s" # TriggerIntro + "[34]@[04]sn[00]s" # TriggerOutro + "[44]@[00]sn[00]s" # TriggerValue + "[68]@[%s]sn[%s]s" % leh(d['trigger_level'], 2) # TriggerLevel + "[07]@[%s]s" % leh(from_bin_array(d['spock_option']), 1) # SpockOption ) ### Range / Span ### high, low = to_span(d['range']['offset'], d['range']['span'], d['device']['model']) si( "[66]@[%s]sn[%s]s" % l_endian_hexify(high) + "[64]@[%s]sn[%s]s" % l_endian_hexify(low) ) ### AWG ### siw( "[7c]@[c0]s[86]@[00]s" # AWG on, clock gen off # Synthesize + "[46]@[00]sn[" + str(d['waveform']) + "]s" # CV, Op Mode + "[5a]@[%s]sn[%s]s" % leh(d['symetry'], 2) # Phase Ratio + "Y" ) siw( # Translate "[47]@[00]s" # CV, Op Mode + "[4a]@[e8]sn[03]sn[00]sn[00]sn[00]sn[00]s" # Size, Index, Address + "[54]@[ff]sn[ff]sn[00]sn[00]s" # Level, Offset + "[5a]@[26]sn[31]sn[08]sn[00]s" # Phase Ratio + "X" ) siw( # Generate "[48]@[f4]sn[80]s[52]@[e8]sn[03]s" # Option, Modulo + "[50]@[%s]sn[%s]s" % leh(self.freq_to_ticks()) # Clock ticks + "[5e]@[0a]sn[01]sn[01]sn[00]s" # Mark, Space + "[78]@[00]sn[7f]s" # Dac Output + "[46]@[02]s" + "Z" ) ### Update ### self.s_t.issue_wait(">") siw("U") self.s_t.clear_waiting() self.ser.flushInput() self.state = self.s_init_req def s_init_req(self): self.s_t.clear_waiting() self.ser.flushInput() self.s_t.issue_wait(">") self.s_t.issue("D") self.state = self.s_dump def s_dump(self): self.s_t.clear_waiting() self.ser.read(24) end_address = unhexify(self.ser.read(8)) self.ser.read(1) start_address = ((end_address + self.buffer_size) - self.whole_trace) % self.buffer_size self.s_t.issue("[08]@[%s]sn[%s]sn[%s]s" % l_endian_hexify(start_address, 3)) self.s_t.issue_wait(">") self.s_t.issue("A") self.state = self.s_proc_req def s_proc_req(self): self.s_t.clear_waiting() self.ser.read(self.extra_dump) self.active['trace'] = convert_8bit_dump(self.ser.read(self.dump_size)) if self.write_buffer: self.s_t.issue(self.write_buffer) self.s_t.issue_wait("U") self.write_buffer = "" self.s_t.issue_wait(">") self.s_t.issue("D") self.state = self.s_dump """Data Processing Functions""" """Update Functions""" def update(self): try: self.state() except serial.SerialException: print "Device disconected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconected | Error: STE" self.state = self.s_find_device
class MachineSound(object): def __init__(self): """Serial Setup""" self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False self.state = self.s_find_device self.base_freq = 440 # A self.note = 0 """Data exposed through API""" self.data = { "device": { "connected": False, "model": None }, "accepted_models": ["BS0005", "BS0010"] } """ Utilities """ def generate_frequency(self, note_id): # fn = f0 * (a)n # a = 1.05946309 return self.base_freq * (1.05946309**note_id) def inc_note(self, inc): notes = [ "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#" ] self.note += inc # The ocatave number print "Note ID", self.note print "Note", notes[self.note % 12], int(self.note / 12) + 4 print "Freq", self.generate_frequency(self.note) """API Functions""" def change_wf(self, val): if self.ser: self.s_t.issue_wait("[50]@[%s]sn[%s]s" % l_endian_hexify(val)) self.s_t.issue_wait("Z") def change_wf2(self, val=0): freq = int(to_range(val, [0, 100], [100, 260])) if self.ser: self.s_t.issue_wait("[50]@[%s]sn[%s]s" % l_endian_hexify(freq)) self.s_t.issue_wait("Z") """States""" def s_find_device(self): self.ser = self.s_t.find_device(self.data['accepted_models']) if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.read(10000) # Try to get anything in the buffer. self.s_t.clear_waiting() # Force the counter to reset. self.s_t.issue_wait("?") model = (self.ser.read(20)[1:7]) self.data['device']['model'] = model print self.data['device']['model'] + " Connected." self.dirty = True if model in self.data['accepted_models']: self.state = self.s_setup_bs else: self.state = self.s_check_model def s_setup_bs(self): """AWG""" self.s_t.issue_wait( "[7c]@[c0]s" # Kitchen sink register B! ENABLES AWG!!! "[86]@[00]s" # Turn the clock generator off. ) self.s_t.issue_wait("U") self.s_t.issue_wait( "[46]@[00]s" # vpCmd (Command Vector) "[47]@[03]s" # vpMode (Operating Mode) "[5a]@[00]sn[80]sn[00]sn[00]s" # vpRatio (Phase Ratio) ) self.s_t.issue_wait("Y") # SYNTHESIZE! self.s_t.issue_wait( "[46]@[00]s" # vpCmd (Command Vector) "[47]@[00]s" # vpMode (Operating Mode) "[4a]@[e8]sn[03]s" # vpSize (Operation size, 1000 samples) "[4c]@[00]sn[00]s" # vpIndex (Operation Index, table start) "[4e]@[00]sn[00]s" # vpAddress (Destination Address, buffer start) "[54]@[ff]sn[ff]s" # vpLevel (Output Level, full scale) "[56]@[00]sn[00]s" # vpOffset (Output Offset, zero) "[5a]@[93]sn[18]sn[04]sn[00]s" # vpRatio (Phase Ratio) ) self.s_t.issue_wait("X") # TRANSLATE! self.s_t.issue_wait("[48]@[f4]sn[80]s" # vpOption (control flags) "[50]@[af]sn[00]s" # vpClock (Sample Clock Ticks) "[52]@[e8]sn[03]s" # vpModulo (Table Modulo Size) "[5e]@[0a]sn[01]s" # vpMark (Mark Count/Phase) "[60]@[01]sn[00]s" # vpSpace (Space Count/Phase) "[78]@[00]sn[7f]s" # vrDacOutput (DAC Level) "[46]@[02]s" # vmCmd (Command Vector) ) self.s_t.issue_wait("Z") # GENERATE! self.s_t.issue_wait(">") self.s_t.issue("U") self.ser.flushInput() self.state = self.s_waiting def s_waiting(self): # Connected and doing nothing really. self.s_t.issue_wait("?") self.ser.flushInput() """Data Processing Functions""" """Update Functions""" def update(self): try: self.state() except serial.SerialException: print "Device disconected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconected | Error: STE" self.state = self.s_find_device
class MachineVerify(object): def __init__(self): self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False self.state = self.s_find_device self.trace_size = 1024 #captured trace size in samples. self.extra_trace = 16 #ADC warmup junk self.whole_trace = self.trace_size + self.extra_trace self.d_size = self.trace_size * 2 #dump size in bytes self.ex_size = self.extra_trace * 2 #extra samples for ADC warmup in bytes self.whole_dump = (self.trace_size + self.extra_trace) * 2 self.data = { "device":{"model":None, "connected":False, "tested":False, "pass":False, "connected_not_tested":False}, "test":{"status":"connect", "op_count":1}, "ch":{"a":{}, "b":{}}, "mask_height":5000, "masks":{ #Lists of tuples containing mask ranges. "comp":[(123,190)] }, "cal_points":{ "comp":{ "shape":[50,205,250], "top":range(191,212) } }, "mode":"pre_test", "sample_offset":{ "comp":35, "adc":125, "logic":70 }, } self.data['ranges'] = {} self.data['ranges']['BS0005'] = { 1.1 : "[64]@[56]sn[7e]sn[77]sn[82]s", 3.5 : "[64]@[c3]sn[6a]sn[a3]sn[95]s", 5.2 : "[64]@[68]sn[44]sn[ff]sn[8a]s", 11 : "[64]@[6a]sn[12]sn[8c]sn[ba]s", "better_11" : "[64]@[6f]sn[14]sn[8c]sn[ba]s", "max" : "[64]@[00]sn[00]sn[ff]sn[ff]s" } self.data['ranges']['BS0010'] = { 0.520: "[64]@[40]sn[65]sn[82]sn[6c]s", 1.1 : "[64]@[14]sn[61]sn[6f]sn[70]s", 3.5 : "[64]@[5c]sn[50]sn[3a]sn[81]s", 5.2 : "[64]@[9d]sn[44]sn[38]sn[8d]s", 11 : "[64]@[28]sn[1c]sn[c1]sn[b5]s", "max" : "[64]@[00]sn[00]sn[ff]sn[ff]s" } self.data['range'] = 11 self.data['templates'] = { "comp":([19792] * 119) + ([480] * 152) + ([19762] * 151) } self.data['results'] = { "comp" : False, "adc" : False, "logic": False, "test" : False } self.data['ch']['a'] = { "trace":[], "display_trace":[], "ena":False, "zero": {16:10131, 8:162}, "sos": 0, "sos_string":"", "errors":{"shape":0, "top":0}, "pass":{"comp":False, "adc":False}, "limits":{"comp":30, "adc":100}, "queue":[0]*5, "result":0, "ready":False, "frame_count":0, "ready_on_frame":5 } # Clone ch a to ch b self.data['ch']['b'] = {} copy_dict(self.data['ch']['a'], self.data['ch']['b']) # Aliases self.ch = self.data['ch'] self.a = self.ch['a'] self.b = self.ch['b'] self.dev = self.data['device'] self.test = self.data['test'] """API funcs""" def capture_trace(self, ch): if ch == "logic": print self.ch[ch]['traces']['combined'] else: print self.ch[ch]['display_trace'] # self.data['templates']['trace'] = self.ch[ch]['display_trace'] def switch_operation(self, op): self.data['mode'] = op self.state = self.s_operation_router def reset_status(self): d = self.data dev = d['device'] res = d['results'] dev['connected_not_tested'] = False dev['model'] = dev['tested'] = dev['pass'] = False res['comp'] = False self.data['test']['status'] = 'connect' if self.data['mode'] == "post_test": self.data['mode'] = "pre_test" """STATES""" def s_operation_router(self): op = self.data['mode'] test = self.data['test'] if self.data['device']['connected']: if op == "post_test": self.state = self.s_post test['op_count'] = 4 elif op == "pre_test": self.state = self.s_idle if test['status'] == 'proceed': test['op_count'] = 2 elif op == "comp": self.state = self.s_setup_for_comp test['op_count'] = 3 else: self.state = self.s_find_device def s_find_device(self): if self.data['mode'] == "post_test": self.reset_status() elif self.data['mode'] == "pre_test": self.data['test']['status'] = 'connect' self.data['test']['op_count'] = 1 else: self.data['test']['status'] = 'connect' self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.read(10000) #Try to get anything in the buffer. self.s_t.clear_waiting() #Force the counter to reset. self.s_t.issue_wait("?") self.data['device']['model'] = (self.ser.read(20)[1:7]) print self.data['device']['model'] + " Connected." self.data['test']['status'] = "proceed" self.dirty = True if self.data['device']['model'] in self.data['ranges']: self.state = self.s_setup_bs else: self.state = self.s_check_model def s_setup_bs(self): bc = "" bc+="[1c]@[%s]sn[%s]s" % l_endian_hexify(self.whole_dump / 2) #Dump size bc+="[1e]@[05]s[21]@[12]s" #set dump and trace bc+="[08]@[00]sn[00]sn[00]s" #Set spock address bc+="[16]@[01]sn[00]s" #Set iterations to 1 bc+="[2a]@[%s]sn[%s]s" % l_endian_hexify(self.whole_trace) #post trig cap bc+="[30]@[00]s" #dump channel (Doesn't do anything?) bc+="[31]@[04]s" #Buffer mode (to macro) bc+="[37]@[00]s" #Analogue channel enable bc+="[26]@[%s]sn[%s]s" % l_endian_hexify(16) #pre trig cap bc+="[2c]@[00]sn[0a]s" #Time out REALLY IMPORTANT bc+="[2e]@[90]sn[00]s" #Set clock ticks bc+="[14]@[01]sn[00]s" #clock scale self.s_t.issue_wait(bc) """TRIGGER""" self.s_t.issue_wait( "[06]@[00]s" #Set trigger mask to "Don't care about anything" "[05]@[00]s" #This doesn't matter because we don't care about triggers. "[28]@[00]sn[00]s" #pre trigger delay "[32]@[00]sn[00]sn[00]sn[00]s" #Edge trigger intro, then outro filter. "[44]@[00]sn[00]s" #dig comparitor trigger (signed) "[40]@[00]sn[00]sn[00]sn[00]s" #stop watch trigger (ticks) ) bc = (self.data['ranges'][self.data['device']['model']][self.data['range']]) #Range entry bc += "U" self.s_t.issue_wait(bc) """WAVE FORM GENERATOR""" self.s_t.issue_wait( "[7c]@[c0]s" # Kitchen sink register B! ENABLES AWG!!! "[86]@[00]s" # Turn the clock generator off. ) self.s_t.issue_wait("U") self.s_t.issue_wait( "[46]@[00]s"# vpCmd (Command Vector) "[47]@[03]s"# vpMode (Operating Mode) "[5a]@[00]sn[80]sn[00]sn[00]s"# vpRatio (Phase Ratio) ) self.s_t.issue_wait("Y")# SYNTHESIZE! self.s_t.issue_wait( "[46]@[00]s"# vpCmd (Command Vector) "[47]@[00]s"# vpMode (Operating Mode) "[4a]@[e8]sn[03]s"# vpSize (Operation size, 1000 samples) "[4c]@[00]sn[00]s"# vpIndex (Operation Index, table start) "[4e]@[00]sn[00]s"# vpAddress (Destination Address, buffer start) "[54]@[ff]sn[ff]s"# vpLevel (Output Level, full scale) "[56]@[00]sn[00]s"# vpOffset (Output Offset, zero) "[5a]@[93]sn[18]sn[04]sn[00]s"# vpRatio (Phase Ratio) ) self.s_t.issue_wait("X")# TRANSLATE! self.s_t.issue_wait( "[48]@[f4]sn[80]s"# vpOption (control flags) "[50]@[af]sn[00]s"# vpClock (Sample Clock Ticks) "[52]@[e8]sn[03]s"# vpModulo (Table Modulo Size) "[5e]@[0a]sn[01]s"# vpMark (Mark Count/Phase) "[60]@[01]sn[00]s"# vpSpace (Space Count/Phase) "[78]@[00]sn[7f]s"# vrDacOutput (DAC Level) "[46]@[02]s"# vmCmd (Command Vector) ) self.s_t.issue_wait("Z")# GENERATE! self.ser.read(10000) self.s_t.issue_wait(">") self.s_t.issue("U") self.state = self.s_operation_router """Idle...........""" def s_idle(self): self.s_t.issue_wait("?") self.ser.flushInput() def s_post(self): dev = self.data['device'] dev['tested'] = True res = self.data['results'] dev['pass'] = res['comp'] if dev['connected'] and dev['tested']: if dev['pass']: self.data['test']['status'] = "passed" else: self.data['test']['status'] = "failed" self.state = self.s_idle """COMP""" def s_setup_for_comp(self): self.s_t.issue_wait( "[1e]@[05]s[21]@[12]s" #set dump and trace mode "[31]@[04]s" #Buffer mode (to macro) "[2e]@[90]sn[00]s" # Clock ticks ) self.s_t.issue_wait(self.data['ranges']['BS0005'][11]) #Range entry """AWG""" self.s_t.issue_wait("[7c]@[c0]s") # Kitchen sink register B! ENABLES AWG!!! self.s_t.issue_wait("[74]@[0f]s") # Logic pins to outputs. self.s_t.issue_wait("[86]@[00]s") # Turn the clock generator off. self.s_t.issue_wait( "[46]@[00]s"# vpCmd (Command Vector) "[47]@[03]s"# vpMode (Operating Mode) "[5a]@[00]sn[80]sn[00]sn[00]s"# vpRatio (Phase Ratio) ) self.s_t.issue_wait("Y")# SYNTHESIZE! self.s_t.issue_wait( "[47]@[00]s"# vpMode (Operating Mode) "[5a]@[93]sn[18]sn[04]sn[00]s"# vpRatio (Phase Ratio) ) self.s_t.issue_wait("X")# TRANSLATE! self.s_t.issue_wait( "[50]@[af]sn[00]s"# vpClock (Sample Clock Ticks) "[46]@[02]s"# vmCmd (Command Vector) ) self.s_t.issue_wait("Z")# GENERATE! self.s_t.issue_wait(">") self.s_t.issue_wait("U") self.ser.read(1000) self.state = self.s_init_a_req # Use alternating capture! def s_init_a_req(self): self.s_t.clear_waiting() self.ser.read(10000) self.s_t.issue_wait(">") self.s_t.issue("[37]@[01]sD") self.state = self.s_a_dump def s_a_dump(self): self.s_t.clear_waiting() self.ser.read(33) self.s_t.issue_wait(">") self.s_t.issue("A") self.state = self.s_a_proc_b_req def s_a_proc_b_req(self): self.s_t.clear_waiting() self.ser.read(self.ex_size) a_dump = self.ser.read(self.d_size) self.a['trace'] = convert_12bit_bin_dump(a_dump) self.a['ready'] = True self.s_t.issue_wait(">") self.s_t.issue("[07]@[04]s[37]@[02]sD") self.state = self.s_b_dump def s_b_dump(self): self.s_t.clear_waiting() self.ser.read(33) self.s_t.issue_wait(">") self.s_t.issue("A") self.state = self.s_b_proc_a_req def s_b_proc_a_req(self): self.s_t.clear_waiting() self.ser.read(self.ex_size) b_dump = self.ser.read(self.d_size) self.b['trace'] = convert_12bit_bin_dump(b_dump) self.b['ready'] = True self.s_t.issue_wait(">") self.s_t.issue("[07]@[00]s[37]@[01]sD") self.state = self.s_a_dump """Process funcs""" def find_trigger(self, ch): tr = self.ch[ch]['trace'] if tr: zero = self.ch[ch]['zero'][16] count = 5 for iii in range(50, len(tr)-count, 1): # 50 sample lead so we can move backwards later. pre_sum = 0 post_sum = 0 #Check previous samples are lower for prev in range(0,count-1,1): pre_sum += tr[iii + prev] #Check proceeding samples are higher for post in range(1,count,1): post_sum += tr[iii + post] post_avg = post_sum / (count) pre_avg = pre_sum / (count) if post_avg >= zero and pre_avg <= zero: return iii return None def triggered_trace(self, ch): trigger = self.find_trigger(ch) if trigger is not None: offset = self.data['sample_offset'][self.data['mode']] self.ch[ch]['display_trace'] = self.ch[ch]['trace'][trigger+offset:] # offset backwards to 0 else: self.ch[ch]['display_trace'] = self.ch[ch]['trace'] def sum_of_squares(self, ch): temp = self.data['templates'][self.data['mode']] trace = self.ch[ch]['display_trace'] error = self.ch[ch]['errors']['top'] mask = self.data['mask_height'] m2 = 500 diff_sqr = 0.0 count = 0 if trace and (len(trace) >= len(temp)): for iii in range(len(temp)): if (temp[iii] < mask and trace[iii] < mask and (trace[iii-1] - trace[iii]) < m2 and (trace[iii+1] - trace[iii]) < m2): diff_sqr += ((float(temp[iii] - error) - float(trace[iii]))**2) / 100.0 count += 1 self.ch[ch]['sos_string'] = str("%.1f"%diff_sqr).rjust(5) if count: self.ch[ch]['sos'] = diff_sqr / count def check_error(self, ch, mode): temp = self.data['templates'][self.data['mode']] trace = self.ch[ch]['display_trace'] temp_diff = 0 if len(trace) > len(temp): for pt in self.data['cal_points'][self.data['mode']][mode]: temp_diff += temp[pt] - trace[pt] self.ch[ch]['errors'][mode] = temp_diff / len(self.data['cal_points'][self.data['mode']][mode]) def test_error_value(self, ch): if abs(self.ch[ch]['errors']['shape']) >= 400: self.ch[ch]['pass']['comp'] = False def correct_error(self, ch): # Moves template wave around. Not used. :( temp = self.data['templates']['comp'] err = self.ch[ch]['error'] for iii in range(len(temp)-1): temp[iii] = temp[iii] - err def process_queue(self, ch): channel = self.ch[ch] channel['queue'].pop(0) channel['queue'].append(self.ch[ch]['sos']) if channel['frame_count'] == channel['ready_on_frame']: avg = sum(channel['queue']) / len(channel['queue']) channel['result'] = int(avg * 10) # *10 to make it pretty. No other reason. channel['frame_count'] = 0 else: channel['frame_count'] += 1 def check_pass(self, ch, op): channel = self.ch[ch] result = channel['result'] limit = channel['limits'][op] if result <= limit: channel['pass'][op] = True else: channel['pass'][op] = False if result >= 10000: channel['result'] = "OVER" def check_operation_pass(self, op): d_ch = self.ch d_res = self.data['results'] d_res['comp'] = d_ch['a']['pass']['comp'] and d_ch['b']['pass']['comp'] """UPDATES""" def comp_update(self): if self.ch['a']['ready']: self.check_error('a', 'top') self.triggered_trace('a') self.sum_of_squares('a') self.process_queue('a') self.check_error('a', 'shape') self.check_pass('a', 'comp') self.test_error_value('a') self.ch['a']['ready'] = False if self.ch['b']['ready']: self.check_error('b', 'top') self.triggered_trace('b') self.sum_of_squares('b') self.process_queue('b') self.check_error('b', 'shape') self.check_pass('b', 'comp') self.test_error_value('b') self.ch['b']['ready'] = False self.check_operation_pass('comp') def update(self): if self.test['op_count'] == 2: self.dev['connected_not_tested'] = True else: self.dev['connected_not_tested'] = False try: self.state() except serial.SerialException: print "Device disconected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconected | Error: STE" self.state = self.s_find_device self.comp_update()
class MachineTest(object): def __init__(self): self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False self.state = self.s_find_device self.trace_size = 1024 # Captured trace size in samples. self.extra_trace = 16 # ADC warmup junk self.whole_trace = self.trace_size + self.extra_trace self.d_size = self.trace_size * 2 # Dump size in bytes self.ex_size = self.extra_trace * 2 # Extra samples for ADC warmup in bytes self.whole_dump = (self.trace_size + self.extra_trace) * 2 self.data = { "device": { "model": None, "connected": False, "tested": False, "pass": False, "connected_not_tested": False }, "test": { "status": "connect", "op_count": 1 }, "ch": { "a": {}, "b": {} }, "mask_height": 5000, "masks": { # Lists of tuples containing mask ranges. "comp": [(123, 190)] }, "cal_points": { "comp": { "shape": [50, 205, 250], "top": range(191, 212) } }, "mode": "pre_test", "sample_offset": { "comp": 35, "adc": 125, "logic": 70 }, } self.data['ranges'] = {} self.data['ranges']['BS0005'] = { 1.1: "[64]@[56]sn[7e]sn[77]sn[82]s", 3.5: "[64]@[c3]sn[6a]sn[a3]sn[95]s", 5.2: "[64]@[68]sn[44]sn[ff]sn[8a]s", 11: "[64]@[6a]sn[12]sn[8c]sn[ba]s", "better_11": "[64]@[6f]sn[14]sn[8c]sn[ba]s", "max": "[64]@[00]sn[00]sn[ff]sn[ff]s" } self.data['ranges']['BS0010'] = { 0.520: "[64]@[40]sn[65]sn[82]sn[6c]s", 1.1: "[64]@[14]sn[61]sn[6f]sn[70]s", 3.5: "[64]@[5c]sn[50]sn[3a]sn[81]s", 5.2: "[64]@[9d]sn[44]sn[38]sn[8d]s", 11: "[64]@[28]sn[1c]sn[c1]sn[b5]s", "max": "[64]@[00]sn[00]sn[ff]sn[ff]s" } self.data['range'] = 11 self.data['templates'] = { "comp": ([19792] * 119) + ([480] * 152) + ([19762] * 151), "adc": float_range(189, 252, 1.15) + float_range(250, 47, -1.15) + float_range(47, 252, 1.15) + float_range(250, 188, -1.15), "logic": [] } self.data['results'] = { "comp": True, "adc": True, "logic": True, "connection": True, "test": True } self.data['c_lost'] = "" self.data['ch']['a'] = { "trace": [], "display_trace": [], "ena": False, "zero": { 16: 10131, 8: 162 }, "sos": 0, "sos_string": "", "errors": { "shape": 0, "top": 0 }, "pass": { "comp": False, "adc": False }, "limits": { "comp": 20, "adc": 100 }, "queue": [0] * 5, "result": 0, "ready": False, "frame_count": 0, "ready_on_frame": 5 } self.data['ch']['logic'] = { "traces": { "combined": [], "triggered": [], "l": [[], [], [], [], [], [], [], []] }, "error": { "a_b": False, "l4_l5": False, "staggered": False }, "ready": False, "cal_points": [361, 485, 350, 450] } # Clone ch a to ch b self.data['ch']['b'] = {} copy_dict(self.data['ch']['a'], self.data['ch']['b']) # Aliases self.a = self.data['ch']['a'] self.b = self.data['ch']['b'] self.logic_traces = self.data['ch']['logic']['traces'] """API funcs""" def capture_trace(self, ch): if ch == "logic": print self.data['ch'][ch]['traces']['combined'] else: print self.data['ch'][ch]['display_trace'] # self.data['templates']['trace'] = self.data['ch'][ch]['display_trace'] def switch_operation(self, op): self.data['mode'] = op self.state = self.s_operation_router def reset_status(self): d = self.data dev = d['device'] res = d['results'] dev['connected_not_tested'] = False dev['tested'] = False dev['model'] = dev['pass'] = True res['comp'] = res['adc'] = res['logic'] = res['test'] = True res['connection'] = True self.data['c_lost'] = '' self.data['test']['status'] = 'connect' if self.data['mode'] == "post_test": self.data['mode'] = "pre_test" """STATES""" def s_operation_router(self): op = self.data['mode'] test = self.data['test'] if self.data['device']['connected']: if op == "post_test": self.state = self.s_post test['op_count'] = 6 elif op == "pre_test": self.state = self.s_pre if self.data['test']['status'] == 'proceed': test['op_count'] = 2 elif op == "comp": self.state = self.s_setup_for_comp test['op_count'] = 3 elif op == "adc": self.state = self.s_setup_for_adc test['op_count'] = 4 elif op == "logic": self.state = self.s_setup_for_logic test['op_count'] = 5 else: self.state = self.s_find_device def s_find_device(self): if self.data['mode'] != "post_test" and self.data['mode'] != "pre_test": self.data['results'][ 'connection'] = False # This is a hack! Move it elsewhere! self.data['c_lost'] = self.data['mode'] if self.data['mode'] == "post_test": self.reset_status() elif self.data['mode'] == "pre_test": self.data['test']['status'] = 'connect' self.data['test']['op_count'] = 1 self.data['device']['connected_not_tested'] = False else: self.data['test']['status'] = 'connect' self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.read(10000) # Try to get anything in the buffer. self.s_t.clear_waiting() # Force the counter to reset. self.s_t.issue_wait("?") self.data['device']['model'] = (self.ser.read(20)[1:7]) self.data['test']['status'] = "proceed" self.dirty = True if self.data['device']['model'] in self.data['ranges']: self.state = self.s_setup_bs print self.data['device']['model'] + " Connected." else: self.state = self.s_check_model def s_setup_bs(self): bc = "" bc += "[1c]@[%s]sn[%s]s" % l_endian_hexify( self.whole_dump / 2) # Dump size bc += "[1e]@[05]s[21]@[12]s" # Set dump and trace bc += "[08]@[00]sn[00]sn[00]s" # Set spock address bc += "[16]@[01]sn[00]s" # Set iterations to 1 bc += "[2a]@[%s]sn[%s]s" % l_endian_hexify( self.whole_trace) # Post trig cap bc += "[30]@[00]s" # Dump channel (Doesn't do anything?) bc += "[31]@[04]s" # Buffer mode (to macro) bc += "[37]@[00]s" # Analogue channel enable bc += "[26]@[%s]sn[%s]s" % l_endian_hexify(16) # Pre trig cap bc += "[2c]@[00]sn[0a]s" # Time out REALLY IMPORTANT bc += "[2e]@[90]sn[00]s" # Set clock ticks bc += "[14]@[01]sn[00]s" # clock scale self.s_t.issue_wait(bc) """TRIGGER""" self.s_t.issue_wait( "[06]@[00]s" # Set trigger mask to "Don't care about anything" "[05]@[00]s" # This doesn't matter because we don't care about triggers. "[28]@[00]sn[00]s" # Pre trigger delay "[32]@[00]sn[00]sn[00]sn[00]s" # Edge trigger intro, then outro filter. "[44]@[00]sn[00]s" # Dig comparitor trigger (signed) "[40]@[00]sn[00]sn[00]sn[00]s" # stop watch trigger (ticks) ) bc = (self.data['ranges'][self.data['device']['model']][ self.data['range']]) #Range entry bc += "U" self.s_t.issue_wait(bc) """WAVE FORM GENERATOR""" self.s_t.issue_wait( "[7c]@[c0]s" # Kitchen sink register B! ENABLES AWG!!! "[86]@[00]s" # Turn the clock generator off. ) self.s_t.issue_wait("U") self.s_t.issue_wait( "[46]@[00]s" # vpCmd (Command Vector) "[47]@[03]s" # vpMode (Operating Mode) "[5a]@[00]sn[80]sn[00]sn[00]s" # vpRatio (Phase Ratio) ) self.s_t.issue_wait("Y") # SYNTHESIZE! self.s_t.issue_wait( "[46]@[00]s" # vpCmd (Command Vector) "[47]@[00]s" # vpMode (Operating Mode) "[4a]@[e8]sn[03]s" # vpSize (Operation size, 1000 samples) "[4c]@[00]sn[00]s" # vpIndex (Operation Index, table start) "[4e]@[00]sn[00]s" # vpAddress (Destination Address, buffer start) "[54]@[ff]sn[ff]s" # vpLevel (Output Level, full scale) "[56]@[00]sn[00]s" # vpOffset (Output Offset, zero) "[5a]@[93]sn[18]sn[04]sn[00]s" # vpRatio (Phase Ratio) ) self.s_t.issue_wait("X") # TRANSLATE! self.s_t.issue_wait("[48]@[f4]sn[80]s" # vpOption (control flags) "[50]@[af]sn[00]s" # vpClock (Sample Clock Ticks) "[52]@[e8]sn[03]s" # vpModulo (Table Modulo Size) "[5e]@[0a]sn[01]s" # vpMark (Mark Count/Phase) "[60]@[01]sn[00]s" # vpSpace (Space Count/Phase) "[78]@[00]sn[7f]s" # vrDacOutput (DAC Level) "[46]@[02]s" # vmCmd (Command Vector) ) self.s_t.issue_wait("Z") # GENERATE! self.ser.read(10000) self.s_t.issue_wait(">") self.s_t.issue("U") self.state = self.s_operation_router """Idle...........""" def s_idle(self): self.s_t.issue_wait("?") self.ser.flushInput() def s_pre(self): dev = self.data['device'] dev['connected_not_tested'] = not dev['tested'] and dev['connected'] self.state = self.s_idle def s_post(self): dev = self.data['device'] dev['tested'] = True res = self.data['results'] cl = self.data['c_lost'] if cl == "comp": res['comp'] = res['adc'] = res['logic'] = 'nt' elif cl == "adc": res['adc'] = res['logic'] = 'nt' elif cl == "logic": res['logic'] = 'nt' dev['pass'] = res['comp'] and res['adc'] and res['logic'] and res[ 'connection'] for p in res.values(): if p == 'nt': dev['pass'] = False dev['connected_not_tested'] = not dev['tested'] and dev['connected'] if dev['connected'] and dev['tested']: if dev['pass']: self.data['test']['status'] = "passed" else: self.data['test']['status'] = "failed" self.state = self.s_idle """COMP""" def s_setup_for_comp(self): self.s_t.issue_wait("[1e]@[05]s[21]@[12]s" # Set dump and trace mode "[31]@[04]s" # Buffer mode (to macro) "[2e]@[90]sn[00]s" # Clock ticks ) self.s_t.issue_wait(self.data['ranges']['BS0005'][11]) #Range entry """AWG""" self.s_t.issue_wait( "[7c]@[c0]s") # Kitchen sink register B! ENABLES AWG!!! self.s_t.issue_wait("[74]@[0f]s") # Logic pins to outputs. self.s_t.issue_wait("[86]@[00]s") # Turn the clock generator off. self.s_t.issue_wait( "[46]@[00]s" # vpCmd (Command Vector) "[47]@[03]s" # vpMode (Operating Mode) "[5a]@[00]sn[80]sn[00]sn[00]s" # vpRatio (Phase Ratio) ) self.s_t.issue_wait("Y") # SYNTHESIZE! self.s_t.issue_wait( "[47]@[00]s" # vpMode (Operating Mode) "[5a]@[93]sn[18]sn[04]sn[00]s" # vpRatio (Phase Ratio) ) self.s_t.issue_wait("X") # TRANSLATE! self.s_t.issue_wait("[50]@[af]sn[00]s" # vpClock (Sample Clock Ticks) "[46]@[02]s" # vmCmd (Command Vector) ) self.s_t.issue_wait("Z") # GENERATE! self.s_t.issue_wait(">") self.s_t.issue_wait("U") self.ser.read(1000) self.state = self.s_init_a_req # Use alternating capture! def s_init_a_req(self): self.s_t.clear_waiting() self.ser.read(10000) self.s_t.issue_wait(">") self.s_t.issue("[37]@[01]sD") self.state = self.s_a_dump def s_a_dump(self): self.s_t.clear_waiting() self.ser.read(33) self.s_t.issue_wait(">") self.s_t.issue("A") self.state = self.s_a_proc_b_req def s_a_proc_b_req(self): self.s_t.clear_waiting() self.ser.read(self.ex_size) a_dump = self.ser.read(self.d_size) self.data['ch']['a']['trace'] = convert_12bit_bin_dump(a_dump) self.data['ch']['a']['ready'] = True self.s_t.issue_wait("[07]@[04]s") self.s_t.issue_wait(">") self.s_t.issue("[37]@[02]sD") self.state = self.s_b_dump def s_b_dump(self): self.s_t.clear_waiting() self.ser.read(33) self.s_t.issue_wait(">") self.s_t.issue("A") self.state = self.s_b_proc_a_req def s_b_proc_a_req(self): self.s_t.clear_waiting() self.ser.read(self.ex_size) b_dump = self.ser.read(self.d_size) self.data['ch']['b']['trace'] = convert_12bit_bin_dump(b_dump) self.data['ch']['b']['ready'] = True self.s_t.issue_wait("[07]@[00]s") self.s_t.issue_wait(">") self.s_t.issue("[37]@[01]sD") self.state = self.s_a_dump """ADC""" def s_setup_for_adc(self): si = self.s_t.issue_wait siw = self.s_t.issue_wait ### General ### si("[1e]@[00]s[21]@[00]s" # set dump and trace "[31]@[00]s" # Buffer mode (to macro) "[64]@[54]sn[60]sn[42]sn[95]s" # Set range (monopole-ish) "[2e]@[90]sn[00]s" # Clock ticks ) ### AWG ### siw("[7c]@[c0]s" # Kitchen sink register B! ENABLES AWG!!! "[74]@[0f]s" # Logic pins to outputs. "[86]@[00]s" # Turn the clock generator off. # SYNTHESIZE! "[46]@[00]s" "[47]@[01]s" # vpMode (Operating Mode) "[5a]@[00]sn[80]sn[00]sn[00]s" # vpRatio (Phase Ratio) "Y") siw( # TRANSLATE! "[47]@[00]s" # vpMode (Operating Mode) "[5a]@[93]sn[18]sn[04]sn[00]s" # vpRatio (Phase Ratio) "X") siw( # GENERATE! "[50]@[cd]sn[00]s" # vpClock (Sample Clock Ticks) "[46]@[02]s" # vmCmd (Command Vector) "Z") siw(">") siw("U") self.s_t.clear_waiting() self.ser.flushInput() self.state = self.s_adc_init_a_req def s_adc_init_a_req(self): self.s_t.clear_waiting() self.ser.read(10000) self.s_t.issue_wait("[07]@[00]s") self.s_t.issue_wait(">") self.s_t.issue("[37]@[01]sD") self.state = self.s_adc_a_dump def s_adc_a_dump(self): self.s_t.clear_waiting() self.ser.read(33) self.s_t.issue_wait(">") self.s_t.issue("A") self.state = self.s_adc_a_proc_req def s_adc_a_proc_req(self): self.s_t.clear_waiting() self.ser.read(self.extra_trace) a_dump = self.ser.read(self.trace_size) self.data['ch']['a']['trace'] = convert_8bit_dump(a_dump) self.data['ch']['a']['ready'] = True self.s_t.issue_wait(">") self.s_t.issue("[37]@[01]sD") self.state = self.s_adc_a_dump """LOGIC""" def s_setup_for_logic(self): """General""" self.s_t.issue_wait("[1e]@[00]s[21]@[0e]s" # set dump and trace "[31]@[00]s" # Buffer mode "[2e]@[28]sn[00]s" # Clock ticks ) """AWG""" self.s_t.issue_wait( "[7c]@[00]sU") # Kitchen sink register B! Disables AWG!!! self.s_t.issue_wait("[74]@[00]sU") # Logic pins to inputs. self.s_t.issue_wait( "[99]@[12]sU") # vpMap (Enable clock 1 on output L5) self.s_t.issue_wait("[46]@[01]sZ") self.s_t.issue_wait( "[50]@[10]sn[27]s" # vpClock (Master clock ticks per period, 20) "[82]@[00]sn[00]s" # vpRise (Rising Edge at tick 0) "[84]@[88]sn[13]s" # vpFall (Falling Edge at tick 10) "[86]@[80]s" # vpControl (Enable Clock, select source 0) "[46]@[03]sn[00]sZ" # vmCmd (Command Vector), GENERATE! ) self.s_t.issue_wait(">") self.s_t.issue_wait("U") self.ser.read(1000) self.state = self.s_logic_init_req def s_logic_init_req(self): self.s_t.clear_waiting() self.ser.read(1000) self.s_t.issue_wait(">") self.s_t.issue("[37]@[80]sD") self.state = self.s_logic_dump def s_logic_dump(self): self.s_t.clear_waiting() self.ser.read(33) self.s_t.issue_wait(">") self.s_t.issue("A") self.state = self.s_logic_proc_req def s_logic_proc_req(self): self.s_t.clear_waiting() self.ser.read(self.extra_trace) dump = self.ser.read(self.trace_size) self.logic_traces['combined'] = convert_logic_dump(dump) self.data['ch']['logic']['ready'] = True self.s_t.issue_wait(">") self.s_t.issue("[37]@[80]sD") self.state = self.s_logic_dump """Process funcs""" def find_trigger(self, ch): if ch != "logic": tr = self.data['ch'][ch]['trace'] else: tr = self.data['ch'][ch]['traces']['combined'] if tr: if self.data['mode'] == "comp": zero = self.data['ch'][ch]['zero'][16] count = 5 for iii in range( 50, len(tr) - count, 1): # 50 sample lead so we can move backwards later. pre_sum = 0 post_sum = 0 # Check previous samples are lower for prev in range(0, count - 1, 1): pre_sum += tr[iii + prev] # Check proceeding samples are higher for post in range(1, count, 1): post_sum += tr[iii + post] post_avg = post_sum / (count) pre_avg = pre_sum / (count) if post_avg >= zero and pre_avg <= zero: return iii return None elif self.data['mode'] == "adc": for iii in range(50, len(tr) - 10, 1): sur = (tr[iii - 1] + tr[iii] + tr[iii + 1]) / 3 if (sur < tr[iii - 5] and sur < tr[iii + 5] and tr[iii] < 55): return iii return None elif self.data['mode'] == "logic": for iii in range(1, len(tr) - 1, 1): if (tr[iii - 1][2] == 0 and tr[iii][2] == 1): return iii return None def triggered_trace(self, ch): trigger = self.find_trigger(ch) if trigger is not None: offset = self.data['sample_offset'][self.data['mode']] if ch != "logic": self.data['ch'][ch]['display_trace'] = self.data['ch'][ch][ 'trace'][trigger + offset:] # offset backwards to 0 else: self.data['ch'][ch]['traces']['triggered'] = self.data['ch'][ ch]['traces']['combined'][trigger + offset:] else: if ch != "logic": self.data['ch'][ch]['display_trace'] = self.data['ch'][ch][ 'trace'] else: self.data['ch'][ch]['traces']['triggered'] = self.data['ch'][ ch]['traces']['combined'] def sum_of_squares(self, ch): template = self.data['templates'][self.data['mode']] trace = self.data['ch'][ch]['display_trace'] error = self.data['ch'][ch]['errors']['top'] max_level = self.data[ 'mask_height'] # Max value a sample can be to be considered max_diff = 500 # Max acceptable diff between two samples diff_sqr = 0.0 samples_to_inspect = 100 count = 0 if trace and (len(trace) >= len(template)): for iii in range(len(template)): if (template[iii] < max_level and trace[iii] < max_level and (trace[iii - 1] - trace[iii]) < max_diff # Check we're 2 sample into the flat and count <= samples_to_inspect): diff_sqr += ((float(template[iii] - error) - float(trace[iii]))**2) / 100.0 count += 1 elif count > samples_to_inspect: break self.data['ch'][ch]['sos_string'] = str("%.1f" % diff_sqr).rjust(5) if count: self.data['ch'][ch]['sos'] = diff_sqr / count def sum_of_squares_adc(self, ch): temp = self.data['templates'][self.data['mode']] trace = self.data['ch'][ch]['display_trace'] diff_sqr = 0.0 if trace and (len(trace) >= len(temp)): for iii in range(len(temp)): diff_sqr += ((float(temp[iii]) - float(trace[iii]))**2) / 5 self.data['ch'][ch]['sos'] = diff_sqr / len(temp) def check_logic_stagger(self): prev_fall = None log = self.data['ch']['logic']['traces']['l'] err = self.data['ch']['logic']['error'] for iii in range(3, 8): prev_fall = self.find_fall(iii - 1) if prev_fall is not None: if log[iii][prev_fall] != 1: err['staggered'] = True else: err['staggered'] = False def check_logic_points(self): pts = self.data['ch']['logic']['cal_points'] tr = self.data['ch']['logic']['traces']['l'] err = self.data['ch']['logic']['error'] error = False for bit in (0, 1, 2, 3): val = 0 tr_bit = tr[bit] for p in pts: if tr_bit[p] != val: error = True val = not val if bit == 0 or bit == 1: err['a_b'] = error if bit == 1: error = False else: err['l4_l5'] = error def find_fall(self, trace): log = self.data['ch']['logic']['traces']['l'][trace] for iii in range(10, len(log)): if log[iii] == 0 and log[iii - 1] == 1: return iii def check_error(self, ch, mode): temp = self.data['templates'][self.data['mode']] trace = self.data['ch'][ch]['display_trace'] temp_diff = 0 if len(trace) > len(temp): for pt in self.data['cal_points'][self.data['mode']][mode]: temp_diff += temp[pt] - trace[pt] self.data['ch'][ch]['errors'][mode] = temp_diff / len( self.data['cal_points'][self.data['mode']][mode]) def test_error_value(self, ch): if abs(self.data['ch'][ch]['errors']['shape']) >= 400: self.data['ch'][ch]['pass']['comp'] = False def correct_error(self, ch): # Moves template wave around. Not used. :( temp = self.data['templates']['comp'] err = self.data['ch'][ch]['error'] for iii in range(len(temp) - 1): temp[iii] = temp[iii] - err def process_queue(self, ch): channel = self.data['ch'][ch] channel['queue'].pop(0) channel['queue'].append(self.data['ch'][ch]['sos']) if channel['frame_count'] == channel['ready_on_frame']: avg = sum(channel['queue']) / len(channel['queue']) channel['result'] = int( avg * 10) # *10 to make it pretty. No other reason. channel['frame_count'] = 0 else: channel['frame_count'] += 1 def process_logic(self): # Separate each bit position into a stream. trig = self.logic_traces['triggered'] if trig: log = self.logic_traces['l'] for stream in range(len(log)): t = [] for sample in trig: t.append(int(sample[stream])) log[stream] = t def check_pass(self, ch, op): channel = self.data['ch'][ch] result = channel['result'] limit = channel['limits'][op] if result <= limit: channel['pass'][op] = True else: channel['pass'][op] = False if result >= 10000: channel['result'] = "OVER" def check_operation_pass(self, op): d_ch = self.data['ch'] d_res = self.data['results'] if op == "comp": d_res['comp'] = d_ch['a']['pass']['comp'] and d_ch['b']['pass'][ 'comp'] elif op == "adc": d_res['adc'] = d_ch['a']['pass']['adc'] elif op == "logic": log_err = d_ch['logic']['error'] a_b = not log_err['a_b'] l4_l5 = not log_err['l4_l5'] stag = not log_err['staggered'] d_res['logic'] = a_b and l4_l5 and stag self.data['device']['pass'] = all(self.data['results'].values()) """UPDATES""" def comp_update(self): if self.data['ch']['a']['ready']: self.check_error('a', 'top') self.triggered_trace('a') self.sum_of_squares('a') self.process_queue('a') self.check_error('a', 'shape') self.check_pass('a', 'comp') self.test_error_value('a') self.data['ch']['a']['ready'] = False if self.data['ch']['b']['ready']: self.check_error('b', 'top') self.triggered_trace('b') self.sum_of_squares('b') self.process_queue('b') self.check_error('b', 'shape') self.check_pass('b', 'comp') self.test_error_value('b') self.data['ch']['b']['ready'] = False self.check_operation_pass('comp') def adc_update(self): if self.data['ch']['a']['ready']: self.triggered_trace('a') self.sum_of_squares_adc('a') self.process_queue('a') self.check_pass('a', 'adc') self.data['ch']['a']['ready'] = False self.check_operation_pass('adc') def logic_update(self): if self.data['ch']['logic']['ready']: self.triggered_trace("logic") self.process_logic() self.check_logic_points() self.check_logic_stagger() self.check_operation_pass('logic') def update(self): try: self.state() except serial.SerialException: print "Device disconected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconected | Error: STE" self.state = self.s_find_device if self.data['mode'] == "comp": self.comp_update() elif self.data['mode'] == "adc": self.adc_update() elif self.data['mode'] == "logic": self.logic_update()
class MachineXYPlot(object): def __init__(self): """Serial Setup""" self.ser = None self.s_t = Serial_Tools(self.ser) self.s_t.show_messages = False self.state = self.s_find_device self.trace_size = 1024 self.extra_trace = 4 self.whole_trace = self.trace_size + self.extra_trace self.dump_size = self.trace_size self.extra_dump = self.extra_trace self.whole_dump = self.dump_size + self.extra_dump """Data exposed through API""" self.data = { "device": { "connected": False, "model": None }, "accepted_models": ["BS0005", "BS0010"], "trace": [] } self.a_d, self.b_d = [], [] """API Functions""" """States""" def s_find_device(self): self.ser = self.s_t.find_device() if self.ser != None: self.data['device']['connected'] = True self.state = self.s_check_model else: if self.data['device']['connected']: self.data['device']['connected'] = False self.state = self.s_find_device def s_check_model(self): self.ser.flushInput # Try to get anything in the buffer. self.s_t.clear_waiting() # Force the counter to reset. self.s_t.issue_wait("?") model = (self.ser.read(20)[1:7]) self.data['device']['model'] = model print self.data['device']['model'] + " Connected." self.dirty = True if model in self.data['accepted_models']: self.state = self.s_setup_bs else: self.state = self.s_check_model def s_setup_bs(self): si = self.s_t.issue siw = self.s_t.issue_wait si("[1c]@[%s]sn[%s]s" % (l_endian_hexify(self.whole_dump))) # Dump size si("[1e]@[00]s[21]@[02]s") # Dump/Trace = Filter/Macro si("[08]@[00]sn[00]sn[00]s") # Spock address si("[16]@[01]sn[00]s") # Iterations = 1 si("[2a]@[%s]sn[%s]s" % (l_endian_hexify(self.whole_trace))) # Post trig cap (was 1024) si("[30]@[00]s") # Dump channel si("[31]@[01]s") # Buffer mode chop si("[37]@[03]s") # Analogue channel enable si("[26]@[%s]sn[%s]s" % (l_endian_hexify(self.whole_trace))) # Pre trig cap si("[2c]@[00]sn[0f]s") # Time out REALLY IMPORTANT si("[2e]@[28]sn[00]s") # Set clock ticks (ticks(6us steps) per sample) si("[14]@[01]sn[00]s") # Clock scale """TRIGGER""" si("[06]@[00]s") # Set trigger mask to "Don't care about anything" si("[05]@[00]s" ) # This doesn't matter because we don't care about triggers. """Filter mode stuff""" #self.issue("[18]@[10]sn[00]s") # Dump send to 16 #self.issue("[1a]@[00]sn[00]s") # skip = 0 (sum 16, skip 0, sum 16, skip 0, ...) r_high, r_low = to_span(2.0, 4.0) siw("[64]@[%s]sn[%s]s" % l_endian_hexify(r_low)) # Range low siw("[66]@[%s]sn[%s]s" % l_endian_hexify(r_high)) # Range high self.ser.read(1000) siw(">") siw("U") self.state = self.s_change_to_chop def s_change_to_chop(self): si = self.s_t.issue siw = self.s_t.issue_wait self.ser.read(1000) self.s_t.clear_waiting() si("[1e]@[00]s") # Set dump mode si("[21]@[02]s") # Set trace mode si("[31]@[01]s") # Buffer mode si("[37]@[03]s") # Ana ch enable siw(">") siw("U") self.state = self.s_pre_idle def s_pre_idle(self): self.s_t.clear_waiting() self.ser.read(1000) self.state = self.s_idle def s_idle(self): #Idle self.s_t.issue_wait("check") self.state = self.s_init_req """Dual chop""" def s_init_req(self): self.s_t.clear_waiting() self.ser.read(10000) self.s_t.issue_wait(">") self.s_t.issue("[37]@[03]sD") self.state = self.s_a_dump def s_a_dump(self): self.s_t.clear_waiting() self.ser.read(33) self.s_t.issue_wait(">") self.s_t.issue("[30]@[00]sA") # Dump chA self.state = self.s_a_proc_b_dump def s_a_proc_b_dump(self): self.s_t.clear_waiting() self.ser.read(self.extra_trace) self.a_d = self.ser.read(self.dump_size) self.s_t.issue_wait(">") self.s_t.issue("[30]@[01]sA") # Dump chA self.state = self.s_b_proc_a_b_req def s_b_proc_a_b_req(self): self.s_t.clear_waiting() self.ser.read(self.extra_trace) self.b_d = self.ser.read(self.dump_size) self.s_t.issue_wait(">") self.s_t.issue("[37]@[03]sD") # Req both self.generate_lissajous() self.state = self.s_a_dump """Data Processing Functions""" def generate_lissajous(self): a = map(ord, self.a_d) b = map(ord, self.b_d) line = [] for i in range(0, len(a) - 1): # line.append([(a[i])*(490/256), (b[i])*(490/256)]) line.append([(a[i]), (b[i])]) self.data['trace'] = line """Update Functions""" def update(self): try: self.state() except serial.SerialException: print "Device disconected | Error: SE" self.state = self.s_find_device except serial.SerialTimeoutException: print "Device disconected | Error: STE" self.state = self.s_find_device