import ast import json import requests import numpy as np from base64 import b64encode from struct import pack from django.db import models from django.urls import reverse from django.core.validators import MinValueValidator, MaxValueValidator from apps.main.models import Configuration from apps.main.utils import Params from devices.rc import api from apps.rc.utils import RCFile LINE_TYPES = ( ('none', 'Not used'), ('tr', 'Transmission/reception selector signal'), ('tx', 'A modulating signal (Transmission pulse)'), ('codes', 'BPSK modulating signal'), ('windows', 'Sample window signal'), ('sync', 'Synchronizing signal'), ('flip', 'IPP related periodic signal'), ('prog_pulses', 'Programmable pulse'), ('mix', 'Mixed line'), ) SAMPLING_REFS = ( ('none', 'No Reference'), ('begin_baud', 'Begin of the first baud'), ('first_baud', 'Middle of the first baud'), ('sub_baud', 'Middle of the sub-baud') ) DAT_CMDS = { # Pulse Design commands 'DISABLE' : 0, # Disables pulse generation 'ENABLE' : 24, # Enables pulse generation 'DELAY_START' : 40, # Write delay status to memory 'FLIP_START' : 48, # Write flip status to memory 'SAMPLING_PERIOD' : 64, # Establish Sampling Period 'TX_ONE' : 72, # Output '0' in line TX 'TX_ZERO' : 88, # Output '0' in line TX 'SW_ONE' : 104, # Output '0' in line SW 'SW_ZERO' : 112, # Output '1' in line SW 'RESTART': 120, # Restarts CR8 Firmware 'CONTINUE' : 253, # Function Unknown # Commands available to new controllers # In Pulse Design Executable, the clock divisor code is written as 12 at the start, but it should be written as code 22(below) just before the final enable. 'CLOCK_DIVISOR_INIT' : 12, # Specifies Clock Divisor. Legacy command, ignored in the actual .dat conversion 'CLOCK_DIVISOR_LAST' : 22, # Specifies Clock Divisor (default 60 if not included) syntax: 255,22 254,N-1. 'CLOCK_DIVIDER' : 8, } MAX_BITS = 8 # Rotate left: 0b1001 --> 0b0011 rol = lambda val, r_bits: \ (val << r_bits%MAX_BITS) & (2**MAX_BITS-1) | \ ((val & (2**MAX_BITS-1)) >> (MAX_BITS-(r_bits%MAX_BITS))) # Rotate right: 0b1001 --> 0b1100 ror = lambda val, r_bits: \ ((val & (2**MAX_BITS-1)) >> r_bits%MAX_BITS) | \ (val << (MAX_BITS-(r_bits%MAX_BITS)) & (2**MAX_BITS-1)) class RCConfiguration(Configuration): ipp = models.FloatField(verbose_name='IPP [Km]', validators=[MinValueValidator(1)], default=300) ntx = models.PositiveIntegerField(verbose_name='Number of TX', validators=[MinValueValidator(1)], 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=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) total_units = models.PositiveIntegerField(default=0) mix = models.BooleanField(default=False) class Meta: db_table = 'rc_configurations' def get_absolute_url_plot(self): return reverse('url_plot_rc_pulses', args=[str(self.id)]) @property def ipp_unit(self): return '{} ({})'.format(self.ipp, int(self.ipp*self.km2unit)) @property def us2unit(self): 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() self.pk = None self.id = None for attr, value in kwargs.items(): setattr(self, attr, value) self.save() for line in lines: line.clone(rc_configuration=self) new_lines = self.get_lines() for line in new_lines: line_params = json.loads(line.params) if 'TX_ref' in line_params and (line_params['TX_ref'] != '0'): ref_line = RCLine.objects.get(pk=line_params['TX_ref']) line_params['TX_ref'] = ['{}'.format(l.pk) for l in new_lines if l.get_name()==ref_line.get_name()][0] line.params = json.dumps(line_params) line.save() return self def get_lines(self, **kwargs): ''' Retrieve configuration lines ''' return RCLine.objects.filter(rc_configuration=self.pk, **kwargs) def clean_lines(self): ''' ''' empty_line = RCLineType.objects.get(name='none') for line in self.get_lines(): line.line_type = empty_line line.params = '{}' line.save() def dict_to_parms(self, params, id=None): ''' ''' if id: data = Params(params).get_conf(id_conf=id) else: data = Params(params).get_conf(dtype='rc') #print(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.total_units = self.ipp*self.ntx*self.km2unit self.save() self.clean_lines() #print(params) positions = {'tx':0, 'tr':0} for i, id in enumerate(data['lines']): line_data = params['lines']['byId'][id] line_type = RCLineType.objects.get(name=line_data['line_type']) if line_type.name == 'codes': code = RCLineCode.objects.get(name=line_data['params']['code']) line_data['params']['code'] = code.pk if line_type.name == 'tx': position = positions['tx'] positions['tx'] += 1 elif line_type.name == 'tr': position = positions['tr'] positions['tr'] += 1 else: position = 0 line, dum = RCLine.objects.update_or_create( rc_configuration=self, channel=i, position=position, defaults={ 'line_type': line_type, 'params': json.dumps(line_data['params']) } ) for i, line in enumerate(self.get_lines()): line_params = json.loads(line.params) if 'TX_ref' in line_params: if line_params['TX_ref'] in (0, '0'): line_params['TX_ref'] = '0' else: ref_id = '{}'.format(line_params['TX_ref']) ref_line = params['lines']['byId'][ref_id] line_params['TX_ref'] = RCLine.objects.get( rc_configuration=self, params=json.dumps(ref_line['params']) ).pk line.params = json.dumps(line_params) print(line.params) line.save() print("Fin de dict to param") def get_delays(self): pulses = [line.pulses_as_points() 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() if points[0]!=0: points.insert(0, 0) return [points[i+1]-points[i] for i in range(len(points)-1)] def get_pulses(self, binary=True): pulses = [line.pulses_as_points() for line in self.get_lines()] tuples = [tup for tups in pulses for tup in tups] points = set([x for tup in tuples for x in tup]) points = list(points) points.sort() states = [] last = [0 for x in pulses] n_pulses = len(pulses) print('len_pulses', n_pulses) print('len_points', len(points)) ups_arr = [[] for _ in range(n_pulses)] dws_arr = [[] for _ in range(n_pulses)] for i, tups in enumerate(pulses): ups_arr[i] = [tup[0] for tup in tups if tup!=(0,0)] dws_arr[i] = [tup[1] for tup in tups if tup!=(0,0)] print('len_points*n_pulses',len(points)*n_pulses) #print('ups_arr', ups_arr) #print('dws_arr', dws_arr) #####################Code for load configuration######################### for x in points: dum = [] print('loading', x*100/max(points)) for i in range(n_pulses): if x in ups_arr[i]: dum.append(1) elif x in dws_arr[i]: dum.append(0) else: dum.append(last[i]) #print(dum) states.append(dum) last = dum print("Finish loading") ######################################################################### if binary: ret = [] for flips in states: flips.reverse() ret.append(int(''.join([str(x) for x in flips]), 2)) states = ret #print(states[:-1]) #print('len_states',len(states[:-1])) return states[:-1] def add_cmd(self, cmd): if cmd in DAT_CMDS: return (255, DAT_CMDS[cmd]) def add_data(self, value): return (254, value-1) def parms_to_binary(self, dat=True): ''' Create "dat" stream to be send to CR ''' data = bytearray() # create header data.extend(self.add_cmd('DISABLE')) data.extend(self.add_cmd('CONTINUE')) data.extend(self.add_cmd('RESTART')) if self.control_sw: data.extend(self.add_cmd('SW_ONE')) else: data.extend(self.add_cmd('SW_ZERO')) if self.control_tx: data.extend(self.add_cmd('TX_ONE')) else: data.extend(self.add_cmd('TX_ZERO')) # write divider data.extend(self.add_cmd('CLOCK_DIVIDER')) data.extend(self.add_data(self.clock_divider)) # write delays data.extend(self.add_cmd('DELAY_START')) # first delay is always zero data.extend(self.add_data(1)) delays = self.get_delays() for delay in delays: while delay>252: data.extend(self.add_data(253)) delay -= 253 data.extend(self.add_data(int(delay))) # write flips data.extend(self.add_cmd('FLIP_START')) states = self.get_pulses(binary=True) last = 0 for flip, delay in zip(states, delays): data.extend(self.add_data((flip^last)+1)) last = flip while delay>252: data.extend(self.add_data(1)) delay -= 253 # write sampling period data.extend(self.add_cmd('SAMPLING_PERIOD')) wins = self.get_lines(line_type__name='windows') if wins: win_params = json.loads(wins[0].params)['params'] if win_params: dh = int(win_params[0]['resolution']*self.km2unit) else: dh = 1 else: dh = 1 data.extend(self.add_data(dh)) # write enable data.extend(self.add_cmd('ENABLE')) if not dat: return data return '\n'.join(['{}'.format(x) for x in data]) def update_pulses(self): contador = 0 for line in self.get_lines(): contador=contador+1 print(contador) line.update_pulses() def plot_pulses2(self, km=False): import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt from bokeh.resources import CDN from bokeh.embed import components from bokeh.mpl import to_bokeh from bokeh.models.tools import WheelZoomTool, ResetTool, PanTool, HoverTool, SaveTool lines = self.get_lines() N = len(lines) npoints = self.total_units/self.km2unit if km else self.total_units fig = plt.figure(figsize=(12, 2+N*0.5)) ax = fig.add_subplot(111) labels = ['IPP'] for i, line in enumerate(lines): labels.append(line.get_name(channel=True)) l = ax.plot((0, npoints),(N-i-1, N-i-1)) points = [(tup[0], tup[1]-tup[0]) for tup in line.pulses_as_points(km=km) if tup!=(0,0)] ax.broken_barh(points, (N-i-1, 0.5), edgecolor=l[0].get_color(), facecolor='none') n = 0 f = ((self.ntx+50)/100)*5 if ((self.ntx+50)/100)*10>0 else 2 for x in np.arange(0, npoints, self.ipp if km else self.ipp*self.km2unit): if n%f==0: ax.text(x, N, '%s' % n, size=10) n += 1 labels.reverse() ax.set_yticks(range(len(labels))) ax.set_yticklabels(labels) ax.set_xlabel = 'Units' plot = to_bokeh(fig, use_pandas=False) plot.tools = [PanTool(dimensions="width"), WheelZoomTool(dimensions="width"), ResetTool(), SaveTool()] plot.toolbar_location="above" return components(plot, CDN) def plot_pulses(self, km=False): from bokeh.plotting import figure from bokeh.resources import CDN from bokeh.embed import components from bokeh.models import FixedTicker, PrintfTickFormatter, Label from bokeh.models.tools import WheelZoomTool, ResetTool, PanTool, HoverTool, SaveTool from bokeh.models.sources import ColumnDataSource lines = self.get_lines().reverse() N = len(lines) npoints = self.total_units/self.km2unit if km else self.total_units ipp = self.ipp if km else self.ipp*self.km2unit hover = HoverTool(tooltips=[("Line", "@name"), ("IPP", "@ipp"), ("X", "@left")]) tools = [PanTool(dimensions="width"), WheelZoomTool(dimensions="width"), hover, SaveTool()] plot = figure(width=1000, height=40+N*50, y_range = (0, N), tools=tools, toolbar_location='above', toolbar_sticky=False,) pulses = [line.pulses_as_points() for line in self.get_lines()] tuples = [tup for tups in pulses for tup in tups] points = set([x for tup in tuples for x in tup]) capacity_bytes = round((8*(len(points)-1)+12)/2) # se divide entre 2 porque la mitad era solo para direcciones capacity_percent = (capacity_bytes/2097152)*100 # Add the used memory message x_label_memory = Label(x=900, y=-1.5, text='Used memory of '+str(capacity_bytes)+'/2097152 bytes ('+'%.3f'%capacity_percent+'%)', text_align="right", x_units='screen', text_font_size='14pt') plot.add_layout(x_label_memory, 'below') plot.xaxis.axis_label = 'Km' if km else 'Units' plot.xaxis[0].formatter = PrintfTickFormatter(format='%d') plot.yaxis.axis_label = 'Pulses' plot.yaxis[0].ticker=FixedTicker(ticks=list(range(N))) plot.yaxis[0].formatter = PrintfTickFormatter(format='Line %d') for i, line in enumerate(lines): points = [tup for tup in line.pulses_as_points(km=km) if tup!=(0,0)] source = ColumnDataSource(data = dict( bottom = [i for tup in points], top = [i+0.5 for tup in points], left = [tup[0] for tup in points], right = [tup[1] for tup in points], ipp = [int(tup[0]/ipp) for tup in points], name = [line.get_name() for tup in points] )) plot.quad( bottom = 'bottom', top = 'top', left = 'left', right = 'right', source = source, fill_alpha = 0, #line_color = 'blue', ) plot.line([0, npoints], [i, i])#, color='blue') return components(plot, CDN) def request(self, cmd, method='get', **kwargs): req = getattr(requests, method)(self.device.url(cmd), **kwargs) payload = req.json() return payload def status_device(self): try: self.device.status = 0 payload = self.request('status') if payload['status']=='enable': self.device.status = 3 elif payload['status']=='disable': self.device.status = 2 else: self.device.status = 1 self.device.save() self.message = 'RC status: {}'.format(payload['status']) return False except Exception as e: if 'No route to host' not in str(e): self.device.status = 4 self.device.save() self.message = 'RC status: {}'.format(str(e)) return False self.device.save() return True def reset_device(self): try: payload = self.request('reset', 'post') if payload['reset']=='ok': self.message = 'RC restarted OK' self.device.status = 2 self.device.save() else: self.message = 'RC restart fail' self.device.status = 4 self.device.save() except Exception as e: self.message = 'RC reset: {}'.format(str(e)) return False return True def stop_device(self): try: payload = self.request('stop', 'post') self.message = 'RC stop: {}'.format(payload['stop']) if payload['stop']=='ok': self.device.status = 2 self.device.save() else: self.device.status = 4 self.device.save() return False except Exception as e: if 'No route to host' not in str(e): self.device.status = 4 else: self.device.status = 0 self.message = 'RC stop: {}'.format(str(e)) self.device.save() return False return True def start_device(self): try: payload = self.request('start', 'post') self.message = 'RC start: {}'.format(payload['start']) if payload['start']=='ok': self.device.status = 3 self.device.save() else: return False except Exception as e: if 'No route to host' not in str(e): self.device.status = 4 else: self.device.status = 0 self.message = 'RC start: {}'.format(str(e)) self.device.save() return False return True def write_device(self, raw=False): print("write device") ##############Comando status a CGS para hacer programacion ############ try: self.device.status = 0 cgs = self.request('status_cgs') print('CGS status ok') # solo para depurar lo que devuelve CGS except Exception as e: cgs = {'multiplier': 60, 'divider': 10, 'reference_clk': 1} # simulando parametros devueltos por el cgs if 'No route to host' not in str(e): self.device.status = 4 self.device.save() self.message = 'CGS status: {}'.format(str(e)) print('not cgs status') print(cgs) if not raw: clock = RCClock.objects.get(rc_configuration=self) print('clock_freq', clock.frequency) print('clock_mult', clock.multiplier) print('clock_div', clock.divisor) print('clock_ref', clock.reference) print('cgs', cgs) if clock.mode: data = {'default': clock.frequency} # mult=72, div=12 else: data = {'manual': [clock.multiplier, clock.divisor, clock.reference]} print('data', data) int_cgs_multiplier=int(cgs['multiplier']) int_cgs_divisor=int(cgs['divider']) int_cgs_reference=int(cgs['reference_clk']) print(cgs['divider']) if clock.multiplier != int_cgs_multiplier or clock.divisor != int_cgs_divisor or clock.reference != int_cgs_reference: print("Program CGS...") payload = self.request('setfreq', 'post', data=json.dumps(data))#data=data)#data=json.dumps(data)) if payload['command'] != 'ok': self.message = 'RC write: {}'.format(payload['command']) else: self.message = payload['programming'] if payload['programming'] == 'fail': self.message = 'RC write: error programming CGS chip' else: print("Not program CGS...") values = [] print('wait delay values...') for pulse, delay in zip(self.get_pulses(), self.get_delays()): #print('wait zip...') while delay > 65536: values.append((pulse, 65535)) delay -= 65536 values.append((pulse, delay-1)) data = bytearray() #reset data.extend((128, 0)) #disable data.extend((129, 0)) #SW switch if self.control_sw: data.extend((130, 2)) else: data.extend((130, 0)) #divider data.extend((131, self.clock_divider-1)) #enable writing data.extend((139, 62)) last = 0 print('wait data...') for tup in values: vals = pack('0 and ranges[0]!='0': y_tx = self.mask_ranges(y_tx, ranges) tr_ranges = tr_params['range'].split(',') if len(tr_ranges)>0 and tr_ranges[0]!='0': y_tx = self.mask_ranges(y_tx, tr_ranges) y.extend(y_tx) self.pulses = str(y) y = self.array_to_points(self.pulses_as_array()) elif self.line_type.name=='tx': params = json.loads(self.params) delays = [float(d)*km2unit for d in params['delays'].split(',') if d] width = float(params['pulse_width'])*km2unit if width>0: before = int(self.rc_configuration.time_before*us2unit) after = 0 y = self.points(ntx, ipp_u, width, delay=delays, before=before, after=after, sync=self.rc_configuration.sync) ranges = params['range'].split(',') if len(ranges)>0 and ranges[0]!='0': y = self.mask_ranges(y, ranges) elif self.line_type.name=='flip': n = float(json.loads(self.params)['number_of_flips']) width = n*ipp*km2unit y = self.points(int((ntx+1)/(2*n)), ipp_u*n*2, width) elif self.line_type.name=='codes': params = json.loads(self.params) tx = RCLine.objects.get(pk=params['TX_ref']) tx_params = json.loads(tx.params) delays = [float(d)*km2unit for d in tx_params['delays'].split(',') if d] f = int(float(tx_params['pulse_width'])*km2unit/len(params['codes'][0])) codes = [(np.fromstring(''.join([s*f for s in code]), dtype=np.uint8)-48).astype(np.int8) for code in params['codes']] codes = [self.array_to_points(code) for code in codes] n = len(codes) ranges = tx_params['range'].split(',') if len(ranges)>0 and ranges[0]!='0': dum = self.mask_ranges(tx.pulses_as_points(), ranges) else: dum = tx.pulses_as_points() for i, tup in enumerate(dum): if tup==(0,0): continue code = codes[i%n] y.extend([(c[0]+tup[0], c[1]+tup[0]) for c in code]) elif self.line_type.name=='sync': params = json.loads(self.params) n = ipp_u*ntx if params['invert'] in ('1', 1): y = [(n-1, n)] else: y = [(0, 1)] elif self.line_type.name=='prog_pulses': params = json.loads(self.params) if int(params['periodic'])==0: nntx = 1 nipp = ipp_u*ntx else: nntx = ntx nipp = ipp_u if 'params' in params and len(params['params'])>0: for p in params['params']: y_pp = self.points(nntx, nipp, p['end']-p['begin'], before=p['begin']) y.extend(y_pp) elif self.line_type.name=='windows': params = json.loads(self.params) if 'params' in params and len(params['params'])>0: tx = RCLine.objects.get(pk=params['TX_ref']) tx_params = json.loads(tx.params) ranges = tx_params['range'].split(',') for p in params['params']: y_win = self.points(ntx, ipp_u, p['resolution']*p['number_of_samples']*km2unit, before=int(self.rc_configuration.time_before*us2unit)+p['first_height']*km2unit, sync=self.rc_configuration.sync+self.get_win_ref(p, params['TX_ref'], km2unit)) if len(ranges)>0 and ranges[0]!='0': y_win = self.mask_ranges(y_win, ranges) y.extend(y_win) elif self.line_type.name=='mix': values = self.rc_configuration.parameters.split('-') confs = [RCConfiguration.objects.get(pk=value.split('|')[0]) for value in values] modes = [value.split('|')[1] for value in values] ops = [value.split('|')[2] for value in values] delays = [value.split('|')[3] for value in values] masks = [value.split('|')[4] for value in values] print("masks") print(masks) print('{:8b}'.format(int(masks[0]))) mask = list('{:8b}'.format(int(masks[0]))) print("mask") print(mask) mask.reverse() print("mask reverse") print(mask) if mask[self.channel] in ('0', '', ' '): y = np.zeros(confs[0].total_units, dtype=np.int8) else: 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 modes[i]=='P': if delay>0: if delaylen(y): y_new = np.zeros(delay+len(Y), dtype=np.int8) y_new[:len(y)] = y y = y_new y_temp = np.zeros(delay+len(Y), dtype=np.int8) y_temp[-len(Y):] = Y elif delay+len(Y)==len(y): y_temp = np.zeros(delay+len(Y)) y_temp[-len(Y):] = Y elif delay+len(Y)