diff --git a/apps/dds/files.py b/apps/dds/files.py new file mode 100644 index 0000000..b21321f --- /dev/null +++ b/apps/dds/files.py @@ -0,0 +1,93 @@ +''' +Created on Feb 8, 2016 + +@author: Miguel Urco +''' + +import string + +def read_dds_file(fp): + """ + Function to extract the parameters from a text file with the next format: + + Input: + + File with the next content: + + Phase Adjust Register 1 + ----------------------- + 00000000 + 00000000 + + ..... + + ----------------------- + Frequency Tuning Word 1 + ----------------------- + 00110101 + 01111111 + 11111111 + 11111111 + 10100000 + 00000000 + + Output: + Return configuration parameters for DDS: multiplier, frequency, phase, amplitude, etc. + + """ + + kwargs = {} + dds_registers = [] + + for this_line in fp: + this_line = str.strip(this_line) + + if not str.isdigit(this_line): + continue + + if len(this_line) != 8: + continue + + dds_registers.append(string.atoi(this_line,2)) + + if len(dds_registers) != 40: + return kwargs + + kwargs['clock'] = 60.0 + + kwargs['phase_bin'] = dds_registers[0]*(2**8) + dds_registers[1] + kwargs['phase_mod_bin'] = dds_registers[2]*(2**8) + dds_registers[3] + + kwargs['frequency_bin'] = dds_registers[4]*(2**40) + dds_registers[5]*(2**32) + dds_registers[6]*(2**24) + dds_registers[7]*(2**16) + dds_registers[8]*(2**8) + dds_registers[9] + kwargs['frequency_mod_bin'] = dds_registers[10]*(2**40) + dds_registers[11]*(2**32) + dds_registers[12]*(2**24) + dds_registers[13]*(2**16) + dds_registers[14]*(2**8) + dds_registers[15] + + kwargs['delta_frequency'] = dds_registers[16]*(2**40) + dds_registers[17]*(2**32) + dds_registers[18]*(2**24) + dds_registers[19]*(2**16) + dds_registers[20]*(2**8) + dds_registers[21] + + kwargs['update_clock'] = dds_registers[22]*(2**24) + dds_registers[23]*(2**16) + dds_registers[24]*(2**8) + dds_registers[25] + + kwargs['ramp_rate_clock'] = dds_registers[26]*(2**16) + dds_registers[27]*(2**8) + dds_registers[28] + + kwargs['control_register'] = dds_registers[29]*(2**24) + dds_registers[30]*(2**16) + dds_registers[31]*(2**8) + dds_registers[32] + + kwargs['multiplier'] = dds_registers[30] & 0x1F + kwargs['modulation'] = (dds_registers[31] & 0x0E) >> 1 + kwargs['amplitude_enabled'] = (dds_registers[32] & 0x20) >> 5 + + kwargs['amplitude_ch_A'] = (dds_registers[33]*(2**8) + dds_registers[34]) & 0x0FFF + kwargs['amplitude_ch_B'] = (dds_registers[35]*(2**8) + dds_registers[36]) & 0x0FFF + + kwargs['amplitude_ramp_rate'] = dds_registers[37] + + return kwargs + +def read_json_file(fp): + + kwargs = {} + + return kwargs + +def write_dds_file(filename): + pass + +def write_json_file(filename): + pass \ No newline at end of file diff --git a/apps/dds/forms.py b/apps/dds/forms.py index bc2cfad..aa116d7 100644 --- a/apps/dds/forms.py +++ b/apps/dds/forms.py @@ -4,6 +4,11 @@ from .models import DDSConfiguration # from django.core.validators import MinValueValidator, MaxValueValidator +EXT_TYPES = ( + ('dds', '.dds'), + ('json', '.json'), +) + class DDSConfigurationForm(forms.ModelForm): # frequency_bin = forms.IntegerField(label='Frequency (Binary)', required=False) @@ -48,3 +53,8 @@ class DDSConfigurationForm(forms.ModelForm): class Meta: model = DDSConfiguration exclude = ('type','parameters') + +class UploadFileForm(forms.Form): + + title = forms.ChoiceField(label='Extension Type', choices=EXT_TYPES) + file = forms.FileField() \ No newline at end of file diff --git a/apps/dds/models.py b/apps/dds/models.py index 82da477..b0c0693 100644 --- a/apps/dds/models.py +++ b/apps/dds/models.py @@ -5,6 +5,8 @@ from apps.main.models import Configuration from django.core.validators import MinValueValidator, MaxValueValidator from django.core.exceptions import ValidationError +from files import read_dds_file, read_json_file + MOD_TYPES = ( (0, 'Single Tone'), (1, 'FSK'), @@ -17,21 +19,21 @@ class DDSConfiguration(Configuration): DDS_NBITS = 48 - clock = models.FloatField(verbose_name='Clock Master (MHz)',validators=[MinValueValidator(5), MaxValueValidator(75)], null=True) + clock = models.FloatField(verbose_name='Clock Input (MHz)',validators=[MinValueValidator(5), MaxValueValidator(75)], null=True) multiplier = models.PositiveIntegerField(verbose_name='Multiplier',validators=[MinValueValidator(1), MaxValueValidator(20)], default=4) - frequency = models.DecimalField(verbose_name='Frequency (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=18, decimal_places=16) + frequency = models.DecimalField(verbose_name='Frequency (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16) frequency_bin = models.BigIntegerField(verbose_name='Frequency (Binary)',validators=[MinValueValidator(0), MaxValueValidator(2**DDS_NBITS-1)]) phase = models.FloatField(verbose_name='Phase (Degrees)', validators=[MinValueValidator(0), MaxValueValidator(360)], default=0) # phase_binary = models.PositiveIntegerField(verbose_name='Phase (Binary)',validators=[MinValueValidator(0), MaxValueValidator(2**14-1)]) - amplitude_ch_A = models.PositiveIntegerField(verbose_name='Amplitude CH A',validators=[MinValueValidator(0), MaxValueValidator(2**10-1)], blank=True, null=True) - amplitude_ch_B = models.PositiveIntegerField(verbose_name='Amplitude CH B',validators=[MinValueValidator(0), MaxValueValidator(2**10-1)], blank=True, null=True) + amplitude_ch_A = models.PositiveIntegerField(verbose_name='Amplitude CH A',validators=[MinValueValidator(0), MaxValueValidator(2**12-1)], blank=True, null=True) + amplitude_ch_B = models.PositiveIntegerField(verbose_name='Amplitude CH B',validators=[MinValueValidator(0), MaxValueValidator(2**12-1)], blank=True, null=True) modulation = models.PositiveIntegerField(verbose_name='Modulation Type', choices = MOD_TYPES, default = 0) - frequency_mod = models.DecimalField(verbose_name='Mod: Frequency (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=18, decimal_places=16, blank=True, null=True) + frequency_mod = models.DecimalField(verbose_name='Mod: Frequency (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, blank=True, null=True) frequency_mod_bin = models.BigIntegerField(verbose_name='Mod: Frequency (Binary)',validators=[MinValueValidator(0), MaxValueValidator(2**DDS_NBITS-1)], blank=True, null=True) phase_mod = models.FloatField(verbose_name='Mod: Phase (Degrees)', validators=[MinValueValidator(0), MaxValueValidator(360)], blank=True, null=True) @@ -71,6 +73,53 @@ class DDSConfiguration(Configuration): return freq + def phase2binary(self, phase): + + binary = phase*8192/180.0 + + return binary + + def binary2phase(self, binary): + + phase = binary*180.0/8192 + + return phase + + def export_file(self, ext_file="dds"): + + pass + + def update_from_file(self, fp, ext_file="dds"): + + if ext_file == "dds": + kwargs = read_dds_file(fp) + else: + kwargs = read_json_file(fp) + + if not kwargs: + return False + + self.clock = kwargs['clock'] + self.multiplier = kwargs['multiplier'] + + mclock = self.clock*self.multiplier + + self.frequency = self.binary2freq(kwargs['frequency_bin'], mclock) + self.frequency_bin = kwargs['frequency_bin'] + + self.frequency_mod = self.binary2freq(kwargs['frequency_mod_bin'], mclock) + self.frequency_mod_bin = kwargs['frequency_mod_bin'] + + self.phase = self.binary2phase(kwargs['phase_bin']) + self.phase_mod = self.binary2phase(kwargs['phase_mod_bin']) + + self.modulation = kwargs['modulation'] + + self.amplitude_ch_A = kwargs['amplitude_ch_A'] + self.amplitude_ch_B = kwargs['amplitude_ch_B'] + + return True + class Meta: db_table = 'dds_configurations' \ No newline at end of file diff --git a/apps/dds/templates/dds_conf_import.html b/apps/dds/templates/dds_conf_import.html new file mode 100644 index 0000000..f67538a --- /dev/null +++ b/apps/dds/templates/dds_conf_import.html @@ -0,0 +1,7 @@ +{% extends "dev_conf_edit.html" %} +{% load bootstrap3 %} +{% load static %} +{% load main_tags %} + +{% block extra-js%} +{% endblock %} \ No newline at end of file diff --git a/apps/dds/urls.py b/apps/dds/urls.py index ac264cb..3f260b0 100644 --- a/apps/dds/urls.py +++ b/apps/dds/urls.py @@ -6,6 +6,6 @@ urlpatterns = ( url(r'^(?P-?\d+)/edit/$', 'apps.dds.views.dds_conf_edit', name='url_edit_dds_conf'), url(r'^(?P-?\d+)/write/$', 'apps.dds.views.dds_conf_write', name='url_write_dds_conf'), url(r'^(?P-?\d+)/read/$', 'apps.dds.views.dds_conf_read', name='url_read_dds_conf'), - url(r'^(?P-?\d+)/import/$', 'apps.main.views.dev_conf_import', name='url_import_dds_conf'), + url(r'^(?P-?\d+)/import/$', 'apps.dds.views.dds_conf_import', name='url_import_dds_conf'), url(r'^(?P-?\d+)/export/$', 'apps.main.views.dev_conf_export', name='url_export_dds_conf'), ) diff --git a/apps/dds/views.py b/apps/dds/views.py index 7f14c25..16104f9 100644 --- a/apps/dds/views.py +++ b/apps/dds/views.py @@ -2,11 +2,13 @@ from django.contrib import messages from django.shortcuts import redirect, render, get_object_or_404 +from django.core.exceptions import ValidationError + # from apps.main.models import Experiment, Configuration from apps.main.views import sidebar from .models import DDSConfiguration -from .forms import DDSConfigurationForm +from .forms import DDSConfigurationForm, UploadFileForm # Create your views here. from radarsys_api import dds @@ -18,8 +20,12 @@ def dds_conf(request, id_conf): answer = dds.echo(ip=str(conf.device.ip_address), port=conf.device.port_address) kwargs = {} + kwargs['connected'] = (answer[0] == "1") + if not kwargs['connected']: + messages.error(request, message=answer) + kwargs['dev_conf'] = conf kwargs['dev_conf_keys'] = ['experiment', 'device', 'clock', 'multiplier', @@ -87,8 +93,8 @@ def dds_conf_write(request, id_conf): freq_regA=conf.frequency_bin, freq_regB=conf.frequency_mod_bin, modulation=conf.modulation, - phaseA=conf.phase, - phaseB=conf.phase_mod, + phaseA=conf.phase2binary(conf.phase), + phaseB=conf.phase2binary(conf.phase_mod), amplitude0=conf.amplitude_ch_A, amplitude1=conf.amplitude_ch_B) @@ -101,9 +107,9 @@ def dds_conf_write(request, id_conf): conf.save() else: - messages.error(request, answer) + messages.error(request, "Could not write the parameters to this device") - return redirect('url_dds_conf', id_conf=conf.id) + return redirect('url_dds_conf', id_conf=id_conf) def dds_conf_read(request, id_conf): @@ -120,7 +126,7 @@ def dds_conf_read(request, id_conf): dds_model.save() return redirect('url_dds_conf', id_conf=conf.id) - messages.error(request, "Parameters could not be saved. Invalid parameters") + messages.error(request, "Parameters could not be saved") data = {} @@ -130,25 +136,22 @@ def dds_conf_read(request, id_conf): port=conf.device.port_address) if not parms: - messages.error(request, "Could not read parameters from Device") + messages.error(request, "Could not read dds parameters from this device") return redirect('url_dds_conf', id_conf=conf.id) - data = {'experiment' : conf.experiment.id, - 'device' : conf.device.id, - 'clock' : conf.clock, - 'multiplier' : parms[0], + data = {'multiplier' : parms[0], 'frequency' : conf.binary2freq(parms[1], parms[0]*conf.clock), 'frequency_bin' : parms[1], - 'phase' : parms[4], + 'phase' : conf.binary2phase(parms[4]), 'amplitude_ch_A' : parms[6], 'amplitude_ch_B' : parms[7], 'modulation' : parms[3], 'frequency_mod' : conf.binary2freq(parms[2], parms[0]*conf.clock), 'frequency_mod_bin' : parms[2], - 'phase_mod' : parms[5], + 'phase_mod' : conf.binary2phase(parms[5]), } - form = DDSConfigurationForm(data) + form = DDSConfigurationForm(initial=data, instance=conf) kwargs = {} kwargs['id_dev'] = conf.id @@ -160,4 +163,59 @@ def dds_conf_read(request, id_conf): ###### SIDEBAR ###### kwargs.update(sidebar(conf)) - return render(request, 'dds_conf_edit.html', kwargs) \ No newline at end of file + return render(request, 'dds_conf_edit.html', kwargs) + +def dds_conf_import(request, id_conf): + + conf = get_object_or_404(DDSConfiguration, pk=id_conf) + + if request.method == 'POST': + file_form = UploadFileForm(request.POST, request.FILES) + + if file_form.is_valid(): + + if conf.update_from_file(request.FILES['file']): + + try: + conf.full_clean() + except ValidationError as e: + messages.error(request, e) + else: + conf.save() + + messages.success(request, "Parameters imported from file: '%s'." %request.FILES['file'].name) + messages.warning(request, "Clock Input could not be read from file, using %3.2fMhz by default. Please update it to its real value" %conf.clock) + return redirect('url_dds_conf', id_conf=conf.id) + + messages.error(request, "Could not import parameters from file") + + else: + file_form = UploadFileForm() + + kwargs = {} + kwargs['id_dev'] = conf.id + kwargs['title'] = 'Device Configuration' + kwargs['form'] = file_form + kwargs['suptitle'] = 'Importing file' + kwargs['button'] = 'Import' + + kwargs.update(sidebar(conf)) + + return render(request, 'dds_conf_import.html', kwargs) + +def handle_uploaded_file(f): + + data = {'multiplier' : 5, + 'frequency' : 49.92, + 'frequency_bin' : 45678, + 'phase' : 0, + 'amplitude_ch_A' : 1024, + 'amplitude_ch_B' : 2014, + 'modulation' : 1, + 'frequency_mod' : 0, + 'frequency_mod_bin' : 0, + 'phase_mod' : 180, + } + + + return data \ No newline at end of file diff --git a/apps/main/models.py b/apps/main/models.py index f276cd3..2e0fb2a 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -2,6 +2,8 @@ from itertools import chain from django.db import models from polymorphic import PolymorphicModel +from django.core.urlresolvers import reverse + CONF_TYPES = ( (0, 'Active'), (1, 'Historical'), @@ -107,25 +109,20 @@ class Configuration(PolymorphicModel): self.experiment.name, self.device.name) def get_absolute_url(self): - from django.core.urlresolvers import reverse + return reverse('url_%s_conf' % self.device.device_type.name, args=[str(self.id)]) def get_absolute_url_edit(self): - from django.core.urlresolvers import reverse return reverse('url_edit_%s_conf' % self.device.device_type.name, args=[str(self.id)]) def get_absolute_url_import(self): - from django.core.urlresolvers import reverse return reverse('url_import_%s_conf' % self.device.device_type.name, args=[str(self.id)]) def get_absolute_url_export(self): - from django.core.urlresolvers import reverse return reverse('url_export_%s_conf' % self.device.device_type.name, args=[str(self.id)]) def get_absolute_url_write(self): - from django.core.urlresolvers import reverse return reverse('url_write_%s_conf' % self.device.device_type.name, args=[str(self.id)]) def get_absolute_url_read(self): - from django.core.urlresolvers import reverse return reverse('url_read_%s_conf' % self.device.device_type.name, args=[str(self.id)]) \ No newline at end of file diff --git a/apps/main/templates/base.html b/apps/main/templates/base.html index 6490546..992e3f2 100644 --- a/apps/main/templates/base.html +++ b/apps/main/templates/base.html @@ -89,14 +89,13 @@ {{message.tags|title}}! {{ message }} - {% endfor %} -
+ {% endfor %} {% endif %} {% endblock %} {% block content %} {% endblock %} -
+ diff --git a/apps/main/templates/dev_conf_edit.html b/apps/main/templates/dev_conf_edit.html index 65f89eb..1cc765b 100644 --- a/apps/main/templates/dev_conf_edit.html +++ b/apps/main/templates/dev_conf_edit.html @@ -9,7 +9,13 @@ {% block content-suptitle %}{{suptitle}}{% endblock %} {% block content %} -
+ +{% if form.is_multipart %} + +{% else %} + +{% endif %} + {% csrf_token %} {% bootstrap_form form layout='horizontal' size='medium' %}
diff --git a/apps/main/views.py b/apps/main/views.py index 73781f8..7464e14 100644 --- a/apps/main/views.py +++ b/apps/main/views.py @@ -440,93 +440,33 @@ def dev_conf_read(request, id_conf): conf = get_object_or_404(Configuration, pk=id_conf) - DevConfModel = CONF_MODELS[conf.device.device_type.name] - dev_conf = DevConfModel.objects.get(pk=id_conf) - - kwargs = {} - kwargs['dev_conf'] = dev_conf - kwargs['dev_conf_keys'] = ['experiment', 'device'] - - kwargs['title'] = 'Configuration' - kwargs['suptitle'] = 'Details' - - kwargs['button'] = 'Edit Configuration' - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf)) + messages.error(request, "Read View not implemented yet") - messages.error(request, "Read View not implemented yet for this configuration") - - return render(request, 'dev_conf.html', kwargs) + return redirect('url_dev_conf', id_conf=conf.id) def dev_conf_write(request, id_conf): conf = get_object_or_404(Configuration, pk=id_conf) - DevConfModel = CONF_MODELS[conf.device.device_type.name] - dev_conf = DevConfModel.objects.get(pk=id_conf) - - kwargs = {} - kwargs['dev_conf'] = dev_conf - kwargs['dev_conf_keys'] = ['experiment', 'device'] - - kwargs['title'] = 'Configuration' - kwargs['suptitle'] = 'Details' - - kwargs['button'] = 'Edit Configuration' - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf)) + messages.error(request, "Write View not implemented yet") - messages.error(request, "Write View not implemented yet for this configuration") - - return render(request, 'dev_conf.html', kwargs) + return redirect('url_dev_conf', id_conf=conf.id) def dev_conf_import(request, id_conf): conf = get_object_or_404(Configuration, pk=id_conf) - DevConfModel = CONF_MODELS[conf.device.device_type.name] - dev_conf = DevConfModel.objects.get(pk=id_conf) - - kwargs = {} - kwargs['dev_conf'] = dev_conf - kwargs['dev_conf_keys'] = ['experiment', 'device'] - - kwargs['title'] = 'Configuration' - kwargs['suptitle'] = 'Details' - - kwargs['button'] = 'Edit Configuration' - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf)) + messages.error(request, "Import View not implemented yet") - messages.error(request, "Import View not implemented yet for this configuration") - - return render(request, 'dev_conf.html', kwargs) + return redirect('url_dev_conf', id_conf=conf.id) def dev_conf_export(request, id_conf): conf = get_object_or_404(Configuration, pk=id_conf) - DevConfModel = CONF_MODELS[conf.device.device_type.name] - dev_conf = DevConfModel.objects.get(pk=id_conf) - - kwargs = {} - kwargs['dev_conf'] = dev_conf - kwargs['dev_conf_keys'] = ['experiment', 'device'] - - kwargs['title'] = 'Configuration' - kwargs['suptitle'] = 'Details' - - kwargs['button'] = 'Edit Configuration' - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf)) + messages.error(request, "Export View not implemented yet") - messages.error(request, "Export View not implemented yet for this configuration") - - return render(request, 'dev_conf.html', kwargs) + return redirect('url_dev_conf', id_conf=conf.id) def dev_conf_delete(request, id_conf):