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