##// END OF EJS Templates
Update RC API methods...
Juan C. Espinoza -
r222:547f05cc34b8
parent child
Show More
@@ -1,927 +1,952
1 1
2 2 import ast
3 3 import json
4 4 import requests
5 5 import numpy as np
6 6 from base64 import b64encode
7 7 from struct import pack
8 8
9 9 from django.db import models
10 10 from django.core.urlresolvers import reverse
11 11 from django.core.validators import MinValueValidator, MaxValueValidator
12 12
13 13 from apps.main.models import Configuration
14 14 from devices.rc import api
15 15 from .utils import RCFile
16 16
17 17 # Create your models here.
18 18
19 19 LINE_TYPES = (
20 20 ('none', 'Not used'),
21 21 ('tr', 'Transmission/reception selector signal'),
22 22 ('tx', 'A modulating signal (Transmission pulse)'),
23 23 ('codes', 'BPSK modulating signal'),
24 24 ('windows', 'Sample window signal'),
25 25 ('sync', 'Synchronizing signal'),
26 26 ('flip', 'IPP related periodic signal'),
27 27 ('prog_pulses', 'Programmable pulse'),
28 28 ('mix', 'Mixed line'),
29 29 )
30 30
31 31
32 32 SAMPLING_REFS = (
33 33 ('none', 'No Reference'),
34 34 ('begin_baud', 'Begin of the first baud'),
35 35 ('first_baud', 'Middle of the first baud'),
36 36 ('sub_baud', 'Middle of the sub-baud')
37 37 )
38 38
39 39 DAT_CMDS = {
40 40 # Pulse Design commands
41 41 'DISABLE' : 0, # Disables pulse generation
42 42 'ENABLE' : 24, # Enables pulse generation
43 43 'DELAY_START' : 40, # Write delay status to memory
44 44 'FLIP_START' : 48, # Write flip status to memory
45 45 'SAMPLING_PERIOD' : 64, # Establish Sampling Period
46 46 'TX_ONE' : 72, # Output '0' in line TX
47 47 'TX_ZERO' : 88, # Output '0' in line TX
48 48 'SW_ONE' : 104, # Output '0' in line SW
49 49 'SW_ZERO' : 112, # Output '1' in line SW
50 50 'RESTART': 120, # Restarts CR8 Firmware
51 51 'CONTINUE' : 253, # Function Unknown
52 52 # Commands available to new controllers
53 53 # 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.
54 54 'CLOCK_DIVISOR_INIT' : 12, # Specifies Clock Divisor. Legacy command, ignored in the actual .dat conversion
55 55 'CLOCK_DIVISOR_LAST' : 22, # Specifies Clock Divisor (default 60 if not included) syntax: 255,22 254,N-1.
56 56 'CLOCK_DIVIDER' : 8,
57 57 }
58 58
59 59
60 60 class RCConfiguration(Configuration):
61 61
62 62 ipp = models.FloatField(verbose_name='IPP [Km]', validators=[MinValueValidator(1), MaxValueValidator(9000)], default=300)
63 63 ntx = models.PositiveIntegerField(verbose_name='Number of TX', validators=[MinValueValidator(1), MaxValueValidator(400)], default=1)
64 64 clock_in = models.FloatField(verbose_name='Clock in [MHz]', validators=[MinValueValidator(1), MaxValueValidator(80)], default=1)
65 65 clock_divider = models.PositiveIntegerField(verbose_name='Clock divider', validators=[MinValueValidator(1), MaxValueValidator(256)], default=1)
66 66 clock = models.FloatField(verbose_name='Clock Master [MHz]', blank=True, default=1)
67 67 time_before = models.PositiveIntegerField(verbose_name='Time before [μS]', default=12)
68 68 time_after = models.PositiveIntegerField(verbose_name='Time after [μS]', default=1)
69 69 sync = models.PositiveIntegerField(verbose_name='Synchro delay', default=0)
70 70 sampling_reference = models.CharField(verbose_name='Sampling Reference', choices=SAMPLING_REFS, default='none', max_length=40)
71 71 control_tx = models.BooleanField(verbose_name='Control Switch TX', default=False)
72 72 control_sw = models.BooleanField(verbose_name='Control Switch SW', default=False)
73 73 total_units = models.PositiveIntegerField(default=0)
74 74 mix = models.BooleanField(default=False)
75 75
76 76 class Meta:
77 77 db_table = 'rc_configurations'
78 78
79 79 def get_absolute_url_plot(self):
80 80 return reverse('url_plot_rc_pulses', args=[str(self.id)])
81 81
82 82 def get_absolute_url_import(self):
83 83 return reverse('url_import_rc_conf', args=[str(self.id)])
84 84
85 85 @property
86 86 def ipp_unit(self):
87 87
88 88 return '{} ({})'.format(self.ipp, int(self.ipp*self.km2unit))
89 89
90 90 @property
91 91 def us2unit(self):
92 92
93 93 return self.clock_in/self.clock_divider
94 94
95 95 @property
96 96 def km2unit(self):
97 97
98 98 return 20./3*(self.clock_in/self.clock_divider)
99 99
100 100 def clone(self, **kwargs):
101 101
102 102 lines = self.get_lines()
103 103 self.pk = None
104 104 self.id = None
105 105 for attr, value in kwargs.items():
106 106 setattr(self, attr, value)
107 107 self.save()
108 108
109 109 for line in lines:
110 110 line.clone(rc_configuration=self)
111 111
112 112 return self
113 113
114 114 def get_lines(self, **kwargs):
115 115 '''
116 116 Retrieve configuration lines
117 117 '''
118 118
119 119 return RCLine.objects.filter(rc_configuration=self.pk, **kwargs)
120 120
121 121
122 122 def clean_lines(self):
123 123 '''
124 124 '''
125 125
126 126 empty_line = RCLineType.objects.get(name='none')
127 127
128 128 for line in self.get_lines():
129 129 line.line_type = empty_line
130 130 line.params = '{}'
131 131 line.save()
132 132
133 133 def parms_to_dict(self):
134 134 '''
135 135 '''
136 136
137 137 ignored = ('parameters', 'type', 'polymorphic_ctype', 'configuration_ptr',
138 138 'created_date', 'programmed_date')
139 139
140 140 data = {}
141 141 for field in self._meta.fields:
142 142 if field.name in ignored:
143 143 continue
144 144 data[field.name] = '{}'.format(field.value_from_object(self))
145 145
146 146 data['device_id'] = data.pop('device')
147 147 data['lines'] = []
148 148
149 149 for line in self.get_lines():
150 150 line_data = json.loads(line.params)
151 151 if 'TX_ref' in line_data and line_data['TX_ref'] not in (0, '0'):
152 152 line_data['TX_ref'] = RCLine.objects.get(pk=line_data['TX_ref']).get_name()
153 153 if 'code' in line_data:
154 154 line_data['code'] = RCLineCode.objects.get(pk=line_data['code']).name
155 155 line_data['type'] = line.line_type.name
156 156 line_data['name'] = line.get_name()
157 157 data['lines'].append(line_data)
158 158
159 159 data['delays'] = self.get_delays()
160 160 data['pulses'] = self.get_pulses()
161 161
162 162 return data
163 163
164 164 def dict_to_parms(self, data):
165 165 '''
166 166 '''
167 167
168 168 self.name = data['name']
169 169 self.ipp = float(data['ipp'])
170 170 self.ntx = int(data['ntx'])
171 171 self.clock_in = float(data['clock_in'])
172 172 self.clock_divider = int(data['clock_divider'])
173 173 self.clock = float(data['clock'])
174 174 self.time_before = data['time_before']
175 175 self.time_after = data['time_after']
176 176 self.sync = data['sync']
177 177 self.sampling_reference = data['sampling_reference']
178 178 self.total_units = self.ipp*self.ntx*self.km2unit
179 179 self.save()
180 180 self.clean_lines()
181 181
182 182 lines = []
183 183 positions = {'tx':0, 'tr':0}
184 184
185 185 for i, line_data in enumerate(data['lines']):
186 186 name = line_data.pop('name', '')
187 187 line_type = RCLineType.objects.get(name=line_data.pop('type'))
188 188 if line_type.name=='codes':
189 189 code = RCLineCode.objects.get(name=line_data['code'])
190 190 line_data['code'] = code.pk
191 191 line = RCLine.objects.filter(rc_configuration=self, channel=i)
192 192 if line:
193 193 line = line[0]
194 194 line.line_type = line_type
195 195 line.params = json.dumps(line_data)
196 196 else:
197 197 line = RCLine(rc_configuration=self, line_type=line_type,
198 198 params=json.dumps(line_data),
199 199 channel=i)
200 200
201 201 if line_type.name=='tx':
202 202 line.position = positions['tx']
203 203 positions['tx'] += 1
204 204
205 205 if line_type.name=='tr':
206 206 line.position = positions['tr']
207 207 positions['tr'] += 1
208 208
209 209 line.save()
210 210 lines.append(line)
211 211
212 212 for line, line_data in zip(lines, data['lines']):
213 213 if 'TX_ref' in line_data:
214 214 params = json.loads(line.params)
215 215 if line_data['TX_ref'] in (0, '0'):
216 216 params['TX_ref'] = '0'
217 217 else:
218 218 params['TX_ref'] = [l.pk for l in lines if l.line_type.name=='tx' and line_data['TX_ref'] in l.get_name()][0]
219 219 line.params = json.dumps(params)
220 220 line.save()
221 221
222 222
223 223 def get_delays(self):
224 224
225 225 pulses = [line.pulses_as_points() for line in self.get_lines()]
226 226 points = [tup for tups in pulses for tup in tups]
227 227 points = set([x for tup in points for x in tup])
228 228 points = list(points)
229 229 points.sort()
230 230
231 231 if points[0]!=0:
232 232 points.insert(0, 0)
233 233
234 234 return [points[i+1]-points[i] for i in range(len(points)-1)]
235 235
236 236
237 237 def get_pulses(self, binary=True):
238 238
239 239 pulses = [line.pulses_as_points() for line in self.get_lines()]
240 240 points = [tup for tups in pulses for tup in tups]
241 241 points = set([x for tup in points for x in tup])
242 242 points = list(points)
243 243 points.sort()
244 244
245 245 line_points = [line.pulses_as_points() for line in self.get_lines()]
246 246 #line_points = [[(x, x+y) for x,y in tups] for tups in line_points]
247 247 line_points = [[t for x in tups for t in x] for tups in line_points]
248 248 states = [[1 if x in tups else 0 for tups in line_points] for x in points]
249 249
250 250 if binary:
251 251 states.reverse()
252 252 states = [int(''.join([str(x) for x in flips]), 2) for flips in states]
253 253
254 254 return states[:-1]
255 255
256 256 def add_cmd(self, cmd):
257 257
258 258 if cmd in DAT_CMDS:
259 259 return (255, DAT_CMDS[cmd])
260 260
261 261 def add_data(self, value):
262 262
263 263 return (254, value-1)
264 264
265 265 def parms_to_binary(self, dat=True):
266 266 '''
267 267 Create "dat" stream to be send to CR
268 268 '''
269 269
270 270 data = bytearray()
271 271 # create header
272 272 data.extend(self.add_cmd('DISABLE'))
273 273 data.extend(self.add_cmd('CONTINUE'))
274 274 data.extend(self.add_cmd('RESTART'))
275 275
276 276 if self.control_sw:
277 277 data.extend(self.add_cmd('SW_ONE'))
278 278 else:
279 279 data.extend(self.add_cmd('SW_ZERO'))
280 280
281 281 if self.control_tx:
282 282 data.extend(self.add_cmd('TX_ONE'))
283 283 else:
284 284 data.extend(self.add_cmd('TX_ZERO'))
285 285
286 286 # write divider
287 287 data.extend(self.add_cmd('CLOCK_DIVIDER'))
288 288 data.extend(self.add_data(self.clock_divider))
289 289
290 290 # write delays
291 291 data.extend(self.add_cmd('DELAY_START'))
292 292 # first delay is always zero
293 293 data.extend(self.add_data(1))
294 294
295 295 delays = self.get_delays()
296 296
297 297 for delay in delays:
298 298 while delay>252:
299 299 data.extend(self.add_data(253))
300 300 delay -= 253
301 301 data.extend(self.add_data(int(delay)))
302 302
303 303 # write flips
304 304 data.extend(self.add_cmd('FLIP_START'))
305 305
306 306 states = self.get_pulses(binary=False)
307 307
308 308 for flips, delay in zip(states, delays):
309 309 flips.reverse()
310 310 flip = int(''.join([str(x) for x in flips]), 2)
311 311 data.extend(self.add_data(flip+1))
312 312 while delay>252:
313 313 data.extend(self.add_data(1))
314 314 delay -= 253
315 315
316 316 # write sampling period
317 317 data.extend(self.add_cmd('SAMPLING_PERIOD'))
318 318 wins = self.get_lines(line_type__name='windows')
319 319 if wins:
320 320 win_params = json.loads(wins[0].params)['params']
321 321 if win_params:
322 322 dh = int(win_params[0]['resolution']*self.km2unit)
323 323 else:
324 324 dh = 1
325 325 else:
326 326 dh = 1
327 327 data.extend(self.add_data(dh))
328 328
329 329 # write enable
330 330 data.extend(self.add_cmd('ENABLE'))
331 331
332 332 if not dat:
333 333 return data
334 334
335 335 return '\n'.join(['{}'.format(x) for x in data])
336 336
337 337
338 338 def update_from_file(self, filename):
339 339 '''
340 340 Update instance from file
341 341 '''
342 342
343 343 f = RCFile(filename)
344 344 self.dict_to_parms(f.data)
345 345 self.update_pulses()
346 346
347 347 def update_pulses(self):
348 348
349 349 for line in self.get_lines():
350 350 line.update_pulses()
351 351
352 352 def plot_pulses2(self, km=False):
353 353
354 354 import matplotlib.pyplot as plt
355 355 from bokeh.resources import CDN
356 356 from bokeh.embed import components
357 357 from bokeh.mpl import to_bokeh
358 358 from bokeh.models.tools import WheelZoomTool, ResetTool, PanTool, HoverTool, SaveTool
359 359
360 360 lines = self.get_lines()
361 361
362 362 N = len(lines)
363 363 npoints = self.total_units/self.km2unit if km else self.total_units
364 364 fig = plt.figure(figsize=(12, 2+N*0.5))
365 365 ax = fig.add_subplot(111)
366 366 labels = ['IPP']
367 367
368 368 for i, line in enumerate(lines):
369 369 labels.append(line.get_name(channel=True))
370 370 l = ax.plot((0, npoints),(N-i-1, N-i-1))
371 371 points = [(tup[0], tup[1]-tup[0]) for tup in line.pulses_as_points(km=km) if tup!=(0,0)]
372 372 ax.broken_barh(points, (N-i-1, 0.5),
373 373 edgecolor=l[0].get_color(), facecolor='none')
374 374
375 375 n = 0
376 376 f = ((self.ntx+50)/100)*5 if ((self.ntx+50)/100)*10>0 else 2
377 377 for x in np.arange(0, npoints, self.ipp if km else self.ipp*self.km2unit):
378 378 if n%f==0:
379 379 ax.text(x, N, '%s' % n, size=10)
380 380 n += 1
381 381
382 382
383 383 labels.reverse()
384 384 ax.set_yticks(range(len(labels)))
385 385 ax.set_yticklabels(labels)
386 386 ax.set_xlabel = 'Units'
387 387 plot = to_bokeh(fig, use_pandas=False)
388 388 plot.tools = [PanTool(dimensions=['width']), WheelZoomTool(dimensions=['width']), ResetTool(), SaveTool()]
389 389 plot.toolbar_location="above"
390 390
391 391 return components(plot, CDN)
392 392
393 393 def plot_pulses(self, km=False):
394 394
395 395 from bokeh.plotting import figure
396 396 from bokeh.resources import CDN
397 397 from bokeh.embed import components
398 398 from bokeh.models import FixedTicker, PrintfTickFormatter
399 399 from bokeh.models.tools import WheelZoomTool, ResetTool, PanTool, HoverTool, SaveTool
400 400 from bokeh.models.sources import ColumnDataSource
401 401
402 402 lines = self.get_lines().reverse()
403 403
404 404 N = len(lines)
405 405 npoints = self.total_units/self.km2unit if km else self.total_units
406 406 ipp = self.ipp if km else self.ipp*self.km2unit
407 407
408 408 hover = HoverTool(tooltips=[("Line", "@name"),
409 409 ("IPP", "@ipp"),
410 410 ("X", "@left")])
411 411
412 412 tools = [PanTool(dimensions=['width']),
413 413 WheelZoomTool(dimensions=['width']),
414 414 hover, SaveTool()]
415 415
416 416 plot = figure(width=1000,
417 417 height=40+N*50,
418 418 y_range = (0, N),
419 419 tools=tools,
420 420 toolbar_location='above',
421 421 toolbar_sticky=False,)
422 422
423 423 plot.xaxis.axis_label = 'Km' if km else 'Units'
424 424 plot.xaxis[0].formatter = PrintfTickFormatter(format='%d')
425 425 plot.yaxis.axis_label = 'Pulses'
426 426 plot.yaxis[0].ticker=FixedTicker(ticks=list(range(N)))
427 427 plot.yaxis[0].formatter = PrintfTickFormatter(format='Line %d')
428 428
429 429 for i, line in enumerate(lines):
430 430
431 431 points = [tup for tup in line.pulses_as_points(km=km) if tup!=(0,0)]
432 432
433 433 source = ColumnDataSource(data = dict(
434 434 bottom = [i for tup in points],
435 435 top = [i+0.5 for tup in points],
436 436 left = [tup[0] for tup in points],
437 437 right = [tup[1] for tup in points],
438 438 ipp = [int(tup[0]/ipp) for tup in points],
439 439 name = [line.get_name() for tup in points]
440 440 ))
441 441
442 442 plot.quad(
443 443 bottom = 'bottom',
444 444 top = 'top',
445 445 left = 'left',
446 446 right = 'right',
447 447 source = source,
448 448 fill_alpha = 0,
449 449 #line_color = 'blue',
450 450 )
451 451
452 452 plot.line([0, npoints], [i, i])#, color='blue')
453 453
454 454 return components(plot, CDN)
455 455
456 456 def status_device(self):
457 457
458 try:
458 try:
459 self.device.status = 0
459 460 req = requests.get(self.device.url('status'))
460 461 payload = req.json()
461 462 if payload['status']=='ok':
462 self.device.status = 1
463 self.device.status = 2
463 464 else:
464 self.device.status = 0
465 self.device.status = 1
466 self.device.save()
467 self.message = payload['status']
468 return False
465 469 except Exception as e:
466 self.device.status = 0
470 if 'No route to host' not in str(e):
471 self.device.status = 4
472 self.device.save()
467 473 self.message = str(e)
468 474 return False
469 475
470 476 self.device.save()
471 return True
472
477 return True
473 478
474 479 def reset_device(self):
475 480
476 481 try:
477 req = requests.post(self.device.url('reset'))
478 if 'ok' in req.text:
482 req = requests.post(self.device.url('reset'))
483 payload = req.json()
484 if payload['reset']=='ok':
479 485 self.message = 'RC restarted'
480 486 else:
481 487 self.message = 'RC restart not ok'
482 488 self.device.status = 4
483 489 self.device.save()
484 490 except Exception as e:
485 491 self.message = str(e)
486 492 return False
487 493
488 494 return True
489 495
490 496 def stop_device(self):
491 497
492 498 try:
493 req = requests.post(self.device.url('stop'))
494 if 'ok' in req.text:
499 req = requests.post(self.device.url('stop'))
500 payload = req.json()
501 if payload['stop']=='ok':
495 502 self.device.status = 2
496 503 self.device.save()
497 504 self.message = 'RC stopped'
498 505 else:
499 506 self.message = 'RC stop not ok'
500 507 self.device.status = 4
501 508 self.device.save()
502 509 return False
503 510 except Exception as e:
504 self.message = str(e)
511 if 'No route to host' not in str(e):
512 self.device.status = 4
513 else:
514 self.device.status = 0
515 self.message = str(e)
516 self.device.save()
505 517 return False
506 518
507 519 return True
508 520
509 521 def start_device(self):
510 522
511 523 try:
512 req = requests.post(self.device.url('start'))
513 if 'ok' in req.text:
524 req = requests.post(self.device.url('start'))
525 payload = req.json()
526 if payload['start']=='ok':
514 527 self.device.status = 3
515 528 self.device.save()
516 529 self.message = 'RC running'
517 530 else:
518 531 self.message = 'RC start not ok'
519 532 return False
520 533 except Exception as e:
521 self.message = str(e)
534 if 'No route to host' not in str(e):
535 self.device.status = 4
536 else:
537 self.device.status = 0
538 self.message = str(e)
539 self.device.save()
522 540 return False
523 541
524 542 return True
525 543
526 544 def write_device(self):
527 545
528 546 values = zip(self.get_pulses(),
529 547 [x-1 for x in self.get_delays()])
530 548 payload = ''
531 549
532 550 for tup in values:
533 551 vals = pack('<HH', *tup)
534 552 payload += '\x05'+vals[0]+'\x04'+vals[1]+'\x05'+vals[2]+'\x05'+vals[3]
535 553
536 554 try:
537 555 ## reset
538 556 if not self.reset_device():
539 557 return False
540 558 ## stop
541 559 if not self.stop_device():
542 560 return False
543 561 ## write clock divider
544 562 req = requests.post(self.device.url('divisor'),
545 563 {'divisor': '{:d}'.format(self.clock_divider-1)})
546 if 'ok' not in req.text:
564 payload = req.json()
565 if payload['divisor']=='ok':
547 566 self.message = 'Error configuring RC clock divider'
548 567 return False
549 568 ## write pulses & delays
550 req = requests.post(self.device.url('write'), data=b64encode(payload))
551 if 'ok' in req.text:
569 req = requests.post(self.device.url('write'), data=b64encode(payload))
570 payload = req.json()
571 if payload['write']=='ok':
552 572 self.device.status = 2
553 573 self.device.save()
554 574 self.message = 'RC configured'
555 575 else:
556 576 self.device.status = 4
557 577 self.device.save()
558 578 self.message = 'RC write not ok'
559 579 return False
560 580
561 581 except Exception as e:
562 self.message = str(e)
582 if 'No route to host' not in str(e):
583 self.device.status = 4
584 else:
585 self.device.status = 0
586 self.message = str(e)
587 self.device.save()
563 588 return False
564 589
565 590 return True
566 591
567 592
568 593 class RCLineCode(models.Model):
569 594
570 595 name = models.CharField(max_length=40)
571 596 bits_per_code = models.PositiveIntegerField(default=0)
572 597 number_of_codes = models.PositiveIntegerField(default=0)
573 598 codes = models.TextField(blank=True, null=True)
574 599
575 600 class Meta:
576 601 db_table = 'rc_line_codes'
577 602 ordering = ('name',)
578 603
579 604 def __str__(self):
580 605 return u'%s' % self.name
581 606
582 607
583 608 class RCLineType(models.Model):
584 609
585 610 name = models.CharField(choices=LINE_TYPES, max_length=40)
586 611 description = models.TextField(blank=True, null=True)
587 612 params = models.TextField(default='[]')
588 613
589 614 class Meta:
590 615 db_table = 'rc_line_types'
591 616
592 617 def __str__(self):
593 618 return u'%s - %s' % (self.name.upper(), self.get_name_display())
594 619
595 620
596 621 class RCLine(models.Model):
597 622
598 623 rc_configuration = models.ForeignKey(RCConfiguration, on_delete=models.CASCADE)
599 624 line_type = models.ForeignKey(RCLineType)
600 625 channel = models.PositiveIntegerField(default=0)
601 626 position = models.PositiveIntegerField(default=0)
602 627 params = models.TextField(default='{}')
603 628 pulses = models.TextField(default='')
604 629
605 630 class Meta:
606 631 db_table = 'rc_lines'
607 632 ordering = ['channel']
608 633
609 634 def __str__(self):
610 635 if self.rc_configuration:
611 636 return u'%s - %s' % (self.rc_configuration, self.get_name())
612 637
613 638 def clone(self, **kwargs):
614 639
615 640 self.pk = None
616 641
617 642 for attr, value in kwargs.items():
618 643 setattr(self, attr, value)
619 644
620 645 self.save()
621 646
622 647 return self
623 648
624 649 def get_name(self, channel=False):
625 650
626 651 chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
627 652 s = ''
628 653
629 654 if self.line_type.name in ('tx',):
630 655 s = chars[self.position]
631 656 elif self.line_type.name in ('codes', 'windows', 'tr'):
632 657 if 'TX_ref' in json.loads(self.params):
633 658 pk = json.loads(self.params)['TX_ref']
634 659 if pk in (0, '0'):
635 660 s = ','.join(chars[l.position] for l in self.rc_configuration.get_lines(line_type__name='tx'))
636 661 else:
637 662 ref = RCLine.objects.get(pk=pk)
638 663 s = chars[ref.position]
639 664 s = '({})'.format(s)
640 665
641 666 s = '{}{}'.format(self.line_type.name.upper(), s)
642 667
643 668 if channel:
644 669 return '{} {}'.format(s, self.channel)
645 670 else:
646 671 return s
647 672
648 673 def get_lines(self, **kwargs):
649 674
650 675 return RCLine.objects.filter(rc_configuration=self.rc_configuration, **kwargs)
651 676
652 677 def pulses_as_array(self):
653 678
654 679 y = np.zeros(self.rc_configuration.total_units)
655 680
656 681 for tup in ast.literal_eval(self.pulses):
657 682 y[tup[0]:tup[1]] = 1
658 683
659 684 return y.astype(np.int8)
660 685
661 686 def pulses_as_points(self, km=False):
662 687
663 688 if km:
664 689 unit2km = 1/self.rc_configuration.km2unit
665 690 return [(tup[0]*unit2km, tup[1]*unit2km) for tup in ast.literal_eval(self.pulses)]
666 691 else:
667 692 return ast.literal_eval(self.pulses)
668 693
669 694 def get_win_ref(self, params, tx_id, km2unit):
670 695
671 696 ref = self.rc_configuration.sampling_reference
672 697 codes = [line for line in self.get_lines(line_type__name='codes') if int(json.loads(line.params)['TX_ref'])==int(tx_id)]
673 698
674 699 if codes:
675 700 tx_width = float(json.loads(RCLine.objects.get(pk=tx_id).params)['pulse_width'])*km2unit/len(json.loads(codes[0].params)['codes'][0])
676 701 else:
677 702 tx_width = float(json.loads(RCLine.objects.get(pk=tx_id).params)['pulse_width'])*km2unit
678 703
679 704 if ref=='first_baud':
680 705 return int(1 + (tx_width + 1)/2 + params['first_height']*km2unit - params['resolution']*km2unit)
681 706 elif ref=='sub_baud':
682 707 return int(1 + params['first_height']*km2unit - params['resolution']*km2unit/2)
683 708 else:
684 709 return 0
685 710
686 711 def update_pulses(self):
687 712 '''
688 713 Update pulses field
689 714 '''
690 715
691 716 km2unit = self.rc_configuration.km2unit
692 717 us2unit = self.rc_configuration.us2unit
693 718 ipp = self.rc_configuration.ipp
694 719 ntx = int(self.rc_configuration.ntx)
695 720 ipp_u = int(ipp*km2unit)
696 721 total = ipp_u*ntx if self.rc_configuration.total_units==0 else self.rc_configuration.total_units
697 722 y = []
698 723
699 724 if self.line_type.name=='tr':
700 725 tr_params = json.loads(self.params)
701 726
702 727 if tr_params['TX_ref'] in ('0', 0):
703 728 txs = self.get_lines(line_type__name='tx')
704 729 else:
705 730 txs = RCLine.objects.filter(pk=tr_params['TX_ref'])
706 731
707 732 for tx in txs:
708 733 params = json.loads(tx.params)
709 734
710 735 if float(params['pulse_width'])==0:
711 736 continue
712 737 delays = [float(d)*km2unit for d in params['delays'].split(',') if d]
713 738 width = float(params['pulse_width'])*km2unit+int(self.rc_configuration.time_before*us2unit)
714 739 before = 0
715 740 after = int(self.rc_configuration.time_after*us2unit)
716 741
717 742 y_tx = self.points(ntx, ipp_u, width,
718 743 delay=delays,
719 744 before=before,
720 745 after=after,
721 746 sync=self.rc_configuration.sync)
722 747
723 748 ranges = params['range'].split(',')
724 749
725 750 if len(ranges)>0 and ranges[0]!='0':
726 751 y_tx = self.mask_ranges(y_tx, ranges)
727 752
728 753 tr_ranges = tr_params['range'].split(',')
729 754
730 755 if len(tr_ranges)>0 and tr_ranges[0]!='0':
731 756 y_tx = self.mask_ranges(y_tx, tr_ranges)
732 757
733 758 y.extend(y_tx)
734 759
735 760 self.pulses = str(y)
736 761 y = self.array_to_points(self.pulses_as_array())
737 762
738 763 elif self.line_type.name=='tx':
739 764 params = json.loads(self.params)
740 765 delays = [float(d)*km2unit for d in params['delays'].split(',') if d]
741 766 width = float(params['pulse_width'])*km2unit
742 767
743 768 if width>0:
744 769 before = int(self.rc_configuration.time_before*us2unit)
745 770 after = 0
746 771
747 772 y = self.points(ntx, ipp_u, width,
748 773 delay=delays,
749 774 before=before,
750 775 after=after,
751 776 sync=self.rc_configuration.sync)
752 777
753 778 ranges = params['range'].split(',')
754 779
755 780 if len(ranges)>0 and ranges[0]!='0':
756 781 y = self.mask_ranges(y, ranges)
757 782
758 783 elif self.line_type.name=='flip':
759 784 n = float(json.loads(self.params)['number_of_flips'])
760 785 width = n*ipp*km2unit
761 786 y = self.points(int((ntx+1)/(2*n)), ipp_u*n*2, width)
762 787
763 788 elif self.line_type.name=='codes':
764 789 params = json.loads(self.params)
765 790 tx = RCLine.objects.get(pk=params['TX_ref'])
766 791 tx_params = json.loads(tx.params)
767 792 delays = [float(d)*km2unit for d in tx_params['delays'].split(',') if d]
768 793 f = int(float(tx_params['pulse_width'])*km2unit/len(params['codes'][0]))
769 794 codes = [(np.fromstring(''.join([s*f for s in code]), dtype=np.uint8)-48).astype(np.int8) for code in params['codes']]
770 795 codes = [self.array_to_points(code) for code in codes]
771 796 n = len(codes)
772 797
773 798 for i, tup in enumerate(tx.pulses_as_points()):
774 799 code = codes[i%n]
775 800 y.extend([(c[0]+tup[0], c[1]+tup[0]) for c in code])
776 801
777 802 ranges = tx_params['range'].split(',')
778 803 if len(ranges)>0 and ranges[0]!='0':
779 804 y = self.mask_ranges(y, ranges)
780 805
781 806 elif self.line_type.name=='sync':
782 807 params = json.loads(self.params)
783 808 n = ipp_u*ntx
784 809 if params['invert'] in ('1', 1):
785 810 y = [(n-1, n)]
786 811 else:
787 812 y = [(0, 1)]
788 813
789 814 elif self.line_type.name=='prog_pulses':
790 815 params = json.loads(self.params)
791 816 if int(params['periodic'])==0:
792 817 nntx = 1
793 818 nipp = ipp_u*ntx
794 819 else:
795 820 nntx = ntx
796 821 nipp = ipp_u
797 822
798 823 if 'params' in params and len(params['params'])>0:
799 824 for p in params['params']:
800 825 y_pp = self.points(nntx, nipp,
801 826 p['end']-p['begin'],
802 827 before=p['begin'])
803 828
804 829 y.extend(y_pp)
805 830
806 831 elif self.line_type.name=='windows':
807 832 params = json.loads(self.params)
808 833
809 834 if 'params' in params and len(params['params'])>0:
810 835 tr_params = json.loads(self.get_lines(line_type__name='tr')[0].params)
811 836 tr_ranges = tr_params['range'].split(',')
812 837 for p in params['params']:
813 838 y_win = self.points(ntx, ipp_u,
814 839 p['resolution']*p['number_of_samples']*km2unit,
815 840 before=int(self.rc_configuration.time_before*us2unit)+self.get_win_ref(p, params['TX_ref'], km2unit),
816 841 sync=self.rc_configuration.sync)
817 842
818 843 if len(tr_ranges)>0 and tr_ranges[0]!='0':
819 844 y_win = self.mask_ranges(y_win, tr_ranges)
820 845
821 846 y.extend(y_win)
822 847
823 848 elif self.line_type.name=='mix':
824 849 values = self.rc_configuration.parameters.split('-')
825 850 confs = [RCConfiguration.objects.get(pk=value.split('|')[0]) for value in values]
826 851 modes = [value.split('|')[1] for value in values]
827 852 ops = [value.split('|')[2] for value in values]
828 853 delays = [value.split('|')[3] for value in values]
829 854 masks = [value.split('|')[4] for value in values]
830 855 mask = list('{:8b}'.format(int(masks[0])))
831 856 mask.reverse()
832 857 if mask[self.channel] in ('0', '', ' '):
833 858 y = np.zeros(confs[0].total_units, dtype=np.int8)
834 859 else:
835 860 y = confs[0].get_lines(channel=self.channel)[0].pulses_as_array()
836 861
837 862 for i in range(1, len(values)):
838 863 mask = list('{:8b}'.format(int(masks[i])))
839 864 mask.reverse()
840 865
841 866 if mask[self.channel] in ('0', '', ' '):
842 867 continue
843 868 Y = confs[i].get_lines(channel=self.channel)[0].pulses_as_array()
844 869 delay = float(delays[i])*km2unit
845 870
846 871 if modes[i]=='P':
847 872 if delay>0:
848 873 if delay<self.rc_configuration.ipp*km2unit and len(Y)==len(y):
849 874 y_temp = np.empty_like(Y)
850 875 y_temp[:delay] = 0
851 876 y_temp[delay:] = Y[:-delay]
852 877 elif delay+len(Y)>len(y):
853 878 y_new = np.zeros(delay+len(Y), dtype=np.int8)
854 879 y_new[:len(y)] = y
855 880 y = y_new
856 881 y_temp = np.zeros(delay+len(Y), dtype=np.int8)
857 882 y_temp[-len(Y):] = Y
858 883 elif delay+len(Y)==len(y):
859 884 y_temp = np.zeros(delay+len(Y))
860 885 y_temp[-len(Y):] = Y
861 886 elif delay+len(Y)<len(y):
862 887 y_temp = np.zeros(len(y), dtype=np.int8)
863 888 y_temp[delay:delay+len(Y)] = Y
864 889
865 890 if ops[i]=='OR':
866 891 y = y | y_temp
867 892 elif ops[i]=='XOR':
868 893 y = y ^ y_temp
869 894 elif ops[i]=='AND':
870 895 y = y & y_temp
871 896 elif ops[i]=='NAND':
872 897 y = y & ~y_temp
873 898 else:
874 899 y = np.concatenate([y, Y])
875 900
876 901 total = len(y)
877 902 y = self.array_to_points(y)
878 903
879 904 else:
880 905 y = []
881 906
882 907 if self.rc_configuration.total_units != total:
883 908 self.rc_configuration.total_units = total
884 909 self.rc_configuration.save()
885 910
886 911 self.pulses = str(y)
887 912 self.save()
888 913
889 914 @staticmethod
890 915 def array_to_points(X):
891 916
892 917 d = X[1:]-X[:-1]
893 918
894 919 up = np.where(d==1)[0]
895 920 if X[0]==1:
896 921 up = np.concatenate((np.array([-1]), up))
897 922 up += 1
898 923
899 924 dw = np.where(d==-1)[0]
900 925 if X[-1]==1:
901 926 dw = np.concatenate((dw, np.array([len(X)-1])))
902 927 dw += 1
903 928
904 929 return [(tup[0], tup[1]) for tup in zip(up, dw)]
905 930
906 931 @staticmethod
907 932 def mask_ranges(Y, ranges):
908 933
909 934 y = [(0, 0) for __ in Y]
910 935
911 936 for index in ranges:
912 937 if '-' in index:
913 938 args = [int(a) for a in index.split('-')]
914 939 y[args[0]-1:args[1]] = Y[args[0]-1:args[1]]
915 940 else:
916 941 y[int(index-1)] = Y[int(index-1)]
917 942
918 943 return y
919 944
920 945 @staticmethod
921 946 def points(ntx, ipp, width, delay=[0], before=0, after=0, sync=0):
922 947
923 948 delays = len(delay)
924 949
925 950 Y = [(int(ipp*x+before+delay[x%delays]+sync), int(ipp*x+width+before+delay[x%delays]+after+sync)) for x in range(ntx)]
926 951
927 952 return Y
General Comments 0
You need to be logged in to leave comments. Login now