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