diff --git a/apps/rc/forms.py b/apps/rc/forms.py index b73ac79..982784d 100644 --- a/apps/rc/forms.py +++ b/apps/rc/forms.py @@ -7,13 +7,13 @@ from django.utils.safestring import mark_safe from apps.main.models import Device from apps.main.forms import add_empty_choice from .models import RCConfiguration, RCLine, RCLineType, RCLineCode -from .widgets import KmUnitWidget, KmUnitHzWidget, KmUnitDcWidget, UnitKmWidget, DefaultWidget, CodesWidget, HiddenWidget +from .widgets import KmUnitWidget, KmUnitHzWidget, KmUnitDcWidget, UnitKmWidget, DefaultWidget, CodesWidget, HiddenWidget, HCheckboxSelectMultiple def create_choices_from_model(model, conf_id, all=False): if model=='RCLine': instance = RCConfiguration.objects.get(pk=conf_id) - choices = [(line.pk, line.get_name()) for line in instance.get_lines(type='tx')] + choices = [(line.pk, line.get_name()) for line in instance.get_lines(line_type__name='tx')] choices = add_empty_choice(choices, label='All') else: instance = globals()[model] @@ -64,7 +64,7 @@ class RCConfigurationForm(forms.ModelForm): devices = Device.objects.filter(device_type__name='rc') if instance.experiment: - self.fields['experiment'].widget.attrs['disabled'] = 'disabled' + self.fields['experiment'].widget.attrs['read_only'] = True #self.fields['experiment'].widget.choices = [(instance.experiment.id, instance.experiment)] self.fields['device'].widget.choices = [(device.id, device) for device in devices] self.fields['ipp'].widget = KmUnitHzWidget(attrs={'km2unit':instance.km2unit}) @@ -91,7 +91,39 @@ class RCConfigurationForm(forms.ModelForm): self.add_error('ipp', 'Invalid IPP units={}'.format(form_data['ipp']*(20./3*(form_data['clock_in']/form_data['clock_divider'])))) return form_data - + + +class RCMixConfigurationForm(forms.Form): + + clock_in = forms.CharField(widget=forms.HiddenInput()) + clock_divider = forms.CharField(widget=forms.HiddenInput()) + name = forms.CharField() + experiment = forms.ChoiceField() + operation = forms.ChoiceField(widget=forms.RadioSelect(), + choices=[(0, 'OR'), (1, 'XOR'), (2, 'AND'), (3, 'NAND')], + initial=1) + delay = forms.CharField() + mask = forms.MultipleChoiceField(choices=[(0, 'L1'),(1, 'L2'),(2, 'L3'),(3, 'L4'),(4, 'L5'),(5, 'L6'),(6, 'L7'),(7, 'L8')], + widget=HCheckboxSelectMultiple()) + result = forms.CharField(required=False, + widget=forms.Textarea(attrs={'readonly':True, 'rows':5, 'class':'tabuled'})) + + def __init__(self, *args, **kwargs): + confs = kwargs.pop('confs', []) + if confs: + km2unit = confs[0].km2unit + clock_in = confs[0].clock_in + clock_divider = confs[0].clock_divider + else: + km2unit = clock_in = clock_divider = 0 + super(RCMixConfigurationForm, self).__init__(*args, **kwargs) + self.fields['experiment'].choices = [(conf.pk, '{} | {}'.format(conf.pk, conf.name)) for conf in confs] + self.fields['delay'].widget = KmUnitWidget(attrs = {'km2unit':km2unit}) + self.fields['clock_in'].initial = clock_in + self.fields['clock_divider'].initial = clock_divider + + + class RCLineForm(forms.ModelForm): def __init__(self, *args, **kwargs): diff --git a/apps/rc/models.py b/apps/rc/models.py index c9c3721..6ffdd6a 100644 --- a/apps/rc/models.py +++ b/apps/rc/models.py @@ -10,6 +10,7 @@ from django.core.urlresolvers import reverse from django.core.validators import MinValueValidator, MaxValueValidator from apps.main.models import Configuration +from devices.rc import api from .utils import RCFile, pulses, pulses_from_code, create_mask, pulses_to_points # Create your models here. @@ -23,6 +24,7 @@ LINE_TYPES = ( ('sync', 'Synchronizing signal'), ('flip', 'IPP related periodic signal'), ('prog_pulses', 'Programmable pulse'), + ('mix', 'Mixed line'), ) @@ -55,23 +57,30 @@ DAT_CMDS = { class RCConfiguration(Configuration): - ipp = models.FloatField(verbose_name='Inter pulse period (Km)', validators=[MinValueValidator(1), MaxValueValidator(1000)], default=10) - ntx = models.PositiveIntegerField(verbose_name='Number of TX', validators=[MinValueValidator(1), MaxValueValidator(256)], default=1) + ipp = models.FloatField(verbose_name='Inter pulse period (Km)', validators=[MinValueValidator(1), MaxValueValidator(9000)], default=300) + ntx = models.PositiveIntegerField(verbose_name='Number of TX', validators=[MinValueValidator(1), MaxValueValidator(300)], default=1) clock_in = models.FloatField(verbose_name='Clock in (MHz)', validators=[MinValueValidator(1), MaxValueValidator(80)], default=1) clock_divider = models.PositiveIntegerField(verbose_name='Clock divider', validators=[MinValueValidator(1), MaxValueValidator(256)], default=1) clock = models.FloatField(verbose_name='Clock Master (MHz)', blank=True, default=1) - time_before = models.PositiveIntegerField(verbose_name='Time before (μS)', default=0) - time_after = models.PositiveIntegerField(verbose_name='Time after (μS)', default=0) + time_before = models.PositiveIntegerField(verbose_name='Time before (μS)', default=12) + time_after = models.PositiveIntegerField(verbose_name='Time after (μS)', default=1) sync = models.PositiveIntegerField(verbose_name='Synchro delay', default=0) sampling_reference = models.CharField(verbose_name='Sampling Reference', choices=SAMPLING_REFS, default='none', max_length=40) control_tx = models.BooleanField(verbose_name='Control Switch TX', default=False) control_sw = models.BooleanField(verbose_name='Control Switch SW', default=False) - + mix = models.BooleanField(default=False) class Meta: db_table = 'rc_configurations' + def __unicode__(self): + + if self.mix: + return u'[MIXED]: %s' % self.name + else: + return u'[%s]: %s' % (self.device.name, self.name) + def get_absolute_url_plot(self): return reverse('url_plot_rc_pulses', args=[str(self.id)]) @@ -83,13 +92,11 @@ class RCConfiguration(Configuration): return self.clock_in/self.clock_divider - @property def km2unit(self): return 20./3*(self.clock_in/self.clock_divider) - def clone(self, **kwargs): lines = self.get_lines() @@ -104,15 +111,13 @@ class RCConfiguration(Configuration): return self - def get_lines(self, type=None): + def get_lines(self, **kwargs): ''' Retrieve configuration lines ''' - if type is not None: - return RCLine.objects.filter(rc_configuration=self.pk, line_type__name=type) - else: - return RCLine.objects.filter(rc_configuration=self.pk) + return RCLine.objects.filter(rc_configuration=self.pk, **kwargs) + def clean_lines(self): ''' @@ -146,9 +151,67 @@ class RCConfiguration(Configuration): line_data['type'] = line.line_type.name data['lines'].append(line_data) + data['delays'] = self.get_delays() + data['pulses'] = self.get_pulses() return data + def dict_to_parms(self, data): + ''' + ''' + + self.name = data['name'] + self.ipp = data['ipp'] + self.ntx = data['ntx'] + self.clock_in = data['clock_in'] + self.clock_divider = data['clock_divider'] + self.clock = data['clock'] + self.time_before = data['time_before'] + self.time_after = data['time_after'] + self.sync = data['sync'] + self.sampling_reference = data['sampling_reference'] + self.clean_lines() + + lines = [] + positions = {'tx':0, 'tr':0} + + for i, line_data in enumerate(data['lines']): + line_type = RCLineType.objects.get(name=line_data.pop('type')) + if line_type.name=='codes': + code = RCLineCode.objects.get(name=line_data['code']) + line_data['code'] = code.pk + line = RCLine.objects.filter(rc_configuration=self, channel=i) + if line: + line = line[0] + line.line_type = line_type + line.params = json.dumps(line_data) + else: + line = RCLine(rc_configuration=self, line_type=line_type, + params=json.dumps(line_data), + channel=i) + + if line_type.name=='tx': + line.position = positions['tx'] + positions['tx'] += 1 + + if line_type.name=='tr': + line.position = positions['tr'] + positions['tr'] += 1 + + line.save() + lines.append(line) + + for line, line_data in zip(lines, data['lines']): + if 'TX_ref' in line_data: + params = json.loads(line.params) + if line_data['TX_ref'] in (0, '0'): + params['TX_ref'] = '0' + else: + params['TX_ref'] = [l.pk for l in lines if l.line_type.name=='tx' and l.get_name()==line_data['TX_ref']][0] + line.params = json.dumps(params) + line.save() + + def get_delays(self): pulses = [line.get_pulses() for line in self.get_lines()] @@ -163,14 +226,24 @@ class RCConfiguration(Configuration): return [points[i+1]-points[i] for i in range(len(points)-1)] - def get_flips(self): + def get_pulses(self, binary=True): + + pulses = [line.get_pulses() for line in self.get_lines()] + points = [tup for tups in pulses for tup in tups] + points = set([x for tup in points for x in tup]) + points = list(points) + points.sort() line_points = [pulses_to_points(line.pulses_as_array()) for line in self.get_lines()] line_points = [[(x, x+y) for x,y in tups] for tups in line_points] line_points = [[t for x in tups for t in x] for tups in line_points] states = [[1 if x in tups else 0 for tups in line_points] for x in points] - return states + if binary: + states.reverse() + states = [int(''.join([str(x) for x in flips]), 2) for flips in states] + + return states[:-1] def add_cmd(self, cmd): @@ -210,17 +283,8 @@ class RCConfiguration(Configuration): data.append(self.add_cmd('DELAY_START')) # first delay is always zero data.append(self.add_data(1)) - line_points = [pulses_to_points(line.pulses_as_array()) for line in self.get_lines()] - points = [tup for tups in line_points for tup in tups] - points = [(x, x+y) for x,y in points] - points = set([x for tup in points for x in tup]) - points = list(points) - points.sort() - - if points[0]<>0: - points.insert(0, 0) - - delays = [points[i+1]-points[i] for i in range(len(points)-1)] + + delays = self.get_delays() for delay in delays: while delay>252: @@ -230,10 +294,10 @@ class RCConfiguration(Configuration): # write flips data.append(self.add_cmd('FLIP_START')) - line_points = [[(x, x+y) for x,y in tups] for tups in line_points] - line_points = [[t for x in tups for t in x] for tups in line_points] - states = [[1 if x in tups else 0 for tups in line_points] for x in points] - for flips, delay in zip(states[:-1], delays): + + states = self.get_pulses(binary=False) + + for flips, delay in zip(states, delays): flips.reverse() flip = int(''.join([str(x) for x in flips]), 2) data.append(self.add_data(flip+1)) @@ -243,7 +307,7 @@ class RCConfiguration(Configuration): # write sampling period data.append(self.add_cmd('SAMPLING_PERIOD')) - wins = self.get_lines(type='windows') + wins = self.get_lines(line_type__name='windows') if wins: win_params = json.loads(wins[0].params)['params'] if win_params: @@ -265,63 +329,59 @@ class RCConfiguration(Configuration): ''' f = RCFile(filename) - data = f.data - self.name = data['name'] - self.ipp = data['ipp'] - self.ntx = data['ntx'] - self.clock_in = data['clock_in'] - self.clock_divider = data['clock_divider'] - self.clock = data['clock'] - self.time_before = data['time_before'] - self.time_after = data['time_after'] - self.sync = data['sync'] - self.sampling_reference = data['sampling_reference'] - self.clean_lines() - - lines = [] - positions = {'tx':0, 'tr':0} + self.dict_to_parms(f.data) + + def update_pulses(self): - for i, line_data in enumerate(data['lines']): - line_type = RCLineType.objects.get(name=line_data.pop('type')) - if line_type.name=='codes': - code = RCLineCode.objects.get(name=line_data['code']) - line_data['code'] = code.pk - line = RCLine.objects.filter(rc_configuration=self, channel=i) - if line: - line = line[0] - line.line_type = line_type - line.params = json.dumps(line_data) - else: - line = RCLine(rc_configuration=self, line_type=line_type, - params=json.dumps(line_data), - channel=i) - - if line_type.name=='tx': - line.position = positions['tx'] - positions['tx'] += 1 - - if line_type.name=='tr': - line.position = positions['tr'] - positions['tr'] += 1 - - line.save() - lines.append(line) - - for line, line_data in zip(lines, data['lines']): - if 'TX_ref' in line_data: - params = json.loads(line.params) - if line_data['TX_ref'] in (0, '0'): - params['TX_ref'] = '0' - else: - params['TX_ref'] = [l.pk for l in lines if l.line_type.name=='tx' and l.get_name()==line_data['TX_ref']][0] - line.params = json.dumps(params) - line.save() + for line in self.get_lines(): + if line.line_type.name=='tr': + continue + line.update_pulses() - + for tr in self.get_lines(line_type__name='tr'): + tr.update_pulses() + def status_device(self): return 0 + def stop_device(self): + + answer = api.disable(ip = self.device.ip_address, + port = self.device.port_address) + + if answer[0] != "1": + self.message = answer[0:] + return 0 + + self.message = answer[2:] + return 1 + + def start_device(self): + + answer = api.enable(ip = self.device.ip_address, + port = self.device.port_address) + + if answer[0] != "1": + self.message = answer[0:] + return 0 + + self.message = answer[2:] + return 1 + + def write_device(self): + answer = api.write_config(ip = self.device.ip_address, + port = self.device.port_address, + parms = self.parms_to_dict()) + + if answer[0] != "1": + self.message = answer[0:] + return 0 + + self.message = answer[2:] + return 1 + + class RCLineCode(models.Model): name = models.CharField(max_length=40) @@ -336,6 +396,7 @@ class RCLineCode(models.Model): def __unicode__(self): return u'%s' % self.name + class RCLineType(models.Model): name = models.CharField(choices=LINE_TYPES, max_length=40) @@ -388,29 +449,24 @@ class RCLine(models.Model): return self.line_type.name.upper() pk = json.loads(self.params)['TX_ref'] if pk in (0, '0'): - refs = ','.join(chars[l.position] for l in self.rc_configuration.get_lines('tx')) + refs = ','.join(chars[l.position] for l in self.rc_configuration.get_lines(line_type__name='tx')) return '%s (%s)' % (self.line_type.name.upper(), refs) else: ref = RCLine.objects.get(pk=pk) return '%s (%s)' % (self.line_type.name.upper(), chars[ref.position]) - elif self.line_type.name in ('flip', 'prog_pulses', 'sync', 'none'): + elif self.line_type.name in ('flip', 'prog_pulses', 'sync', 'none', 'mix'): return '%s %s' % (self.line_type.name.upper(), self.channel) else: return self.line_type.name.upper() - def get_lines(self, type=None): - - if type is not None: - return RCLine.objects.filter(rc_configuration=self.rc_configuration, line_type__name=type) - else: - return RCLine.objects.filter(rc_configuration=self.rc_configuration) - + def get_lines(self, **kwargs): + + return RCLine.objects.filter(rc_configuration=self.rc_configuration, **kwargs) def pulses_as_array(self): return (np.fromstring(self.pulses, dtype=np.uint8)-48).astype(np.int8) - def get_pulses(self): X = self.pulses_as_array() @@ -432,12 +488,10 @@ class RCLine(models.Model): def get_win_ref(self, params, tx_id, km2unit): ref = self.rc_configuration.sampling_reference - - codes = [line for line in self.get_lines(type='code') if int(json.loads(line.params)['TX_ref'])==int(tx_id)] - - if codes: - code_line = RCLineCode.objects.get(pk=json.loads(codes[0].params)['code']) - tx_width = float(json.loads(RCLine.objects.get(pk=tx_id).params)['pulse_width'])*km2unit/code_line.bits_per_code + codes = [line for line in self.get_lines(line_type__name='codes') if int(json.loads(line.params)['TX_ref'])==int(tx_id)] + + if codes: + tx_width = float(json.loads(RCLine.objects.get(pk=tx_id).params)['pulse_width'])*km2unit/len(json.loads(codes[0].params)['codes'][0]) else: tx_width = float(json.loads(RCLine.objects.get(pk=tx_id).params)['pulse_width'])*km2unit @@ -464,7 +518,7 @@ class RCLine(models.Model): if self.line_type.name=='tr': params = json.loads(self.params) if params['TX_ref'] in ('0', 0): - txs = [tx.update_pulses(save=False, tr=True) for tx in self.get_lines('tx')] + txs = [tx.update_pulses(save=False, tr=True) for tx in self.get_lines(line_type__name='tx')] else: txs = [tx.update_pulses(save=False, tr=True) for tx in RCLine.objects.filter(pk=params['TX_ref'])] if len(txs)==0 or 0 in [len(tx) for tx in txs]: @@ -498,13 +552,12 @@ class RCLine(models.Model): elif self.line_type.name=='codes': params = json.loads(self.params) - #codes = ast.literal_eval(RCLineCode.objects.get(pk=json.loads(self.params)['code']).codes) tx = RCLine.objects.get(pk=params['TX_ref']) tx_params = json.loads(tx.params) - - y = pulses_from_code(ipp_u, ntx, params['codes'], - int(float(tx_params['pulse_width'])*km2unit), - before=int(self.rc_configuration.time_before*us2unit)+self.rc_configuration.sync) + delays = [float(d)*km2unit for d in tx_params['delays'].split(',') if d] + y = pulses_from_code(tx.pulses_as_array(), + params['codes'], + int(float(tx_params['pulse_width'])*km2unit)) ranges = tx_params['range'].split(',') if len(ranges)>0 and ranges[0]<>'0': @@ -533,20 +586,53 @@ class RCLine(models.Model): elif self.line_type.name=='windows': params = json.loads(self.params) + if 'params' in params and len(params['params'])>0: - print 'REFS' - print [self.get_win_ref(pp, params['TX_ref'],km2unit) for pp in params['params']] y = sum([pulses(x, ipp_u, pp['resolution']*pp['number_of_samples']*km2unit, shift=0, before=int(self.rc_configuration.time_before*us2unit)+self.get_win_ref(pp, params['TX_ref'],km2unit), sync=self.rc_configuration.sync) for pp in params['params']]) - tr = self.get_lines('tr')[0] + tr = self.get_lines(line_type__name='tr')[0] ranges = json.loads(tr.params)['range'].split(',') if len(ranges)>0 and ranges[0]<>'0': mask = create_mask(ranges, ipp_u, ntx, self.rc_configuration.sync) y = y & mask else: y = np.zeros(ipp_u*ntx) + + elif self.line_type.name=='mix': + values = self.rc_configuration.parameters.split('-') + confs = RCConfiguration.objects.filter(pk__in=[value.split('|')[0] for value in values]) + modes = [value.split('|')[1] for value in values] + delays = [value.split('|')[2] for value in values] + masks = [value.split('|')[3] for value in values] + + y = confs[0].get_lines(channel=self.channel)[0].pulses_as_array() + + for i in range(1, len(values)): + mask = list('{:8b}'.format(int(masks[i]))) + mask.reverse() + + if mask[self.channel] in ('0', '', ' '): + continue + Y = confs[i].get_lines(channel=self.channel)[0].pulses_as_array() + delay = float(delays[i])*km2unit + if delay>0: + y_temp = np.empty_like(Y) + y_temp[:delay] = 0 + y_temp[delay:] = Y[:-delay] + + if modes[i]=='OR': + y2 = y | y_temp + elif modes[i]=='XOR': + y2 = y ^ y_temp + elif modes[i]=='AND': + y2 = y & y_temp + elif modes[i]=='NAND': + y2 = y & ~y_temp + + y = y2 + else: y = np.zeros(ipp_u*ntx) @@ -555,5 +641,5 @@ class RCLine(models.Model): self.save() else: return y - + \ No newline at end of file diff --git a/apps/rc/utils.py b/apps/rc/utils.py index 7ab1af8..11c218f 100644 --- a/apps/rc/utils.py +++ b/apps/rc/utils.py @@ -223,27 +223,28 @@ def pulses_to_points(X): dw += 1 return [(tup[0], tup[1]-tup[0]) for tup in zip(up, dw)] - -def pulses_from_code(ipp, ntx, codes, width, before=0): +def pulses_from_code(X, codes, width): - if ntx>len(codes): - ipp_codes = [c for __ in xrange(ntx) for c in codes][:ntx] - else: - ipp_codes = codes[:ntx] + d = X[1:]-X[:-1] - f = width/len(codes[0]) + up = np.where(d==1)[0] + if X[0]==1: + up = np.concatenate((np.array([-1]), up)) + up += 1 - ipp_codes = [''.join([s*f for s in code]) for code in ipp_codes] + f = width/len(codes[0]) + codes = [(np.fromstring(''.join([s*f for s in code]), dtype=np.uint8)-48).astype(np.int8) for code in codes] - if before>0: - sbefore = '{0:0{1}d}'.format(0, before) - else: - sbefore = '' + y = np.zeros(len(X)) - temp = ['{0}{1}{2:0{3}d}'.format(sbefore, ipp_codes[i], 0, int(ipp)-len(ipp_codes[i])-before) for i in range(ntx)] + j=0 + n = len(codes) + for i in up: + y[i:i+width] = codes[j%n] + j += 1 - return (np.fromstring(''.join(temp), dtype=np.uint8)-48).astype(np.int8) + return y def create_mask(ranges, ipp, ntx, sync): @@ -301,10 +302,10 @@ def plot_pulses(unit, maximun, lines): labels = [] for i, line in enumerate(lines): - print line labels.append(line.get_name()) + l = ax.plot((0, maximun),(N-i-1, N-i-1)) ax.broken_barh(pulses_to_points(line.pulses_as_array()), (N-i-1, 0.5), - edgecolor='none', facecolor='#2c3e50') + edgecolor=l[0].get_color(), facecolor='none') labels.reverse() ax.set_yticklabels(labels) diff --git a/apps/rc/views.py b/apps/rc/views.py index 0ceaa4a..3848262 100644 --- a/apps/rc/views.py +++ b/apps/rc/views.py @@ -117,13 +117,7 @@ def conf_edit(request, conf_id): line.save() #update pulses field - for line in conf.get_lines(): - if line.line_type.name=='tr': - continue - line.update_pulses() - - for tr in conf.get_lines('tr'): - tr.update_pulses() + conf.update_pulses() messages.success(request, 'RC Configuration successfully updated') @@ -376,7 +370,7 @@ def view_pulses(request, conf_id): unit = (conf.clock/conf.clock_divider)*3./20 - N = int(conf.ipp*(conf.clock/conf.clock_divider)*20./3)*conf.ntx + N = conf.ipp*conf.km2unit*conf.ntx script, div = plot_pulses(unit, N, lines) diff --git a/apps/rc/widgets.py b/apps/rc/widgets.py index d26ce34..a725541 100644 --- a/apps/rc/widgets.py +++ b/apps/rc/widgets.py @@ -1,9 +1,12 @@ import ast import json +from itertools import chain from django import forms from django.utils.safestring import mark_safe +from django.utils.encoding import force_unicode +from django.utils.html import conditional_escape class KmUnitWidget(forms.widgets.TextInput): @@ -26,7 +29,7 @@ class KmUnitWidget(forms.widgets.TextInput): if 'line' in attrs: label += '_{0}'.format(attrs['line'].pk) - html = '