diff --git a/apps/abs/__init__.py b/apps/abs/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/abs/__init__.py +++ /dev/null diff --git a/apps/abs/admin.py b/apps/abs/admin.py deleted file mode 100644 index 921ced6..0000000 --- a/apps/abs/admin.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.contrib import admin -from .models import ABSConfiguration, ABSBeam, ABSActive - -# Register your models here. - -admin.site.register(ABSConfiguration) -admin.site.register(ABSBeam) -admin.site.register(ABSActive) \ No newline at end of file diff --git a/apps/abs/forms.py b/apps/abs/forms.py deleted file mode 100644 index 6753c1f..0000000 --- a/apps/abs/forms.py +++ /dev/null @@ -1,75 +0,0 @@ -from django import forms -from .models import ABSConfiguration, ABSBeam -from .widgets import UpDataWidget, DownDataWidget, EditUpDataWidget, EditDownDataWidget -from apps.main.models import Configuration -import os - -class ABSConfigurationForm(forms.ModelForm): - def __init__(self, *args, **kwargs): - super(ABSConfigurationForm, self).__init__(*args, **kwargs) - - class Meta: - model = ABSConfiguration - exclude = ('type', 'status', 'parameters', 'active_beam', - 'module_status', 'module_messages', 'module_mode', - 'author', 'hash') - - -class ABSBeamAddForm(forms.Form): - - up_data = forms.CharField(widget=UpDataWidget, label='') - down_data = forms.CharField(widget=DownDataWidget, label='') - - def __init__(self, *args, **kwargs): - super(ABSBeamAddForm, self).__init__(*args, **kwargs) - - - -class ABSBeamEditForm(forms.Form): - - up_data = forms.CharField(widget=EditUpDataWidget, label='') - down_data = forms.CharField(widget=EditDownDataWidget, label='') - - def __init__(self, *args, **kwargs): - super(ABSBeamEditForm, self).__init__(*args, **kwargs) - - if 'initial' in kwargs: - if 'beam' in self.initial: - self.fields['up_data'].initial = self.initial['beam'] - self.fields['down_data'].initial = self.initial['beam'] - - -class ExtFileField(forms.FileField): - """ - Same as forms.FileField, but you can specify a file extension whitelist. - - >>> from django.core.files.uploadedfile import SimpleUploadedFile - >>> - >>> t = ExtFileField(ext_whitelist=(".pdf", ".txt")) - >>> - >>> t.clean(SimpleUploadedFile('filename.pdf', 'Some File Content')) - >>> t.clean(SimpleUploadedFile('filename.txt', 'Some File Content')) - >>> - >>> t.clean(SimpleUploadedFile('filename.exe', 'Some File Content')) - Traceback (most recent call last): - ... - ValidationError: [u'Not allowed filetype!'] - """ - def __init__(self, *args, **kwargs): - extensions = kwargs.pop("extensions") - self.extensions = [i.lower() for i in extensions] - - super(ExtFileField, self).__init__(*args, **kwargs) - - def clean(self, *args, **kwargs): - data = super(ExtFileField, self).clean(*args, **kwargs) - filename = data.name - ext = os.path.splitext(filename)[1] - ext = ext.lower() - if ext not in self.extensions: - raise forms.ValidationError('Not allowed file type: %s' % ext) - - -class ABSImportForm(forms.Form): - - file_name = ExtFileField(extensions=['.json']) diff --git a/apps/abs/migrations/__init__.py b/apps/abs/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/abs/migrations/__init__.py +++ /dev/null diff --git a/apps/abs/models.py b/apps/abs/models.py deleted file mode 100644 index e0a0368..0000000 --- a/apps/abs/models.py +++ /dev/null @@ -1,1007 +0,0 @@ -from django.db import models -from apps.main.models import Configuration , User -from django.urls import reverse -from celery.execute import send_task -from datetime import datetime -import ast -import socket -import json -import requests -import struct -import os, sys, time - -antenna_default = json.dumps({ - "antenna_up": [[0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5], - [0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5], - [0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5], - [0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5], - [0.5,0.5,0.5,0.5,1.0,1.0,1.0,1.0], - [0.5,0.5,0.5,0.5,1.0,1.0,1.0,1.0], - [0.5,0.5,0.5,0.5,1.0,1.0,1.0,1.0], - [0.5,0.5,0.5,0.5,1.0,1.0,1.0,1.0] - ] - , - "antenna_down": [[0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5], - [0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5], - [0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5], - [0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5], - [0.5,0.5,0.5,0.5,3.0,3.0,3.0,3.0], - [0.5,0.5,0.5,0.5,3.0,3.0,3.0,3.0], - [0.5,0.5,0.5,0.5,3.0,3.0,3.0,3.0], - [0.5,0.5,0.5,0.5,3.0,3.0,3.0,3.0]], - }) - - -tx_default = json.dumps({ - "up": [[1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1]], - - "down": [[1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1]], - }) - -rx_default = json.dumps({ - "up": [[1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1]], - - "down": [[1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [1,1,1,1,0,0,0,0], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1], - [0,0,0,0,1,1,1,1]], - }) - -status_default = '0000000000000000000000000000000000000000000000000000000000000000' -default_messages = {} - -for i in range(1,65): - default_messages[str(i)] = "Module "+str(i) - - -ues_default = json.dumps({ - "up": [0.533333,0.00000,1.06667,0.00000], - "down": [0.533333,0.00000,1.06667,0.00000] - }) - -onlyrx_default = json.dumps({ - "up": False, - "down": False - }) - -def up_convertion(cadena): - valores = [] - for c in cadena: - if c == 1.0: valores=valores+['000'] - if c == 2.0: valores=valores+['001'] - if c == 3.0: valores=valores+['010'] - if c == 0.0: valores=valores+['011'] - if c == 0.5: valores=valores+['100'] - if c == 1.5: valores=valores+['101'] - if c == 2.5: valores=valores+['110'] - if c == 3.5: valores=valores+['111'] - - return valores - -def up_conv_bits(value): - - if value == 1.0: bits="000" - if value == 2.0: bits="001" - if value == 3.0: bits="010" - if value == 0.0: bits="011" - if value == 0.5: bits="100" - if value == 1.5: bits="101" - if value == 2.5: bits="110" - if value == 3.5: bits="111" - - return bits - -def down_convertion(cadena): - valores = [] - for c in cadena: - if c == 1.0: valores=valores+['000'] - if c == 2.0: valores=valores+['001'] - if c == 3.0: valores=valores+['010'] - if c == 0.0: valores=valores+['011'] - if c == 0.5: valores=valores+['100'] - if c == 1.5: valores=valores+['101'] - if c == 2.5: valores=valores+['110'] - if c == 3.5: valores=valores+['111'] - - return valores - -def down_conv_bits(value): - - if value == 1.0: bits="000" - if value == 2.0: bits="001" - if value == 3.0: bits="010" - if value == 0.0: bits="011" - if value == 0.5: bits="100" - if value == 1.5: bits="101" - if value == 2.5: bits="110" - if value == 3.5: bits="111" - - return bits - -def up_conv_value(bits): - - if bits == "000": value=1.0 - if bits == "001": value=2.0 - if bits == "010": value=3.0 - if bits == "011": value=0.0 - if bits == "100": value=0.5 - if bits == "101": value=1.5 - if bits == "110": value=2.5 - if bits == "111": value=3.5 - - return value - -def down_conv_value(bits): - - if bits == "000": value=1.0 - if bits == "001": value=2.0 - if bits == "010": value=3.0 - if bits == "011": value=0.0 - if bits == "100": value=0.5 - if bits == "101": value=1.5 - if bits == "110": value=2.5 - if bits == "111": value=3.5 - - return value - -def ip2position(module_number): - j=0 - i=0 - for x in range(0,module_number-1): - j=j+1 - if j==8: - i=i+1 - j=0 - - pos = [i,j] - return pos - - -def fromBinary2Char(binary_string): - number = int(binary_string, 2) - #Plus 33 to avoid more than 1 characters values such as: '\x01'-'\x1f' - number = number + 33 - char = chr(number) - return char - -def fromChar2Binary(char): - number = ord(char) - 33 - #Minus 33 to get the real value - bits = bin(number)[2:] - #To ensure we have a string with 6bits - if len(bits) < 6: - bits = bits.zfill(6) - return bits - -OPERATION_MODES = ( - (0, 'Manual'), - (1, 'Automatic'), - ) - -class ABSConfiguration(Configuration): - active_beam = models.PositiveSmallIntegerField(verbose_name='Active Beam', default=0) - module_status = models.CharField(verbose_name='Module Status', max_length=10000, default=status_default) - operation_mode = models.PositiveSmallIntegerField(verbose_name='Operation Mode', choices=OPERATION_MODES, default = 0) - operation_value = models.FloatField(verbose_name='Periodic (seconds)', default="10", null=True, blank=True) - module_messages = models.CharField(verbose_name='Modules Messages', max_length=10000, default=json.dumps(default_messages)) - - class Meta: - db_table = 'abs_configurations' - - def get_absolute_url_plot(self): - return reverse('url_plot_abs_patterns', args=[str(self.id)]) - - - def parms_to_dict(self): - - parameters = {} - - parameters['device_id'] = self.device.id - parameters['label'] = self.label - parameters['device_type'] = self.device.device_type.name - parameters['beams'] = {} - - beams = ABSBeam.objects.filter(abs_conf=self) - b=1 - for beam in beams: - #absbeam = ABSBeam.objects.get(pk=beams[beam]) - parameters['beams']['beam'+str(b)] = beam.parms_to_dict()#absbeam.parms_to_dict() - b+=1 - - return parameters - - - def dict_to_parms(self, parameters): - - self.label = parameters['label'] - - absbeams = ABSBeam.objects.filter(abs_conf=self) - beams = parameters['beams'] - - if absbeams: - beams_number = len(beams) - absbeams_number = len(absbeams) - if beams_number==absbeams_number: - i = 1 - for absbeam in absbeams: - absbeam.dict_to_parms(beams['beam'+str(i)]) - i = i+1 - elif beams_number > absbeams_number: - i = 1 - for absbeam in absbeams: - absbeam.dict_to_parms(beams['beam'+str(i)]) - i=i+1 - for x in range(i,beams_number+1): - new_beam = ABSBeam( - name =beams['beam'+str(i)]['name'], - antenna =json.dumps(beams['beam'+str(i)]['antenna']), - abs_conf = self, - tx =json.dumps(beams['beam'+str(i)]['tx']), - rx =json.dumps(beams['beam'+str(i)]['rx']), - ues =json.dumps(beams['beam'+str(i)]['ues']), - only_rx =json.dumps(beams['beam'+str(i)]['only_rx']) - ) - new_beam.save() - i=i+1 - else: #beams_number < absbeams_number: - i = 1 - for absbeam in absbeams: - if i <= beams_number: - absbeam.dict_to_parms(beams['beam'+str(i)]) - i=i+1 - else: - absbeam.delete() - else: - for beam in beams: - new_beam = ABSBeam( - name =beams[beam]['name'], - antenna =json.dumps(beams[beam]['antenna']), - abs_conf = self, - tx =json.dumps(beams[beam]['tx']), - rx =json.dumps(beams[beam]['rx']), - ues =json.dumps(beams[beam]['ues']), - only_rx =json.dumps(beams[beam]['only_rx']) - ) - new_beam.save() - - - - def update_from_file(self, parameters): - - self.dict_to_parms(parameters) - self.save() - - - def get_beams(self, **kwargs): - ''' - This function returns ABS Configuration beams - ''' - return ABSBeam.objects.filter(abs_conf=self.pk, **kwargs) - - def clone(self, **kwargs): - - beams = self.get_beams() - self.pk = None - self.id = None - for attr, value in kwargs.items(): - setattr(self, attr, value) - self.save() - - for beam in beams: - beam.clone(abs_conf=self) - - #-----For Active Beam----- - new_beams = ABSBeam.objects.filter(abs_conf=self) - self.active_beam = new_beams[0].id - self.save() - #-----For Active Beam----- - #-----For Device Status--- - self.device.status = 3 - self.device.save() - #-----For Device Status--- - - return self - - - def start_device(self): - - if self.device.status == 3: - - try: - #self.write_device() - send_task('task_change_beam', [self.id],) - self.message = 'ABS running' - - except Exception as e: - self.message = str(e) - return False - - return True - - else: - self.message = 'Please, select Write ABS Device first.' - return False - - - def stop_device(self): - - self.device.status = 2 - self.device.save() - self.message = 'ABS has been stopped.' - self.save() - - return True - - - def write_device(self): - - """ - This function sends the beams list to every abs module. - It needs 'module_conf' function - """ - print("Write") - beams = ABSBeam.objects.filter(abs_conf=self) - nbeams = len(beams) - - # Se manda a cero RC para poder realizar cambio de beam - if self.experiment is None: - confs = [] - else: - confs = Configuration.objects.filter(experiment = self.experiment).filter(type=0) - confdds = '' - confjars = '' - confrc = '' - #TO STOP DEVICES: DDS-JARS-RC - for i in range(0,len(confs)): - if i==0: - for conf in confs: - if conf.device.device_type.name == 'dds': - confdds = conf - confdds.stop_device() - break - if i==1: - for conf in confs: - if conf.device.device_type.name == 'jars': - confjars = conf - confjars.stop_device() - break - if i==2: - for conf in confs: - if conf.device.device_type.name == 'rc': - confrc = conf - confrc.stop_device() - break - - ''' - if self.connected_modules() == 0 : - print("No encuentra modulos") - self.message = "No ABS Module detected." - return False - ''' - #-------------Write each abs module----------- - - if beams: - block_id = 0 - message = 'SNDF{:03d}{:02d}{:02d}'.format(nbeams, nbeams, block_id) - for i, status in enumerate(self.module_status): - message += ''.join([fromBinary2Char(beam.module_6bits(i)) for beam in beams]) - status = ['0'] * 64 - n = 0 - print("Llega una antes entrar a multicast") - sock = self.send_multicast(message) - - while True: - #for i in range(32): - try: - data, address = sock.recvfrom(1024) - print (address, data) - data = data.decode("utf-8") - if data == '1': - status[int(address[0][10:])-1] = '3' - #print (int(address[0][10:])-1) - elif data == '0': - status[int(address[0][10:])-1] = '1' - except socket.timeout: - print('Timeout') - break - except Exception as e: - print ('Error {}'.format(e)) - n += 1 - sock.close() - else: - self.message = "ABS Configuration does not have beams" - print('No beams') - #Start DDS-RC-JARS - if confdds: - confdds.start_device() - if confrc: - #print confrc - confrc.start_device() - if confjars: - confjars.start_device() - return False - - if n == 64: - self.message = "Could not write ABS Modules" - self.device.status = 0 - self.module_status = ''.join(status) - self.save() - print('Could not write ABS') - #Start DDS-RC-JARS - if confdds: - confdds.start_device() - if confrc: - #print confrc - confrc.start_device() - if confjars: - confjars.start_device() - return False - else: - self.message = "ABS Beams List have been sent to ABS Modules" - print('ABS beams list sent') - self.active_beam = beams[0].pk - - #Start DDS-RC-JARS - if confdds: - confdds.start_device() - if confrc: - #print confrc - confrc.start_device() - if confjars: - confjars.start_device() - - print('Inicia intento de salvar device.status') - self.device.status = 3 - self.module_status = ''.join(status) - #print(status) - self.save() - print('Estatus salvado') - conf_active, __ = ABSActive.objects.get_or_create(pk=1) - conf_active.conf = self - conf_active.save() - return True - - - def read_module(self, module): - - """ - Read out-bits (up-down) of 1 abs module NOT for Configuration - """ - - ip_address = self.device.ip_address - ip_address = ip_address.split('.') - module_seq = (ip_address[0],ip_address[1],ip_address[2]) - dot = '.' - module_ip = dot.join(module_seq)+'.'+str(module) - module_port = self.device.port_address - read_route = 'http://'+module_ip+':'+str(module_port)+'/read' - - module_status = json.loads(self.module_status) - print(read_route) - - module_bits = '' - - try: - r_read = requests.get(read_route, timeout=0.5) - answer = r_read.json() - module_bits = answer['allbits'] - except: - return {} - - return module_bits - - def read_device(self): - - parms = {} - # Reads active modules. - module_status = json.loads(self.module_status) - total = 0 - for status in module_status: - if module_status[status] != 0: - module_bits = self.read_module(int(status)) - bits={} - if module_bits: - bits = (str(module_bits['um2']) + str(module_bits['um1']) + str(module_bits['um0']) + - str(module_bits['dm2']) + str(module_bits['dm1']) + str(module_bits['dm0']) ) - parms[str(status)] = bits - - total +=1 - - if total==0: - self.message = "No ABS Module detected. Please select 'Status'." - return False - - - - self.message = "ABS Modules have been read" - #monitoreo_tx = JROABSClnt_01CeCnMod000000MNTR10 - return parms - - - def connected_modules(self): - """ - This function returns the number of connected abs-modules without updating. - """ - num = 0 - print(self.module_status) - for i, status in enumerate(self.module_status): - if status != '0': - num += 1 - #print('status {}:{}'.format(i+1, status)) - return num - - def send_multicast(self, message): - #print("Send multicast") - multicast_group = ('224.3.29.71', 10000) - # Create the datagram socket - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.settimeout(1) - local_ip = os.environ.get('LOCAL_IP', '192.168.2.128') - sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(local_ip)) - sock.sendto(message.encode(), multicast_group) - print('Sending ' + message) - return sock - - def status_device(self): - """ - This function returns the status of all abs-modules as one. - If at least one module is connected, its answer is "1" - """ - print ('Status device') - print (self.active_beam) - beams = ABSBeam.objects.filter(abs_conf=self) - #print beams[self.active_beam-1].module_6bits(0) - active = ABSActive.objects.get(pk=1) - if active.conf != self: - self.message = 'La configuracion actual es la del siguiente enlace %s.' % active.conf.get_absolute_url() - self.message += "\n" - self.message += 'Se debe realizar un write en esta configuracion para luego obtener un status valido.' - - return False - - sock = self.send_multicast('MNTR') - - n = 0 - status = ['0'] * 64 - - while True: - #for i in range(32): - #if True: - try: - print("Recibiendo") - address = None - data, address = sock.recvfrom(2) - print (address, data) - print("!!!!") - data = data.decode() - aux_mon = "1" - aux_expected = aux_mon - if(len(data)==2): - print ("data[1]: ") - print (data[1]) - aux_mon = fromChar2Binary(data[1]) - print (aux_mon) - aux_i = (str(address[0]).split('.'))[3] - print (aux_i) - print ('Active beam') - beam_active = ABSBeam.objects.get(pk=self.active_beam) - print (beam_active) - aux_expected = beam_active.module_6bits(int(aux_i)-1) - print (aux_expected) - - print ("data[0]: ") - print (data[0]) - - if data[0] == '1': - status[int(address[0][10:])-1] = '3' - if aux_mon == aux_expected: - print ('Es igual') - else: - print ('Es diferente') - status[int(address[0][10:])-1] = '2' - - elif data[0] == '0': - status[int(address[0][10:])-1] = '1' - n += 1 - print('Module: {} connected'.format(address)) - except socket.timeout: - print('Timeout') - break - except: - print('Module: {} error'.format(address)) - pass - - sock.close() - - if n > 0: - self.message = 'ABS modules Status have been updated.' - self.device.status = 1 - else: - self.device.status = 0 - self.message = 'No ABS module is connected.' - self.module_status = ''.join(status) - self.save() - - return self.device.status - - - def send_beam(self, beam_pos): - """ - This function connects to a multicast group and sends the beam number - to all abs modules. - """ - print ('Send beam') - print (self.active_beam) - beams = ABSBeam.objects.filter(abs_conf=self) - #print beams[self.active_beam-1].module_6bits(0) - active = ABSActive.objects.get(pk=1) - if active.conf != self: - self.message = 'La configuracion actual es la del siguiente enlace %s.' % active.conf.get_absolute_url() - self.message += "\n" - self.message += 'Se debe realizar un write en esta configuracion para luego obtener un status valido.' - - return False - - # Se manda a cero RC para poder realizar cambio de beam - if self.experiment is None: - confs = [] - else: - confs = Configuration.objects.filter(experiment = self.experiment).filter(type=0) - confdds = '' - confjars = '' - confrc = '' - #TO STOP DEVICES: DDS-JARS-RC - for i in range(0,len(confs)): - if i==0: - for conf in confs: - if conf.device.device_type.name == 'dds': - confdds = conf - confdds.stop_device() - break - if i==1: - for conf in confs: - if conf.device.device_type.name == 'jars': - confjars = conf - confjars.stop_device() - break - if i==2: - for conf in confs: - if conf.device.device_type.name == 'rc': - confrc = conf - confrc.stop_device() - break - if beam_pos > 0: - beam_pos = beam_pos - 1 - else: - beam_pos = 0 - - #El indice del apunte debe ser menor que el numero total de apuntes - #El servidor tcp en el embebido comienza a contar desde 0 - status = ['0'] * 64 - message = 'CHGB{}'.format(beam_pos) - sock = self.send_multicast(message) - while True: - #for i in range(32): - try: - data, address = sock.recvfrom(1024) - print (address, data) - data = data.decode() - if data == '1': - status[int(address[0][10:])-1] = '3' - elif data == '0': - status[int(address[0][10:])-1] = '1' - except socket.timeout: - print('Timeout') - break - except Exception as e: - print ('Error {}'.format(e)) - pass - - sock.close() - - #Start DDS-RC-JARS - if confdds: - confdds.start_device() - if confrc: - #print confrc - confrc.start_device() - if confjars: - confjars.start_device() - - self.message = "ABS Beam has been changed" - self.module_status = ''.join(status) - self.save() - return True - - - def get_absolute_url_import(self): - return reverse('url_import_abs_conf', args=[str(self.id)]) - -class ABSActive(models.Model): - conf = models.ForeignKey(ABSConfiguration, null=True, verbose_name='ABS Configuration', on_delete=models.CASCADE) - - class Meta: - db_table = 'abs_absactive' - -class ABSBeam(models.Model): - - name = models.CharField(max_length=60, default='Beam') - antenna = models.CharField(verbose_name='Antenna', max_length=1000, default=antenna_default) - abs_conf = models.ForeignKey('ABSConfiguration', null=True, - verbose_name='ABS Configuration', on_delete=models.CASCADE) - tx = models.CharField(verbose_name='Tx', max_length=1000, default=tx_default) - rx = models.CharField(verbose_name='Rx', max_length=1000, default=rx_default) - s_time = models.TimeField(verbose_name='Star Time', default='00:00:00') - e_time = models.TimeField(verbose_name='End Time', default='23:59:59') - ues = models.CharField(verbose_name='Ues', max_length=100, default=ues_default) - only_rx = models.CharField(verbose_name='Only RX', max_length=40, default=onlyrx_default) - - class Meta: - db_table = 'abs_beams' - - def __unicode__(self): - return u'%s' % (self.name) - - def parms_to_dict(self): - - parameters = {} - parameters['name'] = self.name - parameters['antenna'] = ast.literal_eval(self.antenna) - parameters['abs_conf'] = self.abs_conf.name - parameters['tx'] = ast.literal_eval(self.tx) - parameters['rx'] = ast.literal_eval(self.rx) - parameters['s_time'] = self.s_time.strftime("%H:%M:%S") - parameters['e_time'] = self.e_time.strftime("%H:%M:%S") - parameters['ues'] = ast.literal_eval(self.ues) - parameters['only_rx'] = json.loads(self.only_rx) - - return parameters - - def dict_to_parms(self, parameters): - - self.name = parameters['name'] - self.antenna = json.dumps(parameters['antenna']) - #self.abs_conf = parameters['abs_conf'] - self.tx = json.dumps(parameters['tx']) - self.rx = json.dumps(parameters['rx']) - #self.s_time = parameters['s_time'] - #self.e_time = parameters['e_time'] - self.ues = json.dumps(parameters['ues']) - self.only_rx = json.dumps(parameters['only_rx']) - self.save() - - - def clone(self, **kwargs): - - self.pk = None - self.id = None - for attr, value in kwargs.items(): - setattr(self, attr, value) - - self.save() - - return self - - - def module_6bits(self, module): - """ - This function reads antenna pattern and choose 6bits (upbits-downbits) for one abs module - """ - module += 1 - if module > 64: - beam_bits = "" - return beam_bits - - data = ast.literal_eval(self.antenna) - up_data = data['antenna_up'] - down_data = data['antenna_down'] - - pos = ip2position(module) - up_value = up_data[pos[0]][pos[1]] - down_value = down_data[pos[0]][pos[1]] - - up_bits = up_conv_bits(up_value) - down_bits = down_conv_bits(down_value) - beam_bits = up_bits+down_bits - - return beam_bits - - - @property - def get_upvalues(self): - """ - This function reads antenna pattern and show the up-value of one abs module - """ - - data = ast.literal_eval(self.antenna) - up_data = data['antenna_up'] - - up_values = [] - for data in up_data: - for i in range(0,8): - up_values.append(data[i]) - - return up_values - - @property - def antenna_upvalues(self): - """ - This function reads antenna pattern and show the up - values of one abs beam - in a particular order - """ - data = ast.literal_eval(self.antenna) - up_data = data['antenna_up'] - - return up_data - - @property - def antenna_downvalues(self): - """ - This function reads antenna pattern and show the down - values of one abs beam - in a particular order - """ - data = ast.literal_eval(self.antenna) - down_data = data['antenna_down'] - - return down_data - - @property - def get_downvalues(self): - """ - This function reads antenna pattern and show the down-value of one abs module - """ - - data = ast.literal_eval(self.antenna) - down_data = data['antenna_down'] - - down_values = [] - for data in down_data: - for i in range(0,8): - down_values.append(data[i]) - - return down_values - - @property - def get_up_ues(self): - """ - This function shows the up-ues-value of one beam - """ - data = ast.literal_eval(self.ues) - up_ues = data['up'] - - return up_ues - - @property - def get_down_ues(self): - """ - This function shows the down-ues-value of one beam - """ - data = ast.literal_eval(self.ues) - down_ues = data['down'] - - return down_ues - - @property - def get_up_onlyrx(self): - """ - This function shows the up-onlyrx-value of one beam - """ - data = json.loads(self.only_rx) - up_onlyrx = data['up'] - - return up_onlyrx - - @property - def get_down_onlyrx(self): - """ - This function shows the down-onlyrx-value of one beam - """ - data = json.loads(self.only_rx) - down_onlyrx = data['down'] - - return down_onlyrx - - @property - def get_tx(self): - """ - This function shows the tx-values of one beam - """ - data = json.loads(self.tx) - - return data - - @property - def get_uptx(self): - """ - This function shows the up-tx-values of one beam - """ - data = json.loads(self.tx) - up_data = data['up'] - - up_values = [] - for data in up_data: - for i in range(0,8): - up_values.append(data[i]) - - return up_values - - @property - def get_downtx(self): - """ - This function shows the down-tx-values of one beam - """ - data = json.loads(self.tx) - down_data = data['down'] - - down_values = [] - for data in down_data: - for i in range(0,8): - down_values.append(data[i]) - - return down_values - - - - @property - def get_rx(self): - """ - This function shows the rx-values of one beam - """ - data = json.loads(self.rx) - - return data - - @property - def get_uprx(self): - """ - This function shows the up-rx-values of one beam - """ - data = json.loads(self.rx) - up_data = data['up'] - - up_values = [] - for data in up_data: - for i in range(0,8): - up_values.append(data[i]) - - return up_values - - @property - def get_downrx(self): - """ - This function shows the down-rx-values of one beam - """ - data = json.loads(self.rx) - down_data = data['down'] - - down_values = [] - for data in down_data: - for i in range(0,8): - down_values.append(data[i]) - - return down_values diff --git a/apps/abs/static/images/loader.gif b/apps/abs/static/images/loader.gif deleted file mode 100644 index dcf8e4f948c088716076a398a83f21beb0a652df..0000000000000000000000000000000000000000 GIT binary patch literal 0 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 diff --git a/apps/abs/static/js/abs.js b/apps/abs/static/js/abs.js deleted file mode 100644 index 4fdc38d..0000000 --- a/apps/abs/static/js/abs.js +++ /dev/null @@ -1,19 +0,0 @@ -$(document).ready(function() { - updateOperationMode() -}); - -$("#id_operation_mode").on('change', function() { - updateOperationMode() -}); - -function updateOperationMode(){ - var operation_mode = $("#id_operation_mode").val(); - if (operation_mode==0){ - document.getElementById("id_operation_value").disabled=true; - $("#id_operation_value").hide(); - } - else { - document.getElementById("id_operation_value").disabled=false; - $("#id_operation_value").show(); - } -} diff --git a/apps/abs/tasks.py b/apps/abs/tasks.py deleted file mode 100644 index a8a355c..0000000 --- a/apps/abs/tasks.py +++ /dev/null @@ -1,66 +0,0 @@ -from __future__ import absolute_import - -from apps.main.models import Configuration -from .models import ABSBeam -import json -from datetime import timedelta, datetime -from celery.task import task - -from celery.utils.log import get_task_logger -logger = get_task_logger(__name__) - -@task(name='task_change_beam') -def task_change_beam(id_conf): - - abs_conf = Configuration.objects.get(pk=id_conf) - beams_list = ABSBeam.objects.filter(abs_conf=abs_conf) - active_beam = json.loads(abs_conf.active_beam) - - run_every = timedelta(seconds=abs_conf.operation_value) - now = datetime.utcnow() - date = now + run_every - - if abs_conf.device.status != 3: - return abs_conf.device.status - - if abs_conf.operation_mode == 0: #Manual Mode - return 1 - - if active_beam: - current_beam = ABSBeam.objects.get(pk=active_beam['active_beam']) - i=0 - for beam in beams_list: - if beam == current_beam: - i+=1 - break - i+=1 - - if i < len(beams_list): - next_beam = beams_list[i] - abs_conf.send_beam_num(i+1) - next_beam.set_as_activebeam() - task = task_change_beam.apply_async((abs_conf.pk,), eta=date) - print (next_beam) - else: - abs_conf.send_beam_num(1) - beams_list[0].set_as_activebeam() - task = task_change_beam.apply_async((abs_conf.pk,), eta=date) - print (beams_list[0]) - i=0 - - else: - abs_conf.send_beam_num(1) - beams_list[0].set_as_activebeam() - task = task_change_beam.apply_async((abs_conf.pk,), eta=date) - - - return 2 - - -@task(name='status_absdevice') -def status_absdevice(id_conf): - - abs_conf = Configuration.objects.get(pk=id_conf) - abs_conf.absmodule_status() - - return diff --git a/apps/abs/templates/abs_beam_values.html b/apps/abs/templates/abs_beam_values.html deleted file mode 100644 index 027ef08..0000000 --- a/apps/abs/templates/abs_beam_values.html +++ /dev/null @@ -1,691 +0,0 @@ -{% load static %} -{% load bootstrap4 %} -{% load main_tags %} - -{% block content %} - - - - - -
-
-
UP
-
- - - - -
Antenna - - - - - - - - - -
North Quarter - - - - - - - - - - - - - -
{{beam.get_upvalues.0}} {{beam.get_upvalues.1}} {{beam.get_upvalues.2}} {{beam.get_upvalues.3}}
{{beam.get_upvalues.8}} {{beam.get_upvalues.9}} {{beam.get_upvalues.10}} {{beam.get_upvalues.11}}
{{beam.get_upvalues.16}} {{beam.get_upvalues.17}} {{beam.get_upvalues.18}} {{beam.get_upvalues.19}}
{{beam.get_upvalues.24}} {{beam.get_upvalues.25}} {{beam.get_upvalues.26}} {{beam.get_upvalues.27}}
-
East Quarter - - - - - - - - - - - - - -
{{beam.get_upvalues.4}} {{beam.get_upvalues.5}} {{beam.get_upvalues.6}} {{beam.get_upvalues.7}}
{{beam.get_upvalues.12}} {{beam.get_upvalues.13}} {{beam.get_upvalues.14}} {{beam.get_upvalues.15}}
{{beam.get_upvalues.20}} {{beam.get_upvalues.21}} {{beam.get_upvalues.22}} {{beam.get_upvalues.23}}
{{beam.get_upvalues.28}} {{beam.get_upvalues.29}} {{beam.get_upvalues.30}} {{beam.get_upvalues.31}}
-
West Quarter - - - - - - - - - - - - - -
{{beam.get_upvalues.32}} {{beam.get_upvalues.33}} {{beam.get_upvalues.34}} {{beam.get_upvalues.35}}
{{beam.get_upvalues.40}} {{beam.get_upvalues.41}} {{beam.get_upvalues.42}} {{beam.get_upvalues.43}}
{{beam.get_upvalues.48}} {{beam.get_upvalues.49}} {{beam.get_upvalues.50}} {{beam.get_upvalues.51}}
{{beam.get_upvalues.56}} {{beam.get_upvalues.57}} {{beam.get_upvalues.58}} {{beam.get_upvalues.59}}
-
South Quarter - - - - - - - - - - - - - -
{{beam.get_upvalues.36}} {{beam.get_upvalues.37}} {{beam.get_upvalues.38}} {{beam.get_upvalues.39}}
{{beam.get_upvalues.44}} {{beam.get_upvalues.45}} {{beam.get_upvalues.46}} {{beam.get_upvalues.47}}
{{beam.get_upvalues.52}} {{beam.get_upvalues.53}} {{beam.get_upvalues.54}} {{beam.get_upvalues.55}}
{{beam.get_upvalues.60}} {{beam.get_upvalues.61}} {{beam.get_upvalues.62}} {{beam.get_upvalues.63}}
-
-
- - - - - - -
TX - - - - - - - - - -
North Quarter - - - - - - - - - - - - - -
{{beam.get_tx.up.0.0}} {{beam.get_tx.up.0.1}} {{beam.get_tx.up.0.2}} {{beam.get_tx.up.0.3}}
{{beam.get_tx.up.1.0}} {{beam.get_tx.up.1.1}} {{beam.get_tx.up.1.2}} {{beam.get_tx.up.1.3}}
{{beam.get_tx.up.2.0}} {{beam.get_tx.up.2.1}} {{beam.get_tx.up.2.2}} {{beam.get_tx.up.2.3}}
{{beam.get_tx.up.3.0}} {{beam.get_tx.up.3.1}} {{beam.get_tx.up.3.2}} {{beam.get_tx.up.3.3}}
-
East Quarter - - - - - - - - - - - - - -
{{beam.get_tx.up.0.4}} {{beam.get_tx.up.0.5}} {{beam.get_tx.up.0.6}} {{beam.get_tx.up.0.7}}
{{beam.get_tx.up.1.4}} {{beam.get_tx.up.1.5}} {{beam.get_tx.up.1.6}} {{beam.get_tx.up.1.7}}
{{beam.get_tx.up.2.4}} {{beam.get_tx.up.2.5}} {{beam.get_tx.up.2.6}} {{beam.get_tx.up.2.7}}
{{beam.get_tx.up.3.4}} {{beam.get_tx.up.3.5}} {{beam.get_tx.up.3.6}} {{beam.get_tx.up.3.7}}
-
West Quarter - - - - - - - - - - - - - -
{{beam.get_tx.up.4.0}} {{beam.get_tx.up.4.1}} {{beam.get_tx.up.4.2}} {{beam.get_tx.up.4.3}}
{{beam.get_tx.up.5.0}} {{beam.get_tx.up.5.1}} {{beam.get_tx.up.5.2}} {{beam.get_tx.up.5.3}}
{{beam.get_tx.up.6.0}} {{beam.get_tx.up.6.1}} {{beam.get_tx.up.6.2}} {{beam.get_tx.up.6.3}}
{{beam.get_tx.up.7.0}} {{beam.get_tx.up.7.1}} {{beam.get_tx.up.7.2}} {{beam.get_tx.up.7.3}}
-
South Quarter - - - - - - - - - - - - - -
{{beam.get_tx.up.4.4}} {{beam.get_tx.up.4.5}} {{beam.get_tx.up.4.6}} {{beam.get_tx.up.4.7}}
{{beam.get_tx.up.5.4}} {{beam.get_tx.up.5.5}} {{beam.get_tx.up.5.6}} {{beam.get_tx.up.5.7}}
{{beam.get_tx.up.6.4}} {{beam.get_tx.up.6.5}} {{beam.get_tx.up.6.6}} {{beam.get_tx.up.6.7}}
{{beam.get_tx.up.7.4}} {{beam.get_tx.up.7.5}} {{beam.get_tx.up.7.6}} {{beam.get_tx.up.7.7}}
-
-
- - - - - - -
RX - - - - - - - - - -
North Quarter - - - - - - - - - - - - - -
{{beam.get_rx.up.0.0}} {{beam.get_rx.up.0.1}} {{beam.get_rx.up.0.2}} {{beam.get_rx.up.0.3}}
{{beam.get_rx.up.1.0}} {{beam.get_rx.up.1.1}} {{beam.get_rx.up.1.2}} {{beam.get_rx.up.1.3}}
{{beam.get_rx.up.2.0}} {{beam.get_rx.up.2.1}} {{beam.get_rx.up.2.2}} {{beam.get_rx.up.2.3}}
{{beam.get_rx.up.3.0}} {{beam.get_rx.up.3.1}} {{beam.get_rx.up.3.2}} {{beam.get_rx.up.3.3}}
-
East Quarter - - - - - - - - - - - - - -
{{beam.get_rx.up.0.4}} {{beam.get_rx.up.0.5}} {{beam.get_rx.up.0.6}} {{beam.get_rx.up.0.7}}
{{beam.get_rx.up.1.4}} {{beam.get_rx.up.1.5}} {{beam.get_rx.up.1.6}} {{beam.get_rx.up.1.7}}
{{beam.get_rx.up.2.4}} {{beam.get_rx.up.2.5}} {{beam.get_rx.up.2.6}} {{beam.get_rx.up.2.7}}
{{beam.get_rx.up.3.4}} {{beam.get_rx.up.3.5}} {{beam.get_rx.up.3.6}} {{beam.get_rx.up.3.7}}
-
West Quarter - - - - - - - - - - - - - -
{{beam.get_rx.up.4.0}} {{beam.get_rx.up.4.1}} {{beam.get_rx.up.4.2}} {{beam.get_rx.up.4.3}}
{{beam.get_rx.up.5.0}} {{beam.get_rx.up.5.1}} {{beam.get_rx.up.5.2}} {{beam.get_rx.up.5.3}}
{{beam.get_rx.up.6.0}} {{beam.get_rx.up.6.1}} {{beam.get_rx.up.6.2}} {{beam.get_rx.up.6.3}}
{{beam.get_rx.up.7.0}} {{beam.get_rx.up.7.1}} {{beam.get_rx.up.7.2}} {{beam.get_rx.up.7.3}}
-
South Quarter - - - - - - - - - - - - - -
{{beam.get_rx.up.4.4}} {{beam.get_rx.up.4.5}} {{beam.get_rx.up.4.6}} {{beam.get_rx.up.4.7}}
{{beam.get_rx.up.5.4}} {{beam.get_rx.up.5.5}} {{beam.get_rx.up.5.6}} {{beam.get_rx.up.5.7}}
{{beam.get_rx.up.6.4}} {{beam.get_rx.up.6.5}} {{beam.get_rx.up.6.6}} {{beam.get_rx.up.6.7}}
{{beam.get_rx.up.7.4}} {{beam.get_rx.up.7.5}} {{beam.get_rx.up.7.6}} {{beam.get_rx.up.7.7}}
-
-
- - - -
- -
- Ues: {{beam.get_up_ues}} -
- -
- - Only RX - -
- -
-
-
- -
-
-
DOWN
-
- - - - -
Antenna - - - - - - - - - -
North Quarter - - - - - - - - - - - - - -
{{beam.get_downvalues.0}} {{beam.get_downvalues.1}} {{beam.get_downvalues.2}} {{beam.get_downvalues.3}}
{{beam.get_downvalues.8}} {{beam.get_downvalues.9}} {{beam.get_downvalues.10}} {{beam.get_downvalues.11}}
{{beam.get_downvalues.16}} {{beam.get_downvalues.17}} {{beam.get_downvalues.18}} {{beam.get_downvalues.19}}
{{beam.get_downvalues.24}} {{beam.get_downvalues.25}} {{beam.get_downvalues.26}} {{beam.get_downvalues.27}}
-
East Quarter - - - - - - - - - - - - - -
{{beam.get_downvalues.4}} {{beam.get_downvalues.5}} {{beam.get_downvalues.6}} {{beam.get_downvalues.7}}
{{beam.get_downvalues.12}} {{beam.get_downvalues.13}} {{beam.get_downvalues.14}} {{beam.get_downvalues.15}}
{{beam.get_downvalues.20}} {{beam.get_downvalues.21}} {{beam.get_downvalues.22}} {{beam.get_downvalues.23}}
{{beam.get_downvalues.28}} {{beam.get_downvalues.29}} {{beam.get_downvalues.30}} {{beam.get_downvalues.31}}
-
West Quarter - - - - - - - - - - - - - -
{{beam.get_downvalues.32}} {{beam.get_downvalues.33}} {{beam.get_downvalues.34}} {{beam.get_downvalues.35}}
{{beam.get_downvalues.40}} {{beam.get_downvalues.41}} {{beam.get_downvalues.42}} {{beam.get_downvalues.43}}
{{beam.get_downvalues.48}} {{beam.get_downvalues.49}} {{beam.get_downvalues.50}} {{beam.get_downvalues.51}}
{{beam.get_downvalues.56}} {{beam.get_downvalues.57}} {{beam.get_downvalues.58}} {{beam.get_downvalues.59}}
-
South Quarter - - - - - - - - - - - - - -
{{beam.get_downvalues.36}} {{beam.get_downvalues.37}} {{beam.get_downvalues.38}} {{beam.get_downvalues.39}}
{{beam.get_downvalues.44}} {{beam.get_downvalues.45}} {{beam.get_downvalues.46}} {{beam.get_downvalues.47}}
{{beam.get_downvalues.52}} {{beam.get_downvalues.53}} {{beam.get_downvalues.54}} {{beam.get_downvalues.55}}
{{beam.get_downvalues.60}} {{beam.get_downvalues.61}} {{beam.get_downvalues.62}} {{beam.get_downvalues.63}}
-
-
- - - - - -
TX - - - - - - - - - -
North Quarter - - - - - - - - - - - - - -
{{beam.get_tx.down.0.0}} {{beam.get_tx.down.0.1}} {{beam.get_tx.down.0.2}} {{beam.get_tx.down.0.3}}
{{beam.get_tx.down.1.0}} {{beam.get_tx.down.1.1}} {{beam.get_tx.down.1.2}} {{beam.get_tx.down.1.3}}
{{beam.get_tx.down.2.0}} {{beam.get_tx.down.2.1}} {{beam.get_tx.down.2.2}} {{beam.get_tx.down.2.3}}
{{beam.get_tx.down.3.0}} {{beam.get_tx.down.3.1}} {{beam.get_tx.down.3.2}} {{beam.get_tx.down.3.3}}
-
East Quarter - - - - - - - - - - - - - -
{{beam.get_tx.down.0.4}} {{beam.get_tx.down.0.5}} {{beam.get_tx.down.0.6}} {{beam.get_tx.down.0.7}}
{{beam.get_tx.down.1.4}} {{beam.get_tx.down.1.5}} {{beam.get_tx.down.1.6}} {{beam.get_tx.down.1.7}}
{{beam.get_tx.down.2.4}} {{beam.get_tx.down.2.5}} {{beam.get_tx.down.2.6}} {{beam.get_tx.down.2.7}}
{{beam.get_tx.down.3.4}} {{beam.get_tx.down.3.5}} {{beam.get_tx.down.3.6}} {{beam.get_tx.down.3.7}}
-
West Quarter - - - - - - - - - - - - - -
{{beam.get_tx.down.4.0}} {{beam.get_tx.down.4.1}} {{beam.get_tx.down.4.2}} {{beam.get_tx.down.4.3}}
{{beam.get_tx.down.5.0}} {{beam.get_tx.down.5.1}} {{beam.get_tx.down.5.2}} {{beam.get_tx.down.5.3}}
{{beam.get_tx.down.6.0}} {{beam.get_tx.down.6.1}} {{beam.get_tx.down.6.2}} {{beam.get_tx.down.6.3}}
{{beam.get_tx.down.7.0}} {{beam.get_tx.down.7.1}} {{beam.get_tx.down.7.2}} {{beam.get_tx.down.7.3}}
-
South Quarter - - - - - - - - - - - - - -
{{beam.get_tx.down.4.4}} {{beam.get_tx.down.4.5}} {{beam.get_tx.down.4.6}} {{beam.get_tx.down.4.7}}
{{beam.get_tx.down.5.4}} {{beam.get_tx.down.5.5}} {{beam.get_tx.down.5.6}} {{beam.get_tx.down.5.7}}
{{beam.get_tx.down.6.4}} {{beam.get_tx.down.6.5}} {{beam.get_tx.down.6.6}} {{beam.get_tx.down.6.7}}
{{beam.get_tx.down.7.4}} {{beam.get_tx.down.7.5}} {{beam.get_tx.down.7.6}} {{beam.get_tx.down.7.7}}
-
-
- - - - - - -
RX - - - - - - - - - -
North Quarter - - - - - - - - - - - - - -
{{beam.get_rx.down.0.0}} {{beam.get_rx.down.0.1}} {{beam.get_rx.down.0.2}} {{beam.get_rx.down.0.3}}
{{beam.get_rx.down.1.0}} {{beam.get_rx.down.1.1}} {{beam.get_rx.down.1.2}} {{beam.get_rx.down.1.3}}
{{beam.get_rx.down.2.0}} {{beam.get_rx.down.2.1}} {{beam.get_rx.down.2.2}} {{beam.get_rx.down.2.3}}
{{beam.get_rx.down.3.0}} {{beam.get_rx.down.3.1}} {{beam.get_rx.down.3.2}} {{beam.get_rx.down.3.3}}
-
East Quarter - - - - - - - - - - - - - -
{{beam.get_rx.down.0.4}} {{beam.get_rx.down.0.5}} {{beam.get_rx.down.0.6}} {{beam.get_rx.down.0.7}}
{{beam.get_rx.down.1.4}} {{beam.get_rx.down.1.5}} {{beam.get_rx.down.1.6}} {{beam.get_rx.down.1.7}}
{{beam.get_rx.down.2.4}} {{beam.get_rx.down.2.5}} {{beam.get_rx.down.2.6}} {{beam.get_rx.down.2.7}}
{{beam.get_rx.down.3.4}} {{beam.get_rx.down.3.5}} {{beam.get_rx.down.3.6}} {{beam.get_rx.down.3.7}}
-
West Quarter - - - - - - - - - - - - - -
{{beam.get_rx.down.4.0}} {{beam.get_rx.down.4.1}} {{beam.get_rx.down.4.2}} {{beam.get_rx.down.4.3}}
{{beam.get_rx.down.5.0}} {{beam.get_rx.down.5.1}} {{beam.get_rx.down.5.2}} {{beam.get_rx.down.5.3}}
{{beam.get_rx.down.6.0}} {{beam.get_rx.down.6.1}} {{beam.get_rx.down.6.2}} {{beam.get_rx.down.6.3}}
{{beam.get_rx.down.7.0}} {{beam.get_rx.down.7.1}} {{beam.get_rx.down.7.2}} {{beam.get_rx.down.7.3}}
-
South Quarter - - - - - - - - - - - - - -
{{beam.get_rx.down.4.4}} {{beam.get_rx.down.4.5}} {{beam.get_rx.down.4.6}} {{beam.get_rx.down.4.7}}
{{beam.get_rx.down.5.4}} {{beam.get_rx.down.5.5}} {{beam.get_rx.down.5.6}} {{beam.get_rx.down.5.7}}
{{beam.get_rx.down.6.4}} {{beam.get_rx.down.6.5}} {{beam.get_rx.down.6.6}} {{beam.get_rx.down.6.7}}
{{beam.get_rx.down.7.4}} {{beam.get_rx.down.7.5}} {{beam.get_rx.down.7.6}} {{beam.get_rx.down.7.7}}
-
-
- - - -
- -
- Ues: {{beam.get_down_ues}} -
- -
- - Only RX - -
- -
-
-
- -{% endblock %} diff --git a/apps/abs/templates/abs_beams_list.html b/apps/abs/templates/abs_beams_list.html deleted file mode 100644 index 7ec2da8..0000000 --- a/apps/abs/templates/abs_beams_list.html +++ /dev/null @@ -1,42 +0,0 @@ -{% load bootstrap4 %} - -{% if abs_beams %} - -
- -


- - - {% for beam in abs_beams %} -
- -
-
- {% include "abs_beam_values.html" %} - {# bootstrap_form beam.form layout='horizontal' size='small' #} -
- -
- - {# endif #} - -
-
-
-{% endfor%} - -{% else %} -

No Beams...

-{% endif %} diff --git a/apps/abs/templates/abs_conf.html b/apps/abs/templates/abs_conf.html deleted file mode 100644 index 50756fb..0000000 --- a/apps/abs/templates/abs_conf.html +++ /dev/null @@ -1,339 +0,0 @@ -{% extends "dev_conf.html" %} {% load static %} {% load bootstrap4 %} {% load main_tags %} -{% block extra-head %} - -{% endblock %} -{% block extra-menu-actions %} -
  • - - View Patterns -
  • -{% endblock %} -{% block extra-content %} -{% if beams %} -

    Beams:

    -
    - - -
    - {% for beam in beams %} - - {% endfor %} -
    -
    - - -{% else %} -

    - No Beams... -

    -{% endif %} -{% endblock extra-content %} -{% block extra-js%} - -{% endblock %} diff --git a/apps/abs/templates/abs_conf_edit.html b/apps/abs/templates/abs_conf_edit.html deleted file mode 100644 index 3606ef3..0000000 --- a/apps/abs/templates/abs_conf_edit.html +++ /dev/null @@ -1,47 +0,0 @@ -{% extends "dev_conf_edit.html" %} -{% load bootstrap4 %} -{% load static %} - -{% block extra-head %} - - - -{% endblock %} - -{% block content %} -
    - {% csrf_token %} - {% bootstrap_form form layout='horizontal' size='medium' %} -
    -

    ABS Beams


    -
    - {% include "abs_beams_list.html" %} -
    -
    -
    -
    - - - -
    -
    -{% endblock %} - - -{% block extra-js%} - - -{% endblock %} diff --git a/apps/abs/templates/abs_downpattern_img.html b/apps/abs/templates/abs_downpattern_img.html deleted file mode 100644 index 1ea4e11..0000000 --- a/apps/abs/templates/abs_downpattern_img.html +++ /dev/null @@ -1,11 +0,0 @@ -{% load static %} -{% load bootstrap4 %} -{% load main_tags %} - -{% block content %} - -
    - Error ploting... -
    - -{% endblock %} diff --git a/apps/abs/templates/abs_edit_beam.html b/apps/abs/templates/abs_edit_beam.html deleted file mode 100644 index 40a4f64..0000000 --- a/apps/abs/templates/abs_edit_beam.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "dev_conf_edit.html" %} - -{% load bootstrap4 %} -{% load static %} -{% load main_tags %} - -{% block content %} -
    - {% csrf_token %} - {#% bootstrap_form form layout='horizontal' size='medium' %#} - {{form}} -
    -
    -
    - - -
    -
    -{% endblock %} diff --git a/apps/abs/templates/abs_pattern.html b/apps/abs/templates/abs_pattern.html deleted file mode 100644 index 0ec16c5..0000000 --- a/apps/abs/templates/abs_pattern.html +++ /dev/null @@ -1,624 +0,0 @@ -{% load static %} -{% load bootstrap4 %} -{% load main_tags %} - -{% block content %} - - - -
    -
    -
    UP
    -
    - - - - -
    Antenna - - - - - - - - - -
    North Quarter - - - - - -
    {{beam.get_upvalues.0}} {{beam.get_upvalues.1}} {{beam.get_upvalues.2}} {{beam.get_upvalues.3}}
    {{beam.get_upvalues.8}} {{beam.get_upvalues.9}} {{beam.get_upvalues.10}} {{beam.get_upvalues.11}}
    {{beam.get_upvalues.16}} {{beam.get_upvalues.17}} {{beam.get_upvalues.18}} {{beam.get_upvalues.19}}
    {{beam.get_upvalues.24}} {{beam.get_upvalues.25}} {{beam.get_upvalues.26}} {{beam.get_upvalues.27}}
    -
    East Quarter - - - - - -
    {{beam.get_upvalues.4}} {{beam.get_upvalues.5}} {{beam.get_upvalues.6}} {{beam.get_upvalues.7}}
    {{beam.get_upvalues.12}} {{beam.get_upvalues.13}} {{beam.get_upvalues.14}} {{beam.get_upvalues.15}}
    {{beam.get_upvalues.20}} {{beam.get_upvalues.21}} {{beam.get_upvalues.22}} {{beam.get_upvalues.23}}
    {{beam.get_upvalues.28}} {{beam.get_upvalues.29}} {{beam.get_upvalues.30}} {{beam.get_upvalues.31}}
    -
    West Quarter - - - - - -
    {{beam.get_upvalues.32}} {{beam.get_upvalues.33}} {{beam.get_upvalues.34}} {{beam.get_upvalues.35}}
    {{beam.get_upvalues.40}} {{beam.get_upvalues.41}} {{beam.get_upvalues.42}} {{beam.get_upvalues.43}}
    {{beam.get_upvalues.48}} {{beam.get_upvalues.49}} {{beam.get_upvalues.50}} {{beam.get_upvalues.51}}
    {{beam.get_upvalues.56}} {{beam.get_upvalues.57}} {{beam.get_upvalues.58}} {{beam.get_upvalues.59}}
    -
    South Quarter - - - - - -
    {{beam.get_upvalues.36}} {{beam.get_upvalues.37}} {{beam.get_upvalues.38}} {{beam.get_upvalues.39}}
    {{beam.get_upvalues.44}} {{beam.get_upvalues.45}} {{beam.get_upvalues.46}} {{beam.get_upvalues.47}}
    {{beam.get_upvalues.52}} {{beam.get_upvalues.53}} {{beam.get_upvalues.54}} {{beam.get_upvalues.55}}
    {{beam.get_upvalues.60}} {{beam.get_upvalues.61}} {{beam.get_upvalues.62}} {{beam.get_upvalues.63}}
    -
    -
    - - - - - - -
    TX - - - - - - - - - -
    North Quarter - - - - - - - - - - - - - -
    {{beam.get_tx.up.0.0}} {{beam.get_tx.up.0.1}} {{beam.get_tx.up.0.2}} {{beam.get_tx.up.0.3}}
    {{beam.get_tx.up.1.0}} {{beam.get_tx.up.1.1}} {{beam.get_tx.up.1.2}} {{beam.get_tx.up.1.3}}
    {{beam.get_tx.up.2.0}} {{beam.get_tx.up.2.1}} {{beam.get_tx.up.2.2}} {{beam.get_tx.up.2.3}}
    {{beam.get_tx.up.3.0}} {{beam.get_tx.up.3.1}} {{beam.get_tx.up.3.2}} {{beam.get_tx.up.3.3}}
    -
    East Quarter - - - - - - - - - - - - - -
    {{beam.get_tx.up.0.4}} {{beam.get_tx.up.0.5}} {{beam.get_tx.up.0.6}} {{beam.get_tx.up.0.7}}
    {{beam.get_tx.up.1.4}} {{beam.get_tx.up.1.5}} {{beam.get_tx.up.1.6}} {{beam.get_tx.up.1.7}}
    {{beam.get_tx.up.2.4}} {{beam.get_tx.up.2.5}} {{beam.get_tx.up.2.6}} {{beam.get_tx.up.2.7}}
    {{beam.get_tx.up.3.4}} {{beam.get_tx.up.3.5}} {{beam.get_tx.up.3.6}} {{beam.get_tx.up.3.7}}
    -
    West Quarter - - - - - - - - - - - - - -
    {{beam.get_tx.up.4.0}} {{beam.get_tx.up.4.1}} {{beam.get_tx.up.4.2}} {{beam.get_tx.up.4.3}}
    {{beam.get_tx.up.5.0}} {{beam.get_tx.up.5.1}} {{beam.get_tx.up.5.2}} {{beam.get_tx.up.5.3}}
    {{beam.get_tx.up.6.0}} {{beam.get_tx.up.6.1}} {{beam.get_tx.up.6.2}} {{beam.get_tx.up.6.3}}
    {{beam.get_tx.up.7.0}} {{beam.get_tx.up.7.1}} {{beam.get_tx.up.7.2}} {{beam.get_tx.up.7.3}}
    -
    South Quarter - - - - - - - - - - - - - -
    {{beam.get_tx.up.4.4}} {{beam.get_tx.up.4.5}} {{beam.get_tx.up.4.6}} {{beam.get_tx.up.4.7}}
    {{beam.get_tx.up.5.4}} {{beam.get_tx.up.5.5}} {{beam.get_tx.up.5.6}} {{beam.get_tx.up.5.7}}
    {{beam.get_tx.up.6.4}} {{beam.get_tx.up.6.5}} {{beam.get_tx.up.6.6}} {{beam.get_tx.up.6.7}}
    {{beam.get_tx.up.7.4}} {{beam.get_tx.up.7.5}} {{beam.get_tx.up.7.6}} {{beam.get_tx.up.7.7}}
    -
    -
    - - - - - - -
    RX - - - - - - - - - -
    North Quarter - - - - - - - - - - - - - -
    {{beam.get_rx.up.0.0}} {{beam.get_rx.up.0.1}} {{beam.get_rx.up.0.2}} {{beam.get_rx.up.0.3}}
    {{beam.get_rx.up.1.0}} {{beam.get_rx.up.1.1}} {{beam.get_rx.up.1.2}} {{beam.get_rx.up.1.3}}
    {{beam.get_rx.up.2.0}} {{beam.get_rx.up.2.1}} {{beam.get_rx.up.2.2}} {{beam.get_rx.up.2.3}}
    {{beam.get_rx.up.3.0}} {{beam.get_rx.up.3.1}} {{beam.get_rx.up.3.2}} {{beam.get_rx.up.3.3}}
    -
    East Quarter - - - - - - - - - - - - - -
    {{beam.get_rx.up.0.4}} {{beam.get_rx.up.0.5}} {{beam.get_rx.up.0.6}} {{beam.get_rx.up.0.7}}
    {{beam.get_rx.up.1.4}} {{beam.get_rx.up.1.5}} {{beam.get_rx.up.1.6}} {{beam.get_rx.up.1.7}}
    {{beam.get_rx.up.2.4}} {{beam.get_rx.up.2.5}} {{beam.get_rx.up.2.6}} {{beam.get_rx.up.2.7}}
    {{beam.get_rx.up.3.4}} {{beam.get_rx.up.3.5}} {{beam.get_rx.up.3.6}} {{beam.get_rx.up.3.7}}
    -
    West Quarter - - - - - - - - - - - - - -
    {{beam.get_rx.up.4.0}} {{beam.get_rx.up.4.1}} {{beam.get_rx.up.4.2}} {{beam.get_rx.up.4.3}}
    {{beam.get_rx.up.5.0}} {{beam.get_rx.up.5.1}} {{beam.get_rx.up.5.2}} {{beam.get_rx.up.5.3}}
    {{beam.get_rx.up.6.0}} {{beam.get_rx.up.6.1}} {{beam.get_rx.up.6.2}} {{beam.get_rx.up.6.3}}
    {{beam.get_rx.up.7.0}} {{beam.get_rx.up.7.1}} {{beam.get_rx.up.7.2}} {{beam.get_rx.up.7.3}}
    -
    South Quarter - - - - - - - - - - - - - -
    {{beam.get_rx.up.4.4}} {{beam.get_rx.up.4.5}} {{beam.get_rx.up.4.6}} {{beam.get_rx.up.4.7}}
    {{beam.get_rx.up.5.4}} {{beam.get_rx.up.5.5}} {{beam.get_rx.up.5.6}} {{beam.get_rx.up.5.7}}
    {{beam.get_rx.up.6.4}} {{beam.get_rx.up.6.5}} {{beam.get_rx.up.6.6}} {{beam.get_rx.up.6.7}}
    {{beam.get_rx.up.7.4}} {{beam.get_rx.up.7.5}} {{beam.get_rx.up.7.6}} {{beam.get_rx.up.7.7}}
    -
    -
    - - {% if not edit %} - {% include "abs_uppattern_img.html" %} - {% endif %} - -
    - -
    - Ues: {{beam.get_up_ues}} -
    - -
    - - Only RX - -
    - -
    -
    -
    - -
    -
    -
    DOWN
    -
    - - - - -
    Antenna - - - - - - - - - -
    North Quarter - - - - - -
    {{beam.get_downvalues.0}} {{beam.get_downvalues.1}} {{beam.get_downvalues.2}} {{beam.get_downvalues.3}}
    {{beam.get_downvalues.8}} {{beam.get_downvalues.9}} {{beam.get_downvalues.10}} {{beam.get_downvalues.11}}
    {{beam.get_downvalues.16}} {{beam.get_downvalues.17}} {{beam.get_downvalues.18}} {{beam.get_downvalues.19}}
    {{beam.get_downvalues.24}} {{beam.get_downvalues.25}} {{beam.get_downvalues.26}} {{beam.get_downvalues.27}}
    -
    East Quarter - - - - - -
    {{beam.get_downvalues.4}} {{beam.get_downvalues.5}} {{beam.get_downvalues.6}} {{beam.get_downvalues.7}}
    {{beam.get_downvalues.12}} {{beam.get_downvalues.13}} {{beam.get_downvalues.14}} {{beam.get_downvalues.15}}
    {{beam.get_downvalues.20}} {{beam.get_downvalues.21}} {{beam.get_downvalues.22}} {{beam.get_downvalues.23}}
    {{beam.get_downvalues.28}} {{beam.get_downvalues.29}} {{beam.get_downvalues.30}} {{beam.get_downvalues.31}}
    -
    West Quarter - - - - - -
    {{beam.get_downvalues.32}} {{beam.get_downvalues.33}} {{beam.get_downvalues.34}} {{beam.get_downvalues.35}}
    {{beam.get_downvalues.40}} {{beam.get_downvalues.41}} {{beam.get_downvalues.42}} {{beam.get_downvalues.43}}
    {{beam.get_downvalues.48}} {{beam.get_downvalues.49}} {{beam.get_downvalues.50}} {{beam.get_downvalues.51}}
    {{beam.get_downvalues.56}} {{beam.get_downvalues.57}} {{beam.get_downvalues.58}} {{beam.get_downvalues.59}}
    -
    South Quarter - - - - - -
    {{beam.get_downvalues.36}} {{beam.get_downvalues.37}} {{beam.get_downvalues.38}} {{beam.get_downvalues.39}}
    {{beam.get_downvalues.44}} {{beam.get_downvalues.45}} {{beam.get_downvalues.46}} {{beam.get_downvalues.47}}
    {{beam.get_downvalues.52}} {{beam.get_downvalues.53}} {{beam.get_downvalues.54}} {{beam.get_downvalues.55}}
    {{beam.get_downvalues.60}} {{beam.get_downvalues.61}} {{beam.get_downvalues.62}} {{beam.get_downvalues.63}}
    -
    -
    - - - - - -
    TX - - - - - - - - - -
    North Quarter - - - - - - - - - - - - - -
    {{beam.get_tx.down.0.0}} {{beam.get_tx.down.0.1}} {{beam.get_tx.down.0.2}} {{beam.get_tx.down.0.3}}
    {{beam.get_tx.down.1.0}} {{beam.get_tx.down.1.1}} {{beam.get_tx.down.1.2}} {{beam.get_tx.down.1.3}}
    {{beam.get_tx.down.2.0}} {{beam.get_tx.down.2.1}} {{beam.get_tx.down.2.2}} {{beam.get_tx.down.2.3}}
    {{beam.get_tx.down.3.0}} {{beam.get_tx.down.3.1}} {{beam.get_tx.down.3.2}} {{beam.get_tx.down.3.3}}
    -
    East Quarter - - - - - - - - - - - - - -
    {{beam.get_tx.down.0.4}} {{beam.get_tx.down.0.5}} {{beam.get_tx.down.0.6}} {{beam.get_tx.down.0.7}}
    {{beam.get_tx.down.1.4}} {{beam.get_tx.down.1.5}} {{beam.get_tx.down.1.6}} {{beam.get_tx.down.1.7}}
    {{beam.get_tx.down.2.4}} {{beam.get_tx.down.2.5}} {{beam.get_tx.down.2.6}} {{beam.get_tx.down.2.7}}
    {{beam.get_tx.down.3.4}} {{beam.get_tx.down.3.5}} {{beam.get_tx.down.3.6}} {{beam.get_tx.down.3.7}}
    -
    West Quarter - - - - - - - - - - - - - -
    {{beam.get_tx.down.4.0}} {{beam.get_tx.down.4.1}} {{beam.get_tx.down.4.2}} {{beam.get_tx.down.4.3}}
    {{beam.get_tx.down.5.0}} {{beam.get_tx.down.5.1}} {{beam.get_tx.down.5.2}} {{beam.get_tx.down.5.3}}
    {{beam.get_tx.down.6.0}} {{beam.get_tx.down.6.1}} {{beam.get_tx.down.6.2}} {{beam.get_tx.down.6.3}}
    {{beam.get_tx.down.7.0}} {{beam.get_tx.down.7.1}} {{beam.get_tx.down.7.2}} {{beam.get_tx.down.7.3}}
    -
    South Quarter - - - - - - - - - - - - - -
    {{beam.get_tx.down.4.4}} {{beam.get_tx.down.4.5}} {{beam.get_tx.down.4.6}} {{beam.get_tx.down.4.7}}
    {{beam.get_tx.down.5.4}} {{beam.get_tx.down.5.5}} {{beam.get_tx.down.5.6}} {{beam.get_tx.down.5.7}}
    {{beam.get_tx.down.6.4}} {{beam.get_tx.down.6.5}} {{beam.get_tx.down.6.6}} {{beam.get_tx.down.6.7}}
    {{beam.get_tx.down.7.4}} {{beam.get_tx.down.7.5}} {{beam.get_tx.down.7.6}} {{beam.get_tx.down.7.7}}
    -
    -
    - - - - - - -
    RX - - - - - - - - - -
    North Quarter - - - - - - - - - - - - - -
    {{beam.get_rx.down.0.0}} {{beam.get_rx.down.0.1}} {{beam.get_rx.down.0.2}} {{beam.get_rx.down.0.3}}
    {{beam.get_rx.down.1.0}} {{beam.get_rx.down.1.1}} {{beam.get_rx.down.1.2}} {{beam.get_rx.down.1.3}}
    {{beam.get_rx.down.2.0}} {{beam.get_rx.down.2.1}} {{beam.get_rx.down.2.2}} {{beam.get_rx.down.2.3}}
    {{beam.get_rx.down.3.0}} {{beam.get_rx.down.3.1}} {{beam.get_rx.down.3.2}} {{beam.get_rx.down.3.3}}
    -
    East Quarter - - - - - - - - - - - - - -
    {{beam.get_rx.down.0.4}} {{beam.get_rx.down.0.5}} {{beam.get_rx.down.0.6}} {{beam.get_rx.down.0.7}}
    {{beam.get_rx.down.1.4}} {{beam.get_rx.down.1.5}} {{beam.get_rx.down.1.6}} {{beam.get_rx.down.1.7}}
    {{beam.get_rx.down.2.4}} {{beam.get_rx.down.2.5}} {{beam.get_rx.down.2.6}} {{beam.get_rx.down.2.7}}
    {{beam.get_rx.down.3.4}} {{beam.get_rx.down.3.5}} {{beam.get_rx.down.3.6}} {{beam.get_rx.down.3.7}}
    -
    West Quarter - - - - - - - - - - - - - -
    {{beam.get_rx.down.4.0}} {{beam.get_rx.down.4.1}} {{beam.get_rx.down.4.2}} {{beam.get_rx.down.4.3}}
    {{beam.get_rx.down.5.0}} {{beam.get_rx.down.5.1}} {{beam.get_rx.down.5.2}} {{beam.get_rx.down.5.3}}
    {{beam.get_rx.down.6.0}} {{beam.get_rx.down.6.1}} {{beam.get_rx.down.6.2}} {{beam.get_rx.down.6.3}}
    {{beam.get_rx.down.7.0}} {{beam.get_rx.down.7.1}} {{beam.get_rx.down.7.2}} {{beam.get_rx.down.7.3}}
    -
    South Quarter - - - - - - - - - - - - - -
    {{beam.get_rx.down.4.4}} {{beam.get_rx.down.4.5}} {{beam.get_rx.down.4.6}} {{beam.get_rx.down.4.7}}
    {{beam.get_rx.down.5.4}} {{beam.get_rx.down.5.5}} {{beam.get_rx.down.5.6}} {{beam.get_rx.down.5.7}}
    {{beam.get_rx.down.6.4}} {{beam.get_rx.down.6.5}} {{beam.get_rx.down.6.6}} {{beam.get_rx.down.6.7}}
    {{beam.get_rx.down.7.4}} {{beam.get_rx.down.7.5}} {{beam.get_rx.down.7.6}} {{beam.get_rx.down.7.7}}
    -
    -
    - - {% if not edit %} - {% include "abs_downpattern_img.html" %} - {% endif %} - -
    - -
    - Ues: {{beam.get_down_ues}} -
    - -
    - - Only RX - -
    - -
    -
    -
    - -{% endblock %} - - diff --git a/apps/abs/templates/abs_patterns.html b/apps/abs/templates/abs_patterns.html deleted file mode 100644 index 3ba0a59..0000000 --- a/apps/abs/templates/abs_patterns.html +++ /dev/null @@ -1,48 +0,0 @@ -{% extends "dev_conf.html" %} -{% load static %} -{% load bootstrap4 %} -{% load main_tags %} - -{% block content %} - -{% if abs_beams %} -
    -

    Beams:

    - -
    -
    - {% for abs_beam in abs_beams %} - - {% endfor %} -
    -
    - - -
    -
    - - {% if beam %} - {% include "abs_pattern.html" %} - {% endif %} - -{% else %} -
    -

    Beams:

    -

    No Beams...

    -
    -{% endif %} - - - - -{% endblock %} diff --git a/apps/abs/templates/abs_uppattern_img.html b/apps/abs/templates/abs_uppattern_img.html deleted file mode 100644 index 43d045a..0000000 --- a/apps/abs/templates/abs_uppattern_img.html +++ /dev/null @@ -1,11 +0,0 @@ -{% load static %} -{% load bootstrap4 %} -{% load main_tags %} - -{% block content %} - -
    - Error ploting... -
    - -{% endblock %} diff --git a/apps/abs/tests.py b/apps/abs/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/apps/abs/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/abs/urls.py b/apps/abs/urls.py deleted file mode 100644 index a45ebe4..0000000 --- a/apps/abs/urls.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.urls import path - -from apps.abs import views - -urlpatterns = ( - path('/', views.abs_conf, name='url_abs_conf'), - path('/edit/', views.abs_conf_edit, name='url_edit_abs_conf'), - path('alert/', views.abs_conf_alert, name='url_alert_abs_conf'), - path('/import/', views.import_file, name='url_import_abs_conf'), - #url(r'^(?P-?\d+)/status/', views.abs_conf, {'status_request':True},name='url_status_abs_conf'), - path('/change_beam//', views.send_beam, name='url_send_beam'), - path('/plot/', views.plot_patterns, name='url_plot_abs_patterns'), - path('/plot//', views.plot_patterns, name='url_plot_abs_patterns'), - path('/plot///pattern.png', views.plot_pattern, name='url_plot_beam'), - path('/add_beam/', views.add_beam, name='url_add_abs_beam'), - path('/beam//delete/', views.remove_beam, name='url_remove_abs_beam'), - path('/beam//edit/', views.edit_beam, name='url_edit_abs_beam'), -) diff --git a/apps/abs/utils/Astro_Coords.py b/apps/abs/utils/Astro_Coords.py deleted file mode 100644 index 6392594..0000000 --- a/apps/abs/utils/Astro_Coords.py +++ /dev/null @@ -1,1419 +0,0 @@ -""" -The module ASTRO_COORDS.py gathers classes and functions for coordinates transformation. Additiona- -lly a class EquatorialCorrections and celestial bodies are defined. The first of these is to correct -any error in the location of the body and the second to know the location of certain celestial bo- -dies in the sky. - -MODULES CALLED: -OS, NUMPY, NUMERIC, SCIPY, TIME_CONVERSIONS - -MODIFICATION HISTORY: -Created by Ing. Freddy Galindo (frederickgalindo@gmail.com). ROJ Sep 20, 2009. -""" - -import numpy -#import Numeric -import scipy.interpolate -import os -import sys -from .TimeTools import Julian , Time -from .Misc_Routines import CoFactors - -class EquatorialCorrections(): - def __init__(self): - """ - EquatorialCorrections class creates an object to call methods to correct the loca- - tion of the celestial bodies. - - Modification History - -------------------- - Converted to Object-oriented Programming by Freddy Galindo, ROJ, 27 September 2009. - """ - - pass - - def co_nutate(self,jd,ra,dec): - """ - co_nutate calculates changes in RA and Dec due to nutation of the Earth's rotation - Additionally it returns the obliquity of the ecliptic (eps), nutation in the longi- - tude of the ecliptic (d_psi) and nutation in the pbliquity of the ecliptic (d_eps). - - Parameters - ---------- - jd = Julian Date (Scalar or array). - RA = A scalar o array giving the Right Ascention of interest. - Dec = A scalar o array giving the Right Ascention of interest. - - Return - ------ - d_ra = Correction to ra due to nutation. - d_dec = Correction to dec due to nutation. - - Examples - -------- - >> Julian = 2462088.7 - >> Ra = 41.547213 - >> Dec = 49.348483 - >> [d_ra,d_dec,eps,d_psi,d_eps] = co_nutate(julian,Ra,Dec) - >> print d_ra, d_dec, eps, d_psi, d_eps - [ 15.84276651] [ 6.21641029] [ 0.4090404] [ 14.85990198] [ 2.70408658] - - Modification history - -------------------- - Written by Chris O'Dell, 2002. - Converted to Python by Freddy R. Galindo, ROJ, 26 September 2009. - """ - - jd = numpy.atleast_1d(jd) - ra = numpy.atleast_1d(ra) - dec = numpy.atleast_1d(dec) - - # Useful transformation constants - d2as = numpy.pi/(180.*3600.) - - # Julian centuries from J2000 of jd - T = (jd - 2451545.0)/36525.0 - - # Must calculate obliquity of ecliptic - [d_psi, d_eps] = self.nutate(jd) - d_psi = numpy.atleast_1d(d_psi) - d_eps = numpy.atleast_1d(d_eps) - - eps0 = (23.4392911*3600.) - (46.8150*T) - (0.00059*T**2) + (0.001813*T**3) - # True obliquity of the ecliptic in radians - eps = (eps0 + d_eps)/3600.*CoFactors.d2r - - # Useful numbers - ce = numpy.cos(eps) - se = numpy.sin(eps) - - # Convert Ra-Dec to equatorial rectangular coordinates - x = numpy.cos(ra*CoFactors.d2r)*numpy.cos(dec*CoFactors.d2r) - y = numpy.sin(ra*CoFactors.d2r)*numpy.cos(dec*CoFactors.d2r) - z = numpy.sin(dec*CoFactors.d2r) - - # Apply corrections to each rectangular coordinate - x2 = x - (y*ce + z*se)*d_psi*CoFactors.s2r - y2 = y + (x*ce*d_psi - z*d_eps)*CoFactors.s2r - z2 = z + (x*se*d_psi + y*d_eps)*CoFactors.s2r - - # Convert bask to equatorial spherical coordinates - r = numpy.sqrt(x2**2. + y2**2. + z2**2.) - xyproj =numpy.sqrt(x2**2. + y2**2.) - - ra2 = x2*0.0 - dec2 = x2*0.0 - - xyproj = numpy.atleast_1d(xyproj) - z = numpy.atleast_1d(z) - r = numpy.atleast_1d(r) - x2 = numpy.atleast_1d(x2) - y2 = numpy.atleast_1d(y2) - z2 = numpy.atleast_1d(z2) - ra2 = numpy.atleast_1d(ra2) - dec2 = numpy.atleast_1d(dec2) - - w1 = numpy.where((xyproj==0) & (z!=0)) - w2 = numpy.where(xyproj!=0) - - # Calculate Ra and Dec in radians (later convert to degrees) - if w1[0].size>0: - # Places where xyproj=0 (point at NCP or SCP) - dec2[w1] = numpy.arcsin(z2[w1]/r[w1]) - ra2[w1] = 0 - - if w2[0].size>0: - # Places other than NCP or SCP - ra2[w2] = numpy.arctan2(y2[w2],x2[w2]) - dec2[w2] = numpy.arcsin(z2[w2]/r[w2]) - - # Converting to degree - ra2 = ra2/CoFactors.d2r - dec2 = dec2/CoFactors.d2r - - w = numpy.where(ra2<0.) - if w[0].size>0: - ra2[w] = ra2[w] + 360. - - # Return changes in Ra and Dec in arcseconds - d_ra = (ra2 -ra)*3600. - d_dec = (dec2 - dec)*3600. - - return d_ra, d_dec, eps, d_psi, d_eps - - def nutate(self,jd): - """ - nutate returns the nutation in longitude and obliquity for a given Julian date. - - Parameters - ---------- - jd = Julian ephemeris date, scalar or vector. - - Return - ------ - nut_long = The nutation in longitude. - nut_obliq = The nutation in latitude. - - Example - ------- - >> julian = 2446895.5 - >> [nut_long,nut_obliq] = nutate(julian) - >> print nut_long, nut_obliq - -3.78793107711 9.44252069864 - - >> julians = 2415020.5 + numpy.arange(50) - >> [nut_long,nut_obliq] = nutate(julians) - - Modification History - -------------------- - Written by W.Landsman (Goddard/HSTX), June 1996. - Converted to Python by Freddy R. Galindo, ROJ, 26 September 2009. - """ - - jd = numpy.atleast_1d(jd) - - # Form time in Julian centuries from 1900 - t = (jd - 2451545.0)/36525.0 - - # Mean elongation of the moon - coeff1 = numpy.array([1/189474.0,-0.0019142,445267.111480,297.85036]) - d = numpy.poly1d(coeff1) - d = d(t)*CoFactors.d2r - d = self.cirrange(d,rad=1) - - # Sun's mean elongation - coeff2 = numpy.array([-1./3e5,-0.0001603,35999.050340,357.52772]) - m = numpy.poly1d(coeff2) - m = m(t)*CoFactors.d2r - m = self.cirrange(m,rad=1) - - # Moon's mean elongation - coeff3 = numpy.array([1.0/5.625e4,0.0086972,477198.867398,134.96298]) - mprime = numpy.poly1d(coeff3) - mprime = mprime(t)*CoFactors.d2r - mprime = self.cirrange(mprime,rad=1) - - # Moon's argument of latitude - coeff4 = numpy.array([-1.0/3.27270e5,-0.0036825,483202.017538,93.27191]) - f = numpy.poly1d(coeff4) - f = f(t)*CoFactors.d2r - f = self.cirrange(f,rad=1) - - # Longitude fo the ascending node of the Moon's mean orbit on the ecliptic, measu- - # red from the mean equinox of the date. - coeff5 = numpy.array([1.0/4.5e5,0.0020708,-1934.136261,125.04452]) - omega = numpy.poly1d(coeff5) - omega = omega(t)*CoFactors.d2r - omega = self.cirrange(omega,rad=1) - - d_lng = numpy.array([0,-2,0,0,0,0,-2,0,0,-2,-2,-2,0,2,0,2,0,0,-2,0,2,0,0,-2,0,-2,0,0,\ - 2,-2,0,-2,0,0,2,2,0,-2,0,2,2,-2,-2,2,2,0,-2,-2,0,-2,-2,0,-1,-2,1,0,0,-1,0,\ - 0,2,0,2]) - - m_lng = numpy.array([0,0,0,0,1,0,1,0,0,-1]) - m_lng = numpy.append(m_lng,numpy.zeros(17)) - m_lng = numpy.append(m_lng,numpy.array([2,0,2,1,0,-1,0,0,0,1,1,-1,0,0,0,0,0,0,-1,-1,0,0,\ - 0,1,0,0,1,0,0,0,-1,1,-1,-1,0,-1])) - - mp_lng = numpy.array([0,0,0,0,0,1,0,0,1,0,1,0,-1,0,1,-1,-1,1,2,-2,0,2,2,1,0,0, -1, 0,\ - -1,0,0,1,0,2,-1,1,0,1,0,0,1,2,1,-2,0,1,0,0,2,2,0,1,1,0,0,1,-2,1,1,1,-1,3,0]) - - f_lng = numpy.array([0,2,2,0,0,0,2,2,2,2,0,2,2,0,0,2,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0,\ - 0,-2,2,2,2,0,2,2,0,2,2,0,0,0,2,0,2,0,2,-2,0,0,0,2,2,0,0,2,2,2,2]) - - om_lng = numpy.array([1,2,2,2,0,0,2,1,2,2,0,1,2,0,1,2,1,1,0,1,2,2,0,2,0,0,1,0,1,2,1, \ - 1,1,0,1,2,2,0,2,1,0,2,1,1,1,0,1,1,1,1,1,0,0,0,0,0,2,0,0,2,2,2,2]) - - sin_lng = numpy.array([-171996,-13187,-2274,2062,1426,712,-517,-386,-301, 217, -158, \ - 129,123,63,63,-59,-58,-51,48,46,-38,-31,29,29,26,-22,21,17,16,-16,-15,-13,\ - -12,11,-10,-8,7,-7,-7,-7,6,6,6,-6,-6,5,-5,-5,-5,4,4,4,-4,-4,-4,3,-3,-3,-3,\ - -3,-3,-3,-3]) - - sdelt = numpy.array([-174.2,-1.6,-0.2,0.2,-3.4,0.1,1.2,-0.4,0,-0.5,0, 0.1, 0, 0, 0.1,\ - 0,-0.1]) - sdelt = numpy.append(sdelt,numpy.zeros(10)) - sdelt = numpy.append(sdelt,numpy.array([-0.1, 0, 0.1])) - sdelt = numpy.append(sdelt,numpy.zeros(33)) - - cos_lng = numpy.array([92025,5736,977,-895,54,-7,224,200,129,-95,0,-70,-53,0,-33,26, \ - 32,27,0,-24,16,13,0,-12,0,0,-10,0,-8,7,9,7,6,0,5,3,-3,0,3,3,0,-3,-3,3,3,0,\ - 3,3,3]) - cos_lng = numpy.append(cos_lng,numpy.zeros(14)) - - cdelt = numpy.array([8.9,-3.1,-0.5,0.5,-0.1,0.0,-0.6,0.0,-0.1,0.3]) - cdelt = numpy.append(cdelt,numpy.zeros(53)) - - # Sum the periodic terms. - n = numpy.size(jd) - nut_long = numpy.zeros(n) - nut_obliq = numpy.zeros(n) - - d_lng = d_lng.reshape(numpy.size(d_lng),1) - d = d.reshape(numpy.size(d),1) - matrix_d_lng = numpy.dot(d_lng,d.transpose()) - - m_lng = m_lng.reshape(numpy.size(m_lng),1) - m = m.reshape(numpy.size(m),1) - matrix_m_lng = numpy.dot(m_lng,m.transpose()) - - mp_lng = mp_lng.reshape(numpy.size(mp_lng),1) - mprime = mprime.reshape(numpy.size(mprime),1) - matrix_mp_lng = numpy.dot(mp_lng,mprime.transpose()) - - f_lng = f_lng.reshape(numpy.size(f_lng),1) - f = f.reshape(numpy.size(f),1) - matrix_f_lng = numpy.dot(f_lng,f.transpose()) - - om_lng = om_lng.reshape(numpy.size(om_lng),1) - omega = omega.reshape(numpy.size(omega),1) - matrix_om_lng = numpy.dot(om_lng,omega.transpose()) - - arg = matrix_d_lng + matrix_m_lng + matrix_mp_lng + matrix_f_lng + matrix_om_lng - - sarg = numpy.sin(arg) - carg = numpy.cos(arg) - - for ii in numpy.arange(n): - nut_long[ii] = 0.0001*numpy.sum((sdelt*t[ii] + sin_lng)*sarg[:,ii]) - nut_obliq[ii] = 0.0001*numpy.sum((cdelt*t[ii] + cos_lng)*carg[:,ii]) - - if numpy.size(jd)==1: - nut_long = nut_long[0] - nut_obliq = nut_obliq[0] - - return nut_long, nut_obliq - - def co_aberration(self,jd,ra,dec): - """ - co_aberration calculates changes to Ra and Dec due to "the effect of aberration". - - Parameters - ---------- - jd = Julian Date (Scalar or vector). - ra = A scalar o vector giving the Right Ascention of interest. - dec = A scalar o vector giving the Declination of interest. - - Return - ------ - d_ra = The correction to right ascension due to aberration (must be added to ra to - get the correct value). - d_dec = The correction to declination due to aberration (must be added to the dec - to get the correct value). - eps = True obliquity of the ecliptic (in radians). - - Examples - -------- - >> Julian = 2462088.7 - >> Ra = 41.547213 - >> Dec = 49.348483 - >> [d_ra,d_dec,eps] = co_aberration(julian,Ra,Dec) - >> print d_ra, d_dec, eps - [ 30.04441796] [ 6.69837858] [ 0.40904059] - - Modification history - -------------------- - Written by Chris O'Dell , Univ. of Wisconsin, June 2002. - Converted to Python by Freddy R. Galindo, ROJ, 27 September 2009. - """ - - # Julian centuries from J2000 of jd. - T = (jd - 2451545.0)/36525.0 - - # Getting obliquity of ecliptic - njd = numpy.size(jd) - jd = numpy.atleast_1d(jd) - ra = numpy.atleast_1d(ra) - dec = numpy.atleast_1d(dec) - - d_psi = numpy.zeros(njd) - d_epsilon = d_psi - for ii in numpy.arange(njd): - [dp,de] = self.nutate(jd[ii]) - d_psi[ii] = dp - d_epsilon[ii] = de - - coeff = 23 + 26/60. + 21.488/3600. - eps0 = coeff*3600. - 46.8150*T - 0.00059*T**2. + 0.001813*T**3. - # True obliquity of the ecliptic in radians - eps = (eps0 + d_epsilon)/3600*CoFactors.d2r - - celestialbodies = CelestialBodies() - [sunra,sundec,sunlon,sunobliq] = celestialbodies.sunpos(jd) - - # Earth's orbital eccentricity - e = 0.016708634 - 0.000042037*T - 0.0000001267*T**2. - - # longitude of perihelion, in degrees - pi = 102.93735 + 1.71946*T + 0.00046*T**2 - - # Constant of aberration, in arcseconds - k = 20.49552 - - cd = numpy.cos(dec*CoFactors.d2r) ; sd = numpy.sin(dec*CoFactors.d2r) - ce = numpy.cos(eps) ; te = numpy.tan(eps) - cp = numpy.cos(pi*CoFactors.d2r) ; sp = numpy.sin(pi*CoFactors.d2r) - cs = numpy.cos(sunlon*CoFactors.d2r) ; ss = numpy.sin(sunlon*CoFactors.d2r) - ca = numpy.cos(ra*CoFactors.d2r) ; sa = numpy.sin(ra*CoFactors.d2r) - - term1 = (ca*cs*ce + sa*ss)/cd - term2 = (ca*cp*ce + sa*sp)/cd - term3 = (cs*ce*(te*cd - sa*sd) + ca*sd*ss) - term4 = (cp*ce*(te*cd - sa*sd) + ca*sd*sp) - - d_ra = -k*term1 + e*k*term2 - d_dec = -k*term3 + e*k*term4 - - return d_ra, d_dec, eps - - def precess(self,ra,dec,equinox1=None,equinox2=None,FK4=0,rad=0): - """ - precess coordinates from EQUINOX1 to EQUINOX2 - - Parameters - ----------- - ra = A scalar o vector giving the Right Ascention of interest. - dec = A scalar o vector giving the Declination of interest. - equinox1 = Original equinox of coordinates, numeric scalar. If omitted, the __Pre- - cess will query for equinox1 and equinox2. - equinox2 = Original equinox of coordinates. - FK4 = If this keyword is set and non-zero, the FK4 (B1950) system will be used - otherwise FK5 (J2000) will be used instead. - rad = If this keyword is set and non-zero, then the input and output RAD and DEC - vectors are in radian rather than degree. - - Return - ------ - ra = Right ascension after precession (scalar or vector) in degrees, unless the rad - keyword is set. - dec = Declination after precession (scalar or vector) in degrees, unless the rad - keyword is set. - - Examples - -------- - >> Ra = 329.88772 - >> Dec = -56.992515 - >> [p_ra,p_dec] = precess(Ra,Dec,1950,1975,FK4=1) - >> print p_ra, p_dec - [ 330.31442971] [-56.87186154] - - Modification history - -------------------- - Written by Wayne Landsman, STI Corporation, August 1986. - Converted to Python by Freddy R. Galindo, ROJ, 27 September 2009. - """ - - npts = numpy.size(ra) - ra = numpy.atleast_1d(ra) - dec = numpy.atleast_1d(dec) - - if rad==0: - ra_rad = ra*CoFactors.d2r - dec_rad = dec*CoFactors.d2r - else: - ra_rad = ra - dec_rad = dec - - x = numpy.zeros((npts,3)) - x[:,0] = numpy.cos(dec_rad)*numpy.cos(ra_rad) - x[:,1] = numpy.cos(dec_rad)*numpy.sin(ra_rad) - x[:,2] = numpy.sin(dec_rad) - - # Use premat function to get precession matrix from equinox1 to equinox2 - r = self.premat(equinox1,equinox2,FK4) - - x2 = numpy.dot(r,x.transpose()) - - ra_rad = numpy.arctan2(x2[1,:],x2[0,:]) - dec_rad = numpy.arcsin(x2[2,:]) - - if rad==0: - ra = ra_rad/CoFactors.d2r - ra = ra + (ra<0)*360. - dec = dec_rad/CoFactors.d2r - else: - ra = ra_rad - ra = ra + (ra<0)*numpy.pi*2. - dec = dec_rad - - return ra, dec - - def premat(self,equinox1,equinox2,FK4=0): - """ - premat returns the precession matrix needed to go from EQUINOX1 to EQUINOX2. - - Parameters - ---------- - equinox1 = Original equinox of coordinates, numeric scalar. - equinox2 = Equinox of precessed coordinates. - FK4 = If this keyword is set and non-zero, the FK4 (B1950) system precession angles - are used to compute the precession matrix. The default is to use FK5 (J2000) pre- - cession angles. - - Return - ------ - r = Precession matrix, used to precess equatorial rectangular coordinates. - - Examples - -------- - >> matrix = premat(1950.0,1975.0,FK4=1) - >> print matrix - [[ 9.99981438e-01 -5.58774959e-03 -2.42908517e-03] - [ 5.58774959e-03 9.99984388e-01 -6.78691471e-06] - [ 2.42908517e-03 -6.78633095e-06 9.99997050e-01]] - - Modification history - -------------------- - Written by Wayne Landsman, HSTX Corporation, June 1994. - Converted to Python by Freddy R. Galindo, ROJ, 27 September 2009. - """ - - t = 0.001*(equinox2 - equinox1) - - if FK4==0: - st=0.001*(equinox1 - 2000.) - # Computing 3 rotation angles. - A=CoFactors.s2r*t*(23062.181+st*(139.656+0.0139*st)+t*(30.188-0.344*st+17.998*t)) - B=CoFactors.s2r*t*t*(79.280+0.410*st+0.205*t)+A - C=CoFactors.s2r*t*(20043.109-st*(85.33+0.217*st)+ t*(-42.665-0.217*st-41.833*t)) - else: - st=0.001*(equinox1 - 1900) - # Computing 3 rotation angles - A=CoFactors.s2r*t*(23042.53+st*(139.75+0.06*st)+t*(30.23-0.27*st+18.0*t)) - B=CoFactors.s2r*t*t*(79.27+0.66*st+0.32*t)+A - C=CoFactors.s2r*t*(20046.85-st*(85.33+0.37*st)+t*(-42.67-0.37*st-41.8*t)) - - sina = numpy.sin(A); sinb = numpy.sin(B); sinc = numpy.sin(C) - cosa = numpy.cos(A); cosb = numpy.cos(B); cosc = numpy.cos(C) - - r = numpy.zeros((3,3)) - r[:,0] = numpy.array([cosa*cosb*cosc-sina*sinb,sina*cosb+cosa*sinb*cosc,cosa*sinc]) - r[:,1] = numpy.array([-cosa*sinb-sina*cosb*cosc,cosa*cosb-sina*sinb*cosc,-sina*sinc]) - r[:,2] = numpy.array([-cosb*sinc,-sinb*sinc,cosc]) - - return r - - def cirrange(self,angle,rad=0): - """ - cirrange forces an angle into the range 0<= angle < 360. - - Parameters - ---------- - angle = The angle to modify, in degrees. Can be scalar or vector. - rad = Set to 1 if the angle is specified in radians rather than degrees. It is for- - ced into the range 0 <= angle < 2 PI - - Return - ------ - angle = The angle after the modification. - - Example - ------- - >> angle = cirrange(numpy.array([420,400,361])) - >> print angle - >> [60, 40, 1] - - Modification History - -------------------- - Written by Michael R. Greason, Hughes STX, 10 February 1994. - Converted to Python by Freddy R. Galindo, ROJ, 26 September 2009. - """ - - angle = numpy.atleast_1d(angle) - - if rad==1: - cnst = numpy.pi*2. - elif rad==0: - cnst = 360. - - # Deal with the lower limit. - angle = angle % cnst - - # Deal with negative values, if way - neg = numpy.where(angle<0.0) - if neg[0].size>0: angle[neg] = angle[neg] + cnst - - return angle - - -class CelestialBodies(EquatorialCorrections): - def __init__(self): - """ - CelestialBodies class creates a object to call methods of celestial bodies location. - - Modification History - -------------------- - Converted to Object-oriented Programming by Freddy Galindo, ROJ, 27 September 2009. - """ - - EquatorialCorrections.__init__(self) - - def sunpos(self,jd,rad=0): - """ - sunpos method computes the RA and Dec of the Sun at a given date. - - Parameters - ---------- - jd = The julian date of the day (and time), scalar or vector. - rad = If this keyword is set and non-zero, then the input and output RAD and DEC - vectors are in radian rather than degree. - - Return - ------ - ra = The right ascension of the sun at that date in degrees. - dec = The declination of the sun at that date in degrees. - elong = Ecliptic longitude of the sun at that date in degrees. - obliquity = The declination of the sun at that date in degrees. - - Examples - -------- - >> jd = 2466880 - >> [ra,dec,elong,obliquity] = sunpos(jd) - >> print ra, dec, elong, obliquity - [ 275.53499556] [-23.33840558] [ 275.08917968] [ 23.43596165] - - >> [ra,dec,elong,obliquity] = sunpos(jd,rad=1) - >> print ra, dec, elong, obliquity - [ 4.80899288] [-0.40733202] [ 4.80121192] [ 0.40903469] - - >> jd = 2450449.5 + numpy.arange(365) - >> [ra,dec,elong,obliquity] = sunpos(jd) - - Modification history - -------------------- - Written by Micheal R. Greason, STX Corporation, 28 October 1988. - Converted to Python by Freddy R. Galindo, ROJ, 27 September 2009. - """ - - jd = numpy.atleast_1d(jd) - - # Form time in Julian centuries from 1900. - t = (jd -2415020.0)/36525.0 - - # Form sun's mean longitude - l = (279.696678+((36000.768925*t) % 360.0))*3600.0 - - # Allow for ellipticity of the orbit (equation of centre) using the Earth's mean - # anomoly ME - me = 358.475844 + ((35999.049750*t) % 360.0) - ellcor = (6910.1 - 17.2*t)*numpy.sin(me*CoFactors.d2r) + 72.3*numpy.sin(2.0*me*CoFactors.d2r) - l = l + ellcor - - # Allow for the Venus perturbations using the mean anomaly of Venus MV - mv = 212.603219 + ((58517.803875*t) % 360.0) - vencorr = 4.8*numpy.cos((299.1017 + mv - me)*CoFactors.d2r) + \ - 5.5*numpy.cos((148.3133 + 2.0*mv - 2.0*me )*CoFactors.d2r) + \ - 2.5*numpy.cos((315.9433 + 2.0*mv - 3.0*me )*CoFactors.d2r) + \ - 1.6*numpy.cos((345.2533 + 3.0*mv - 4.0*me )*CoFactors.d2r) + \ - 1.0*numpy.cos((318.15 + 3.0*mv - 5.0*me )*CoFactors.d2r) - l = l + vencorr - - # Allow for the Mars perturbations using the mean anomaly of Mars MM - mm = 319.529425 + ((19139.858500*t) % 360.0) - marscorr = 2.0*numpy.cos((343.8883 - 2.0*mm + 2.0*me)*CoFactors.d2r ) + \ - 1.8*numpy.cos((200.4017 - 2.0*mm + me)*CoFactors.d2r) - l = l + marscorr - - # Allow for the Jupiter perturbations using the mean anomaly of Jupiter MJ - mj = 225.328328 + ((3034.6920239*t) % 360.0) - jupcorr = 7.2*numpy.cos((179.5317 - mj + me )*CoFactors.d2r) + \ - 2.6*numpy.cos((263.2167 - mj)*CoFactors.d2r) + \ - 2.7*numpy.cos((87.1450 - 2.0*mj + 2.0*me)*CoFactors.d2r) + \ - 1.6*numpy.cos((109.4933 - 2.0*mj + me)*CoFactors.d2r) - l = l + jupcorr - - # Allow for Moons perturbations using mean elongation of the Moon from the Sun D - d = 350.7376814 + ((445267.11422*t) % 360.0) - mooncorr = 6.5*numpy.sin(d*CoFactors.d2r) - l = l + mooncorr - - # Allow for long period terms - longterm = + 6.4*numpy.sin((231.19 + 20.20*t)*CoFactors.d2r) - l = l + longterm - l = (l + 2592000.0) % 1296000.0 - longmed = l/3600.0 - - # Allow for Aberration - l = l - 20.5 - - # Allow for Nutation using the longitude of the Moons mean node OMEGA - omega = 259.183275 - ((1934.142008*t) % 360.0) - l = l - 17.2*numpy.sin(omega*CoFactors.d2r) - - # Form the True Obliquity - oblt = 23.452294 - 0.0130125*t + (9.2*numpy.cos(omega*CoFactors.d2r))/3600.0 - - # Form Right Ascension and Declination - l = l/3600.0 - ra = numpy.arctan2((numpy.sin(l*CoFactors.d2r)*numpy.cos(oblt*CoFactors.d2r)),numpy.cos(l*CoFactors.d2r)) - - neg = numpy.where(ra < 0.0) - if neg[0].size > 0: ra[neg] = ra[neg] + 2.0*numpy.pi - - dec = numpy.arcsin(numpy.sin(l*CoFactors.d2r)*numpy.sin(oblt*CoFactors.d2r)) - - if rad==1: - oblt = oblt*CoFactors.d2r - longmed = longmed*CoFactors.d2r - else: - ra = ra/CoFactors.d2r - dec = dec/CoFactors.d2r - - return ra, dec, longmed, oblt - - def moonpos(self,jd,rad=0): - """ - moonpos method computes the RA and Dec of the Moon at specified Julian date(s). - - Parameters - ---------- - jd = The julian date of the day (and time), scalar or vector. - rad = If this keyword is set and non-zero, then the input and output RAD and DEC - vectors are in radian rather than degree. - - Return - ------ - ra = The right ascension of the sun at that date in degrees. - dec = The declination of the sun at that date in degrees. - dist = The Earth-moon distance in kilometers (between the center of the Earth and - the center of the moon). - geolon = Apparent longitude of the moon in degrees, referred to the ecliptic of the - specified date(s). - geolat = Apparent latitude the moon in degrees, referred to the ecliptic of the - specified date(s). - - Examples - -------- - >> jd = 2448724.5 - >> [ra,dec,dist,geolon,geolat] = sunpos(jd) - >> print ra, dec, dist, geolon, geolat - [ 134.68846855] [ 13.76836663] [ 368409.68481613] [ 133.16726428] [-3.22912642] - - >> [ra,dec,dist,geolon, geolat] = sunpos(jd,rad=1) - >> print ra, dec, dist, geolon, geolat - [ 2.35075724] [ 0.24030333] [ 368409.68481613] [ 2.32420722] [-0.05635889] - - >> jd = 2450449.5 + numpy.arange(365) - >> [ra,dec,dist,geolon, geolat] = sunpos(jd) - - Modification history - -------------------- - Written by Micheal R. Greason, STX Corporation, 31 October 1988. - Converted to Python by Freddy R. Galindo, ROJ, 06 October 2009. - """ - - jd = numpy.atleast_1d(jd) - - # Form time in Julian centuries from 1900. - t = (jd - 2451545.0)/36525.0 - - d_lng = numpy.array([0,2,2,0,0,0,2,2,2,2,0,1,0,2,0,0,4,0,4,2,2,1,1,2,2,4,2,0,2,2,1,2,\ - 0,0,2,2,2,4,0,3,2,4,0,2,2,2,4,0,4,1,2,0,1,3,4,2,0,1,2,2]) - - m_lng = numpy.array([0,0,0,0,1,0,0,-1,0,-1,1,0,1,0,0,0,0,0,0,1,1,0,1,-1,0,0,0,1,0,-1,\ - 0,-2,1,2,-2,0,0,-1,0,0,1,-1,2,2,1,-1,0,0,-1,0,1,0,1,0,0,-1,2,1,0,0]) - - mp_lng = numpy.array([1,-1,0,2,0,0,-2,-1,1,0,-1,0,1,0,1,1,-1,3,-2,-1,0,-1,0,1,2,0,-3,\ - -2,-1,-2,1,0,2,0,-1,1,0,-1,2,-1,1,-2,-1,-1,-2,0,1,4,0,-2,0,2,1,-2,-3,2,1,-1,3,-1]) - - f_lng = numpy.array([0,0,0,0,0,2,0,0,0,0,0,0,0,-2,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,\ - 0,0,0,0,-2,2,0,2,0,0,0,0,0,0,-2,0,0,0,0,-2,-2,0,0,0,0,0,0,0,-2]) - - sin_lng = numpy.array([6288774,1274027,658314,213618,-185116,-114332,58793,57066,\ - 53322,45758,-40923,-34720,-30383,15327,-12528,10980,10675,10034,8548,-7888,\ - -6766,-5163,4987,4036,3994,3861,3665,-2689,-2602,2390,-2348,2236,-2120,-2069,\ - 2048,-1773,-1595,1215,-1110,-892,-810,759,-713,-700,691,596,549,537,520,-487,\ - -399,-381,351,-340,330,327,-323,299,294,0.0]) - - cos_lng = numpy.array([-20905355,-3699111,-2955968,-569925,48888,-3149,246158,-152138,\ - -170733,-204586,-129620,108743,104755,10321,0,79661,-34782,-23210,-21636,24208,\ - 30824,-8379,-16675,-12831,-10445,-11650,14403,-7003,0,10056,6322, -9884,5751,0,\ - -4950,4130,0,-3958,0,3258,2616,-1897,-2117,2354,0,0,-1423,-1117,-1571,-1739,0, \ - -4421,0,0,0,0,1165,0,0,8752.0]) - - d_lat = numpy.array([0,0,0,2,2,2,2,0,2,0,2,2,2,2,2,2,2,0,4,0,0,0,1,0,0,0,1,0,4,4,0,4,\ - 2,2,2,2,0,2,2,2,2,4,2,2,0,2,1,1,0,2,1,2,0,4,4,1,4,1,4,2]) - - m_lat = numpy.array([0,0,0,0,0,0,0,0,0,0,-1,0,0,1,-1,-1,-1,1,0,1,0,1,0,1,1,1,0,0,0,0,\ - 0,0,0,0,-1,0,0,0,0,1,1,0,-1,-2,0,1,1,1,1,1,0,-1,1,0,-1,0,0,0,-1,-2]) - - mp_lat = numpy.array([0,1,1,0,-1,-1,0,2,1,2,0,-2,1,0,-1,0,-1,-1,-1,0,0,-1,0,1,1,0,0,\ - 3,0,-1,1,-2,0,2,1,-2,3,2,-3,-1,0,0,1,0,1,1,0,0,-2,-1,1,-2,2,-2,-1,1,1,-1,0,0]) - - f_lat = numpy.array([1,1,-1,-1,1,-1,1,1,-1,-1,-1,-1,1,-1,1,1,-1,-1,-1,1,3,1,1,1,-1,\ - -1,-1,1,-1,1,-3,1,-3,-1,-1,1,-1,1,-1,1,1,1,1,-1,3,-1,-1,1,-1,-1,1,-1,1,-1,-1, \ - -1,-1,-1,-1,1]) - - sin_lat = numpy.array([5128122,280602,277693,173237,55413,46271, 32573, 17198, 9266, \ - 8822,8216,4324,4200,-3359,2463,2211,2065,-1870,1828,-1794, -1749, -1565, -1491, \ - -1475,-1410,-1344,-1335,1107,1021,833,777,671,607,596,491,-451,439,422,421,-366,\ - -351,331,315,302,-283,-229,223,223,-220,-220,-185,181,-177,176, 166, -164, 132, \ - -119,115,107.0]) - - # Mean longitude of the moon refered to mean equinox of the date. - coeff0 = numpy.array([-1./6.5194e7,1./538841.,-0.0015786,481267.88123421,218.3164477]) - lprimed = numpy.poly1d(coeff0) - lprimed = lprimed(t) - lprimed = self.cirrange(lprimed,rad=0) - lprime = lprimed*CoFactors.d2r - - # Mean elongation of the moon - coeff1 = numpy.array([-1./1.13065e8,1./545868.,-0.0018819,445267.1114034,297.8501921]) - d = numpy.poly1d(coeff1) - d = d(t)*CoFactors.d2r - d = self.cirrange(d,rad=1) - - # Sun's mean anomaly - coeff2 = numpy.array([1.0/2.449e7,-0.0001536,35999.0502909,357.5291092]) - M = numpy.poly1d(coeff2) - M = M(t)*CoFactors.d2r - M = self.cirrange(M,rad=1) - - # Moon's mean anomaly - coeff3 = numpy.array([-1.0/1.4712e7,1.0/6.9699e4,0.0087414,477198.8675055,134.9633964]) - Mprime = numpy.poly1d(coeff3) - Mprime = Mprime(t)*CoFactors.d2r - Mprime = self.cirrange(Mprime,rad=1) - - # Moon's argument of latitude - coeff4 = numpy.array([1.0/8.6331e8,-1.0/3.526e7,-0.0036539,483202.0175233,93.2720950]) - F = numpy.poly1d(coeff4) - F = F(t)*CoFactors.d2r - F = self.cirrange(F,rad=1) - - # Eccentricity of Earth's orbit around the sun - e = 1 - 0.002516*t - 7.4e-6*(t**2.) - e2 = e**2. - - ecorr1 = numpy.where((numpy.abs(m_lng))==1) - ecorr2 = numpy.where((numpy.abs(m_lat))==1) - ecorr3 = numpy.where((numpy.abs(m_lng))==2) - ecorr4 = numpy.where((numpy.abs(m_lat))==2) - - # Additional arguments. - A1 = (119.75 + 131.849*t)*CoFactors.d2r - A2 = (53.09 + 479264.290*t)*CoFactors.d2r - A3 = (313.45 + 481266.484*t)*CoFactors.d2r - suml_add = 3958.*numpy.sin(A1) + 1962.*numpy.sin(lprime - F) + 318*numpy.sin(A2) - sumb_add = -2235.*numpy.sin(lprime) + 382.*numpy.sin(A3) + 175.*numpy.sin(A1-F) + \ - 175.*numpy.sin(A1 + F) + 127.*numpy.sin(lprime - Mprime) - 115.*numpy.sin(lprime + Mprime) - - # Sum the periodic terms - geolon = numpy.zeros(jd.size) - geolat = numpy.zeros(jd.size) - dist = numpy.zeros(jd.size) - - for i in numpy.arange(jd.size): - sinlng = sin_lng - coslng = cos_lng - sinlat = sin_lat - - sinlng[ecorr1] = e[i]*sinlng[ecorr1] - coslng[ecorr1] = e[i]*coslng[ecorr1] - sinlat[ecorr2] = e[i]*sinlat[ecorr2] - sinlng[ecorr3] = e2[i]*sinlng[ecorr3] - coslng[ecorr3] = e2[i]*coslng[ecorr3] - sinlat[ecorr4] = e2[i]*sinlat[ecorr4] - - arg = d_lng*d[i] + m_lng*M[i] + mp_lng*Mprime[i] + f_lng*F[i] - geolon[i] = lprimed[i] + (numpy.sum(sinlng*numpy.sin(arg)) + suml_add[i] )/1.e6 - dist[i] = 385000.56 + numpy.sum(coslng*numpy.cos(arg))/1.e3 - arg = d_lat*d[i] + m_lat*M[i] + mp_lat*Mprime[i] + f_lat*F[i] - geolat[i] = (numpy.sum(sinlat*numpy.sin(arg)) + sumb_add[i])/1.e6 - - [nlon, elon] = self.nutate(jd) - geolon = geolon + nlon/3.6e3 - geolon = self.cirrange(geolon,rad=0) - lamb = geolon*CoFactors.d2r - beta = geolat*CoFactors.d2r - - # Find mean obliquity and convert lamb, beta to RA, Dec - c = numpy.array([2.45,5.79,27.87,7.12,-39.05,-249.67,-51.38,1999.25,-1.55,-4680.93, \ - 21.448]) - junk = numpy.poly1d(c); - epsilon = 23. + (26./60.) + (junk(t/1.e2)/3600.) - # True obliquity in radians - eps = (epsilon + elon/3600. )*CoFactors.d2r - - ra = numpy.arctan2(numpy.sin(lamb)*numpy.cos(eps)-numpy.tan(beta)*numpy.sin(eps),numpy.cos(lamb)) - ra = self.cirrange(ra,rad=1) - - dec = numpy.arcsin(numpy.sin(beta)*numpy.cos(eps) + numpy.cos(beta)*numpy.sin(eps)*numpy.sin(lamb)) - - if rad==1: - geolon = lamb - geolat = beta - else: - ra = ra/CoFactors.d2r - dec = dec/CoFactors.d2r - - return ra, dec, dist, geolon, geolat - - def hydrapos(self): - """ - hydrapos method returns RA and Dec provided by Bill Coles (Oct 2003). - - Parameters - ---------- - None - - Return - ------ - ra = The right ascension of the sun at that date in degrees. - dec = The declination of the sun at that date in degrees. - Examples - -------- - >> [ra,dec] = hydrapos() - >> print ra, dec - 139.45 -12.0833333333 - - Modification history - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 06 October 2009. - """ - - ra = (9. + 17.8/60.)*15. - dec = -(12. + 5./60.) - - return ra, dec - - - def skynoise_jro(self,dec_cut=-11.95,filename='skynoise_jro.dat',filepath=None): - """ - hydrapos returns RA and Dec provided by Bill Coles (Oct 2003). - - Parameters - ---------- - dec_cut = A scalar giving the declination to get a cut of the skynoise over Jica- - marca. The default value is -11.95. - filename = A string to specify name the skynoise file. The default value is skynoi- - se_jro.dat - - Return - ------ - maxra = The maximum right ascension to the declination used to get a cut. - ra = The right ascension. - Examples - -------- - >> [maxra,ra] = skynoise_jro() - >> print maxra, ra - 139.45 -12.0833333333 - - Modification history - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 06 October 2009. - """ - - if filepath==None:filepath = './resource' - - f = open(os.path.join(filepath,filename),'rb') - - # Reading SkyNoise Power (lineal scale) - ha_sky = numpy.fromfile(f,numpy.dtype([('var','> [maxra,ra] = skynoise_jro() - >> print maxra, ra - 139.45 -12.0833333333 - - Modification history - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 06 October 2009. - """ - - # Defining date to compute SkyNoise. - [year, month, dom, hour, mis, secs] = Julian(jd).change2time() - is_dom = (month==9) & (dom==21) - if is_dom: - tmp = jd - jd = Time(year,9,22).change2julian() - dom = 22 - - # Reading SkyNoise - if filepath==None:filepath='./resource' - f = open(os.path.join(filepath,filename)) - - lines = f.read() - f.close() - - nlines = 99 - lines = lines.split('\n') - data = numpy.zeros((2,nlines))*numpy.float32(0.) - for ii in numpy.arange(nlines): - line = numpy.array([lines[ii][0:6],lines[ii][6:]]) - data[:,ii] = numpy.float32(line) - - # Getting SkyNoise to the date desired. - otime = data[0,:]*60.0 - opowr = data[1,:] - - hour = numpy.array([0,23]); - mins = numpy.array([0,59]); - secs = numpy.array([0,59]); - LTrange = Time(year,month,dom,hour,mins,secs).change2julday() - LTtime = LTrange[0] + numpy.arange(1440)*((LTrange[1] - LTrange[0])/(1440.-1)) - lst = Julian(LTtime + (-3600.*ut/86400.)).change2lst() - - ipowr = lst*0.0 - # Interpolating using scipy (inside max and min "x") - otime = otime/3600. - val = numpy.where((lst>numpy.min(otime)) & (lstnumpy.max(otime)) - if uval[0].size>0: - ii = numpy.min(uval[0]) - m = (ipowr[ii-1] - ipowr[ii-2])/(lst[ii-1] - lst[ii-2]) - b = ipowr[ii-1] - m*lst[ii-1] - ipowr[uval] = m*lst[uval] + b - - if is_dom: - lst = numpy.roll(lst,4) - ipowr = numpy.roll(ipowr,4) - - new_lst = numpy.int32(lst*3600.) - new_pow = ipowr - - return ipowr, LTtime, lst - - -class AltAz(EquatorialCorrections): - def __init__(self,alt,az,jd,lat=-11.95,lon=-76.8667,WS=0,altitude=500,nutate_=0,precess_=0,\ - aberration_=0,B1950=0): - """ - The AltAz class creates an object which represents the target position in horizontal - coordinates (alt-az) and allows to convert (using the methods) from this coordinate - system to others (e.g. Equatorial). - - Parameters - ---------- - alt = Altitude in degrees. Scalar or vector. - az = Azimuth angle in degrees (measured EAST from NORTH, but see keyword WS). Sca- - lar or vector. - jd = Julian date. Scalar or vector. - lat = North geodetic latitude of location in degrees. The default value is -11.95. - lon = East longitude of location in degrees. The default value is -76.8667. - WS = Set this to 1 to get the azimuth measured westward from south. - altitude = The altitude of the observing location, in meters. The default 500. - nutate_ = Set this to 1 to force nutation, 0 for no nutation. - precess_ = Set this to 1 to force precession, 0 for no precession. - aberration_ = Set this to 1 to force aberration correction, 0 for no correction. - B1950 = Set this if your RA and DEC are specified in B1950, FK4 coordinates (ins- - tead of J2000, FK5) - - Modification History - -------------------- - Converted to Object-oriented Programming by Freddy Galindo, ROJ, 26 September 2009. - """ - - EquatorialCorrections.__init__(self) - - self.alt = numpy.atleast_1d(alt) - self.az = numpy.atleast_1d(az) - self.jd = numpy.atleast_1d(jd) - self.lat = lat - self.lon = lon - self.WS = WS - self.altitude = altitude - - self.nutate_ = nutate_ - self.aberration_ = aberration_ - self.precess_ = precess_ - self.B1950 = B1950 - - def change2equatorial(self): - """ - change2equatorial method converts horizon (Alt-Az) coordinates to equatorial coordi- - nates (ra-dec). - - Return - ------ - ra = Right ascension of object (J2000) in degrees (FK5). Scalar or vector. - dec = Declination of object (J2000), in degrees (FK5). Scalar or vector. - ha = Hour angle in degrees. - - Example - ------- - >> alt = 88.5401 - >> az = -128.990 - >> jd = 2452640.5 - >> ObjAltAz = AltAz(alt,az,jd) - >> [ra, dec, ha] = ObjAltAz.change2equatorial() - >> print ra, dec, ha - [ 22.20280632] [-12.86610025] [ 1.1638927] - - Modification History - -------------------- - Written Chris O'Dell Univ. of Wisconsin-Madison, May 2002. - Converted to Python by Freddy R. Galindo, ROJ, 26 September 2009. - """ - - az = self.az - alt = self.alt - if self.WS>0:az = az -180. - ra_tmp = numpy.zeros(numpy.size(self.jd)) + 45. - dec_tmp = numpy.zeros(numpy.size(self.jd)) + 45. - [dra1,ddec1,eps,d_psi,d_eps] = self.co_nutate(self.jd,ra_tmp, dec_tmp) - - # Getting local mean sidereal time (lmst) - lmst = Julian(self.jd[0]).change2lst() - lmst = lmst*CoFactors.h2d - # Getting local apparent sidereal time (last) - last = lmst + d_psi*numpy.cos(eps)/3600. - - # Now do the spherical trig to get APPARENT hour angle and declination (Degrees). - [ha, dec] = self.change2HaDec() - - # Finding Right Ascension (in degrees, from 0 to 360.) - ra = (last - ha + 360.) % 360. - - # Calculate NUTATION and ABERRATION Correction to Ra-Dec - [dra1, ddec1,eps,d_psi,d_eps] = self.co_nutate(self.jd,ra,dec) - [dra2,ddec2,eps] = self.co_aberration(self.jd,ra,dec) - - # Make Nutation and Aberration correction (if wanted) - ra = ra - (dra1*self.nutate_ + dra2*self.aberration_)/3600. - dec = dec - (ddec1*self.nutate_ + ddec2*self.aberration_)/3600. - - # Computing current equinox - j_now = (self.jd - 2451545.)/365.25 + 2000 - - # Precess coordinates to current date - if self.precess_==1: - njd = numpy.size(self.jd) - for ii in numpy.arange(njd): - ra_i = ra[ii] - dec_i = dec[ii] - now = j_now[ii] - - if self.B1950==1: - [ra_i,dec_i] = self.precess(ra_i,dec_i,now,1950.,FK4=1) - elif self.B1950==0: - [ra_i,dec_i] = self.precess(ra_i,dec_i,now,2000.,FK4=0) - - ra[ii] = ra_i - dec[ii] = dec_i - - return ra, dec, ha - - def change2HaDec(self): - """ - change2HaDec method converts from horizon (Alt-Az) coordinates to hour angle and de- - clination. - - Return - ------ - ha = The local apparent hour angle, in degrees. The hour angle is the time that ri- - ght ascension of 0 hours crosses the local meridian. It is unambiguisoly defined. - dec = The local apparent declination, in degrees. - - Example - ------- - >> alt = 88.5401 - >> az = -128.990 - >> jd = 2452640.5 - >> ObjAltAz = AltAz(alt,az,jd) - >> [ha, dec] = ObjAltAz.change2HaDec() - >> print ha, dec - [ 1.1638927] [-12.86610025] - - Modification History - -------------------- - Written Chris O'Dell Univ. of Wisconsin-Madison, May 2002. - Converted to Python by Freddy R. Galindo, ROJ, 26 September 2009. - """ - - alt_r = numpy.atleast_1d(self.alt*CoFactors.d2r) - az_r = numpy.atleast_1d(self.az*CoFactors.d2r) - lat_r = numpy.atleast_1d(self.lat*CoFactors.d2r) - - # Find local hour angle (in degrees, from 0 to 360.) - y_ha = -1*numpy.sin(az_r)*numpy.cos(alt_r) - x_ha = -1*numpy.cos(az_r)*numpy.sin(lat_r)*numpy.cos(alt_r) + numpy.sin(alt_r)*numpy.cos(lat_r) - - ha = numpy.arctan2(y_ha,x_ha) - ha = ha/CoFactors.d2r - - w = numpy.where(ha<0.) - if w[0].size>0:ha[w] = ha[w] + 360. - ha = ha % 360. - - # Find declination (positive if north of celestial equatorial, negative if south) - sindec = numpy.sin(lat_r)*numpy.sin(alt_r) + numpy.cos(lat_r)*numpy.cos(alt_r)*numpy.cos(az_r) - dec = numpy.arcsin(sindec)/CoFactors.d2r - - return ha, dec - - -class Equatorial(EquatorialCorrections): - def __init__(self,ra,dec,jd,lat=-11.95,lon=-76.8667,WS=0,altitude=500,nutate_=0,precess_=0,\ - aberration_=0,B1950=0): - """ - The Equatorial class creates an object which represents the target position in equa- - torial coordinates (ha-dec) and allows to convert (using the class methods) from - this coordinate system to others (e.g. AltAz). - - Parameters - ---------- - ra = Right ascension of object (J2000) in degrees (FK5). Scalar or vector. - dec = Declination of object (J2000), in degrees (FK5). Scalar or vector. - jd = Julian date. Scalar or vector. - lat = North geodetic latitude of location in degrees. The default value is -11.95. - lon = East longitude of location in degrees. The default value is -76.8667. - WS = Set this to 1 to get the azimuth measured westward from south. - altitude = The altitude of the observing location, in meters. The default 500. - nutate = Set this to 1 to force nutation, 0 for no nutation. - precess = Set this to 1 to force precession, 0 for no precession. - aberration = Set this to 1 to force aberration correction, 0 for no correction. - B1950 = Set this if your RA and DEC are specified in B1950, FK4 coordinates (ins- - tead of J2000, FK5) - - Modification History - -------------------- - Converted to Object-oriented Programming by Freddy Galindo, ROJ, 29 September 2009. - """ - - EquatorialCorrections.__init__(self) - - self.ra = numpy.atleast_1d(ra) - self.dec = numpy.atleast_1d(dec) - self.jd = numpy.atleast_1d(jd) - self.lat = lat - self.lon = lon - self.WS = WS - self.altitude = altitude - - self.nutate_ = nutate_ - self.aberration_ = aberration_ - self.precess_ = precess_ - self.B1950 = B1950 - - def change2AltAz(self): - """ - change2AltAz method converts from equatorial coordinates (ha-dec) to horizon coordi- - nates (alt-az). - - Return - ------ - alt = Altitude in degrees. Scalar or vector. - az = Azimuth angle in degrees (measured EAST from NORTH, but see keyword WS). Sca- - lar or vector. - ha = Hour angle in degrees. - - Example - ------- - >> ra = 43.370609 - >> dec = -28.0000 - >> jd = 2452640.5 - >> ObjEq = Equatorial(ra,dec,jd) - >> [alt, az, ha] = ObjEq.change2AltAz() - >> print alt, az, ha - [ 65.3546497] [ 133.58753124] [ 339.99609002] - - Modification History - -------------------- - Written Chris O'Dell Univ. of Wisconsin-Madison. May 2002 - Converted to Python by Freddy R. Galindo, ROJ, 29 September 2009. - """ - - ra = self.ra - dec = self.dec - - # Computing current equinox - j_now = (self.jd - 2451545.)/365.25 + 2000 - - # Precess coordinates to current date - if self.precess_==1: - njd = numpy.size(self.jd) - for ii in numpy.arange(njd): - ra_i = ra[ii] - dec_i = dec[ii] - now = j_now[ii] - - if self.B1950==1: - [ra_i,dec_i] = self.precess(ra_i,dec_i,now,1950.,FK4=1) - elif self.B1950==0: - [ra_i,dec_i] = self.precess(ra_i,dec_i,now,2000.,FK4=0) - - ra[ii] = ra_i - dec[ii] = dec_i - - # Calculate NUTATION and ABERRATION Correction to Ra-Dec - [dra1, ddec1,eps,d_psi,d_eps] = self.co_nutate(self.jd,ra,dec) - [dra2,ddec2,eps] = self.co_aberration(self.jd,ra,dec) - - # Make Nutation and Aberration correction (if wanted) - ra = ra + (dra1*self.nutate_ + dra2*self.aberration_)/3600. - dec = dec + (ddec1*self.nutate_ + ddec2*self.aberration_)/3600. - - # Getting local mean sidereal time (lmst) - lmst = Julian(self.jd).change2lst() - - lmst = lmst*CoFactors.h2d - # Getting local apparent sidereal time (last) - last = lmst + d_psi*numpy.cos(eps)/3600. - - # Finding Hour Angle (in degrees, from 0 to 360.) - ha = last - ra - w = numpy.where(ha<0.) - if w[0].size>0:ha[w] = ha[w] + 360. - ha = ha % 360. - - # Now do the spherical trig to get APPARENT hour angle and declination (Degrees). - [alt, az] = self.HaDec2AltAz(ha,dec) - - return alt, az, ha - - def HaDec2AltAz(self,ha,dec): - """ - HaDec2AltAz convert hour angle and declination (ha-dec) to horizon coords (alt-az). - - Parameters - ---------- - ha = The local apparent hour angle, in DEGREES, scalar or vector. - dec = The local apparent declination, in DEGREES, scalar or vector. - - Return - ------ - alt = Altitude in degrees. Scalar or vector. - az = Azimuth angle in degrees (measured EAST from NORTH, but see keyword WS). Sca- - lar or vector. - - Modification History - -------------------- - Written Chris O'Dell Univ. of Wisconsin-Madison, May 2002. - Converted to Python by Freddy R. Galindo, ROJ, 26 September 2009. - """ - - sh = numpy.sin(ha*CoFactors.d2r) ; ch = numpy.cos(ha*CoFactors.d2r) - sd = numpy.sin(dec*CoFactors.d2r) ; cd = numpy.cos(dec*CoFactors.d2r) - sl = numpy.sin(self.lat*CoFactors.d2r) ; cl = numpy.cos(self.lat*CoFactors.d2r) - - x = -1*ch*cd*sl + sd*cl - y = -1*sh*cd - z = ch*cd*cl + sd*sl - r = numpy.sqrt(x**2. + y**2.) - - az = numpy.arctan2(y,x)/CoFactors.d2r - alt = numpy.arctan2(z,r)/CoFactors.d2r - - # correct for negative az. - w = numpy.where(az<0.) - if w[0].size>0:az[w] = az[w] + 360. - - # Convert az to West from South, if desired - if self.WS==1: az = (az + 180.) % 360. - - return alt, az - - -class Geodetic(): - def __init__(self,lat=-11.95,alt=0): - """ - The Geodetic class creates an object which represents the real position on earth of - a target (Geodetic Coordinates: lat-alt) and allows to convert (using the class me- - thods) from this coordinate system to others (e.g. geocentric). - - Parameters - ---------- - lat = Geodetic latitude of location in degrees. The default value is -11.95. - - alt = Geodetic altitude (km). The default value is 0. - - Modification History - -------------------- - Converted to Object-oriented Programming by Freddy R. Galindo, ROJ, 02 October 2009. - """ - - self.lat = numpy.atleast_1d(lat) - self.alt = numpy.atleast_1d(alt) - - self.a = 6378.16 - self.ab2 = 1.0067397 - self.ep2 = 0.0067397 - - def change2geocentric(self): - """ - change2geocentric method converts from Geodetic to Geocentric coordinates. The re- - ference geoid is that adopted by the IAU in 1964. - - Return - ------ - gclat = Geocentric latitude (in degrees), scalar or vector. - gcalt = Geocentric radial distance (km), scalar or vector. - - Example - ------- - >> ObjGeoid = Geodetic(lat=-11.95,alt=0) - >> [gclat, gcalt] = ObjGeoid.change2geocentric() - >> print gclat, gcalt - [-11.87227742] [ 6377.25048195] - - Modification History - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 02 October 2009. - """ - - gdl = self.lat*CoFactors.d2r - slat = numpy.sin(gdl) - clat = numpy.cos(gdl) - slat2 = slat**2. - clat2 = (self.ab2*clat)**2. - - sbet = slat/numpy.sqrt(slat2 + clat2) - sbet2 = (sbet**2.) # < 1 - noval = numpy.where(sbet2>1) - if noval[0].size>0:sbet2[noval] = 1 - cbet = numpy.sqrt(1. - sbet2) - - rgeoid = self.a/numpy.sqrt(1. + self.ep2*sbet2) - - x = rgeoid*cbet + self.alt*clat - y = rgeoid*sbet + self.alt*slat - - gcalt = numpy.sqrt(x**2. + y**2.) - gclat = numpy.arctan2(y,x)/CoFactors.d2r - - return gclat, gcalt diff --git a/apps/abs/utils/Files.py b/apps/abs/utils/Files.py deleted file mode 100644 index f392d95..0000000 --- a/apps/abs/utils/Files.py +++ /dev/null @@ -1,18 +0,0 @@ -''' -Created on Jun 19, 2013 - -@author: Jose Antonio Sal y Rosas Celi -@contact: jose.salyrosas@jro.igp.gob.pe -''' - -from datetime import datetime - -class Files(object): - - def setFilename(self): - return datetime.today().strftime("%Y%m%d%H%M%S%f") - - def save(self, filename, contentFile): - f = open(filename, 'a+') - f.write(contentFile) - f.close() diff --git a/apps/abs/utils/Graphics_Miscens.py b/apps/abs/utils/Graphics_Miscens.py deleted file mode 100644 index f2c15c2..0000000 --- a/apps/abs/utils/Graphics_Miscens.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -The GRAPHICS_MISC.py module gathers classes and/or functions useful for generation of plots. - -MODULES CALLED: -NUMPY, OS - -MODIFICATION HISTORY: -Created by Ing. Freddy Galindo (frederickgalindo@gmail.com). ROJ, 13 August 2009. -""" - -import os -import numpy -import sys - - -class ColorTable: - def __init__(self,table=1,filepath=None): - self.table = table - #set to path for data folder, file: col_koki.dat - if filepath==None: - filepath= './apps/abs/utils/data/' - self.filepath = filepath - - def readTable(self): - if self.table>0: - if self.table==1: - - f = open(os.path.join(self.filepath, './col_koki.dat') ,'rb') - - - #f = open('./col_koki.dat','rb') - - # Reading SkyNoise Power (lineal scale) - blue = numpy.fromfile(f,numpy.dtype([('var','b')]),256) - blue = numpy.int32(blue['var']) - val = numpy.where(blue<0) - if val[0].size:blue[val] = blue[val] + numpy.int32(256) - - green = numpy.fromfile(f,numpy.dtype([('var','b')]),256) - green = numpy.int32(green['var']) - val = numpy.where(green<0) - if val[0].size:green[val] = green[val] + numpy.int32(256) - - red = numpy.fromfile(f,numpy.dtype([('var','b')]),256) - red = numpy.int32(red['var']) - val = numpy.where(red<0) - if val[0].size:red[val] = red[val] + numpy.int32(256) - - f.close() - - colortable = numpy.array([red/255.,green/255.,blue/255.]) - - return colortable diff --git a/apps/abs/utils/Graphics_OverJro.py b/apps/abs/utils/Graphics_OverJro.py deleted file mode 100644 index f68ebf5..0000000 --- a/apps/abs/utils/Graphics_OverJro.py +++ /dev/null @@ -1,620 +0,0 @@ -""" -The module GRAPHICS_OVERJRO.py gathers classes or/and functions to create graphics from OVER-JRO -project (e.g. antenna patterns, skynoise, ...). - -MODULES CALLED: -TIME, NUMPY, MATPLOTLIB, TIMETOOLS - -MODIFICATION HISTORY: -Created by Ing. Freddy Galindo (frederickgalindo@gmail.com). ROJ Oct 18, 2009. -""" - -import time -import numpy -import sys -import os - -# set HOME environment variable to a directory the httpd server can write to -#os.environ[ 'HOME' ] = '/usr/local/www/htdocs/overJro/tempReports' -#os.environ[ 'HOME' ] = '/home/dsuarez/Pictures' -#os.environ[ 'HOME' ] = '/tmp/' -import matplotlib -#if ide==1: -# matplotlib.use('Qt4Agg') -#elif ide==2: -# matplotlib.use("Agg") -#else: -# matplotlib.use('TKAgg') -matplotlib.use("Agg") -#matplotlib.interactive(1) -import matplotlib.pyplot -#import Numeric -#import scipy -import scipy.interpolate - -from .Astro_Coords import Equatorial , CelestialBodies -from .TimeTools import Time , Julian -from .Graphics_Miscens import ColorTable -from .Misc_Routines import CoFactors,Vector - -class AntPatternPlot: - def __init__(self): - """ - AntPatternPlot creates an object to call methods to plot the antenna pattern. - - Modification History - -------------------- - Created by Freddy Galindo, ROJ, 06 October 2009. - """ - - self.fig = matplotlib.pyplot.figure(figsize=(8,8), facecolor='white') - self.ax = self.fig.add_subplot(111) - - def contPattern(self,iplot=0,gpath='',filename='',mesg='',amp=None ,x=None ,y=None ,getCut=None,title='', save=True): - """ - contPattern plots a contour map of the antenna pattern. - - Parameters - ---------- - iplot = A integer to specify if the plot is the first, second, ... The default va- - lue is 0. - - Examples - -------- - >> Over_Jro.JroPattern(pattern=2).contPattern() - - Modification history - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 06 October 2009. - """ - - if getCut == 1: - return - - xmax = numpy.max(x) - xmin = numpy.min(x) - ymax = numpy.max(y) - ymin = numpy.min(y) - - levels = numpy.array([1e-3,1e-2,1e-1,0.5,1.0]) - tmp = numpy.round(10*numpy.log10(levels),decimals=1) - labels = [] - for i in numpy.arange(5):labels.append(str(numpy.int(tmp[i]))) - - - colors = ((0,0,1.),(0,170/255.,0),(127/255.,1.,0),(1.,109/255.,0),(128/255.,0,0)) - CS = self.ax.contour(x,y,amp.transpose(),levels,colors=colors) - fmt = {} - for l,s in zip(CS.levels,labels): - fmt[l] = s - - self.ax.annotate('Ng',xy=(-0.05,1.04),xytext=(0.01,0.962),xycoords='axes fraction',arrowprops=dict(facecolor='black', width=1.,shrink=0.2),fontsize=15.) - self.ax.annotate(mesg,xy=(0,0),xytext=(0.01,0.01),xycoords='figure fraction') - self.ax.clabel(CS,CS.levels,inline=True,fmt=fmt,fontsize=10) - self.ax.set_xlim(xmin,xmax) - self.ax.set_ylim(ymin,ymax) - self.ax.set_title("Total Pattern: " + title) - self.ax.set_xlabel("West to South") - self.ax.set_ylabel("West to North") - self.ax.grid(True) - - if save: - save_fig = os.path.join(gpath,filename) - self.fig.savefig(save_fig,format='png') - - - - def close(self): - - matplotlib.pyplot.close(self.fig) - - def plotRaDec(self,gpath=None,filename=None,jd=2452640.5,ra_obs=None,xg=None,yg=None,x=None,y=None, save=True): - """ - plotRaDec draws right ascension and declination lines on a JRO plane. This function - must call after conPattern. - - Parameters - ---------- - jd = A scalar giving the Julian date. - ra_obs = Scalar giving the right ascension of the observatory. - xg = A 3-element array to specify .. - yg = A 3-element array to specify .. - - Examples - -------- - >> Over_Jro.JroPattern(pattern=2).contPattern() - >> Over_Jro.JroPattern(pattern=2).plotRaDec(jd=jd,ra_obs=ra_obs,xg=xg,yg=yg) - - Modification history - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 06 October 2009. - """ - - # Finding RA of observatory for a specific date - if ra_obs is None:ra_obs = numpy.array([23.37060849]) - if xg is None:xg = numpy.array([0.62918474,-0.77725579,0.]) - if yg is None:yg = numpy.array([0.77700346,0.62898048,0.02547905]) - - # Getting HA and DEC axes - mindec = -28; maxdec = 4; incdec = 2. - ndec = numpy.int((maxdec - mindec)/incdec) + 1 - - minha = -20; maxha = 20; incha = 2. - nha = numpy.int((maxha - minha)/incha) + 1 - - #mcosx = numpy.zeros((nha,ndec)) - #mcosy = numpy.zeros((nha,ndec)) - - ha_axes = numpy.reshape(numpy.arange(nha)*incha + minha,(nha,1)) - ones_dec = numpy.reshape(numpy.zeros(ndec) + 1,(ndec,1)) - ha_axes = numpy.dot(ha_axes,ones_dec.transpose()) - ha_axes2 = numpy.array(ra_obs - ha_axes) - - dec_axes = numpy.reshape(numpy.arange(ndec)*incdec + mindec,(ndec,1)) - ones_ra = numpy.reshape(numpy.zeros(nha) + 1,(nha,1)) - dec_axes = numpy.dot(ones_ra,dec_axes.transpose()) - dec_axes2 = numpy.array(dec_axes) - - ObjHor = Equatorial(ha_axes2,dec_axes2,jd) - [alt,az,ha] = ObjHor.change2AltAz() - - z = numpy.transpose(alt)*CoFactors.d2r ; z = z.flatten() - az = numpy.transpose(az)*CoFactors.d2r ; az = az.flatten() - - vect = numpy.array([numpy.cos(z)*numpy.sin(az),numpy.cos(z)*numpy.cos(az),numpy.sin(z)]) - - xg = numpy.atleast_2d(xg) - dcosx = numpy.array(numpy.dot(xg,vect)) - yg = numpy.atleast_2d(yg) - dcosy = numpy.array(numpy.dot(yg,vect)) - - mcosx = dcosx.reshape(ndec,nha) - mcosy = dcosy.reshape(ndec,nha) - - # Defining NAN for points outof limits. - xmax = numpy.max(x) - xmin = numpy.min(x) - ymax = numpy.max(y) - ymin = numpy.min(y) - - factor = 1.3 - noval = numpy.where((mcosx>(xmax*factor)) | (mcosx<(xmin*factor))) - if noval[0].size>0:mcosx[noval] = numpy.nan - noval = numpy.where((mcosy>(ymax*factor)) | (mcosy<(ymin*factor))) - if noval[0].size>0:mcosy[noval] = numpy.nan - - # Plotting HA and declination grid. - iha0 = numpy.int((0 - minha)/incha) - idec0 = numpy.int((-14 - mindec)/incdec) - - colorgrid = (1.,109/255.,0) - self.ax.plot(mcosx.transpose(),mcosy.transpose(),color=colorgrid,linestyle='--') - for idec in numpy.arange(ndec): - if idec != idec0: - valx = (mcosx[idec,iha0]<=xmax) & (mcosx[idec,iha0]>=xmin) - valy = (mcosy[idec,iha0]<=ymax) & (mcosy[idec,iha0]>=ymin) - if valx & valy: - text = str(numpy.int(mindec + incdec*idec))+'$^o$' - self.ax.text(mcosx[idec,iha0],mcosy[idec,iha0],text) - - matplotlib.pyplot.plot(mcosx,mcosy,color=colorgrid,linestyle='--') - for iha in numpy.arange(nha): - if iha != iha0: - valx = (mcosx[idec0,iha]<=xmax) & (mcosx[idec0,iha]>=xmin) - valy = (mcosy[idec0,iha]<=ymax) & (mcosy[idec0,iha]>=ymin) - if valx & valy: - text = str(4*numpy.int(minha + incha*iha))+"'" - self.ax.text(mcosx[idec0,iha],mcosy[idec0,iha],text) - - if save: - save_fig = os.path.join(gpath,filename) - matplotlib.pyplot.savefig(save_fig,format='png') - - - def plotBField(self,gpath,filename,dcos,alpha, nlon, nlat, dcosxrange, dcosyrange, heights, alpha_i, save=True): - """ - plotBField draws the magnetic field in a directional cosines plot. - - Parameters - ---------- - dcos = An 4-dimensional array giving the directional cosines of the magnetic field - over the desired place. - alpha = An 3-dimensional array giving the angle of the magnetic field over the desi- - red place. - nlon = An integer to specify the number of elements per longitude. - nlat = An integer to specify the number of elements per latitude. - dcosxrange = A 2-element array giving the range of the directional cosines in the - "x" axis. - dcosyrange = A 2-element array giving the range of the directional cosines in the - "y" axis. - heights = An array giving the heights (km) where the magnetic field will be modeled By default the magnetic field will be computed at 100, 500 and 1000km. - alpha_i = Angle to interpolate the magnetic field. - Modification History - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 07 October 2009. - """ - - handles = [] - objects = [] - colors = ['k','m','c','b','g','r','y'] - marker = ['-+','-*','-D','-x','-s','->','-o','-^'] - - alpha_location = numpy.zeros((nlon,2,heights.size)) - - for ih in numpy.arange(heights.size): - alpha_location[:,0,ih] = dcos[:,0,ih,0] - for ilon in numpy.arange(nlon): - myx = (alpha[ilon,:,ih])[::-1] - myy = (dcos[ilon,:,ih,0])[::-1] - tck = scipy.interpolate.splrep(myx,myy,s=0) - mydcosx = scipy.interpolate.splev(alpha_i,tck,der=0) - - myx = (alpha[ilon,:,ih])[::-1] - myy = (dcos[ilon,:,ih,1])[::-1] - tck = scipy.interpolate.splrep(myx,myy,s=0) - mydcosy = scipy.interpolate.splev(alpha_i,tck,der=0) - alpha_location[ilon,:,ih] = numpy.array([mydcosx, mydcosy]) - - - ObjFig, = self.ax.plot(alpha_location[:,0,ih],alpha_location[:,1,ih], - marker[ih % 8],color=colors[numpy.int(ih/8)],ms=4.5,lw=0.5) - handles.append(ObjFig) - objects.append(numpy.str(heights[ih]) + ' km') - - self.ax.legend(handles,objects,loc="lower right", numpoints=1, handlelength=0.3, - handletextpad=0.02, borderpad=0.3, labelspacing=0.1) - - if save: - save_fig = os.path.join(gpath,filename) - matplotlib.pyplot.savefig(save_fig,format='png') - - - -class BFieldPlot: - def __init__(self): - """ - BFieldPlot creates an object for drawing magnetic Field lines over Jicamarca. - - Modification History - -------------------- - Created by Freddy Galindo, ROJ, 07 October 2009. - """ - - self.alpha_location = 1 -# pass - - def plotBField(self,gpath,filename,dcos,alpha, nlon, nlat, dcosxrange, dcosyrange, heights, alpha_i): - """ - plotBField draws the magnetic field in a directional cosines plot. - - Parameters - ---------- - dcos = An 4-dimensional array giving the directional cosines of the magnetic field - over the desired place. - alpha = An 3-dimensional array giving the angle of the magnetic field over the desi- - red place. - nlon = An integer to specify the number of elements per longitude. - nlat = An integer to specify the number of elements per latitude. - dcosxrange = A 2-element array giving the range of the directional cosines in the - "x" axis. - dcosyrange = A 2-element array giving the range of the directional cosines in the - "y" axis. - heights = An array giving the heights (km) where the magnetic field will be modeled By default the magnetic field will be computed at 100, 500 and 1000km. - alpha_i = Angle to interpolate the magnetic field. - Modification History - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 07 October 2009. - """ - - handles = [] - objects = [] - colors = ['k','m','c','b','g','r','y'] - marker = ['-+','-*','-D','-x','-s','->','-o','-^'] - - alpha_location = numpy.zeros((nlon,2,heights.size)) - - for ih in numpy.arange(heights.size): - alpha_location[:,0,ih] = dcos[:,0,ih,0] - for ilon in numpy.arange(nlon): - myx = (alpha[ilon,:,ih])[::-1] - myy = (dcos[ilon,:,ih,0])[::-1] - tck = scipy.interpolate.splrep(myx,myy,s=0) - mydcosx = scipy.interpolate.splev(alpha_i,tck,der=0) - - myx = (alpha[ilon,:,ih])[::-1] - myy = (dcos[ilon,:,ih,1])[::-1] - tck = scipy.interpolate.splrep(myx,myy,s=0) - mydcosy = scipy.interpolate.splev(alpha_i,tck,der=0) - alpha_location[ilon,:,ih] = numpy.array([mydcosx, mydcosy]) - - - ObjFig, = matplotlib.pyplot.plot(alpha_location[:,0,ih],alpha_location[:,1,ih], \ - marker[ih % 8],color=colors[numpy.int(ih/8)],ms=4.5,lw=0.5) - handles.append(ObjFig) - objects.append(numpy.str(heights[ih]) + ' km') - - matplotlib.pyplot.xlim(dcosxrange[0],dcosxrange[1]) - matplotlib.pyplot.ylim(dcosyrange[0],dcosyrange[1]) - - try: - ObjlegB = matplotlib.pyplot.legend(handles,objects,loc="lower right", numpoints=1, handlelength=0.3, \ - handletextpad=0.02, borderpad=0.3, labelspacing=0.1) - except: - ObjlegB = matplotlib.pyplot.legend(handles,objects,loc=[0.01,0.75], numpoints=1, handlelength=0, \ - pad=0.015, handletextsep=0.02,labelsep=0.01) - - matplotlib.pyplot.setp(ObjlegB.get_texts(),fontsize='small') - matplotlib.pyplot.gca().add_artist(ObjlegB) - - save_fig = os.path.join(gpath,filename) - matplotlib.pyplot.savefig(save_fig,format='png') - self.alpha_location = alpha_location - - -class CelestialObjectsPlot: - def __init__(self,jd,dec,tod,maxha_min,show_object=None): - - self.jd = jd - self.dec = dec - self.tod = tod - self.maxha_min = maxha_min - - if show_object==None:show_object=numpy.zeros(4)+2 - self.show_object = show_object - - self.dcosx_sun = 1 - self.dcosy_sun = 1 - self.ha_sun = 1 - self.time_sun = 1 - - self.dcosx_moon = 1 - self.dcosy_moon = 1 - self.ha_moon = 1 - self.time_moon = 1 - - self.dcosx_hydra = 1 - self.dcosy_hydra = 1 - self.ha_hydra = 1 - self.time_hydra = 1 - - self.dcosx_galaxy = 1 - self.dcosy_galaxy = 1 - self.ha_galaxy = 1 - self.time_galaxy = 1 - - def drawObject(self,glat,glon,xg,yg,dcosxrange,dcosyrange,gpath='',filename=''): - - jd = self.jd - main_dec = self.dec - tod = self.tod - maxha_min = self.maxha_min - - mesg = "Drawing celestial objects over Observatory" -# print mesg -# if textid!=None:textid.append(mesg) - - maxlev = 24; minlev = 0; maxcol = 39; mincol = 10 - handles = [] - objects = ['$Sun$','$Moon$','$Hydra$','$Galaxy$'] - marker = ['--^','--s','--*','--o'] - - # Getting RGB table to plot celestial object over Jicamarca - colortable = ColorTable(table=1).readTable() - - for io in (numpy.arange(4)+1): - if self.show_object[io]!=0: - ObjBodies = CelestialBodies() - if io==1: - [ra,dec,sunlon,sunobliq] = ObjBodies.sunpos(jd) - elif io==2: - [ra,dec,dist,moonlon,moonlat] = ObjBodies.moonpos(jd) - elif io==3: - [ra,dec] = ObjBodies.hydrapos() - elif io==4: - [maxra,ra] = ObjBodies.skynoise_jro(dec_cut=main_dec) - ra = maxra*15. - dec = main_dec - - ObjEq = Equatorial(ra,dec,jd,lat=glat,lon=glon) - [alt, az, ha] = ObjEq.change2AltAz() - vect = numpy.array([az,alt]).transpose() - vect = Vector(vect,direction=0).Polar2Rect() - - dcosx = numpy.array(numpy.dot(vect,xg)) - dcosy = numpy.array(numpy.dot(vect,yg)) - wrap = numpy.where(ha>=180.) - if wrap[0].size>0:ha[wrap] = ha[wrap] - 360. - - val = numpy.where((numpy.abs(ha))<=(maxha_min*0.25)) - if val[0].size>2: - tod_1 = tod*1. - shift_1 = numpy.where(tod>12.) - tod_1[shift_1] = tod_1[shift_1] - 24. - tod_2 = tod*1. - shift_2 = numpy.where(tod<12.) - tod_2[shift_2] = tod_2[shift_2] + 24. - - diff0 = numpy.nanmax(tod[val]) - numpy.nanmin(tod[val]) - diff1 = numpy.nanmax(tod_1[val]) - numpy.nanmin(tod_1[val]) - diff2 = numpy.nanmax(tod_2[val]) - numpy.nanmin(tod_2[val]) - - if ((diff0<=diff1) & (diff0<=diff2)): - tod_0 = tod - elif ((diff10) - objects = numpy.array(objects) - objects = list(objects[val]) - try: - ObjlegC = matplotlib.pyplot.legend(handles,objects,loc="lower left", numpoints=1, handlelength=0.3, \ - borderpad=0.3, handletextpad=0.02,labelspacing=0.1) - except: - ObjlegC = matplotlib.pyplot.legend(handles,objects,loc=[0.01,0.75], numpoints=1, handlelength=0, \ - pad=0.015, handletextsep=0.02,labelsep=0.01) - - matplotlib.pyplot.setp(ObjlegC.get_texts(),fontsize='small') - ObjlegC.isaxes = False - save_fig = os.path.join(gpath,filename) - matplotlib.pyplot.savefig(save_fig,format='png') - - -class PatternCutPlot: - def __init__(self,nsubplots): - self.nsubplots = nsubplots - - self.fig = None - - self.__plot_width = 8 - - if self.nsubplots == 5: - self.__plot_height = 11 - - if self.nsubplots == 4: - self.__plot_height = 9 - - if self.nsubplots == 3: - self.__plot_height = 7 - - if self.nsubplots == 2: - self.__plot_height = 5 - - if self.nsubplots == 1: - self.__plot_height = 3 - - self.fig = matplotlib.pyplot.figure(num = 4,figsize = (self.__plot_width, self.__plot_height)) - - if self.nsubplots < 5: - self.__height_inch = 1.1 #altura de los subplots (pulgadas) - top_inch = 1.5/2.7 #espacio entre el primer subplot y el limite superior del plot - self.__vspace_plot_inch = 1.0#1.5/2 # espacio vertical entre subplots - self.__left = 0.1 - else: - self.__height_inch = 1.1 #altura de los subplots (pulgadas) - top_inch = 1.5/2.7 #espacio entre el primer subplot y el limite superior del plot - self.__vspace_plot_inch = 1.0 # espacio vertical entre subplots - self.__left = 0.1 - - self.__bottom_inch = self.__plot_height - (self.__height_inch + top_inch) - self.__height = self.__height_inch/self.__plot_height - - self.__width = 0.8 - - - def drawCut(self,io,patterns,npatterns,ha,otitle,subtitle,ptitle): - - t_cuts = ['B','Sun','Moon','Hydra','Galaxy'] - self.__bottom = self.__bottom_inch/self.__plot_height - - - subp = self.fig.add_axes([self.__left,self.__bottom,self.__width,self.__height]) - - on_axis_angle = -4.65562 - for icut in numpy.arange(npatterns): - # Getting Antenna cut. - pattern = patterns[icut] - power = numpy.abs(pattern/numpy.nanmax(pattern)) - max_power_db = numpy.round(10.*numpy.log10(numpy.nanmax(pattern)),2) - - bval = numpy.where(power[:,0]==numpy.nanmax(power)) - beta = -0.25*(ha[bval[0]] + on_axis_angle) -# print 'Angle (deg): '+"%f"%(beta) - - subp.plot(ha,power) - - - xmax = numpy.max(numpy.nanmin(ha)) - xmin = numpy.min(numpy.nanmax(ha)) - ymax = numpy.max(1) - ymin = numpy.min(0) - - - subp.set_xlim(xmin, xmax) - - subp.set_ylim(ymin, ymax) - - subp.set_title(otitle + ' ' + ptitle,size="medium") - - subp.text(0.5, 1.26,subtitle[0], - horizontalalignment='center', - verticalalignment='center', - transform = subp.transAxes) - - xlabels = subp.get_xticks() - - subp.set_xticklabels(xlabels,size="small") - - ylabels = subp.get_yticks() - - subp.set_yticklabels(ylabels,size="small") - - subp.set_xlabel('Hour angle (min) (+ve to West)',size="small") - - subp.set_ylabel("Power [Max: " + str(max_power_db) + ' dB]',size="small") - - subp.grid() - - - self.__bottom_inch = self.__bottom_inch - (self.__height_inch + self.__vspace_plot_inch) - - -class SkyNoisePlot: - def __init__(self,date,powr,time,time_lst): - """ - SkyNoisePlot class creates an object which represents the SkyNoise Object to genera- - te a SkyNoise map. - - Parameters - ---------- - date = A List of 3 elements to define the desired date ([year, month, day]). - powr = An array giving the SkyNoise power for the desired time. - time = An array giving the number of seconds since 1970 to the desired time. - time_lst = Set this input to an array to define the Local Sidereal Time of the desi- - red time. - - Modification History - -------------------- - Created by Freddy Galindo, ROJ, 18 October 2009. - """ - - self.date = date - self.powr = powr - self.time = time - self.time_lst = time_lst diff --git a/apps/abs/utils/JroAntSetup.py b/apps/abs/utils/JroAntSetup.py deleted file mode 100644 index 1c98353..0000000 --- a/apps/abs/utils/JroAntSetup.py +++ /dev/null @@ -1,1035 +0,0 @@ -''' -The module JroAntSetup contains the pre-defined parameters for beam modelling of the Jicamarca ante- -nna. Any new configuration must be added in this module (if the user decides that) using a specific -ID (pattern value) or it would be read from a file using pattern=None. - -MODULES CALLED: -OS, NUMPY - -MODIFICATION HISTORY: -Created by Ing. Freddy Galindo (frederickgalindo@gmail.com). ROJ Sep 20, 2009. -''' - -import os -import numpy - -def ReturnSetup(path=None,filename=None,pattern=0): - """ - ReturnSetup is a pre-defined list of Jicamarca antenna configurations which returns a dic- - tionary giving the configuration parameters (e.g. transmitted phases). To choose one, the - user must define the input "pattern" (See valid values below). - - Parameters: - ----------- - - pattern = A integer (>=0) to specify the setup to choose. The default value is zero. If the - antenna configuration is user-defined pattern must be None. - - path = Set this input a string to specifiy the folder path where the user-defined configu- - ration file is placed. If this value is not defined ReturnSetup will return None. - - file = Set this input a string to specifiy the name of the user-defined configuration file - (*.txt). if this value is not defined ReturnSEtup will return None. - - Examples: - --------- - - Choosing a pre-defined antenna configuration - setup = ReturnSetup(pattern=1) - - Reading a user-defined antenna configuration - setup = ReturnSetup(path="/users/users/Progs/Patterns/",file="ExpSep232009.txt") - """ - - - if pattern == 0: - title = "for module (rx)" - - ues = numpy.array([1.,2.,2.,1.]) - phase = numpy.zeros([8,8]) - phase[0:4,:] = 4 - phase[4:8,:] = 5 - - gaintx = numpy.zeros([8,8]) - gaintx[0,0] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0,0] = 1 - - justrx = 1 - - elif pattern==1: - # Configuration 1/16 on-axis (rx) - title = " for 1/16 on-axis (rx)" - - ues = numpy.array([1.,2.,2.,1.]) - phase = numpy.zeros([8,8]) - phase[0:4,:] = 4 - phase[4:8,:] = 5 - - gaintx = numpy.zeros([8,8]) - gaintx[0:2,0:2] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:2,0:2] = 1 - - justrx = 1 - - elif pattern == 2: - # Configuration for On-Axis - title = " for 1/4 on-axis (rx)" - - ues = numpy.array([1.,2.,2.,1.]) - phase = numpy.zeros([8,8]) - phase[0:4,:] = 4 - phase[4:8,:] = 5 - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - - justrx = 1 - - elif pattern == 3: - # Configuration for On-Axis - title = " for all on-axis (rx)" - - ues = numpy.array([1.,2.,2.,1.]) - phase = numpy.zeros([8,8]) - phase[0:4,:] = 4 - phase[4:8,:] = 5 - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 4: - # Configuration for oblique ISR On-Axis - title = " for Oblique ISR On-axis" - - ues = numpy.array([1.,2.,2.,1.]) - phase = numpy.zeros([8,8]) - phase[0:4,:] = 4 - phase[4:8,:] = 5 - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 5: - # Configuration for oblique ISR "4.5" - title = " for Oblique ISR '4.5'" - - ues = numpy.array([1.,2.,2.,1.]) - phase = numpy.array([[4,4,5,5,2,2,3,3], - [4,5,5,2,2,3,3,4], - [5,5,2,2,3,3,4,4], - [5,2,2,3,3,4,4,5], - [3,3,4,4,5,5,2,2], - [3,4,4,5,5,2,2,3], - [4,4,5,5,2,2,3,3], - [4,5,5,2,2,3,3,4]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 6: - # Configuration for oblique ISR "6.0S" - title = " for Oblique ISR '6.0S'" - - ues = numpy.array([1.,2.,2.,1.]) - phase = numpy.array([[4,5,2,3,4,5,2,3], - [5,2,3,4,5,2,3,4], - [2,3,4,5,2,3,4,5], - [3,4,5,2,3,4,5,2], - [5,2,3,4,5,2,3,4], - [2,3,4,5,2,3,4,5], - [3,4,5,2,3,4,5,2], - [4,5,2,3,4,5,2,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 7: - # Configuration for oblique ISR "3.0N" - title = " for Oblique ISR '3.0N'" - - ues = numpy.array([1.,2.,2.,1.]) - phase = numpy.array([[4,3,2,5,4,3,2,5], - [3,2,5,4,3,2,5,4], - [2,5,4,3,2,5,4,3], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2], - [4,3,2,5,4,3,2,5], - [3,2,5,4,3,2,5,4], - [2,5,4,3,2,5,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 8: - # Configuration for North Fritts" - title = " for North (Fritts)" - - ues = numpy.array([2.513, 1.0, 3.0, 0.413]) - phase = numpy.array([[4.29, 3.55, 2.82, 2.08, 4.20, 3.47, 2.73, 2.00], - [2.94, 2.20, 5.44, 4.70, 4.32, 3.59, 2.85, 2.12], - [5.56, 4.82, 4.09, 3.35, 4.44, 3.71, 2.97, 2.24], - [4.20, 3.47, 2.73, 2.00, 4.56, 3.82, 3.09, 2.35], - [4.20, 3.47, 2.73, 2.00, 4.56, 3.82, 3.09, 2.35], - [4.32, 3.59, 2.85, 2.12, 2.94, 2.20, 5.44, 4.70], - [4.44, 3.71, 2.97, 2.24, 5.56, 4.82, 4.09, 3.35], - [4.56, 3.82, 3.09, 2.35, 4.20, 3.47, 2.73, 2.00]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - gaintx[4:8,4:8] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - gainrx[4:8,4:8] = 1 - - justrx = 0 - - elif pattern == 9: - # Configuration for West Fritts" - title = " for West (Fritts)" - - ues = numpy.array([2.513, 1.0, 3.0, 0.413]) - phase = numpy.array([[4.29, 3.55, 2.82, 2.08, 4.20, 3.47, 2.73, 2.00], - [2.94, 2.20, 5.44, 4.70, 4.32, 3.59, 2.85, 2.12], - [5.56, 4.82, 4.09, 3.35, 4.44, 3.71, 2.97, 2.24], - [4.20, 3.47, 2.73, 2.00, 4.56, 3.82, 3.09, 2.35], - [4.20, 3.47, 2.73, 2.00, 4.56, 3.82, 3.09, 2.35], - [4.32, 3.59, 2.85, 2.12, 2.94, 2.20, 5.44, 4.70], - [4.44, 3.71, 2.97, 2.24, 5.56, 4.82, 4.09, 3.35], - [4.56, 3.82, 3.09, 2.35, 4.20, 3.47, 2.73, 2.00]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[4:8,0:4] = 1 - gaintx[4:8,0:4] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[4:8,0:4] = 1 - gainrx[4:8,0:4] = 1 - - justrx = 0 - - elif pattern == 10: - # Configuration for South Fritts" - title = " for South (Fritts)" - - ues = numpy.array([0.413, 2.0, 1.0, 1.513]) - phase = numpy.array([[2.0 , 2.73, 3.47, 4.2 , 2.08, 2.82, 3.55, 4.29], - [2.12, 2.85, 3.59, 4.32, 4.7 , 5.44, 2.20, 2.94], - [2.24, 2.97, 3.71, 4.44, 3.35, 4.09, 4.82, 5.56], - [2.35, 3.09, 3.82, 4.56, 2.0 , 2.73, 3.47, 4.20], - [2.08, 2.82, 3.55, 4.29, 2.0 , 2.73, 3.47, 4.20], - [4.70, 5.44, 2.20, 2.94, 2.12, 2.85, 3.59, 4.32], - [3.35, 4.09, 4.82, 5.56, 2.24, 2.97, 3.71, 4.44], - [2.00, 2.73, 3.47, 4.20, 2.35, 3.09, 3.82, 4.56]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - gaintx[4:8,4:8] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - gainrx[4:8,4:8] = 1 - - justrx = 0 - - elif pattern == 11: - # Configuration for East Fritts" - title = " for East (Fritts)" - - ues = numpy.array([0.413, 2.0, 1.0, 1.513]) - phase = numpy.array([[2.0 , 2.73, 3.47, 4.2 , 2.08, 2.82, 3.55, 4.29], - [2.12, 2.85, 3.59, 4.32, 4.7 , 5.44, 2.20, 2.94], - [2.24, 2.97, 3.71, 4.44, 3.35, 4.09, 4.82, 5.56], - [2.35, 3.09, 3.82, 4.56, 2.0 , 2.73, 3.47, 4.20], - [2.08, 2.82, 3.55, 4.29, 2.0 , 2.73, 3.47, 4.20], - [4.70, 5.44, 2.20, 2.94, 2.12, 2.85, 3.59, 4.32], - [3.35, 4.09, 4.82, 5.56, 2.24, 2.97, 3.71, 4.44], - [2.00, 2.73, 3.47, 4.20, 2.35, 3.09, 3.82, 4.56]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[4:8,0:4] = 1 - gaintx[4:8,0:4] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[4:8,0:4] = 1 - gainrx[4:8,0:4] = 1 - - justrx = 0 - - elif pattern == 12: - # Configuration for DEWD position (2009) - title = " for DEWD position (2009) East Beam" - - ues = numpy.array([0.,0.,0.75,0.75]) - phase = numpy.array([[2,3,3,3,3,4,4,4], - [5,2,2,2,2,3,3,3], - [3,4,4,4,4,5,5,5], - [2,3,3,3,3,4,4,4], - [4,5,5,5,5,2,2,2], - [3,4,4,4,4,5,5,5], - [5,2,2,2,2,3,3,3], - [4,5,5,5,5,2,2,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,0:4] = 1 - - justrx = 0 - - elif pattern == 13: - # Configuration for DEWD position (2009) - title = " for DEWD position (2009) West Beam" - - ues = numpy.array([1.0,0.5,1.5,2.0]) - phase = numpy.array([[5,4,2,5,3,2,4,3], - [2,5,3,2,4,3,5,4], - [2,5,3,2,4,3,5,4], - [3,2,4,3,5,4,2,5], - [3,2,4,3,5,4,2,5], - [4,3,5,4,2,5,3,2], - [4,3,5,4,2,5,3,2], - [5,4,2,5,3,2,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[4:8,:] = 1 - - justrx = 0 - - elif pattern == 14: - # Configuration for DVD position (2009) - title = " for DVD position (2009)" - - ues = numpy.array([1.0,2.0,2.0,1.25]) - phase = numpy.array([[2,2,5,5,4,4,3,3], - [2,5,5,4,4,3,3,2], - [5,5,4,4,3,3,2,2], - [5,4,4,3,3,2,2,5], - [5,5,4,4,3,3,2,2], - [5,4,4,3,3,2,2,5], - [4,4,3,3,2,2,5,5], - [4,3,3,2,2,5,5,4]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - - justrx = 0 - - elif pattern == 15: - # Configuration for Julia CP2 - title = " for Julia CP2 Ew" - - ues = numpy.array([0.0,1.0,1.0,0.0]) - phase = numpy.array([[2,2,5,4,3,3,2,5], - [2,5,4,4,3,2,5,5], - [5,4,3,3,2,5,4,4], - [4,4,3,2,5,5,4,3], - [4,4,3,2,5,5,4,3], - [4,3,2,2,5,4,3,3], - [3,2,5,5,4,3,2,2], - [2,2,5,4,3,3,2,5]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,4:8] = 1 - gaintx[4:8,0:4] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0,0] = 1 - - justrx = 0 - - elif pattern == 16: - # Configuration for Julia CP2 - title = " for Julia CP2 NS" - - ues = numpy.array([1.0,2.0,2.0,1.0]) - phase = numpy.array([[4,4,3,2,5,5,4,3], - [4,3,2,2,5,4,3,3], - [3,2,5,5,4,3,2,2], - [2,2,5,4,3,3,2,5], - [2,2,5,4,3,3,2,5], - [2,5,4,4,3,2,5,5], - [5,4,3,3,2,5,4,4], - [4,4,3,2,5,5,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - gaintx[4:8,4:8] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - - justrx = 0 - - elif pattern == 17: - # Configuration for Julia CP3 - title = " for Julia CP3 NS" - - ues = numpy.array([1.0,1.0,1.0,1.0]) - phase = numpy.array([[4,4,3,2,5,5,4,3], - [4,3,2,2,5,4,3,3], - [3,2,5,5,4,3,2,2], - [2,2,5,4,3,3,2,5], - [2,2,5,4,3,3,2,5], - [2,5,4,4,3,2,5,5], - [5,4,3,3,2,5,4,4], - [4,4,3,2,5,5,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - gaintx[4:8,4:8] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - - justrx = 0 - - elif pattern == 18: - # Configuration for Julia V - title = " for Julia V" - - ues = (2/3.)*numpy.array([1.5,3.0+0.75,3.0,1.5-0.75]) - phase = numpy.array([[4,4,3,3,2,2,5,5], - [4,3,3,2,2,5,5,4], - [3,3,2,2,5,5,4,4], - [3,2,2,5,5,4,4,3], - [3,3,2,2,5,5,4,4], - [3,2,2,5,5,4,4,3], - [2,2,5,5,4,4,3,3], - [2,5,5,4,4,3,3,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - gaintx[4:8,4:8] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - - justrx = 0 - - elif pattern == 19: - # Configuration for Julia V - title = " for Julia EW 2006-2007 (W)" - - ues = numpy.array([1.0+0.66,2.0+0.66,2.0,1.0]) - phase = numpy.array([[4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 20: - # Configuration for Julia V - title = " for Julia EW 2006-2007 (E)" - - ues = numpy.array([1.0,1.0,1.0,1.0]) - phase = numpy.array([[4,4,4,4,5,5,5,5], - [3,3,3,3,4,4,4,4], - [5,5,5,5,2,2,2,2], - [4,4,4,4,5,5,5,5], - [2,2,2,2,3,3,3,3], - [5,5,5,5,2,2,2,2], - [3,3,3,3,4,4,4,4], - [2,2,2,2,3,3,3,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - gainrx[4:8,4:8] = 1 - - justrx = 0 - - elif pattern == 21: - # Configuration for EW Imaging 1996 - title = " for EW Imaging 1996" - - ues = numpy.array([1.0,2.0,2.0,1.0]) - phase = numpy.array([[4,4,3,2,5,5,4,3], - [4,3,2,2,5,4,3,3], - [3,2,5,5,4,3,2,2], - [2,2,5,4,3,3,2,5], - [2,2,5,4,3,3,2,5], - [2,5,4,4,3,2,5,5], - [5,4,3,3,2,5,4,4], - [4,4,3,2,5,5,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - gaintx[4:8,4:8] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0,0] = 1 - - justrx = 0 - - elif pattern == 22: - # Configuration for EW Imaging 2003 - title = " for EW Imaging 2003" - - ues = numpy.array([1.0,1.0,1.0,1.0]) - phase = numpy.array([[4,4,3,2,0,0,0,0], - [2,3,2,2,0,0,0,0], - [5,0,2,5,0,0,0,0], - [2,4,3,4,0,0,0,0], - [0,0,0,0,3,3,2,5], - [0,0,0,0,2,2,5,5], - [0,0,0,0,4,3,5,4], - [0,0,0,0,5,3,2,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - gaintx[4:8,4:8] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0,0] = 1 - - justrx = 0 - - elif pattern == 23: - # Configuration for EW Imaging 2003 - title = " for EW Imaging 2006-2008" - - ues = numpy.array([1.0,1.0,1.0,2.0]) - phase = numpy.array([[4,4,3,2,0,0,0,0], - [2,3,2,2,0,0,0,0], - [5,0,2,5,0,0,0,0], - [2,4,3,4,0,0,0,0], - [0,0,0,0,3,3,2,5], - [0,0,0,0,2,2,5,5], - [0,0,0,0,4,3,5,4], - [0,0,0,0,5,3,2,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[0:4,0:4] = 1 - gaintx[4:8,4:8] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0,0] = 1 - - justrx = 0 - - elif pattern == 50: - # Configuration for vertical drift 1996 - title = " for Vertical drift 1996" - - ues = (2/3.)*numpy.array([0.,1.5,1.5,0.]) - phase = numpy.array([[4,4,3,2,5,5,4,3], - [4,3,2,2,5,4,3,3], - [3,2,5,5,4,3,2,2], - [2,2,5,4,3,3,2,5], - [2,2,5,4,3,3,2,5], - [2,5,4,4,3,2,5,5], - [5,4,3,3,2,5,4,4], - [4,4,3,2,5,5,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 51: - # Configuration for vertical drift 1996 - title = " for East-West Drifts 1996 (W beam)" - - ues = numpy.array([0.0,1.0,2.0,1.0]) - phase = numpy.array([[4,3,5,4,2,5,3,2], - [4,3,5,4,2,5,3,2], - [4,3,5,4,2,5,3,2], - [4,3,5,4,2,5,3,2], - [5,4,2,5,3,2,4,3], - [5,4,2,5,3,2,4,3], - [5,4,2,5,3,2,4,3], - [5,4,2,5,3,2,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[4:8,:] = 1 - - justrx = 0 - - elif pattern == 52: - # Configuration for vertical drift 1996 - title = " for East-West Drifts 1996 (E Beam)" - - ues = numpy.array([1.0,1.0,0.0,0.0]) - phase = numpy.array([[4,4,4,4,5,5,5,5], - [3,3,3,3,4,4,4,4], - [5,5,5,5,2,2,2,2], - [4,4,4,4,5,5,5,5], - [2,2,2,2,3,3,3,3], - [5,5,5,5,2,2,2,2], - [3,3,3,3,4,4,4,4], - [2,2,2,2,3,3,3,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,0:4] = 1 - - justrx = 0 - - elif pattern == 53: - # Configuration for vertical drift 1996 - title = " for DVD position 3 (2006-2008)" - - ues = numpy.array([1.,2,2,1]) - phase = numpy.array([[4,4,3,3,2,2,5,5], - [4,3,3,2,2,5,5,4], - [3,3,2,2,5,5,4,4], - [3,2,2,5,5,4,4,3], - [3,3,2,2,5,5,4,4], - [3,2,2,5,5,4,4,3], - [2,2,5,5,4,4,3,3], - [2,5,5,4,4,3,3,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,4:8] = 1 - - justrx = 0 - - elif pattern == 54: - # Configuration for vertical drift 1996 - title = " for DEWD (Mar 2005)" - - ues = numpy.array([0.,1.,1/3.,1]) - phase = numpy.array([[4,3,2,5,3,3,3,3], - [4,3,2,5,2,2,2,2], - [4,3,2,4,5,5,5,5], - [4,3,2,4,4,4,3,3], - [5,4,3,2,2,2,2,2], - [5,4,3,2,5,5,5,5], - [5,4,3,5,4,4,4,4], - [5,4,3,5,3,3,2,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,0:4] = 1 - - justrx = 0 - - elif pattern == 55: - # Configuration for vertical drift 1996 - title = " for DEWD (Mar 2005)" - - ues = numpy.array([0.,1.,1/3.,1]) - phase = numpy.array([[4,3,2,5,3,3,3,3], - [4,3,2,5,2,2,2,2], - [4,3,2,4,5,5,5,5], - [4,3,2,4,4,4,3,3], - [5,4,3,2,2,2,2,2], - [5,4,3,2,5,5,5,5], - [5,4,3,5,4,4,4,4], - [5,4,3,5,3,3,2,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4:,4:8] = 1 - - justrx = 0 - - elif pattern ==56: - # Configuration using antenna compression - title = " for antenna compression AA*" - - ues = numpy.array([0.0,0.0,0.0,0.0]) - phase = numpy.array([[4,4,4,2,4,4,2,4], - [4,4,4,2,4,4,2,4], - [2,2,2,4,2,2,4,2], - [4,4,4,2,4,4,2,4], - [2,2,2,4,2,2,4,2], - [2,2,2,4,2,2,4,2], - [4,4,4,2,4,4,2,4], - [2,2,2,4,2,2,4,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - - justrx = 0 - - elif pattern ==57: - # Configuration using antenna compression - title = " for antenna compression AB*" - - ues = numpy.array([0.0,0.0,0.0,0.0]) - phase = numpy.array([[4,4,2,4,2,2,4,2], - [4,4,2,4,2,2,4,2], - [2,2,4,2,4,4,2,4], - [4,4,2,4,2,2,4,2], - [2,2,4,2,4,4,2,4], - [2,2,4,2,4,4,2,4], - [4,4,2,4,2,2,4,2], - [2,2,4,2,4,4,2,4]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - - justrx = 0 - - elif pattern ==58: - # Configuration using in Oblique ISR 4.5 - title = " for Oblique ISR 4.5" - - ues = numpy.array([1.0,2.0,2.0,1.0]) - phase = numpy.array([[4,4,5,5,2,2,3,3], - [4,5,5,2,2,3,3,4], - [5,5,2,2,3,3,4,4], - [5,2,2,3,3,4,4,5], - [3,3,4,4,5,5,2,2], - [3,4,4,5,5,2,2,3], - [4,4,5,5,2,2,3,3], - [4,5,5,2,2,3,3,4]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 1 - - elif pattern == 60: - title=" for Differential phase 2000" - ues = (2/3.)*numpy.array([0.,1.5-0.5,1.5,0.+0.5]) - - phase = numpy.array([[4,4,3,2,5,5,4,3], - [4,3,2,2,5,4,3,3], - [3,2,5,5,4,3,2,2], - [2,2,5,4,3,3,2,5], - [2,2,5,4,3,3,2,5], - [2,5,4,4,3,2,5,5], - [5,4,3,3,2,5,4,4], - [4,4,3,2,5,5,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,0:4] = 1 - - justrx = 0 - - elif pattern == 61: - #for East-West 2003 W - title=" for East-West 2003" - - ues = numpy.array([1.+0.66,2.+0.66,2.,1.]) - - phase = numpy.array([[4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[4:8,:] = 1 - - justrx = 0 - - elif pattern == 62: - #for East-West 2003 E - title=" for East-West 2003" - - ues = numpy.array([1.,1.,0.+1.0,0.+1.0]) - - phase = numpy.array([[4,4,4,4,5,5,5,5], - [3,3,3,3,4,4,4,4], - [5,5,5,5,2,2,2,2], - [4,4,4,4,5,5,5,5], - [2,2,2,2,3,3,3,3], - [5,5,5,5,2,2,2,2], - [3,3,3,3,4,4,4,4], - [2,2,2,2,3,3,3,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,0:4] = 1 - - justrx = 0 - - elif pattern == 63: - - title=" for Differential phase 2004 High Alt." - - ues = (2/3.)*numpy.array([0.,1.5-1.0,1.5,0.+1.0]) - - phase = numpy.array([[4,4,3,2,5,5,4,3], - [4,3,2,2,5,4,3,3], - [3,2,5,5,4,3,2,2], - [2,2,5,4,3,3,2,5], - [2,2,5,4,3,3,2,5], - [2,5,4,4,3,2,5,5], - [5,4,3,3,2,5,4,4], - [4,4,3,2,5,5,4,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 64: - - title=" for Differential Phase Perp to B 2005-2006" - - ues = (2/3.)*numpy.array([1.5,3.0+0.75,3.0,1.5-0.75]) - - phase = numpy.array([[4,4,3,3,2,2,5,5], - [4,3,3,2,2,5,5,4], - [3,3,2,2,5,5,4,4], - [3,2,2,5,5,4,4,3], - [3,3,2,2,5,5,4,4], - [3,2,2,5,5,4,4,3], - [2,2,5,5,4,4,3,3], - [2,5,5,4,4,3,3,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[0:4,4:8] = 1 - - justrx = 0 - - elif pattern == 65: - #for JULIA EW 2003 W - title=" for JULIA EW 2003" - - ues = numpy.array([1+0.66,2+0.66,2.,1.]) - - phase = numpy.array([[4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [4,3,2,5,4,3,2,5], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2], - [5,4,3,2,5,4,3,2]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[4:8,:] = 1 - - justrx = 0 - - elif pattern == 66: - #for JULIA EW 2003 E - title=" for JULIA EW 2003" - - ues = numpy.array([1.,1.,0.,0.]) - - phase = numpy.array([[4,4,4,4,5,5,5,5], - [3,3,3,3,4,4,4,4], - [5,5,5,5,2,2,2,2], - [4,4,4,4,5,5,5,5], - [2,2,2,2,3,3,3,3], - [5,5,5,5,2,2,2,2], - [3,3,3,3,4,4,4,4], - [2,2,2,2,3,3,3,3]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,0:4] = 1 - - justrx = 0 - - elif pattern == 67: - - title=" for Vertical (Yellow Cables)" - - ues = numpy.array([0.25, 0.25, 0.25, 0.25]) - - phase = numpy.array([[3.41, 3.41, 3.41, 3.41, 3.41, 3.41, 3.41, 3.41], - [2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78], - [2.15, 2.15, 2.15, 2.15, 2.15, 2.15, 2.15, 2.15], - [5.52, 5.52, 5.52, 5.52, 5.52, 5.52, 5.52, 5.52], - [4.89, 4.89, 4.89, 4.89, 4.89, 4.89, 4.89, 4.89], - [4.26, 4.26, 4.26, 4.26, 4.26, 4.26, 4.26, 4.26], - [3.63, 3.63, 3.63, 3.63, 3.63, 3.63, 3.63, 3.63], - [3.00, 3.00, 3.00, 3.00, 3.00, 3.00, 3.00, 3.00]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - elif pattern == 100: - - title=" for High Altitude Drift" - - ues = numpy.array([ 2.0,0.8,1.0,2.2]) - - phase = numpy.array([[5,5,4,4, 4,4,3,3], - [5,4,4,4, 4,3,3,3], - [4,4,4,4, 3,3,3,3], - [4,4,4,3, 3,3,3,2], - [3,3,2,2, 2,2,5,5], - [3,2,2,2, 2,5,5,5], - [2,2,2,2, 5,5,5,5], - [2,2,2,5, 5,5,5,4]],dtype=float) - - gaintx = numpy.zeros([8,8]) - gaintx[:,:] = 1 - - gainrx = numpy.zeros([8,8]) - gainrx[:,:] = 1 - - justrx = 0 - - - elif pattern==None: - - inputs = numpy.array(["title","ues_tx","phase_tx","gain_tx","gain_rx","just_rx"]) - - # Reading user-defined configuration. - if path==None:path = os.getcwd() + os.sep + "patterns" + os.sep - if filename==None:filename = "jropattern.txt" - - ff = open(os.path.join(path,filename),'r') - - while 1: - # Checking EOF. - init = ff.tell() - if not ff.readline():break - else:ff.seek(init) - - line = ff.readline().lstrip() - if line.__len__()!=0: - if line[0]!='#': - keys = line.split("=") - key = keys[0].lstrip().rstrip().lower() - vv = numpy.where(inputs==key) - if vv[0][0]==0: - title = keys[1].lstrip().rstrip() - elif vv[0][0]==1: - ues = (keys[1].lstrip().rstrip()) - ues = numpy.float32(ues[1:-1].split(",")) - elif vv[0][0]==2: - phase = numpy.zeros([8,8]) - tx = (keys[1].lstrip().rstrip()) - tx = numpy.float32(tx[2:-3].split(",")) - phase[0,:] = tx - for ii in numpy.arange(7): - tx = ff.readline().lstrip().rstrip() - tx = numpy.float32(tx[1:-3+(ii==6)].split(",")) - phase[ii+1,:] = tx - elif vv[0][0]==3: - gaintx = numpy.zeros([8,8]) - gg = (keys[1].lstrip().rstrip()) - gg = numpy.float32(gg[2:-3].split(",")) - gaintx[0,:] = gg - for ii in numpy.arange(7): - gg = ff.readline().lstrip().rstrip() - gg = numpy.float32(gg[1:-3+(ii==6)].split(",")) - gaintx[ii+1,:] = gg - elif vv[0][0]==4: - gainrx = numpy.zeros([8,8]) - gg = (keys[1].lstrip().rstrip()) - gg = numpy.float32(gg[2:-3].split(",")) - gainrx[0,:] = gg - for ii in numpy.arange(7): - gg = ff.readline().lstrip().rstrip() - gg = numpy.float32(gg[1:-3+(ii==6)].split(",")) - gainrx[ii+1,:] = gg - elif vv[0][0]==5: - justrx = numpy.float(keys[1].lstrip().rstrip()) - - ff.close() - - - - setup = {"ues":ues, "phase":phase, "gaintx":gaintx, "gainrx":gainrx, "justrx":justrx, \ - "title":title} - - return setup - - - - \ No newline at end of file diff --git a/apps/abs/utils/Misc_Routines.py b/apps/abs/utils/Misc_Routines.py deleted file mode 100644 index 85c46f0..0000000 --- a/apps/abs/utils/Misc_Routines.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -The module MISC_ROUTINES gathers classes and functions which are useful for daily processing. As an -example we have conversion factor or universal constants. - -MODULES CALLED: -NUMPY, SYS - -MODIFICATION HISTORY: -Created by Ing. Freddy Galindo (frederickgalindo@gmail.com). ROJ, 21 October 2009. -""" - -import numpy -import sys - -class CoFactors(object): - """ - CoFactor class used to call pre-defined conversion factor (e.g. degree to radian). The cu- - The current available factor are: - - d2r = degree to radian. - s2r = seconds to radian?, degree to arcsecond.? - h2r = hour to radian. - h2d = hour to degree - """ - - d2r = numpy.pi/180. - s2r = numpy.pi/(180.*3600.) - h2r = numpy.pi/12. - h2d = 15. - -class Redirect(object): - def __init__(self,stdout=None): - self.stdout = stdout - - def write(self,message): - self.stdout.insertPlainText(message) - -class WidgetPrint(object): - """ - WidgetPrint class allows to define the standard output. - """ - def __init__(self,textid=None): - self.__stdout = sys.stdout - self.textid = textid - self.wPrint() - - def wPrint(self): - if self.textid == None: sys.stdout = self.__stdout - if self.textid != None: sys.stdout = Redirect(self.textid) - print ("") - -class Vector(object): - """ - direction = 0 Polar to rectangular; direction=1 rectangular to polar - """ - def __init__(self,vect=numpy.array([]),direction=0): - nsize = numpy.size(vect) - if nsize <= 3: - vect = vect.reshape(1,nsize) - - self.vect = vect - self.dirc = direction - - - - def Polar2Rect(self): - if self.dirc == 0: - jvect = self.vect*numpy.pi/180. - mmx = numpy.cos(jvect[:,1])*numpy.sin(jvect[:,0]) - mmy = numpy.cos(jvect[:,1])*numpy.cos(jvect[:,0]) - mmz = numpy.sin(jvect[:,1]) - mm = numpy.array([mmx,mmy,mmz]).transpose() - - elif self.dirc == 1: - mm = [numpy.arctan2(self.vect[:,0],self.vect[:,1]),numpy.arcsin(self.vect[:,2])] - mm = numpy.array(mm)*180./numpy.pi - - return mm - -if __name__ == "__main__": - - a=CoFactors() - a=Redirect() - a=WidgetPrint() - a=WidgetPrint() - a=Vector() diff --git a/apps/abs/utils/OverJRO.py b/apps/abs/utils/OverJRO.py deleted file mode 100644 index 08f302e..0000000 --- a/apps/abs/utils/OverJRO.py +++ /dev/null @@ -1,97 +0,0 @@ -''' -Created on May 8, 2013 - -@author: Jose Antonio Sal y Rosas Celi -@contact: jose.salyrosas@jro.igp.gob.pe -''' - -import os -from Files import Files - -class OverJRO(Files): - - __scriptName = "OverJRO.py" - - def __init__(self): - pass - - def setParameters(self, path, exp_name, phase_tx, gain_tx, gain_rx, ues_tx, just_rx): - self.path = path - self.exp_name = exp_name - self.phase_tx = phase_tx - self.gain_tx = gain_tx - self.gain_rx = gain_rx - self.ues_tx = ues_tx - self.just_rx = just_rx - - def saveFile(self, contentFile): - filename = self.setFilename() - finalpath = os.path.join(self.path, self.setFileExtension(filename)) - print ("HAHAH") - finalpath = "apps/abs/static/data/"+finalpath - self.save(finalpath, contentFile) - return finalpath - - def setTextContent(self): - title = "title ='%s'" % self.exp_name - ues_tx = "ues_tx = %s" % self.ues_tx - phase_tx = "phase_tx = %s" % (self.convertValue(self.phase_tx)) - gain_tx = "gain_tx = %s" % (self.convertValue(self.gain_tx)) - gain_rx = "gain_rx = %s" % (self.convertValue(self.gain_rx)) - just_rx = "just_rx = %d" % self.just_rx - content = " %s\r\n\n %s\r\n\n %s\r\n %s\r\n %s\r\n %s\r\n" % (title, ues_tx, phase_tx, gain_tx, gain_rx, just_rx) - return content - - def setFileExtension(self, filename): - txtFile = filename + ".txt" - - return txtFile - - def convertValue(self, strAntenna): - value = "" - strAntenna = strAntenna.replace("],[","]+[") - lsAntenna = strAntenna.split("+") - for i,element in enumerate(lsAntenna): - if i == 0: - value += "%s,$\n" % element - elif i == 7: - value += " %s\n" % element - else: - value += " %s,$\n" % element - - return value - - -if __name__ == '__main__': - path = "/home/fquino/workspace/radarsys/webapp/apps/abs/static/data" - exp_name = "MST-ISR 2009 (NS-Up)" - phase_tx = "[[0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]," \ - "[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]," \ - "[0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]," \ - "[0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]," \ - "[1.0,1.0,1.0,1.0,1.5,1.5,1.5,1.5]," \ - "[0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]," \ - "[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]," \ - "[0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]]" - gain_tx = "[[1,1,1,1,1,1,1,1]," \ - "[1,1,1,1,1,1,1,1]," \ - "[1,1,1,1,1,1,1,1]," \ - "[1,1,1,1,1,1,1,1]," \ - "[1,1,1,1,1,1,1,1]," \ - "[1,1,1,1,1,1,1,1]," \ - "[1,1,1,1,1,1,1,1]," \ - "[1,1,1,1,1,1,1,1]]" - gain_rx = "[[1,1,1,1,0,0,0,0]," \ - "[1,1,1,1,0,0,0,0]," \ - "[1,1,1,1,0,0,0,0]," \ - "[1,1,1,1,0,0,0,0]," \ - "[0,0,0,0,1,1,1,1]," \ - "[0,0,0,0,1,1,1,1]," \ - "[0,0,0,0,1,1,1,1]," \ - "[0,0,0,0,1,1,1,1]]" - ues_tx = "[0.533333,0.00000,1.06667,0.00000]" - just_rx = 0 - data = OverJRO() - data.setParameters(path, exp_name, phase_tx, gain_tx, gain_rx, ues_tx, just_rx) - contentFile = data.setTextContent() - data.saveFile(contentFile) diff --git a/apps/abs/utils/TimeTools.py b/apps/abs/utils/TimeTools.py deleted file mode 100644 index 19f8f9c..0000000 --- a/apps/abs/utils/TimeTools.py +++ /dev/null @@ -1,427 +0,0 @@ -""" -The TIME_CONVERSIONS.py module gathers classes and functions for time system transformations -(e.g. between seconds from 1970 to datetime format). - -MODULES CALLED: -NUMPY, TIME, DATETIME, CALENDAR - -MODIFICATION HISTORY: -Created by Ing. Freddy Galindo (frederickgalindo@gmail.com). ROJ Aug 13, 2009. -""" - -import numpy -import time -#import datetime as dt -import calendar - -class Time: - """ - time(year,month,dom,hour,min,secs) - - An object represents a date and time of certain event.. - - Parameters - ---------- - YEAR = Number of the desired year. Year must be valid values from the civil calendar. - Years B.C.E must be represented as negative integers. Years in the common era are repre- - sented as positive integers. In particular, note that there is no year 0 in the civil - calendar. 1 B.C.E. (-1) is followed by 1 C.E. (1). - - MONTH = Number of desired month (1=Jan, ..., 12=December). - - DOM = Number of day of the month. - - HOUR = Number of the hour of the day. By default hour=0 - - MINS = Number of the minute of the hour. By default min=0 - - SECS = Number of the second of the minute. By default secs=0. - - Examples - -------- - time_info = time(2008,9,30,12,30,00) - - time_info = time(2008,9,30) - """ - - def __init__(self,year=None,month=None,dom=None,hour=0,mins=0,secs=0): - # If one the first three inputs are not defined, it takes the current date. - date = time.localtime() - if year==None:year=date[0] - if month==None:month=date[1] - if dom==None:dom=date[2] - - # Converting to arrays - year = numpy.array([year]); month = numpy.array([month]); dom = numpy.array([dom]) - hour = numpy.array([hour]); mins = numpy.array([mins]); secs = numpy.array([secs]) - - # Defining time information object. - self.year = numpy.atleast_1d(year) - self.month = numpy.atleast_1d(month) - self.dom = numpy.atleast_1d(dom) - self.hour = numpy.atleast_1d(hour) - self.mins = numpy.atleast_1d(mins) - self.secs = numpy.atleast_1d(secs) - - def change2julday(self): - """ - Converts a datetime to Julian days. - """ - - # Defining constants - greg = 2299171 # incorrect Julian day for Oct, 25, 1582. - min_calendar = -4716 - max_calendar = 5000000 - - min_year = numpy.nanmin(self.year) - max_year = numpy.nanmax(self.year) - if (min_yearmax_calendar): - print ("Value of Julian date is out of allowed range") - return -1 - - noyear = numpy.sum(self.year==0) - if noyear>0: - print ("There is no year zero in the civil calendar") - return -1 - - # Knowing if the year is less than 0. - bc = self.year<0 - - # Knowing if the month is less than March. - inJanFeb = self.month<=2 - - jy = self.year + bc - inJanFeb - jm = self.month + (1 + 12*inJanFeb) - - # Computing Julian days. - jul= numpy.floor(365.25*jy) + numpy.floor(30.6001*jm) + (self.dom+1720995.0) - - # Test whether to change to Gregorian Calendar - if numpy.min(jul) >= greg: - ja = numpy.int32(0.01*jy) - jul = jul + 2 - ja + numpy.int32(0.25*ja) - else: - gregchange = numpy.where(jul >= greg) - if gregchange[0].size>0: - ja = numpy.int32(0.01 + jy[gregchange]) - jy[gregchange] = jy[gregchange] + 2 - ja + numpy.int32(0.25*ja) - - # Determining machine-specific parameters affecting floating-point. - eps = 0.0 # Replace this line for a function to get precision. - eps = abs(jul)*0.0 > eps - - jul = jul + (self.hour/24. -0.5) + (self.mins/1440.) + (self.secs/86400.) + eps - - return jul[0] - - def change2secs(self): - """ - Converts datetime to number of seconds respect to 1970. - """ - - year = self.year - if year.size>1: year = year[0] - - month = self.month - if month.size>1: month = month[0] - - dom = self.dom - if dom.size>1: dom = dom[0] - - # Resizing hour, mins and secs if it was necessary. - hour = self.hour - if hour.size>1:hour = hour[0] - if hour.size==1:hour = numpy.resize(hour,year.size) - - mins = self.mins - if mins.size>1:mins = mins[0] - if mins.size==1:mins = numpy.resize(mins,year.size) - - secs = self.secs - if secs.size>1:secs = secs[0] - if secs.size==1:secs = numpy.resize(secs,year.size) - - # Using time.mktime to compute seconds respect to 1970. - secs1970 = numpy.zeros(year.size) - for ii in numpy.arange(year.size): - secs1970[ii] = time.mktime((int(year[ii]),int(month[ii]),int(dom[ii]),\ - int(hour[ii]),int(mins[ii]),int(secs[ii]),0,0,0)) - - secs1970 = numpy.int32(secs1970 - time.timezone) - - return secs1970 - - def change2strdate(self,mode=1): - """ - change2strdate method converts a date and time of certain event to date string. The - string format is like localtime (e.g. Fri Oct 9 15:00:19 2009). - - Parameters - ---------- - None. - - Return - ------ - - Modification History - -------------------- - Created by Freddy R. Galindo, ROJ, 09 October 2009. - - """ - - secs = numpy.atleast_1d(self.change2secs()) - strdate = [] - for ii in numpy.arange(numpy.size(secs)): - secs_tmp = time.localtime(secs[ii] + time.timezone) - if mode==1: - strdate.append(time.strftime("%d-%b-%Y (%j) %H:%M:%S",secs_tmp)) - elif mode==2: - strdate.append(time.strftime("%d-%b-%Y (%j)",secs_tmp)) - - strdate = numpy.array(strdate) - - return strdate - - -class Secs: - """ - secs(secs): - - An object represents the number of seconds respect to 1970. - - Parameters - ---------- - - SECS = A scalar or array giving the number of seconds respect to 1970. - - Example: - -------- - secs_info = secs(1251241373) - - secs_info = secs([1251241373,1251241383,1251241393]) - """ - def __init__(self,secs): - self.secs = secs - - def change2julday(self): - """ - Convert seconds from 1970 to Julian days. - """ - - secs_1970 = time(1970,1,1,0,0,0).change2julday() - - julian = self.secs/86400.0 + secs_1970 - - return julian - - def change2time(self): - """ - Converts seconds from 1970 to datetime. - """ - - secs1970 = numpy.atleast_1d(self.secs) - - datetime = numpy.zeros((9,secs1970.size)) - for ii in numpy.arange(secs1970.size): - tuple = time.gmtime(secs1970[ii]) - datetime[0,ii] = tuple[0] - datetime[1,ii] = tuple[1] - datetime[2,ii] = tuple[2] - datetime[3,ii] = tuple[3] - datetime[4,ii] = tuple[4] - datetime[5,ii] = tuple[5] - datetime[6,ii] = tuple[6] - datetime[7,ii] = tuple[7] - datetime[8,ii] = tuple[8] - - datetime = numpy.int32(datetime) - - return datetime - - -class Julian: - """ - julian(julian): - - An object represents julian days. - - Parameters - ---------- - - JULIAN = A scalar or array giving the julina days. - - Example: - -------- - julian_info = julian(2454740) - - julian_info = julian([2454740,2454760,2454780]) - """ - def __init__(self,julian): - self.julian = numpy.atleast_1d(julian) - - def change2time(self): - """ - change2time method converts from julian day to calendar date and time. - - Return - ------ - year = An array giving the year of the desired julian day. - month = An array giving the month of the desired julian day. - dom = An array giving the day of the desired julian day. - hour = An array giving the hour of the desired julian day. - mins = An array giving the minute of the desired julian day. - secs = An array giving the second of the desired julian day. - - Examples - -------- - >> jd = 2455119.0 - >> [yy,mo,dd,hh,mi,ss] = TimeTools.julian(jd).change2time() - >> print ([yy,mo,dd,hh,mi,ss]) - [2009] [10] [ 14.] [ 12.] [ 0.] [ 0.] - - Modification history - -------------------- - Translated from "Numerical Recipies in C", by William H. Press, Brian P. Flannery, - Saul A. Teukolsky, and William T. Vetterling. Cambridge University Press, 1988. - Converted to Python by Freddy R. Galindo, ROJ, 06 October 2009. - """ - - min_julian = -1095 - max_julian = 1827933925 - if (numpy.min(self.julian) < min_julian) or (numpy.max(self.julian) > max_julian): - print ('Value of Julian date is out of allowed range.') - return None - - # Beginning of Gregorian calendar - igreg = 2299161 - julLong = numpy.floor(self.julian + 0.5) - minJul = numpy.min(julLong) - - if (minJul >= igreg): - # All are Gregorian - jalpha = numpy.int32(((julLong - 1867216) - 0.25)/36524.25) - ja = julLong + 1 + jalpha - numpy.int32(0.25*jalpha) - else: - ja = julLong - gregChange = numpy.where(julLong >= igreg) - if gregChange[0].size>0: - jalpha = numpy.int32(((julLong[gregChange]-1867216) - 0.25)/36524.25) - ja[gregChange] = julLong[gregChange]+1+jalpha-numpy.int32(0.25*jalpha) - - # clear memory. - jalpha = -1 - - jb = ja + 1524 - jc = numpy.int32(6680. + ((jb-2439870)-122.1)/365.25) - jd = numpy.int32(365.*jc + (0.25*jc)) - je = numpy.int32((jb - jd)/30.6001) - - dom = jb - jd - numpy.int32(30.6001*je) - month = je - 1 - month = ((month - 1) % 12) + 1 - month = numpy.atleast_1d(month) - year = jc - 4715 - year = year - (month > 2)*1 - year = year - (year <= 0)*1 - year = numpy.atleast_1d(year) - - # Getting hours, minutes, seconds - fraction = self.julian + 0.5 - julLong - eps_0 = dom*0.0 + 1.0e-12 - eps_1 = 1.0e-12*numpy.abs(julLong) - eps = (eps_0>eps_1)*eps_0 + (eps_0<=eps_1)*eps_1 - - hour_0 = dom*0 + 23 - hour_2 = dom*0 + 0 - hour_1 = numpy.floor(fraction*24.0 + eps) - hour = ((hour_1>hour_0)*23) + ((hour_1<=hour_0)*hour_1) - hour = ((hour_1=hour_2)*hour_1) - - fraction = fraction - (hour/24.0) - mins_0 = dom*0 + 59 - mins_2 = dom*0 + 0 - mins_1 = numpy.floor(fraction*1440.0 + eps) - mins = ((mins_1>mins_0)*59) + ((mins_1<=mins_0)*mins_1) - mins = ((mins_1=mins_2)*mins_1) - - secs_2 = dom*0 + 0 - secs_1 = (fraction - mins/1440.0)*86400.0 - secs = ((secs_1=secs_2)*secs_1) - - return year,month,dom,hour,mins,secs - - def change2secs(self): - """ - Converts from Julian days to seconds from 1970. - """ - - jul_1970 = Time(1970,1,1,0,0,0).change2julday() - - secs = numpy.int32((self.julian - jul_1970)*86400) - - return secs - - def change2lst(self,longitude=-76.8667): - """ - CT2LST converts from local civil time to local mean sideral time - - longitude = The longitude in degrees (east of Greenwich) of the place for which - the local sideral time is desired, scalar. The Greenwich mean sideral time (GMST) - can be found by setting longitude=0. - """ - - # Useful constants, see Meus, p. 84 - c = numpy.array([280.46061837, 360.98564736629, 0.000387933, 38710000.0]) - jd2000 = 2451545.0 - t0 = self.julian - jd2000 - t = t0/36525. - - # Computing GST in seconds - theta = c[0] + (c[1]*t0) + (t**2)*(c[2]-t/c[3]) - - # Computing LST in hours - lst = (theta + longitude)/15.0 - neg = numpy.where(lst < 0.0) - if neg[0].size>0:lst[neg] = 24.0 + (lst[neg] % 24) - lst = lst % 24.0 - - return lst - - -class date2doy: - def __init__(self,year,month,day): - self.year = year - self.month = month - self.day = day - - def change2doy(self): - if calendar.isleap(self.year) == True: - tfactor = 1 - else: - tfactor = 2 - - day = self.day - month = self.month - - doy = numpy.floor((275*month)/9.0) - (tfactor*numpy.floor((month+9)/12.0)) + day - 30 - - return numpy.int32(doy) - - -class Doy2Date: - def __init__(self,year,doy): - self.year = year - self.doy = doy - - def change2date(self): - months = numpy.arange(12) + 1 - - first_dem = date2doy(self.year,months,1) - first_dem = first_dem.change2doy() - - imm = numpy.where((self.doy - first_dem) > 0) - - month = imm[0].size - dom = self.doy -first_dem[month - 1] + 1 - - return month, dom diff --git a/apps/abs/utils/__init__.py b/apps/abs/utils/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/abs/utils/__init__.py +++ /dev/null diff --git a/apps/abs/utils/col_koki.dat b/apps/abs/utils/col_koki.dat deleted file mode 100755 index 1b22c9d39f1893aef283867e94b9a5ab7ee78390..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@mD&i`Przuuk!8~6_g4Kp{Lc?w~Ee0=r9(xx~YNfCZdR;=0}K*l1p zuy@t5NB{qSd;8?}ZDtY1EFYEN@@QD#E8zniUzwz8BU2SZP9U;l)OlO|7@I&J!l znX_iknLBU(f`y9~FIl>5`HGdRRKIRx^4T8ox67L*}HH5frEz*A31vL z_=%IJPM=S6+fWcx&6lTn`mg|3kcFoHp|e znEUqj$?eNplARRMJ(R{y1R%{{{p&{Qn<vw=Za&C(~KxNYXzExQk&x^(m5ix1zC)C`dSGl2lR{{c04 B)}jCa diff --git a/apps/abs/utils/contributors.txt b/apps/abs/utils/contributors.txt deleted file mode 100644 index 537363c..0000000 --- a/apps/abs/utils/contributors.txt +++ /dev/null @@ -1 +0,0 @@ -Edwin Christian Yllanes Cucho diff --git a/apps/abs/utils/data/col_koki.dat b/apps/abs/utils/data/col_koki.dat deleted file mode 100755 index 1b22c9d39f1893aef283867e94b9a5ab7ee78390..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@mD&i`Przuuk!8~6_g4Kp{Lc?w~Ee0=r9(xx~YNfCZdR;=0}K*l1p zuy@t5NB{qSd;8?}ZDtY1EFYEN@@QD#E8zniUzwz8BU2SZP9U;l)OlO|7@I&J!l znX_iknLBU(f`y9~FIl>5`HGdRRKIRx^4T8ox67L*}HH5frEz*A31vL z_=%IJPM=S6+fWcx&6lTn`mg|3kcFoHp|e znEUqj$?eNplARRMJ(R{y1R%{{{p&{Qn<vw=Za&C(~KxNYXzExQk&x^(m5ix1zC)C`dSGl2lR{{c04 B)}jCa diff --git a/apps/abs/utils/gaussfit.py b/apps/abs/utils/gaussfit.py deleted file mode 100644 index 6a2776f..0000000 --- a/apps/abs/utils/gaussfit.py +++ /dev/null @@ -1,34 +0,0 @@ -from numpy import * -from scipy import optimize - -def gaussian(height, center_x, center_y, width_x, width_y): - """Returns a gaussian function with the given parameters""" - width_x = float(width_x) - width_y = float(width_y) - return lambda x,y: height*exp( - -(((center_x-x)/width_x)**2+((center_y-y)/width_y)**2)/2) - -def moments(data): - """Returns (height, x, y, width_x, width_y) - the gaussian parameters of a 2D distribution by calculating its - moments """ - total = data.sum() - X, Y = indices(data.shape) - x = (X*data).sum()/total - y = (Y*data).sum()/total - col = data[:, int(y)] - width_x = sqrt(abs((arange(col.size)-y)**2*col).sum()/col.sum()) - row = data[int(x), :] - width_y = sqrt(abs((arange(row.size)-x)**2*row).sum()/row.sum()) - height = data.max() - return height, x, y, width_x, width_y - -def fitgaussian(data): - """Returns (height, x, y, width_x, width_y) - the gaussian parameters of a 2D distribution found by a fit""" - params = moments(data) - errorfunction = lambda p: ravel(gaussian(*p)(*indices(data.shape)) - - data) - p, success = optimize.leastsq(errorfunction, params) - return p - diff --git a/apps/abs/utils/manage.py b/apps/abs/utils/manage.py deleted file mode 100644 index 93103d8..0000000 --- a/apps/abs/utils/manage.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "radarsys.settings") - - from django.core.management import execute_from_command_line - - execute_from_command_line(sys.argv) diff --git a/apps/abs/utils/overJroShow.py b/apps/abs/utils/overJroShow.py deleted file mode 100644 index 7fcaa7a..0000000 --- a/apps/abs/utils/overJroShow.py +++ /dev/null @@ -1,1775 +0,0 @@ -#!/usr/bin/python -import sys, os, os.path -import traceback -import cgi -from http import cookies - -import time, datetime -import types -import numpy -import numpy.fft -import scipy.linalg -import scipy.special -from io import StringIO - - -#import Misc_Routines -from .Misc_Routines import CoFactors -#import TimeTools -from .TimeTools import Time , Julian ,Doy2Date -#import JroAntSetup -from .JroAntSetup import ReturnSetup -#import Graphics_OverJro -from .Graphics_OverJro import AntPatternPlot ,BFieldPlot,CelestialObjectsPlot,PatternCutPlot,SkyNoisePlot -#import Astro_Coords -from .Astro_Coords import Geodetic ,AltAz ,CelestialBodies - -class JroPattern(): - def __init__(self,pattern=0,path=None,filename=None,nptsx=101,nptsy=101,maxphi=5,fftopt=0, \ - getcut=0,dcosx=None,dcosy=None,eomwl=6,airwl=4, **kwargs): - """ - JroPattern class creates an object to represent the useful parameters for beam mode- - lling of the Jicamarca VHF radar. - - Parameters - ---------- - pattern = An integer (See JroAntSetup to know the available values) to load a prede- - fined configuration. The default value is 0. To use a user-defined configuration - pattern must be None. - path = A string giving the directory that contains the user-configuration file. PATH - will work if pattern is None. - filename = A string giving the name of the user-configuration file. FILENAME will - work if pattern is None. - nptsx = A scalar to specify the number of points used to define the angular resolu- - tion in the "x" axis. The default value is 101. - nptsy = A scalar to specify the number of points used to define the angular resolu- - tion in the "x" axis. The default value is 101. - maxphi = A scalar giving the maximum (absolute) angle (in degree) to model the ante- - nna pattern. The default value is 5 degrees. - fftopt = Set this input to 1 to model the beam using FFT. To model using antenna - theory set to 0 (default value). - getcut = Set to 1 to show an antenna cut instead of a contour plot of itself (set to - 0). The defautl value is 0. - dcosx = An array giving the directional cosines for the x-axis. DCOSX will work if - getcut is actived. - dcosy = An array giving the directional cosines for the y-axis. DCOSY will work if - getcut is actived. - eomwl = A scalar giving the radar wavelength. The default value is 6m (50 MHZ). - airwl = Set this input to float (or intger) to specify the wavelength (in meters) of - the transmitted EOM wave in the air. The default value is 4m. - - Modification History - -------------------- - Converted to Object-oriented Programming by Freddy Galindo, ROJ, 20 September 2009. - """ - - - - # Getting antenna configuration. - if filename: - setup = ReturnSetup(path=path,filename=filename,pattern=pattern) - - ues = setup["ues"] - phase = setup["phase"] - gaintx = setup["gaintx"] - gainrx = setup["gainrx"] - justrx = setup["justrx"] - self.title = setup["title"] - else: - ues = kwargs["ues"] - phase = kwargs["phases"] - gaintx = kwargs["gain_tx"] - gainrx = kwargs["gain_rx"] - justrx = kwargs["just_rx"] - self.title = kwargs.get("title", "JRO Pattern") - - # Defining attributes for JroPattern class. - # Antenna configuration - - self.uestx = ues - self.phasetx = phase - self.gaintx = gaintx - self.uesrx = ues - self.phaserx = phase - self.gainrx = gainrx - self.justrx = justrx - - # Pattern resolution & method to model - self.maxphi = maxphi - self.nptsx = nptsx - self.nptsy = nptsy - self.fftopt = fftopt - - # To get a cut of the pattern. - self.getcut = getcut - - maxdcos = numpy.sin(maxphi*CoFactors.d2r) - if dcosx==None:dcosx = ((numpy.arange(nptsx,dtype=float)/(nptsx-1))-0.5)*2*maxdcos - if dcosy==None:dcosy = ((numpy.arange(nptsy,dtype=float)/(nptsy-1))-0.5)*2*maxdcos - self.dcosx = dcosx - self.dcosy = dcosy - self.nx = dcosx.size - self.ny = dcosy.size*(getcut==0) + (getcut==1) - - self.eomwl = eomwl - self.airwl = airwl - - self.kk = 2.*numpy.pi/eomwl - - self.pattern = None - self.meanpos = None - self.norpattern = None - self.maxpattern = None - - - - self.getPattern() - - def getPattern(self): - """ - getpattern method returns the modeled total antenna pattern and its mean position. - - Return - ------ - pattern = An array giving the Modelled antenna pattern. - mean_pos = A 2-elements array giving the mean position of the main beam. - - Examples - -------- - >> [pattern, mean_pos] = JroPattern(pattern=2).getPattern() - >> print meanpos - [ 8.08728085e-14 -4.78193873e-14] - - Modification history - -------------------- - Developed by Jorge L. Chau. - Converted to Python by Freddy R. Galindo, ROJ, 20 September 2009. - """ - - if (self.fftopt>0) and (self.getcut>0): - #print "Conflict bewteen fftopt and getcut" - #print "To get a cut of the antenna pattern uses ffopt=0" - return None, None - - if (self.fftopt==0): - # Getting antenna pattern using the array method - self.pattern = self.__usingArray(rx=1) - if (self.justrx==0):self.pattern = self.pattern*self.__usingArray(rx=0) - - elif (self.fftopt>0): - # Getting antenna pattern using FFT method - self.pattern = self.__usingFFT(rx=1) - if (self.justrx==0):self.pattern = self.pattern*self.__usingFFT(rx=0) - - self.maxpattern = numpy.nanmax(self.pattern) - self.norpattern = self.pattern/self.maxpattern - if self.getcut==0:self.__getBeamPars() - - def __usingArray(self,rx): - """ - __usingArray method returns the Jicamarca antenna pattern computed using array model - - pattern = dipolepattern x modulepattern - - Parameters - ---------- - rx = Set to 1 to use the Rx information. Otherwise set to 0 for Tx. - - Return - ------ - pattern = An array giving the modelled antenna pattern using the array model. - - Modification history - -------------------- - Developed by Jorge L. Chau. - Converted to Python by Freddy R. Galindo, ROJ, 20 September 2009. - """ - - if rx==1: - ues = self.uesrx - phase = self.phaserx - gain = self.gainrx - elif rx==0: - ues = self.uestx - phase = self.phasetx - gain = self.gaintx - - ues = ues*360./self.airwl - phase = phase*360./self.airwl - - for ii in range(4): - if ii==0:dim = numpy.array([4,0,8,4]) # WEST - elif ii==1:dim = numpy.array([0,0,4,4]) # NORTH - elif ii==2:dim = numpy.array([0,4,4,8]) # EAST - elif ii==3:dim = numpy.array([4,4,8,8]) # SOUTH - xi = dim[0]; xf = dim[2]; yi = dim[1]; yf = dim[3] - phase[xi:xf,yi:yf] = phase[xi:xf,yi:yf] + ues[ii] - - phase = -phase - - ar = self.eomwl*numpy.array([[0.5,6., 24.5],[0.5,6.,24.5]]) - nr = numpy.array([[12.,4.,2.],[12.,4.,2.]]) - lr = 0.25*self.eomwl*numpy.array([[0,0.,0],[0.,0,0]]) - - # Computing module and dipole patterns. - pattern = (numpy.abs(self.__dipPattern(ar,nr,lr)*self.__modPattern(phase,gain)))**2 - - return pattern - - def __usingFFT(self,rx): - """ - __usingFFT method returns the Jicamarca antenna pattern computed using The Fast Fou- - rier Transform. - - pattern = iFFT(FFT(gain*EXP(j*phase))) - - Parameters - ---------- - rx = Set to 1 to use the Rx information. Otherwise set to 0 for Tx. - - Return - ------ - pattern = An array giving the modelled antenna pattern using the array model. - - Modification history - -------------------- - Developed by Jorge L. Chau. - Converted to Python by Freddy R. Galindo, ROJ, 20 September 2009. - """ - - if rx==1: - ues = self.uesrx - phase = self.phaserx - gain = self.gainrx - elif rx==0: - ues = self.uestx - phase = self.phasetx - gain = self.gaintx - - ues = ues*360./self.airwl - phase = phase*360./self.airwl - - for ii in range(4): - if ii==0:dim = numpy.array([4,0,8,4]) # WEST - elif ii==1:dim = numpy.array([0,0,4,4]) # NORTH - elif ii==2:dim = numpy.array([0,4,4,8]) # EAST - elif ii==3:dim = numpy.array([4,4,8,8]) # SOUTH - xi = dim[0]; xf = dim[2]; yi = dim[1]; yf = dim[3] - phase[xi:xf,yi:yf] = phase[xi:xf,yi:yf] + ues[ii] - - phase = -phase - - delta_x = self.eomwl/2. - delta_y = self.eomwl/2. - - nxfft = 2048 - nyfft = 2048 - dcosx = (numpy.arange(nxfft) - (0.5*nxfft))/(nxfft*delta_x)*self.eomwl - dcosy = (numpy.arange(nyfft) - (0.5*nyfft))/(nyfft*delta_y)*self.eomwl - - fft_gain = numpy.zeros((nxfft,nyfft)) - fft_phase = numpy.zeros((nxfft,nyfft)) - - nx = 8 - ny = 8 - ndx =12 - ndy =12 - for iy in numpy.arange(ny): - for ix in numpy.arange(nx): - ix1 = nxfft/2-self.nx/2*ndx+ix*ndx - if ix<(nx/2):ix1 = ix1 - 1 - if ix>=(nx/2):ix1 = ix1 + 1 - - iy1 = nyfft/2-ny/2*ndx+iy*ndy - if iy<(ny/2):iy1 = iy1 - 1 - if iy>=(ny/2):iy1 = iy1 + 1 - - fft_gain[ix1:ix1+ndx-1,iy1:iy1+ndy-1] = gain[ix,ny-1-iy] - fft_phase[ix1:ix1+ndx-1,iy1:iy1+ndy-1] = phase[ix,ny-1-iy] - - - fft_phase = fft_phase*CoFactors.d2r - - pattern = numpy.abs(numpy.fft.fft2(fft_gain*numpy.exp(numpy.complex(0,1)*fft_phase)))**2 - pattern = numpy.fft.fftshift(pattern) - - xvals = numpy.where((dcosx>=(numpy.min(self.dcosx))) & (dcosx<=(numpy.max(self.dcosx)))) - yvals = numpy.where((dcosy>=(numpy.min(self.dcosy))) & (dcosy<=(numpy.max(self.dcosy)))) - - pattern = pattern[xvals[0][0]:xvals[0][-1],yvals[0][0]:yvals[0][-1]] - - return pattern - - def __readAttenuation(self): - """ - _readAttenuation reads the attenuations' file and returns an array giving these va- - lues (dB). The ext file must be in the directory "resource". - - Return - ------ - attenuation = An array giving attenuation values read from the text file. - - Modification history - -------------------- - Developed by Jorge L. Chau. - Converted to Python by Freddy R. Galindo, ROJ, 20 September 2009. - """ - -# attenuation = None -# # foldr = sys.path[-1] + os.sep + "resource" + os.sep -# base_path = os.path.dirname(os.path.abspath(__file__)) -# #foldr = './resource' -# #filen = "attenuation.txt" -# attenuationFile = os.path.join(base_path,"resource","attenuation.txt") -# #ff = open(os.path.join(foldr,filen),'r') -# ff = open(attenuationFile,'r') -# print(ff.read()) -# exec(ff.read()) -# ff.close() - attenuation = numpy.array([[[-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25]], - [[21.25,21.25,21.25,21.25,21.25,21.25,21.25,21.25], - [15.25,15.25,15.25,15.25,15.25,15.25,15.25,15.25], - [09.25,09.25,09.25,09.25,09.25,09.25,09.25,09.25], - [03.25,03.25,03.25,03.25,03.25,03.25,03.25,03.25], - [-03.25,-03.25,-03.25,-03.25,-03.25,-03.25,-03.25,-03.25], - [-09.25,-09.25,-09.25,-09.25,-09.25,-09.25,-09.25,-09.25], - [-15.25,-15.25,-15.25,-15.25,-15.25,-15.25,-15.25,-15.25], - [-21.25,-21.25,-21.25,-21.25,-21.25,-21.25,-21.25,-21.25]]]) - - return attenuation - - def __dipPattern(self,ar,nr,lr): - """ - _dipPattern function computes the dipole's pattern to the Jicamarca radar. The next - equation defines the pattern as a function of the mainlobe direction: - - sincx = SIN(k/2*n0x*(a0x*SIN(phi)*COS(alpha)))/SIN(k/2*(a0x*SIN(phi)*COS(alpha))) - sincy = SIN(k/2*n0y*(a0y*SIN(phi)*SIN(alpha)))/SIN(k/2*(a0y*SIN(phi)*SIN(alpha))) - A0(phi,alpha) = sincx*sincy - Parameters - ---------- - ar = ? - nr = ? - lr = ? - - Return - ------ - dipole = An array giving antenna pattern from the dipole point of view.. - - Modification history - -------------------- - Developed by Jorge L. Chau. - Converted to Python by Freddy R. Galindo, ROJ, 20 September 2009. - """ - - dipole = numpy.zeros((self.nx,self.ny),dtype=complex) - for iy in range(self.ny): - for ix in range(self.nx): - yindex = iy*(self.getcut==0) + ix*(self.getcut==1) - - argx = ar[0,0]*self.dcosx[ix] - lr[0,0] - if argx == 0.0: - junkx = nr[0,0] - else: - junkx = numpy.sin(0.5*self.kk*nr[0,0]*argx)/numpy.sin(0.5*self.kk*argx) - - - argy = ar[1,0]*self.dcosy[yindex] - lr[1,0] - if argy == 0.0: - junky = nr[1,0] - else: - junky = numpy.sin(0.5*self.kk*nr[1,0]*argy)/numpy.sin(0.5*self.kk*argy) - - - dipole[ix,iy] = junkx*junky - - return dipole - - def __modPattern(self,phase,gain): - """ - ModPattern computes the module's pattern to the Jicamarca radar. The next equation - defines the pattern as a function mainlobe direction: - - phasex = pos(x)*SIN(phi)*COS(alpha) - phasey = pos(y)*SIN(phi)*SIN(alpha) - - A1(phi,alpha) = TOTAL(gain*EXP(COMPLEX(0,k*(phasex+phasey)+phase))) - - Parameters - ---------- - phase = Bidimensional array (8x8) giving the phase (in meters) of each module. - gain = Bidimensional array (8x8) giving to define modules will be active (ones) - and which will not (zeros). - - Return - ------ - module = An array giving antenna pattern from the module point of view.. - - Modification history - -------------------- - Developed by Jorge L. Chau. - Converted to Python by Freddy R. Galindo, ROJ, 20 September 2009. - """ - - pos = self.eomwl*self.__readAttenuation() - posx = pos[0,:,:] - posy = pos[1,:,:] - - phase = phase*CoFactors.d2r - module = numpy.zeros((self.nx,self.ny),dtype=complex) - for iy in range(self.ny): - for ix in range(self.nx): - yindex = iy*(self.getcut==0) + ix*(self.getcut==1) - phasex = posx*self.dcosx[ix] - phasey = posy*self.dcosy[yindex] - tmp = gain*numpy.exp(numpy.complex(0,1.)*(self.kk*(phasex+phasey)+phase)) - module[ix,iy] = tmp.sum() - - return module - - def __getBeamPars(self): - """ - _getBeamPars computes the main-beam parameters of the antenna. - - Modification history - -------------------- - Developed by Jorge L. Chau. - Converted to Python by Freddy R. Galindo, ROJ, 20 September 2009. - """ - - dx = self.dcosx[1] - self.dcosx[0] - dy = self.dcosy[1] - self.dcosy[0] - - amp = self.norpattern - - xx = numpy.resize(self.dcosx,(self.nx,self.nx)).transpose() - yy = numpy.resize(self.dcosy,(self.ny,self.ny)) - - mm0 = amp[numpy.where(amp > 0.5)] - xx0 = xx[numpy.where(amp > 0.5)] - yy0 = yy[numpy.where(amp > 0.5)] - - xc = numpy.sum(mm0*xx0)/numpy.sum(mm0) - yc = numpy.sum(mm0*yy0)/numpy.sum(mm0) - rc = numpy.sqrt(mm0.size*dx*dy/numpy.pi) - - nnx = numpy.where(numpy.abs(self.dcosx - xc) < rc) - nny = numpy.where(numpy.abs(self.dcosy - yc) < rc) - - mm1 = amp[numpy.min(nnx):numpy.max(nnx)+1,numpy.min(nny):numpy.max(nny)+1] - xx1 = self.dcosx[numpy.min(nnx):numpy.max(nnx)+1] - yy1 = self.dcosy[numpy.min(nny):numpy.max(nny)+1] - - # fitting data into the main beam. - import gaussfit - params = gaussfit.fitgaussian(mm1) - - # Tranforming from indexes to axis' values - xcenter = xx1[0] + (((xx1[xx1.size-1] - xx1[0])/(xx1.size -1))*(params[1])) - ycenter = yy1[0] + (((yy1[yy1.size-1] - yy1[0])/(yy1.size -1))*(params[2])) - xwidth = ((xx1[xx1.size-1] - xx1[0])/(xx1.size-1))*(params[3])*(1/CoFactors.d2r) - ywidth = ((yy1[yy1.size-1] - yy1[0])/(yy1.size-1))*(params[4])*(1/CoFactors.d2r) - meanwx = (xwidth*ywidth) - meanpos = numpy.array([xcenter,ycenter]) - - #print 'Position: %f %f' %(xcenter,ycenter) - #print 'Widths: %f %f' %(xwidth, ywidth) - #print 'BWHP: %f' %(2*numpy.sqrt(2*meanwx)*numpy.sqrt(-numpy.log(0.5))) - - self.meanpos = meanpos - - -class BField(): - def __init__(self,year=None,doy=None,site=1,heights=None,alpha_i=90): - """ - BField class creates an object to get the Magnetic field for a specific date and - height(s). - - Parameters - ---------- - year = A scalar giving the desired year. If the value is None (default value) then - the current year will be used. - doy = A scalar giving the desired day of the year. If the value is None (default va- - lue) then the current doy will be used. - site = An integer to choose the geographic coordinates of the place where the magne- - tic field will be computed. The default value is over Jicamarca (site=1) - heights = An array giving the heights (km) where the magnetic field will be modeled By default the magnetic field will be computed at 100, 500 and 1000km. - alpha_i = Angle to interpolate the magnetic field. - - Modification History - -------------------- - Converted to Object-oriented Programming by Freddy Galindo, ROJ, 07 October 2009. - """ - - tmp = time.localtime() - if year==None: year = tmp[0] - if doy==None: doy = tmp[7] - self.year = year - self.doy = doy - self.site = site - if heights==None:heights = numpy.array([100,500,1000]) - self.heights = heights - self.alpha_i = alpha_i - - def getBField(self,maglimits=numpy.array([-7,-7,7,7])): - """ - getBField models the magnetic field for a different heights in a specific date. - - Parameters - ---------- - maglimits = An 4-elements array giving ..... The default value is [-7,-7,7,7]. - - Return - ------ - dcos = An 4-dimensional array giving the directional cosines of the magnetic field - over the desired place. - alpha = An 3-dimensional array giving the angle of the magnetic field over the desi- - red place. - - Modification History - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 07 October 2009. - """ - - x_ant = numpy.array([1,0,0]) - y_ant = numpy.array([0,1,0]) - z_ant = numpy.array([0,0,1]) - - if self.site==0: - title_site = "Magnetic equator" - coord_site = numpy.array([-76+52./60.,-11+57/60.,0.5]) - elif self.site==1: - title_site = 'Jicamarca' - coord_site = [-76-52./60.,-11-57/60.,0.5] - theta = (45+5.35)*numpy.pi/180. # (50.35 and 1.46 from Fleish Thesis) - delta = -1.46*numpy.pi/180 - - x_ant1 = numpy.roll(self.rotvector(self.rotvector(x_ant,1,delta),3,theta),1) - y_ant1 = numpy.roll(self.rotvector(self.rotvector(y_ant,1,delta),3,theta),1) - z_ant1 = numpy.roll(self.rotvector(self.rotvector(z_ant,1,delta),3,theta),1) - - ang0 = -1*coord_site[0]*numpy.pi/180. - ang1 = coord_site[1]*numpy.pi/180. - x_ant = self.rotvector(self.rotvector(x_ant1,2,ang1),3,ang0) - y_ant = self.rotvector(self.rotvector(y_ant1,2,ang1),3,ang0) - z_ant = self.rotvector(self.rotvector(z_ant1,2,ang1),3,ang0) - else: -# print "No defined Site. Skip..." - return None - - nhei = self.heights.size - pt_intercep = numpy.zeros((nhei,2)) - nfields = 1 - - grid_res = 0.5 - nlon = int(numpy.int(maglimits[2] - maglimits[0])/grid_res + 1) - nlat = int(numpy.int(maglimits[3] - maglimits[1])/grid_res + 1) - - location = numpy.zeros((nlon,nlat,2)) - mlon = numpy.atleast_2d(numpy.arange(nlon)*grid_res + maglimits[0]) - mrep = numpy.atleast_2d(numpy.zeros(nlat) + 1) - location0 = numpy.dot(mlon.transpose(),mrep) - - mlat = numpy.atleast_2d(numpy.arange(nlat)*grid_res + maglimits[1]) - mrep = numpy.atleast_2d(numpy.zeros(nlon) + 1) - location1 = numpy.dot(mrep.transpose(),mlat) - - location[:,:,0] = location0 - location[:,:,1] = location1 - - alpha = numpy.zeros((nlon,nlat,nhei)) - rr = numpy.zeros((nlon,nlat,nhei,3)) - dcos = numpy.zeros((nlon,nlat,nhei,2)) - - global first_time - - first_time = None - for ilon in numpy.arange(nlon): - for ilat in numpy.arange(nlat): - outs = self.__bdotk(self.heights, - self.year + self.doy/366., - coord_site[1], - coord_site[0], - coord_site[2], - coord_site[1]+location[ilon,ilat,1], - location[ilon,ilat,0]*720./180.) - - alpha[ilon, ilat,:] = outs[1] - rr[ilon, ilat,:,:] = outs[3] - - mrep = numpy.atleast_2d((numpy.zeros(nhei)+1)).transpose() - tmp = outs[3]*numpy.dot(mrep,numpy.atleast_2d(x_ant)) - tmp = tmp.sum(axis=1) - dcos[ilon,ilat,:,0] = tmp/numpy.sqrt((outs[3]**2).sum(axis=1)) - - mrep = numpy.atleast_2d((numpy.zeros(nhei)+1)).transpose() - tmp = outs[3]*numpy.dot(mrep,numpy.atleast_2d(y_ant)) - tmp = tmp.sum(axis=1) - dcos[ilon,ilat,:,1] = tmp/numpy.sqrt((outs[3]**2).sum(axis=1)) - - return dcos, alpha, nlon, nlat - - - def __bdotk(self,heights,tm,gdlat=-11.95,gdlon=-76.8667,gdalt=0.0,decd=-12.88, ham=-4.61666667): - - global first_time - # Mean Earth radius in Km WGS 84 - a_igrf = 6371.2 - - bk = numpy.zeros(heights.size) - alpha = numpy.zeros(heights.size) - bfm = numpy.zeros(heights.size) - rr = numpy.zeros((heights.size,3)) - rgc = numpy.zeros((heights.size,3)) - - ObjGeodetic = Astro_Coords.Geodetic(gdlat,gdalt) - [gclat,gcalt] = ObjGeodetic.change2geocentric() - - gclat = gclat*numpy.pi/180. - gclon = gdlon*numpy.pi/180. - - # Antenna position from center of Earth - ca_vector = [numpy.cos(gclat)*numpy.cos(gclon),numpy.cos(gclat)*numpy.sin(gclon),numpy.sin(gclat)] - ca_vector = gcalt*numpy.array(ca_vector) - - dec = decd*numpy.pi/180. - - # K vector respect to the center of earth. - klon = gclon + ham*numpy.pi/720. - k_vector = [numpy.cos(dec)*numpy.cos(klon),numpy.cos(dec)*numpy.sin(klon),numpy.sin(dec)] - k_vector = numpy.array(k_vector) - - for ih in numpy.arange(heights.size): - # Vector from Earth's center to volume of interest - rr[ih,:] = k_vector*heights[ih] - cv_vector = numpy.squeeze(ca_vector) + rr[ih,:] - - cv_gcalt = numpy.sqrt(numpy.sum(cv_vector**2.)) - cvxy = numpy.sqrt(numpy.sum(cv_vector[0:2]**2.)) - - radial = cv_vector/cv_gcalt - east = numpy.array([-1*cv_vector[1],cv_vector[0],0])/cvxy - comp1 = east[1]*radial[2] - radial[1]*east[2] - comp2 = east[2]*radial[0] - radial[2]*east[0] - comp3 = east[0]*radial[1] - radial[0]*east[1] - north = -1*numpy.array([comp1, comp2, comp3]) - - rr_k = cv_vector - numpy.squeeze(ca_vector) - u_rr = rr_k/numpy.sqrt(numpy.sum(rr_k**2.)) - - cv_gclat = numpy.arctan2(cv_vector[2],cvxy) - cv_gclon = numpy.arctan2(cv_vector[1],cv_vector[0]) - - bhei = cv_gcalt-a_igrf - blat = cv_gclat*180./numpy.pi - blon = cv_gclon*180./numpy.pi - bfield = self.__igrfkudeki(bhei,tm,blat,blon) - - B = (bfield[0]*north + bfield[1]*east - bfield[2]*radial)*1.0e-5 - - bfm[ih] = numpy.sqrt(numpy.sum(B**2.)) #module - bk[ih] = numpy.sum(u_rr*B) - alpha[ih] = numpy.arccos(bk[ih]/bfm[ih])*180/numpy.pi - rgc[ih,:] = numpy.array([cv_gclon, cv_gclat, cv_gcalt]) - - return bk, alpha, bfm, rr, rgc - - - def __igrfkudeki(self,heights,time,latitude,longitude,ae=6371.2): - """ - __igrfkudeki calculates the International Geomagnetic Reference Field for given in- - put conditions based on IGRF2005 coefficients. - - Parameters - ---------- - heights = Scalar or vector giving the height above the Earth of the point in ques- - tion in kilometers. - time = Scalar or vector giving the decimal year of time in question (e.g. 1991.2). - latitude = Latitude of point in question in decimal degrees. Scalar or vector. - longitude = Longitude of point in question in decimal degrees. Scalar or vector. - ae = - first_time = - - Return - ------ - bn = - be = - bd = - bmod = - balpha = - first_time = - - Modification History - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 03 October 2009. - """ - - global first_time - global gs, hs, nvec, mvec, maxcoef - - heights = numpy.atleast_1d(heights) - time = numpy.atleast_1d(time) - latitude = numpy.atleast_1d(latitude) - longitude = numpy.atleast_1d(longitude) - - if numpy.max(latitude)==90: -# print "Field calculations are not supported at geographic poles" - pass - - # output arrays - bn = numpy.zeros(heights.size) - be = numpy.zeros(heights.size) - bd = numpy.zeros(heights.size) - - if first_time==None:first_time=0 - - time0 = time[0] - if time!=first_time: - #print "Getting coefficients for", time0 - [periods,g,h ] = self.__readIGRFcoeff() - top_year = numpy.max(periods) - nperiod = (top_year - 1900)/5 + 1 - - maxcoef = 10 - if time0>=2000:maxcoef = 12 - - - # Normalization array for Schmidt fucntions - multer = numpy.zeros((2+maxcoef,1+maxcoef)) + 1 - for cn in (numpy.arange(maxcoef)+1): - for rm in (numpy.arange(cn)+1): - tmp = numpy.arange(2*rm) + cn - rm + 1. - multer[rm+1,cn] = ((-1.)**rm)*numpy.sqrt(2./tmp.prod()) - - schmidt = multer[1:,1:].transpose() - - # n and m arrays - nvec = numpy.atleast_2d(numpy.arange(maxcoef)+2) - mvec = numpy.atleast_2d(numpy.arange(maxcoef+1)).transpose() - - # Time adjusted igrf g and h with Schmidt normalization - # IGRF coefficient arrays: g0(n,m), n=1, maxcoeff,m=0, maxcoeff, ... - if time0 top_year - dtime = (time0 - top_year) + 5 - ntime = g[:,0,0].size - 2 - - g0 = g[ntime,1:maxcoef+1,:maxcoef+1] - h0 = h[ntime,1:maxcoef+1,:maxcoef+1] - gdot = g[ntime+1,1:maxcoef+1,:maxcoef+1]-g[ntime,1:maxcoef+1,:maxcoef+1] - hdot = h[ntime+1,1:maxcoef+1,:maxcoef+1]-h[ntime,1:maxcoef+1,:maxcoef+1] - gs = (g0 + dtime*(gdot/5.))*schmidt[:maxcoef,0:maxcoef+1] - hs = (h0 + dtime*(hdot/5.))*schmidt[:maxcoef,0:maxcoef+1] - - first_time = time0 - - for ii in numpy.arange(heights.size): - # Height dependence array rad = (ae/(ae+height))**(n+3) - rad = numpy.atleast_2d((ae/(ae + heights[ii]))**(nvec+1)) - - # Sin and Cos of m times longitude phi arrays - mphi = mvec*longitude[ii]*numpy.pi/180. - cosmphi = numpy.atleast_2d(numpy.cos(mphi)) - sinmphi = numpy.atleast_2d(numpy.sin(mphi)) - - # Cos of colatitude theta - c = numpy.cos((90 - latitude[ii])*numpy.pi/180.) - - # Legendre functions p(n,m|c) - [p,dp]= scipy.special.lpmn(maxcoef+1,maxcoef+1,c) - p = p[:,:-1].transpose() - s = numpy.sqrt((1. - c)*(1 + c)) - - # Generate derivative array dpdtheta = -s*dpdc - dpdtheta = c*p/s - for m in numpy.arange(maxcoef+2): dpdtheta[:,m] = m*dpdtheta[:,m] - dpdtheta = dpdtheta + numpy.roll(p,-1,axis=1) - - # Extracting arrays required for field calculations - p = p[1:maxcoef+1,:maxcoef+1] - dpdtheta = dpdtheta[1:maxcoef+1,:maxcoef+1] - - # Weigh p and dpdtheta with gs and hs coefficients. - gp = gs*p - hp = hs*p - gdpdtheta = gs*dpdtheta - hdpdtheta = hs*dpdtheta - # Calcultate field components - matrix0 = numpy.dot(gdpdtheta,cosmphi) - matrix1 = numpy.dot(hdpdtheta,sinmphi) - bn[ii] = numpy.dot(rad,(matrix0 + matrix1)) - matrix0 = numpy.dot(hp,(mvec*cosmphi)) - matrix1 = numpy.dot(gp,(mvec*sinmphi)) - be[ii] = numpy.dot((-1*rad),((matrix0 - matrix1)/s)) - matrix0 = numpy.dot(gp,cosmphi) - matrix1 = numpy.dot(hp,sinmphi) - bd[ii] = numpy.dot((-1*nvec*rad),(matrix0 + matrix1)) - - bmod = numpy.sqrt(bn**2. + be**2. + bd**2.) - btheta = numpy.arctan(bd/numpy.sqrt(be**2. + bn**2.))*180/numpy.pi - balpha = numpy.arctan(be/bn)*180./numpy.pi - - #bn : north - #be : east - #bn : radial - #bmod : module - - - return bn, be, bd, bmod, btheta, balpha - - def str2num(self, datum): - try: - return int(datum) - except: - try: - return float(datum) - except: - return datum - - def __readIGRFfile(self, filename): - list_years=[] - for i in range(1,24): - list_years.append(1895.0 + i*5) - - epochs=list_years - epochs.append(epochs[-1]+5) - nepochs = numpy.shape(epochs) - - gg = numpy.zeros((13,14,nepochs[0]),dtype=float) - hh = numpy.zeros((13,14,nepochs[0]),dtype=float) - - coeffs_file=open(filename) - lines=coeffs_file.readlines() - - coeffs_file.close() - - for line in lines: - items = line.split() - g_h = items[0] - n = self.str2num(items[1]) - m = self.str2num(items[2]) - - coeffs = items[3:] - - for i in range(len(coeffs)-1): - coeffs[i] = self.str2num(coeffs[i]) - - #coeffs = numpy.array(coeffs) - ncoeffs = numpy.shape(coeffs)[0] - - if g_h == 'g': - # print n," g ",m - gg[n-1,m,:]=coeffs - elif g_h=='h': - # print n," h ",m - hh[n-1,m,:]=coeffs - # else : - # continue - - # Ultimo Reordenamiento para almacenar . - gg[:,:,nepochs[0]-1] = gg[:,:,nepochs[0]-2] + 5*gg[:,:,nepochs[0]-1] - hh[:,:,nepochs[0]-1] = hh[:,:,nepochs[0]-2] + 5*hh[:,:,nepochs[0]-1] - -# return numpy.array([gg,hh]) - periods = numpy.array(epochs) - g = gg - h = hh - return periods, g, h - - - def __readIGRFcoeff(self,filename="igrf10coeffs.dat"): - """ - __readIGRFcoeff reads the coefficients from a binary file which is located in the - folder "resource." - - Parameter - --------- - filename = A string to specify the name of the file which contains thec coeffs. The - default value is "igrf10coeffs.dat" - - Return - ------ - periods = A lineal array giving... - g1 = - h1 = - - Modification History - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 03 October 2009. - """ - -# # igrfile = sys.path[-1] + os.sep + "resource" + os.sep + filename -# igrfile = os.path.join('./resource',filename) -# f = open(igrfile,'rb') -# #f = open(os.getcwd() + os.sep + "resource" + os.sep + filename,'rb') -# -# # Reading SkyNoise Power (lineal scale) -# periods = numpy.fromfile(f,numpy.dtype([('var',' Around "x" - axis = 2 -> Around "y" - axis = 3 -> Around "z" - ang = Angle of rotation (in radians). The default value is zero. - - Return - ------ - rotvector = A lineal array of 3 elements giving the new coordinates. - - Modification History - -------------------- - Converted to Python by Freddy R. Galindo, ROJ, 01 October 2009. - """ - - if axis==1: - t = [[1,0,0],[0,numpy.cos(ang),numpy.sin(ang)],[0,-numpy.sin(ang),numpy.cos(ang)]] - elif axis==2: - t = [[numpy.cos(ang),0,-numpy.sin(ang)],[0,1,0],[numpy.sin(ang),0,numpy.cos(ang)]] - elif axis==3: - t = [[numpy.cos(ang),numpy.sin(ang),0],[-numpy.sin(ang),numpy.cos(ang),0],[0,0,1]] - - rotvector = numpy.array(numpy.dot(numpy.array(t),numpy.array(vector))) - - return rotvector - - -class overJroShow: - -# __serverdocspath = '/usr/local/www/htdocs' -# __tmpDir = 'overJro/tempReports' -# __serverdocspath = '/Users/dsuarez/Pictures' -# __tmpDir = 'overjro' - __serverdocspath = '' - __tmpDir = '' - - def __init__(self, title=''): - self.year = None - self.month = None - self.dom = None - self.pattern = None - self.maxphi = None - self.heights = None - self.filename = None - self.showType = None - self.path = None - self.objects = None - self.nptsx = 101 - self.nptsy = 101 - self.fftopt = 0 - self.site = 1 - self.dcosx = 1 - self.dcosy = 1 - self.dcosxrange = None - self.dcosyrange = None - self.maxha_min= 0. - self.show_object = None - self.dcosx_mag = None - self.dcosy_mag = None - self.ha_mag = None - self.time_mag = None - self.main_dec = None - self.ObjC = None - self.ptitle = title - self.path4plotname = None - self.plotname0 = None - self.plotname1 = None - self.plotname2 = None - self.scriptHeaders = 0 - self.glat = -11.95 - self.glon = -76.8667 - self.UT = 5 #timezone - - self.glat = -11.951481 - self.glon = -76.874383 -# self.outputHead('Show Plot') -# self.printBody() - - def setScriptState(self): - self.madForm = cgi.FieldStorage() - - if self.madForm.has_key('serverdocspath'): - self.__serverdocspath = self.madForm.getvalue('serverdocspath')#'/usr/local/www/htdocs' - - if self.madForm.has_key('tmpdir'): - self.__tmpDir = self.madForm.getvalue('tmpdir')#'overJro/tempReports' - - if self.madForm.has_key('showType'): - self.showType = int(self.madForm.getvalue('showType')) - - if self.showType == 0 or self.showType == 1: - -# if self.madForm.has_key('year') and \ -# self.madForm.has_key('month') and \ -# self.madForm.has_key('dom') and \ -# self.madForm.has_key('pattern') and \ -# self.madForm.has_key('maxphi') and \ -# self.madForm.has_key('objects') and \ -# self.madForm.has_key('heights'): - - if self.madForm.has_key('year') and \ - self.madForm.has_key('month') and \ - self.madForm.has_key('dom') and \ - self.madForm.has_key('maxphi') and \ - self.madForm.has_key('objects') and \ - self.madForm.has_key('heights'): - - self.year = int(self.madForm.getvalue('year')) - self.month = int(self.madForm.getvalue('month')) - self.dom = int(self.madForm.getvalue('dom')) - self.maxphi = float(self.madForm.getvalue('maxphi')) - - if self.madForm.has_key('pattern'): - - tmp_pattern = self.madForm.getvalue('pattern') #pattern es predifinido en listado o definido por el usuario - self.pattern=[] - if tmp_pattern[0] == '[': - tmp_pattern=tmp_pattern[1:] - - if tmp_pattern[-1] == ']': - tmp_pattern=tmp_pattern[0:len(tmp_pattern)-1] - - for s in tmp_pattern.split(','): - self.pattern.append(float(s)) - elif self.madForm.has_key('filename'): - if self.madForm.has_key('filename'): - self.filename = self.madForm.getvalue('filename') # nombre de archivo: patron de radiacion definido por el usuario - - if self.madForm.has_key('path'): - self.path = self.madForm.getvalue('path') #path donde se encuentra el archivo: patron de radiacion del usuario - - else: - print ("Content-Type: text/html\n") - print ('

    This cgi plot script was called without the proper arguments.

    ') - print ('

    This is a script used to plot Antenna Cuts over Jicamarca Antenna

    ') - print ('

    Required arguments:

    ') - print ('

    pattern - chekbox indicating objects over jicamarca antenna

    ') - print ('

    or') - print ('

    filename - The pattern defined by users is a file text') - print ('

    path - folder with pattern files') - sys.exit(0) - - - tmp_heights = self.madForm.getvalue('heights') - self.heights=[] - if tmp_heights[0] == '[': - tmp_heights=tmp_heights[1:] - - if tmp_heights[-1] == ']': - tmp_heights=tmp_heights[0:len(tmp_heights)-1] - - for s in tmp_heights.split(','): - self.heights.append(float(s)) - self.heights = numpy.array(self.heights) - - tmp_objects = self.madForm.getvalue('objects') #lista con los objetos a graficar en el patron de radiacion - self.objects=[] - if tmp_objects[0] == '[': - tmp_objects=tmp_objects[1:] - - if tmp_objects[-1] == ']': - tmp_objects=tmp_objects[0:len(tmp_objects)-1] - - for s in tmp_objects.split(','): - self.objects.append(int(s)) - - if self.showType == 1: - if numpy.sum(self.objects) == 0: - if self.scriptHeaders == 0: - print ("Content-Type: text/html\n") - print ('

    This cgi plot script was called without the proper arguments.

    ') - print ('

    This is a script used to plot Antenna Cuts over Jicamarca Antenna

    ') - print ('

    Required arguments:

    ') - print ('

    objects - chekbox indicating objects over jicamarca antenna

    ') - print ('

    Please, options in "Select Object" must be checked') - sys.exit(0) - - #considerar para futura implementacion - if self.madForm.has_key('filename'): - self.filename = self.madForm.getvalue('filename') # nombre de archivo: patron de radiacion definido por el usuario - - if self.madForm.has_key('path'): - self.path = self.madForm.getvalue('path') #path donde se encuentra el archivo: patron de radiacion del usuario - - - else: - if self.scriptHeaders == 0: - print ("Content-Type: text/html\n") - - print ('

    This cgi plot script was called without the proper arguments.

    ') - print ('

    This is a script used to plot Pattern Field and Celestial Objects over Jicamarca Antenna

    ') - print ('

    Required arguments:

    ') - print ('

    year - year of event

    ') - print ('

    month - month of event

    ') - print ('

    dom - day of month

    ') - print ('

    pattern - pattern is defined by "Select an Experiment" list box

    ') - print ('

    maxphi - maxphi is defined by "Max Angle" text box

    ') - print ('

    objects - objects is a list defined by checkbox in "Select Object"

    ') - print ('

    heights - heights is defined by "Heights" text box, for default heights=[100,500,1000]

    ') - print ('

    showType - showType is a hidden element for show plot of Pattern&Object or Antenna Cuts or Sky Noise

    ') - - sys.exit(0) - - if self.showType == 2: - if self.madForm.has_key('year') and \ - self.madForm.has_key('month') and \ - self.madForm.has_key('dom'): - - self.year = int(self.madForm.getvalue('year')) - self.month = int(self.madForm.getvalue('month')) - self.dom = int(self.madForm.getvalue('dom')) - - else: - if self.scriptHeaders == 0: - print ("Content-Type: text/html\n") - print ('

    This cgi plot script was called without the proper arguments.

    ') - print ('

    This is a script used to plot Sky Noise over Jicamarca Antenna

    ') - print ('

    Required arguments:

    ') - print ('

    year - year of event

    ') - print ('

    month - month of event

    ') - print ('

    dom - day of month

    ') - - sys.exit(0) - - - def initParameters1(self): - - gui=1 - if self.pattern==None: - if gui==1: self.filename = self.filename.split(',') - - pattern = numpy.atleast_1d(self.pattern) - filename = numpy.atleast_1d(self.filename) - - npatterns = numpy.max(numpy.array([pattern.size,filename.size])) - - self.pattern = numpy.resize(pattern,npatterns) - self.filename = numpy.resize(filename,npatterns) - - self.doy = datetime.datetime(self.year,self.month,self.dom).timetuple().tm_yday - - - if self.objects==None: - self.objects=numpy.zeros(5) - else: - tmp = numpy.atleast_1d(self.objects) - self.objects = numpy.zeros(5) - self.objects[0:tmp.size] = tmp - - self.show_object = self.objects - - self.maxha_min = 4*self.maxphi*numpy.sqrt(2)*1.25 - - - if self.heights==None: - self.heights = numpy.array([100.,500.,1000.]) - - - - #ROJ geographic coordinates and time zone - self.glat = -11.95 - self.glon = -76.8667 - self.UT = 5 #timezone - - self.glat = -11.951481 - self.glon = -76.874383 - - - self.junkjd = Time(self.year,self.month,self.dom).change2julday() - self.junklst = Julian(self.junkjd).change2lst(longitude=self.glon) - - # Finding RA of observatory for a specific date - self.ra_obs = self.junklst*CoFactors.h2d - - def initParameters(self): - - # Defining plot filenames - self.path4plotname = os.path.join(self.__serverdocspath,self.__tmpDir) - self.plotname0 = 'over_jro_0_%i.png'% (time.time()) #plot pattern & objects - self.plotname1 = 'over_jro_1_%i.png'% (time.time()) #plot antenna cuts - self.plotname2 = 'over_jro_2_%i.png'% (time.time()) #plot sky noise - - # Defining antenna axes respect to geographic coordinates (See Ochs report). -# alfa = 1.46*Misc_Routines.CoFactors.d2r -# theta = 51.01*Misc_Routines.CoFactors.d2r - - alfa = 1.488312*CoFactors.d2r - th = 6.166710 + 45.0 - theta = th*CoFactors.d2r - - sina = numpy.sin(alfa) - cosa = numpy.cos(alfa) - MT1 = numpy.array([[1,0,0],[0,cosa,-sina],[0,sina,cosa]]) - sinb = numpy.sin(theta) - cosb = numpy.cos(theta) - MT2 = numpy.array([[cosb,sinb,0],[-sinb,cosb,0],[0,0,1]]) - self.MT3 = numpy.array(numpy.dot(MT2, MT1)).transpose() - - self.xg = numpy.dot(self.MT3.transpose(),numpy.array([1,0,0])) - self.yg = numpy.dot(self.MT3.transpose(),numpy.array([0,1,0])) - self.zg = numpy.dot(self.MT3.transpose(),numpy.array([0,0,1])) - - def plotPattern2(self, date, phases, gain_tx, gain_rx, ues, just_rx): - # Plotting Antenna patterns. - - self.initParameters() - self.doy = datetime.datetime(date.year,date.month,date.day).timetuple().tm_yday - self.junkjd = Time(self.year,self.month,self.dom).change2julday() - self.junklst = Julian(self.junkjd).change2lst(longitude=self.glon) - self.ra_obs = self.junklst*CoFactors.h2d - - date = Time(date.year,date.month,date.day).change2strdate(mode=2) - - mesg = 'Over Jicamarca: ' + date[0] - - ObjAnt = JroPattern(pattern=0, - filename=None, - path=None, - nptsx=self.nptsx, - nptsy=self.nptsy, - #maxphi=self.maxphi, - fftopt=self.fftopt, - phases=numpy.array(phases), - gain_tx=numpy.array(gain_tx), - gain_rx=numpy.array(gain_rx), - ues=numpy.array(ues), - just_rx=just_rx - ) - - dum = AntPatternPlot() - - dum.contPattern(iplot=0, - gpath=self.path4plotname, - filename=self.plotname0, - mesg=mesg, - amp=ObjAnt.norpattern, - x=ObjAnt.dcosx, - y=ObjAnt.dcosy, - getCut=ObjAnt.getcut, - title=self.ptitle, - save=False) - - - dum.plotRaDec(gpath=self.path4plotname, - filename=self.plotname0, - jd=self.junkjd, - ra_obs=self.ra_obs, - xg=self.xg, - yg=self.yg, - x=ObjAnt.dcosx, - y=ObjAnt.dcosy, - save=False) - - ObjB = BField(self.year,self.doy,1,self.heights) - [dcos, alpha, nlon, nlat] = ObjB.getBField() - - dum.plotBField('', '',dcos,alpha,nlon,nlat, - self.dcosxrange, - self.dcosyrange, - ObjB.heights, - ObjB.alpha_i, - save=False) - - return dum.fig - - - def plotPattern(self): - # Plotting Antenna patterns. - npatterns = numpy.size(self.pattern) - - if npatterns==1: - if self.pattern[0] == None: npatterns = self.filename.__len__() - - date = TimeTools.Time(self.year,self.month,self.dom).change2strdate(mode=2) - - mesg = 'Over Jicamarca: ' + date[0] - - title = '' - - for ii in numpy.arange(npatterns): - ObjAnt = JroPattern(pattern=self.pattern[ii], - filename=self.filename[ii], - path=self.path, - nptsx=self.nptsx, - nptsy=self.nptsy, - maxphi=self.maxphi, - fftopt=self.fftopt) - - title += ObjAnt.title - # Plotting Contour Map - - self.path4plotname = '/home/jespinoza/workspace/radarsys/trunk/webapp/apps/abs/static/images' - dum = AntPatternPlot() - dum.contPattern(iplot=ii, - gpath=self.path4plotname, - filename=self.plotname0, - mesg=mesg, - amp=ObjAnt.norpattern, - x=ObjAnt.dcosx, - y=ObjAnt.dcosy, - getCut=ObjAnt.getcut, - title=title) -# title=ObjAnt.title) -# self.ptitle = ObjAnt.title - - if ii != (npatterns-1): - title += '+' - - - vect_ant = numpy.array([ObjAnt.meanpos[0],ObjAnt.meanpos[1],numpy.sqrt(1-numpy.sum(ObjAnt.meanpos**2.))]) - - vect_geo = numpy.dot(scipy.linalg.inv(self.MT3),vect_ant) - - vect_polar = Misc_Routines.Vector(numpy.array(vect_geo),direction=1).Polar2Rect() - - [ra,dec,ha] = Astro_Coords.AltAz(vect_polar[1],vect_polar[0],self.junkjd).change2equatorial() - - print('Main beam position (HA(min), DEC(degrees)): %f %f')%(ha*4.,dec) - - self.main_dec = dec - - self.ptitle = title - - AntPatternPlot().plotRaDec(gpath=self.path4plotname, - filename=self.plotname0, - jd=self.junkjd, - ra_obs=self.ra_obs, - xg=self.xg, - yg=self.yg, - x=ObjAnt.dcosx, - y=ObjAnt.dcosy) - - self.dcosx = ObjAnt.dcosx - - self.dcosy = ObjAnt.dcosy - - self.dcosxrange = [numpy.min(self.dcosx),numpy.max(self.dcosx)] - - self.dcosyrange = [numpy.min(self.dcosy),numpy.max(self.dcosy)] - - def plotBfield(self): - - if self.show_object[0]>0: - # Getting B field - ObjB = BField(self.year,self.doy,self.site,self.heights) - - - [dcos, alpha, nlon, nlat] = ObjB.getBField() - - # Plotting B field. -# print "Drawing magnetic field over Observatory" - - Obj = BFieldPlot() - - Obj.plotBField(self.path4plotname,self.plotname0,dcos,alpha,nlon,nlat,self.dcosxrange,self.dcosyrange,ObjB.heights,ObjB.alpha_i) - - if self.show_object[0]>1: - - Bhei = 0 - - dcosx = Obj.alpha_location[:,0,Bhei] - - dcosy = Obj.alpha_location[:,1,Bhei] - - vect_ant = [dcosx,dcosy,numpy.sqrt(1.-(dcosx**2. + dcosy**2.))] - - vect_ant = numpy.array(vect_ant) - - vect_geo = numpy.dot(scipy.linalg.inv(self.MT3),vect_ant) - - vect_geo = numpy.array(vect_geo).transpose() - - vect_polar = Misc_Routines.Vector(vect_geo,direction=1).Polar2Rect() - - [ra,dec,ha] = Astro_Coords.AltAz(vect_polar[1,:],vect_polar[0,:],self.junkjd).change2equatorial() - - val = numpy.where(ha>=180) - - if val[0].size>0:ha[val] = ha[val] -360. - - val = numpy.where(numpy.abs(ha)<=self.maxphi) - - if val[0].size>2: - - self.dcosx_mag = dcosx[val] - - self.dcosy_mag = dcosy[val] - - self.ha_mag = ha[val] - - self.time_mag = 0 - - def plotCelestial(self): - - ntod = 24.*16. - - tod = numpy.arange(ntod)/ntod*24. - - [month,dom] = Doy2Date(self.year,self.doy).change2date() - - jd = Time(self.year,month,dom,tod+self.UT).change2julday() - - if numpy.sum(self.show_object[1:]>0)!=0: - - self.ObjC = CelestialObjectsPlot(jd,self.main_dec,tod,self.maxha_min,self.show_object) - - self.ObjC.drawObject(self.glat, - self.glon, - self.xg, - self.yg, - self.dcosxrange, - self.dcosyrange, - self.path4plotname, - self.plotname0) - - def plotAntennaCuts(self): -# print "Drawing antenna cuts" - - incha = 0.05 # min - nha = numpy.int32(2*self.maxha_min/incha) + 1. - newha = numpy.arange(nha)/nha*2.*self.maxha_min - self.maxha_min - nha_star = numpy.int32(200./incha) - star_ha = (numpy.arange(nha_star) - (nha_star/2))*nha_star - - #Init ObjCut for PatternCutPlot() - view_objects = numpy.where(self.show_object>0) - subplots = len(view_objects[0]) - ObjCut = PatternCutPlot(subplots) - - for io in (numpy.arange(5)): - if self.show_object[io]==2: - if io==0: - if self.dcosx_mag.size!=0: - dcosx = self.dcosx_mag - dcosy = self.dcosy_mag - dcosz = 1 - numpy.sqrt(dcosx**2. + dcosy**2.) - - # Finding rotation of B respec to antenna coords. - [mm,bb] = scipy.polyfit(dcosx,dcosy,1) - alfa = 0.0 - theta = -1.*numpy.arctan(mm) - sina = numpy.sin(alfa); cosa = numpy.cos(alfa) - MT1 = [[1,0,0],[0,cosa,-sina],[0,sina,cosa]] - MT1 = numpy.array(MT1) - sinb = numpy.sin(theta); cosb = numpy.cos(theta) - MT2 = [[cosb,sinb,0],[-sinb,cosb,0],[0,0,1]] - MT2 = numpy.array(MT2) - MT3_mag = numpy.dot(MT2, MT1) - MT3_mag = numpy.array(MT3_mag).transpose() - # Getting dcos respec to B coords - vector = numpy.array([dcosx,dcosy,dcosz]) - nvector = numpy.dot(MT3_mag,vector) - nvector = numpy.array(nvector).transpose() - -## print 'Rotation (deg) %f'%(theta/Misc_Routines.CoFactors.d2r) - - yoffset = numpy.sum(nvector[:,1])/nvector[:,1].size -# print 'Dcosyoffset %f'%(yoffset) - - ha = self.ha_mag*4. - time = self.time_mag - width_star = 0.1 # half width in minutes - otitle = 'B Perp. cut' -# else: -# print "No B perp. over Observatory" -# -# - elif io==1: - if self.ObjC.dcosx_sun.size!=0: - dcosx = self.ObjC.dcosx_sun - dcosy = self.ObjC.dcosy_sun - ha = self.ObjC.ha_sun*4.0 - time = self.ObjC.time_sun - width_star = 2. # half width in minutes - otitle = 'Sun cut' -# else: -# print "Sun is not passing over Observatory" - - elif io==2: - if self.ObjC.dcosx_moon.size!=0: - dcosx = self.ObjC.dcosx_moon - dcosy = self.ObjC.dcosy_moon - ha = self.ObjC.ha_moon*4 - time = self.ObjC.time_moon - m_distance = 404114.6 # distance to the Earth in km - m_diameter = 1734.4 # diameter in km. - width_star = numpy.arctan(m_distance/m_diameter) - width_star = width_star/2./CoFactors.d2r*4. - otitle = 'Moon cut' -# else: -# print "Moon is not passing over Observatory" - - elif io==3: - if self.ObjC.dcosx_hydra.size!=0: - dcosx = self.ObjC.dcosx_hydra - dcosy = self.ObjC.dcosy_hydra - ha = self.ObjC.ha_hydra*4. - time = self.ObjC.time_hydra - width_star = 0.25 # half width in minutes - otitle = 'Hydra cut' -# else: -# print "Hydra is not passing over Observatory" - - elif io==4: - if self.ObjC.dcosx_galaxy.size!=0: - dcosx = self.ObjC.dcosx_galaxy - dcosy = self.ObjC.dcosy_galaxy - ha = self.ObjC.ha_galaxy*4. - time = self.ObjC.time_galaxy - width_star = 25. # half width in minutes - otitle = 'Galaxy cut' -# else: -# print "Galaxy center is not passing over Jicamarca" -# -# - hour = numpy.int32(time) - mins = numpy.int32((time - hour)*60.) - secs = numpy.int32(((time - hour)*60. - mins)*60.) - - ObjT = Time(self.year,self.month,self.dom,hour,mins,secs) - subtitle = ObjT.change2strdate() - - star_cut = numpy.exp(-(star_ha/width_star)**2./2.) - - pol = scipy.polyfit(ha,dcosx,3.) - polx = numpy.poly1d(pol); newdcosx = polx(newha) - pol = scipy.polyfit(ha,dcosy,3.) - poly = numpy.poly1d(pol);newdcosy = poly(newha) - - patterns = [] - for icut in numpy.arange(self.pattern.size): - # Getting Antenna cut. - Obj = JroPattern(dcosx=newdcosx, - dcosy=newdcosy, - getcut=1, - pattern=self.pattern[icut], - path=self.path, - filename=self.filename[icut]) - - Obj.getPattern() - - patterns.append(Obj.pattern) - - - ObjCut.drawCut(io, - patterns, - self.pattern.size, - newha, - otitle, - subtitle, - self.ptitle) - - ObjCut.saveFig(self.path4plotname,self.plotname1) - - def plotSkyNoise(self): -# print 'Creating SkyNoise map over Jicamarca' - dom = self.dom - month = self.month - year = self.year - - julian = Time(year,month,dom).change2julday() - - [powr,time, lst] = Astro_Coords.CelestialBodies().skyNoise(julian) - - SkyNoisePlot([year,month,dom],powr,time,lst).getPlot(self.path4plotname,self.plotname2) - - - def outputHead(self,title): - print ("Content-Type: text/html") - print (self).scriptHeaders = 1 - print ('') - print ('') - print ('\t' + title + '') - print ('') -# self.printJavaScript() - print ('') - - def printJavaScript(self): - print - - def printBody(self): - print ('') -# print '

    Test Input Parms

    ' -# for key in self.madForm.keys(): -# #print '

    name=' + str(key) -# if type(self.madForm.getvalue(key)) == types.ListType: -# for value in self.madForm.getvalue(key): -# print '

    name=' + str(key) + \ -# ' value=' + value + '' -# else: -# print '

    name=' + str(key) + \ -# ' value=' + str(cgi.escape(self.madForm.getvalue(key))) + '' - - print ('

    ') - print ('
    ') - print (' ') - print (' ') - print (' ') - print (' ') - print ('
    ') - if self.showType == 0: - print (' ')%(os.path.join(os.sep + self.__tmpDir,self.plotname0)) - if self.showType == 1: - print (' ')%(os.path.join(os.sep + self.__tmpDir,self.plotname1)) - if self.showType == 2: - print (' ')%(os.path.join(os.sep + self.__tmpDir,self.plotname2)) - print ('
    ') - print ('
    ') - print ('
    ') - - print ('') - print ('') - - #def execute(self, serverdocspath, tmpdir, currentdate, finalpath, showType=0, maxphi=5.0, objects="[1,1]", heights="[150,500,1000]"): - def setInputParameters(self, serverpath, currentdate, finalpath, showType=0, maxphi=5.0, objects="[1,1]", heights="[150,500,1000]"): - self.objects=[] - self.heights=[] - #self.__serverdocspath = serverdocspath - self.__serverdocspath = os.path.split(serverpath)[0] - #self.__tmpDir = tmpdir - self.__tmpDir = os.path.split(serverpath)[1] - self.showType = int(showType) - self.year = int(currentdate.strftime("%Y")) # Get year of currentdate - self.month = int(currentdate.strftime("%m")) # Get month of currentdate - self.dom = int(currentdate.strftime("%d")) # Get day of currentdate - self.filename = os.path.split(finalpath)[1] - self.path = os.path.split(finalpath)[0] - self.maxphi = float(maxphi) - - tmp_objects = (objects.replace("[","")).replace("]","") - for s in tmp_objects.split(','): - self.objects.append(int(s)) - - tmp_heights = (heights.replace("[","")).replace("]","") - for s in tmp_heights.split(','): - self.heights.append(float(s)) - self.heights = numpy.array(self.heights) - - def setupParameters(self): - self.initParameters() - - def initParametersCGI(self): - self.setScriptState() - self.initParameters() - - def execute(self): - if self.showType == 0 or self.showType == 1: - self.initParameters1() - self.plotPattern() - - if numpy.sum(self.show_object>0) != 0: - self.plotBfield() - self.plotCelestial() - - if numpy.sum(self.show_object>1) != 0: - self.plotAntennaCuts() - - if self.showType == 2: - self.plotSkyNoise() - - def getPlot(self): - - return os.path.join(self.__serverdocspath,self.__tmpDir,self.plotname0) - - -if __name__ == '__main__': - - # Script overJroShow.py - # This script only calls the init function of the class overJroShow() - # All work is done by the init function - - phases = numpy.array([[2.0,0.0,1.5,1.5,1.0,1.0,1.0,0.5], - [2.0,2.5,2.5,3.5,0.5,1.0,1.0,1.0], - [2.5,2.5,1.0,1.0,0.5,0.5,0.5,0.5], - [1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0], - [0.5,0.5,0.5,0.5,0.5,0.0,0.0,0.0], - [0.5,0.5,1.0,0.5,0.0,0.0,0.0,0.0], - [0.5,0.5,0.5,1.0,0.0,0.0,0.0,0.0], - [0.5,0.5,0.5,0.5,0.0,0.0,0.0,0.0]]) - - gain_tx = numpy.array([[0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0], - [0,0,0,0,1,1,1,1], - [0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0]]) - - gain_rx = numpy.array([[0,0,0,0,0,0,0,0], - [0,0,1,0,0,0,0,0], - [0,0,1,0,0,0,0,0], - [0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0], - [0,0,0,0,0,0,0,0]]) - - jro = overJroShow() - - fig = jro.plotPattern2(datetime.datetime.today(), - phases=phases, - gain_tx=gain_tx, - gain_rx=gain_rx, - ues=numpy.array([0.0,0.0,0.0,0.0]), - just_rx=0) - - fig.savefig('./pat.png') - diff --git a/apps/abs/utils/pat.png b/apps/abs/utils/pat.png deleted file mode 100644 index 19e21fbc9f479c2c7104e0e3681e606768978d84..0000000000000000000000000000000000000000 GIT binary patch literal 0 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

    %PPOrPsUnxO9cq9fAwH$pBW3J`q*{Jcg z#we|KJ&)k!U^oDC_&WQJcb5v0s~;2rbsW^^S0Exw9jQh(Q&RltVMgMYL`BH(9It2HzK{& zrY^$aW(Oocf1OD^F@|K0eu}AN9w2?dA9``&8A~|m#zhF>&+qC%(WRq_ar&2Sj8aG_#C>; zBxVCxiY1*yh(=QyTfpH;FkzXC{)($?b!QbZkyXqMe-`G%Cdaxh!@gyPRilRqH<9 zaMRzN4nAN4LE!MFO23a*_}6XE)Z3gunJwaKzl z@A-W-lNjcjlFw$h+DoNBAE=j7LTgn=8lm$(F*8Yo>5U~z)athB-$0=wgyjxH|@u8$L`PS%JvRxo4eWrLQfnakx9-|{GFLMuJj=`F9>09IHOTS+cu z*?c|YaD9t0Sq_)(_9NfbnQ5ezu6WIUw0;~%Y&z+DtufeW;J+J+f$)hKNpoeOZkz25ZY}zjb#pE9wUF-_OBWgzjC1Q-aP_bg6UvQlI5}<5oy1Sp?Yo9F?a6QnrYcR$CKOR zxfTF0k`L`4DX`L-LsqG%mK96+msr$`OQ@zS$7sZ_V}>0pgj73+)7vMDsBEkTLF10~ zWEzNL>0R|feceU6JpI?9$u}e&GICvrk5*q?`5Mg z<{wU%Xi?)qU;H=C#A#JkFwS+CK^|oj!p2sR9`Rb>cjV|&YIMWFWzv99sDOr|(D#-qH4HvjRG7^{$+!|) zJ6-vxwH5yCR$ahly-h?vc>l5xP^L1|9@V(3_*ofRK7Z zXiw!eE@NV=WKRiyx zfu$Ed8w2;Hb^s~!emlYV-9PlkDy$hta}P%7iCD((_>T}y55yo24-HF%&_+JXmr{s% zK#=#j30t$49AEBDA_+wWawv#&`=CJih-iYZ6KMiCFNX@2{pC zAi2yYuo~xg&)hZ@S~4*AD+G~}zoJ>}%ta>A81$C2`M}-X-9Q^{PNacZXU#S%Nr#V_ zQQy-LoCor_2NNeYog*_j+VBw(>Hdd(s+Ge42mnE6{EDe;na7)go5OK+#xP%&?ljuq zb0Ay`>vDkSYt5X33Mlin3B8UsSP0TslS!YicJ}rL=87a<4$=+oYfn%&j)-K+M=`Wf zd(GwzCEWZ>s1U7sScqm8odtz(|NWFF8{(52MizgwddrYLp`1uSK zYCWozbD2df18h<$#dd!Qzr4JC1ewl&ts{q0Ul+9OV%j1qW7GBrc4BR*PE~@!SBE^y zaL$RTzzAj^Y#8)(*%vIJr{|uo-OGh;d3nOgE%owRqegdUFcOv3egi83+}&Zb6Z*S5 zIhWh1>V}6oiZQ!j5~?(;B{TBtWnxA^?sH$_8jx-q(9z7qAHt6}T&=PU2Z^FE*xZ`| zC795$4ia5|=bQn5!Z7|Hg~w0>Znmiye;FZGQg1&V_?Q_)D}jlKhyu3!zjDZB(#X1@ z3K6_Si1b~};U7HX2-S;=AbMC*!WRz^=WpDa{kjLsm5Pp+t8q)DQ>C>BCcO-c^XQ&a zJ2ui+IFKIE@~Q8zCh^DT(G?w2E zs zY;cGd0jz6$$Bj z&Mi17Xf%h1112WpgF&tT?<@d7@b6yre93E{ zYT*O&-<q?BUvvl=m+ zIL+E@9nIu@S5*^8N>X8YWs|K1vrs=%_!T!M#KnC_So1D}5rxtR%f8(&{}W|O<2j&= zmF7<&*5yFuJvJ2*e2K;( zzf7?>yiDeV>(X2;YSnUQKvJ}wnyxJ(=E{dq=x>2R-=}uRcBLY50HgkD>&tn(SZA?a z{XwCSTXmP0G%uwFF&c}@vD)Fm_+6ym`C$C}GX7)^PdY$2?Oa`b&9NI983BkPJA=hS zxlA5^Je_f7Je3vz7ieV)dGd9a0H%S4hd0}o94Lj~=*EA=%${6@Nv=3KgL{g@ZSrT~ z-ap8Bih@S~KFW)!q(pwdn-S1`>WT9w_qV09Jx*Bh1V4e|cWA`!|HCf<8PcRm3~ zbrtcvm#T@(%TqCPqY1z`>?v2R6h(tpc_KH9h?jXG6Fi3I=wANd*F8Zw8iX)rLBdmz zO6C7FzrA+z)aa;W%9jS>Sbsmr#ftfs^iVO)2rD5W;dfz~qYg;LT7AA0X>yC{>;kh1 z#Ji2RmltGQT$!$)H~bX<(kBH#@ALDsVu>_55CBr*Iy!g&f4cMeG01m3I5>!eiYhRK zNx&d+0m#tE*w_!%Mnuh)NsupZJFNE)alHOo(!*u(^?ik=<}hC*j%)i4sWm_A{_;1< zL-@1_?ZOBIi8EDrxdkah;jgmzi!4Y)f*%&sc+N31z%vDM)9kCpHcfjIV`J2PN9na7 z6?J$-NYyR8nQ^qp<+j71`>KBWc5a!L7zv?;mc&A0F24Edl_vn0 zUMQ6tn4V^s`vN`a>#1wo53Q37E+sm)-|F&CZ?u^CE}62~>1Fk|iQ513#X{FO z9`KaK{rO}cAWW=TZ^_{XYLRp_qy&^}Q91dtD^S!TomXy`!9*gw?c>YtrH@C> zEk_Zjnkg023>~ZP2bL;5l;{5`bp_moo#90`C(1JBj6$9=EW_mqz&rD)%!j^PF@eK; zhs`6P4QK0#DUb`Hf{w)oCHMGtgBL{A6aB*3V&bPI0#mP5pip4S42>#Hp`HHF3jMC1 zh3lrUe*~AJ38-eB>oxre4y?;1Lc@eiu!kCxXrY~x+>C66?^L!YuZkn5jZMvl_p%@H z{L80^^GD??Nal%Eyrqls{*3Cz$gM&;=OxAk*1ps6=GCqC^tE1qHW%u=L(C?g< zEw^z0hN$%81~D;g_v>jYUntno$w?>$_1*JT!g+oFP(RpPe2n!0ffx)Ra(1V4c$_Bu z0JG0rHYdjUiBU8A7h*t*qaJI{vf!%&qGwm->V!M2hHBJ`>HJ|?^)MY#}lHEc@#Q)JaD$(XSm<0k)4?R;px5YqvTu^zSEs&>=r`fDQn z$71&!3_bW0`o7jZ3oor*Ny!mhO1wO23WjCYM`+6{(SX^S_g7oxEVkU68R5nHe;K#I zQLU}DeNjF5PjBEC)7V1wanYMZwQ#!|5XR9mP(Jk*wA~0goo2r2u_CN?^L&F<@*#_k zoTR5Gr&gmmIUOC{+>DJmN)m}~-FI2hR{MYa9rA%7;Et}Y`vBhoomRPI9TXhwMtYknd{Fbpk4u! zz4#6UnQqUGj4QP~al*G!FPH1bF#oS+!wt>>45B4A=*Rd}bXP6RLVpJ1qBNqLPJBL1w%qZ$*n)G9f{|Q zkwHS*{60u&MZut*_IkjgRhgKKN8<2>OO(mAHLL+YGM;8Azt1TG@)ZQB*?I}Zx>?(@ z2`AA-L%t7VuxI9r?RU3GI6@=>J~xzLll77`6tO5kuYePgkdW+Z-1pE7?wSjWQe5)B zVObZTbchn}P$1cZ?3A1^bh_KKjXLdJUNQu}v=G_0u0=(xLJ>D<6C+Ha7)OL6 z36Ku%&kJ{U7J1-6YX7&j z0g=yT8uE8BWe+puyp>5G$=~BaYMq`=IBeFUSDRfo4-W?bFoMHjjR1r{nZ-gLD3{M$ zyxw@|`r!eb$YeO?{(LQYXCU0=`P%6H{rz~ei$|?eJr-7wQL~N+fG1Jj2QzsBH3E>3 zA0-RGLUp=x?JiC+7v4AI~$0GlooMI42}|CvwRdWcf-HbUzsFu>elk>+_xK zyD{1@d_}X@-QoA8;xGI6aQ`NO6Z!=e9v!=)6YtmiZ=$4>NoNp)>T9uE2Lb@9FqF_A z*Q?m`Ln>%r_rR%jD;MJJRi6m2|!I z@8@e`SWE`lzhNoTT8sCsIt|x`FvDS!uEvDqR^e%yEejcYvf>HnUGd4Hp_AxTziNmQ zGbLMi*GT^5S|f%Eh4(V>JR2mQI1UKt?@3#%ng_{O61&)cJenLbZ28Al zi$jJ$_NFLre|Y;;lV-tzLG(9SU5iZbWtXQDR+=m%IRU5aOZ{dR6vW7*R^c=|%$}i+6T+h0m6&E0l^L;^N{wy}YnR{6O3*c5S$DnKAae>L28k%-~J~LKTq*PL1RI~FWK5g*L zhtdNL2AuEi3C!^3W9Y^J?Kg1Idv67d>IF1*!&&zo?%Vb%?;WT!qw6f!Pf=#wc$JUQ>Hns8x}L3tzwPUE1LnTiaQbASO#Y9|P&Hd$DP>_SMg3ONCbf1T7Dl1BEL}9oH6gZ4{1l33y8FUeaA5fG(n3h=1-yTDXt@QgV86`d3C$X8 zZ^MZAPh0Zf6ClWl37|yz-{arwf;i*0)xl%124f=1THM$NThMEn)1ot98I09v+)sNBAF*6|*1|x&gNig3feRC%|tM5oqnM zi{)!GKQ0}SaPdxNh{ug|Q(reSwJab0Fl(KI4f6*!p*acp$yp6r{;bE}38~sAtBnt; zPY=etdXDHxwIg0{=&>|`jJl_QsGt_+`)U6XM4du8)PL$h%p@(UdHOKtPe&_0FoPI{ z7PzX}=1TpwHD_0!NzkPc1msj_to!VWwhSaH>{YgbSi@)B*l@kwDI`_J`mx6pahEn(8$P$(al zI-O_7@htn2D1hFWY?kRT81-WS&JCdTht}7#421oG^K&$KvTd6dsuPp^zdq8tyq|7| zFb&XresDNA0b8@^^YU{m6b}!DM2E}nk(?zEmr<@?mzeb(EhbtmlrnkU?&BF`vDv7{ zb)B)SvS1LrgT{p#UF+|;@-UnHUWjhh0_#;Ob&Q;%Xc_giEKFS|u%9?yeMXrpFg7_A zOOTY~Z54ZpT(n+gM(VE#tmJ^(gl{R8T4*Z7b>?EVBDu`tHq+L*7sBuf)8<%Mfa94C z!pY!fsi>$U`6!vw>QKk*{Q`=ha42<@>JY#pLf~Q(rcvk z!GIz^6E;c^MfL~N#e zYHCvxE5II4U+)Yw^5xau-8^Y?rgoz~@2FqXIT*^&2aCr{mqWi``v-Aa8%M{8P+rM% z(q?-lTnD%$w$s(|`iSXCratD&hRA&wJz@P*c1QH5pT|ab|KW+)_9AKAgT9wPR#nFd zNk)w^^!lV&uA;Q$gZcM^aeV?3QCJen(^GIJ_lv7llKOccjP)Jp^d4Sb=0b-=Mk8+l`R;6FIb?Lo;#ps%)~?u!Sb^~Bm^9r<#STr=j8-WgP$NbJco4Xqt!yVxi!|LDs(pOs(jkJT(Kd? zo_FVWg&Z^IH2XAm7R0(p%aKz)51athMHwL9G5dDe|1Kfw@d5Re`ZIddhc?8*S~jDb zBrgHimu~_igi4;hqbG*aBrIFRYEqdAE&Oo2Hgp`VxSyX!eyt7%&T6QQCJ}+Nemsi+ zd_s`5`X-Yd-76Ekf11ggXZ~V9Oj`YV4FtjVfc#v~@E$qwRHeJUqIxWy&-*bUDM=V0 zb_{-A2>!B+pU;;B?ZoejwO*ObU^&@nXYup%3(Fd;WLCggHbVRWW}_eYIq!VOm$+aL z^1W-~?92=b0S6??ohDr}zW$V!78r9L<5f#5DOm? zChS;OQ9TFWY#<=d`YTz@EUvP_x6R0nI}oDKIyD}iC+HKx7FCu90TrT>L6#ItR^H(oR-@&2)U z^{!C9CMF6#!H$j``)n@<#3{epmG#bd>C4wHvV zem7E|bY6L9W5UqPkJ>HLHVXp_OdGbPvi>9QW6 zh%gv*Ew9`Cj#Nn6!*NSasJ8;oyU@4QL2@^Pu5g97TiziP1O9&{RDeaRYhEhSA05Da z_lO8XRd*hO_IP!IqU41>G5~qrSfs#4^A6?9@fAJYFL0z^I*d{as`ro7fnV{PT9i|) zXo&Vov&n3awjCm|T9n+-Z!^^9xjoo4sH;hjP~M+kQmGERIKd7VafKe|LPy^6?&;2# zM!zU-MPT>7V36rO4ba5mu#b4`ke631ivy-`bOrs&nY9x#H?T!Lf9+<*dnVlAgZQcn z91SmRp)gpqX8Z0k3viezbQz7xH>t<8gT=$!#(Tm!{bA22-Lk^MAOm6WCQHB7R@ZS` zlBek(T>_T^V(e`PGYS=3pk?1NX}YMbz1TY$qnzQ7RZB}sn7&(GPUpw~EJ2gN&8~f7 zu!9(7)V2&qi2RTihD$sUs*b7VU$02~ssKvv&Y7yH*4EE})T`d0(raEQzbx|XyD|^5 zmc^)way^|5eFi~F^I+5$$tElX72$UCc6J^pIJ62=d3h3$LR5G8>pt_jVo{24mNUcl zMQW;ALI4f)bNXs;uv?SevcLz)LR62y7}2z%T<@2*&6>`M)fIwcdJGV{41M^W^{FTn z@RBwFCOeI4d2g1(VU3g1X=ua<$Ae{5?1rL2zwCE-#AA)W!^5EWH{WIGF2(?9-9!#6 zz3ZPnu>#hMw>kaLjhL{+h@c#9seF!ftNlOMN;l{(66br3Zj%d>T1fO0K~Qnvy7)jc zBqQT!xbR_ME<&f=Q8Yd(6Ht9}Qf;Ea;sTu?`E`TCis8$aR_YN!ByT3^1EN zu)d^u+Or;Z?vz}x9Ujr^@;M=>g)Gb&mJ9s19*)l834$LNEg*o@juzPqV70Rj85?H{ zjl>kC9e&cVh*H%W%cE>HoFH=QnX*tU3%9zM3%Wa+`L<>ha+xVdPGuw^pts_495F^1 z-|dgoeyffxP$z-Jt{U68}!c_h&Ig*Bj6P)IQ(sQp^S`-^tlei z0Pn#;DKvEBNJLR-r&qNlZ`Z2@z4#RjnHoW!Vz7{tsF8>gM_#U74hS%w?{5iMQ zh3ZLGy{%!zp1h_oZ6@rc%INueix~jEyvK9UShJmD0VB>phZ{;>zKR2W`%8ZLM0H%! z=B%THsMcT)4B21|pf$K6ZX%)-Aql)7>1vj|4ks*q-XBT87?RG}tEp0Pp)l3k_;(uG zaS;)QyMvM6sqIBDG;ZC@O$u@wRCmbg0 z2#CpMu=-7t@EQ)K-u%JVU!<60SNkdw%~hLgyiMk4S_PduJ{K)va`B6SSoh( z4-8~*yV1~U)QG0ZXzI}vdJPiuL*%0yY{z1B=!i|2EjU*=S!{3Yo%&I z0Vx{D8V0H&lxj|5GMU27Cno7REARNg+|2fJ`>wr#rRDBvFxcM$?Q8{+URv<7r)9i@_!|Q7tk_-V z_YB%a&&?`^XCl*j751jdzeDwsv)@K&C}b}!EvH)7>^hv=hQwXXT=^Ej&c8`eB@@8Y zUqI@0Pi^4KM+9y4pz$oL)1k=0EdT2$Z3Z3HhrT+d$lmlJK~_?7{OoMoV!slML?<-? zWGR#!LMa8AHM8wS!IAOvHD#Sk*t$gM&)2SG3iip6!H{V80Gx9@S&QhjkdTn?jURwf zsd9@_N#KSOynXEG>+AdOX=OD}X1~$a2hbnX2Hm`U5NX?LMTZ{z;Q0)no<(18QO?zZ zr)3uTf7c6nCei}y?0%_#?0Q`M71PzlBlLSNxJ32%kf&1XdF#47(jH4jVa;dnuXu~C zFJ~H27X&X#3`g?jM0FBxMg7+)RyIfx?Bb_aje)<0Add=S&$I*Hv=~J_%Sq%R`%h1+ z8Y5Qk^IfPcO_x$W!nQ98y+D;3^sj}O#Uu~cKWTPWWTa#sBAoYE!N|XbkRH!&ZMQ5b z9P$>$UnrF^W(LRn2x0hS3~X_#YBUw6_x`_C=NGM1Ju2n%lzlDrs@PyMLcsr6No%g? zG|>_?Xxx)Of0qEVW+45VMpB$`Xh9( zX^=Pmi*lE6dBY$$y$iET`k_#vf}i!qGDugwS8$s#c@TWL8>!6`!z)t11>5xf}97@klQp8c?>?wD4uw8g%&r#JUA zI{?!QhDO@C2{Q}t?ABXO)Ls2t2{xYfSh*ajX=4AWI}f50ZW=O6WQ-HameTyYyG$@Q zvdomqj^+%Pt{(q*QmhWk3lrW}KfWh#%1ip%ZtjT8>|^^kM6pzuHtP%A$uzD#XhYoA zE>v%PeP1Ct@Y^B^rBO#>`v`j1uN7RIhY;ZKC$Vi{(n1~YD2hvkclyn-#G5?A9#zDQ zup%OQOA+sB2A9GhZxFG(jL3%kspVHx^wtO(at513Y1&nd#hbCKs(aF6;W-+b=hWR< zH~Vr+y;s*vDG5{Gpgedya>)dY#{QABel>D+L;AkF4zCnfJkS+?c~HJ{8Ejnj5JagZ zo%`ZM<@vorLqbAsF4dD(|2>h`TDU$d66nEb_be`dic7uZNg9QB6Q zA71K1@d2i`K5*}?SZQY5a?%N zQlqXm#IsFQr_@XD(*sMz`-;MzMD%3Eia2} zrPja#kk~#CY6v9WFOKBm?@24p$^2kXQyg24lpm+d>|M%^JN=>MWV1`7(Bx9@Lmog) z=IrkKbcSk%c#ZqCqG15ZT-S*@*kcIr9e`klLcrtfbBX%+@P2PTp3Il*Miyg8QYn!}kHhB~KA1@VZajy8P&>`gRZ%~bxI785jpib`v!unlK5BPOOTxXK z6VI7g zEHtv>AK-ia(gl#?dn+f~Se$Ju>@nzC^zYS~*>hgi8e46irrp>?#cI-GiU9=?!)X6N zj}B#3>X;7BxXZE*)asbhL~l|MZ7`&Hu8=Dm|9AkqSHo1;9kjUKUycrk;d;#~R8VBd}FwDNOuo@W|efJoO=YNe@s?;zsDe`_i zxX`>=Q*}>#l=0jjMXo7(@;u+{;sFK*c0F5$4;UQmTsXZyV9t`<#We0_09+3&r zH+qbu+As1u`VcRP^zsQ01cf~G{=4qbGnH=OC)FyCKZq(-ulT3?n4c_}$!r{r+x>hL zAS=psS{VRAE|W=T_|tsboW$6CtYG9Lwata8Wnt|?Rub6Z7^Kj{`EeVa7;ot7fZTp} z21_q6=YWy{_ctay5m>mgG{H^Gk~)Q&)V>i+4L)8QUgv}Oz5z*;pi|-)@1*zT&RA}? zJdqf#Nm=oGCg(c|ekPX~=W=@YO1u@^X$Y6xPjdg-5-rX?&sPvSkg7E3 zkcb4u$|cfG4qLg#Ls4_(N~Dh$>lU+5x7>xj6r^fbtap;6OM@6v648b9K%AfFo{yL* zV})aq=PGlK`yXoHh3!3gaLkfNIe9%)jtWp}^P})1)SkDtjr=K-j;)UQlmZJiDaM&dAUH zk>$>MWL|xk&j~bN5qQzqU7#&420KvY0RB<_zpu7#RQ62Ig?k zA9lfH&?Tai<(Ok?*3LNzXp`IvAp2jHbM-3C0yjzq>b5CheMeaDjq-5!S4wcR$MZGW zJbu1)7nYgwUwnRTmMd8XA=isAbHEVWQ!^n1h=n?u0CDp@DEQp2+t)h-fcYDH`LDy} z-Sg1+wl5g+d-t*5YN6jpJy|KF`UIHXy3}?l+gYJ)cQ`8U7EOK46t^_r{{9Z{C ztZos66cs69|8cW*Uns`b_GaySin-FAe^yZ=N?l}myqVy?{vm@&IM9Uq;C#JnIZ*^h z!vcj)L!qBDn(q=UsdmsXVRZ%fv7F&6Glov9zI}8w^1WgAEqMI={HiqTh0C1ui#v&w zv7rUUEUdaiOon5O0Q{4}XrP~T({#Yvk6k(AcusA4{o?G*&nhbb+L&&O=BDDMRD+Es z_m%C#Y~#qv#;n)jZnn|pv{<1ME|p9%*Y0AG#%RD0`Jv8SKM*M%fmW>I^E7@O)VAKP zvQSL{M46j2>QmtEfz>2p$&XHN5fCX8-RXzxJ*T+y27&Zb?A5CZEuxBzs3HvET?|ab zSoY9F5 z{(TqVlb!si0rlu3&J7PzJ~_Vd)}>wjxd(1(ETT+cp|X8p+wijOnI%i6u2^_E4oVdu zd_V8={XSmlw40y;)GF1Hv9Lz#j0RkBj?~ZTfp~8Q$4a+5@+&oJhet-j0T7SjNvG#I zh)v*(!qtKeD<#4Lh+e0G0BBQI$`cVkTPW!lVH5^^RBY^&Owpv{=Z#Rh4ahh}?hZQ!*g_%o{%HuBEEfj*dVj^SxG@ zQuTF(sFMz4-LT->(>eV>ixr@MTsd2fdB06;{t2&A0~x3E`p2Rtm2H9QzsV7gxW1}e z$Ful!1b9!B-e;M@y>Sn=9uuJ)@YS)47~@h|ilW9s7ThxjFvIC;uHB$2WZVr7{zk&~ zjO4Vm;S_2W%?@|^sZ406!JiMO-zOkCySm(PWK;q!Nom{EhhOfFxLl8s7E0x4uQmTT zr;!6K7T3D|A$9SLuT5a$31stzeWJ)f9G%$$_Y}8mCHxWjF zIq98%@m!i*Zz~cigg^@__`egS&~Ru(udlo(r6P05HDcLv+sq-qT9mHm@@!4h=e#?K zb`LzT4{(H4n@3eBf7J-xjFBMp~>j|h*5LI9+wTWY|mJ8*Y2lfmgk z0+=1*3>=p;3Mg~B7MMWrSXND59UfPoNyCw0T8-vr9)lp=7}YuH&EXdVJ8$BH_*(r# z(U|R?O2?-gJ^p#on2hG4oD{OZdI6ZM)ohLKakXhSmPCHO)svS<8v8_Gz;Q~71G2IA zgNc{Do^5_MUlP<-^~c`1TU#3}JC=Mfr9?KL;UKXR4^*LB?b=hezb{pKZlK3kcOV7S zGY-QQcxX;Jhx5Xfb_p?=sO{>nRL90t+yB4@^_EWWZJ*4Y7Fq*M@b{!Zx3Kk^)m+aSa4bDy%I4p)@u_SWW$Xt4*EomwlZ-qQ`j7lmNHul&E zq5mYne`c`RsJE=>JM#na(njUF++c zw+9m$EEWiCHp`|Dr%T^QeGjMH7&n><@d;G(S*MhWc2Tlq?c&H%+-atE!x5Td!dQ7M zJRLG_ViRaZp9YyImAX<797IaW6syq3W^Ds-27%if3|L8k=JY#l_dpO|t_vIme_JtT z+7FHXFHFE*C3r|Y62~17uQlj)k2QoOpIPJO!86K(El?eqm;FLU+Qe)cIvOo$8RMDv z2`-Wgeu^?24~{C1U!T4h^xA{(FL&g)cRyohPpF7#3!bn52~?y}tG`&Ir$f`|x@)iu zqKN5linUok_1pFG;9#i9YT*}~4F+=toy8Go{-*a2LsHB0OugX?zq^)OODs5&$OqdfCz@DMJzq|4e+S6=nKEcTE`KGUv6B&3kBc^Tha0*T^ z9ToUrMhyddk*oZ{MGR8-A}+1Nyu5LGcY}UR%aYSNYA9`iLou=Zyz|jjy$;81qP<6i zt~jg?$WBQa1i-0gy>Y0i^s3cq6C7l5jBF3%PPgw{L~Lw{|JP^IYB8-s`>@G0$w-(s z)=1dNLRn9-E5F@eR#h6c%Lhj2#5xC%dx~eNu7}n}v@JlD5^zxnC81bAY z)-Eu^dlSN}7|*}a&L4l|MFhXUn|od(p5xG4gTYW)+lUFUB1G&XC+HOsQ$vjR0RO|3 zWZs|3G6o%4*NKG=x=?Q#1jrSjOtdQH5?Pz@g0|DbeCJp!ckkZV+a)Wl1zG16!go-{ z%OYpmzB0Ec7up>Hm0EeWW;HKn_j65v;|i}00T+4NyGkyW?iacgWO7yL`>sn%t6sJJ zcGZ(UNul?`q1XNgCC6!QkMDRKASoiFqZg!4ctHUe<_p9a)LTR++YQ5q5wropa7F8= zfkiuStU&yG4z;%-b#9N;(2YZp0!6*23a9atbAQ!>Dr`Qy@7bM^>Rc8mkGcH<27?P!lor2pu`COrb+d)ctvKA zG4-0@s9@T)HQzB3Mhn^ck>jMc6;w}|h`2VuA-4i_J&;4#hUGTr%6`!I`f&DrO2(n* zqosIHKe`)2FjZL1RXsO8I@)A^G_gvn5rT(j&z1R&kw#V52sR;u-9+;H$UY!p8zKd) zhlcbVF9b_(Ya4l*-of~}7?jZ)-4h#)F&2%8fHc+MWw4Gw3J_KMaY9` z=QJdhN`C!i4wQwk8y+Yp-QI+n}h(zly~* zhF5C-y#&l;VJmAFklN|>iMP8u_|agVkV?juMQDHMd~ddKv9UJqNei=n?_O-(tQgUa z4&LMY1(1s0wvV5G*vcylHJ(cZZ)bH+9Gn}=wPtiwlK*8N6`;Vvs%xf> z&K`qC4eLk8Pi<3dGWD)+jfk?hK=is(ZAWIK{89?Y89Y%Xu%5W2#fsfNV}rsOeO?ho z!paZVj z9o`z8M5&WK29q|iY#_#mmN4BuKE0cp#if$+Q8b|$Dqp4@>VHK`v$~-DAz%V0s7U2`T1|b3J?_qnoNB2ouERe)ROBy zsKolvbn*XI2Z|d7oJHgD2IW z+%+jK)Faoo1HjKakBDK7B9M#LYy)BNduO#hrIvGr5E&wS0jiEQ`43zp@q|bHp|COd zJj+DvC;f7RVl^;f7ltivXX=04FQk3-QfgDA3gO+Hv)xD5|B5_?ZXZ)vryMSZ>PXzP ztVWTkmhb?zK4-8h%h|@J@wP*=O!OskF@8*|xmCmbssIJqb=dAY`0)}Iv1u9Mt)FjX zl3EW3izoh;eBqVy9C15+fUV(#XR!R7WcH9&-*w|2*TmU>qdsz?@*9!a^xUT@8K-9@ z|10AY+qk-x83%H|zI&bnTB+_&<#Si%D$Z|@lu34Y8g#IdbSSm*{}$x{j^_|yhBTx@ zkR)Yg_9xB^Kz@{*qGj7=>+?%~Z`B*E3OUk%AkGYI{B*p?H;Vbo0sIoYKxLOLdDhA= zfm{_&>jT$Ya4mxU7pv5Cv(xLl8s2a$>?Q{v2Viu(FfXDKzU%V$ z8FNE8Fh^*i*8SqIn6tBU*k#o6`KKt96T?-pZ}eQ|RfK9vk{j_S*ZVWZ`=?siuQK#K z<*+o)T_KPeuTd$LtYiVH`od#gCh7OI-wix+*a(tU8QerT8x6&7`g}kv6T;gHKjGc( zPn4_)YR1O%Qv;rJdhVCW4_7i~Qhuvo>HBeZ$vhm&)pA(vtOe!ad<`D7GAwk7dMuW& z-8#Z9`PB{|!xjPFqX5ZNL7li6s#gmYK?BeJh(5)-G11$5kw42Mrv@u5OvI%X78`rI zl*Hf(Ue8Tr9!CLwrWPGQ*U=g?TdXL}lWK0cR^Uab+eIq-jw#K7WChA29+hK44gC%s z#|XA9++457di3uzDwJk^vy#txu?k9?xEh}OyY^&_Zo9GK(Aj>IDFH)c1<{(9n>fyHzyB$8S12$KE9(7u|N}(Emr&ImcE0MSH&{yC&PVjmfrc+pfvB zZF9n8+qPYk?Vi)`-urs~tN$xQZtOE=IvS55Yw$@b3M83mbVPP2{- zk0>m7S#(F7sH!f%58OAc>lF1-R^-9*+YKKr)HBxF$k_*f_|C+nqWxBZN%sIHA)m5E zX+Oh$?aOh9D_J;ed|q%D)x(&~9Ns43AjHrk*ZZ`7A&)$x>suY-oOZHmS=4u%-vn)j zD@t@DpIfk?>HM1%k1kr^EAk)-ofP0=pJ9%p{|_?oNsv%Z!e-w7IHu*{Hez^4^U!(; zKz7Vx`~Dr0NO47tBwd{^v_SKfGQ+z0A`X#Wm>5oq&R|$AI?m7u5p`CWyw~$v?^*WK zAs%>qe*)C6Yg@aAjI#~<`ufs%z1a9ap9_B#!uCS-mdWo{*$<8LebJHb567B4U+*e2 zC7yM!pF_Z=`l2Es^#U}7daENTTbuTUAYIRt^YG0@dcl51%?hXB_Tu#?*3DC%YGh

    -
    - Name: - -
    -
    - -
    -
    -
    - -
    UP
    - - -
    - - - - - - - - - - - - - -
    Antenna
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - - - -
    TX
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - -
    RX
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - -
    -
    Ues
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - - -
    - - - -
    -
    - '''.format(beam=beam, checked_onlyrx=checked_onlyrx) - - script = ''' - - - - '''.format(beam=beam) - - return mark_safe(style+html+script) - - -class EditDownDataWidget(forms.widgets.TextInput): - - def render(self, name, value, attrs=None, renderer=None): - - try: - beam = attrs.get('beam', value) - except: - return - - checked_onlyrx = '' - if beam.get_down_onlyrx == True: - checked_onlyrx = 'checked="True"' - - html = ''' -
    -
    -
    - -
    Down
    - - -
    - - - - - - - - - - - - - -
    Antenna
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - - - -
    TX
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - -
    RX
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - -
    -
    Ues
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - - -
    - - -
    -
    - '''.format(beam=beam, checked_onlyrx=checked_onlyrx) - - script = ''' - - - - '''.format(beam=beam) - - - - return mark_safe(style+html+script) - - - -class UpDataWidget(forms.widgets.TextInput): - - def render(self, name, value, attrs=None, renderer=None): - - - html = ''' - -
    -
    - Name: - -
    -
    - -
    -
    -
    - -
    UP
    - - -
    - - - - - - - - - - - - - -
    Antenna
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - - - -
    TX
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - -
    RX
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - -
    -
    Ues
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    - - -
    - - -
    -
    - ''' - - script = ''' - - ''' - - return mark_safe(style+html+script) - - -class DownDataWidget(forms.widgets.TextInput): - - def render(self, name, value, attrs=None, renderer=None): - - html = ''' -
    -
    -
    - -
    Down
    - - -
    - - - - - - - - - - - - - -
    Antenna
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - - - -
    TX
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - -
    RX
    North Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    East Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    West Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    South Quarter - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - -
    -
    Ues
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - - -
    - - -
    -
    - ''' - - script = ''' - - - ''' - - - - return mark_safe(style+html+script) diff --git a/apps/cgs/__init__.py b/apps/cgs/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/cgs/__init__.py +++ /dev/null diff --git a/apps/cgs/admin.py b/apps/cgs/admin.py deleted file mode 100644 index b99ed9a..0000000 --- a/apps/cgs/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.contrib import admin -from .models import CGSConfiguration - -# Register your models here. - -admin.site.register(CGSConfiguration) diff --git a/apps/cgs/files.py b/apps/cgs/files.py deleted file mode 100644 index fa1dcab..0000000 --- a/apps/cgs/files.py +++ /dev/null @@ -1,31 +0,0 @@ -''' -Created on Feb 15, 2016 - -@author: Miguel Urco -''' - -import json - -def read_json_file(fp): - - kwargs = {} - - json_data = fp - data = json.load(json_data) - json_data.close() - - frequency0 = data["Frequencies"][0][1] - frequency1 = data["Frequencies"][1][1] - frequency2 = data["Frequencies"][2][1] - frequency3 = data["Frequencies"][3][1] - - kwargs['freq0'] = frequency0 - kwargs['freq1'] = frequency1 - kwargs['freq2'] = frequency2 - kwargs['freq3'] = frequency3 - - return kwargs - - -def write_json_file(filename): - pass \ No newline at end of file diff --git a/apps/cgs/forms.py b/apps/cgs/forms.py deleted file mode 100644 index 88469d2..0000000 --- a/apps/cgs/forms.py +++ /dev/null @@ -1,32 +0,0 @@ -from django import forms -from apps.main.models import Device -from .models import CGSConfiguration - -class CGSConfigurationForm(forms.ModelForm): - - def __init__(self, *args, **kwargs): - #request = kwargs.pop('request') - super(CGSConfigurationForm, self).__init__(*args, **kwargs) - - instance = getattr(self, 'instance', None) - - if instance and instance.pk: - - devices = Device.objects.filter(device_type__name='cgs') - - if instance.experiment: - self.fields['experiment'].widget.attrs['disabled'] = 'disabled' - - self.fields['device'].widget.choices = [(device.id, device) for device in devices] - - def clean(self): - return - - class Meta: - model = CGSConfiguration - exclude = ('type', 'parameters', 'status', 'author', 'hash') - - -class UploadFileForm(forms.Form): - title = forms.CharField(label='Extension Type', widget=forms.TextInput(attrs={'readonly':'readonly'})) - file = forms.FileField() diff --git a/apps/cgs/migrations/__init__.py b/apps/cgs/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/cgs/migrations/__init__.py +++ /dev/null diff --git a/apps/cgs/models.py b/apps/cgs/models.py deleted file mode 100644 index 26c458d..0000000 --- a/apps/cgs/models.py +++ /dev/null @@ -1,184 +0,0 @@ -from django.db import models -from apps.main.models import Configuration -from apps.main.utils import Params -from django.core.validators import MinValueValidator, MaxValueValidator - -from .files import read_json_file -import requests -# Create your models here. validators=[MinValueValidator(62.5e6), MaxValueValidator(450e6)] - -class CGSConfiguration(Configuration): - - freq0 = models.PositiveIntegerField(verbose_name='Frequency 0 (Hz)',validators=[MaxValueValidator(450e6)], default = 60) - freq1 = models.PositiveIntegerField(verbose_name='Frequency 1 (Hz)',validators=[MaxValueValidator(450e6)], default = 60) - freq2 = models.PositiveIntegerField(verbose_name='Frequency 2 (Hz)',validators=[MaxValueValidator(450e6)], default = 60) - freq3 = models.PositiveIntegerField(verbose_name='Frequency 3 (Hz)',validators=[MaxValueValidator(450e6)], default = 60) - - def verify_frequencies(self): - - return True - - - def status_device(self): - - ip=self.device.ip_address - port=self.device.port_address - - route = "http://" + str(ip) + ":" + str(port) + "/status/" - try: - r = requests.get(route, timeout=0.7) - except Exception as e: - self.device.status = 0 - self.device.save() - self.message = 'Could not read CGS status: ' + str(e) - return False - - response = r.json() - self.device.status = response['status'] - self.message = response['message'] - self.device.save() - - if response['components_status']==0: - return False - - return True - - - def start_device(self): - - ip=self.device.ip_address - port=self.device.port_address - - #---Device must be configured - if not self.device.status == 2: - self.message = 'CGS Device must be configured.' - return False - #---Frequencies from form - post_data = self.parms_to_dict() - route = "http://" + str(ip) + ":" + str(port) + "/write/" - - try: - r = requests.post(route, post_data, timeout=0.7) - except Exception as e: - self.message = "Could not start CGS device. "+str(e) - return False - - response = r.json() - if response['status']==1: - self.device.status = 1 - self.device.save() - self.message = response['message'] - return False - - self.device.status = response['status'] - self.device.save() - self.message = response['message'] - - return True - - - def stop_device(self): - - ip=self.device.ip_address - port=self.device.port_address - - if self.device.status == 2: #Configured - self.message = 'CGS device is already stopped.' - return False - - post_data = {"freq0":0, "freq1":0, "freq2":0, "freq3":0} - route = "http://" + str(ip) + ":" + str(port) + "/write/" - - try: - r = requests.post(route, post_data, timeout=0.7) - except Exception as e: - self.message = "Could not write CGS parameters. "+str(e) - self.device.status = 0 - self.device.save() - return False - - response = r.json() - status = response['status'] - if status == 1: - self.device.status = status - self.device.save() - self.message = 'Could not stop CGS device.' - return False - - self.message = 'CGS device has been stopped successfully.' - self.device.status = 2 - self.device.save() - - return True - - - def read_device(self): - - ip=self.device.ip_address - port=self.device.port_address - - route = "http://" + str(ip) + ":" + str(port) + "/read/" - try: - frequencies = requests.get(route,timeout=0.7) - except: - self.message = "Could not read CGS parameters from this device" - return None - - frequencies = frequencies.json() - if frequencies: - frequencies = frequencies.get("Frequencies") - freq0 = frequencies.get("freq0") - freq1 = frequencies.get("freq1") - freq2 = frequencies.get("freq2") - freq3 = frequencies.get("freq3") - - parms = {'freq0': freq0, - 'freq1': freq1, - 'freq2': freq2, - 'freq3': freq3} - - self.message = "CGS parameters have been successfully read" - return parms - else: - self.message = "Error reading CGS parameters" - return None - - - def write_device(self): - - ip=self.device.ip_address - port=self.device.port_address - - #---Frequencies from form - parms = self.parms_to_dict()['configurations'] - for parm in parms['allIds']: - byid = parm - frequencies = parms['byId'][byid] - post_data = {} - for data in frequencies: - if data in ['freq0','freq1','freq2','freq3']: - post_data[data] = frequencies[data] - - route = "http://" + str(ip) + ":" + str(port) + "/write/" - print (post_data) - try: - r = requests.post(route, post_data, timeout=0.7) - except: - self.message = "Could not write CGS parameters" - self.device.status = 0 - self.device.save() - return False - - response = r.json() - self.message = response['message'] - self.device.status = response['status'] - self.device.save() - - if self.device.status==1: - return False - - return True - - - class Meta: - db_table = 'cgs_configurations' diff --git a/apps/cgs/templates/cgs_conf.html b/apps/cgs/templates/cgs_conf.html deleted file mode 100644 index 99944d7..0000000 --- a/apps/cgs/templates/cgs_conf.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "dev_conf.html" %} \ No newline at end of file diff --git a/apps/cgs/templates/cgs_conf_edit.html b/apps/cgs/templates/cgs_conf_edit.html deleted file mode 100644 index cb59184..0000000 --- a/apps/cgs/templates/cgs_conf_edit.html +++ /dev/null @@ -1,76 +0,0 @@ -{% extends "dev_conf_edit.html" %} -{% load bootstrap4 %} -{% load static %} -{% load main_tags %} - -{% block extra-js%} - - -{% endblock %} \ No newline at end of file diff --git a/apps/cgs/templates/cgs_conf_import.html b/apps/cgs/templates/cgs_conf_import.html deleted file mode 100644 index 3647997..0000000 --- a/apps/cgs/templates/cgs_conf_import.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "dev_conf_edit.html" %} -{% load bootstrap4 %} -{% load static %} -{% load main_tags %} - -{% block extra-js%} -{% endblock %} \ No newline at end of file diff --git a/apps/cgs/templates/index.html b/apps/cgs/templates/index.html deleted file mode 100644 index e21ab6b..0000000 --- a/apps/cgs/templates/index.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "base.html" %} -{% block mainactive %}active{% endblock %} - -{% block content-title %}TITLE{% endblock %} -{% block content-suptitle %}Suptitle{% endblock %} - -{% block content %} -

    - {% lorem %} -

    -{% endblock %} - -{% block sidebar%} - -{% endblock %} diff --git a/apps/cgs/templates/index_cgs.html b/apps/cgs/templates/index_cgs.html deleted file mode 100644 index 24e6607..0000000 --- a/apps/cgs/templates/index_cgs.html +++ /dev/null @@ -1,48 +0,0 @@ -{% extends "base.html" %} -{% load bootstrap4 %} -{% block mainactive %}active{% endblock %} - -{% block content-title %}DEVICE CGS{% endblock %} -{% block content-suptitle %}CLOCK GENERATOR AND SYNCHRONIZER{% endblock %} - -{% block content %} -

    - Ingresar Frecuencias -

    - - - - - {% if form.is_multipart %} - - - -
    - {% else %} - - {% endif %} - - {% if step_field %} - - {% endif %} - - {% if submit_method != 'GET' and submit_method != 'get' %} - {% csrf_token %} - {% endif %} - - - - -
    - {% bootstrap_form form size='medium' %} - -
    -{% endblock %} - -{% block sidebar%} - -{% endblock %} - - diff --git a/apps/cgs/tests.py b/apps/cgs/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/apps/cgs/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/cgs/urls.py b/apps/cgs/urls.py deleted file mode 100644 index f969656..0000000 --- a/apps/cgs/urls.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.urls import path - -from . import views - -urlpatterns = ( - path('/', views.cgs_conf, name='url_cgs_conf'), - path('/edit/', views.cgs_conf_edit, name='url_edit_cgs_conf'), -) diff --git a/apps/cgs/views.py b/apps/cgs/views.py deleted file mode 100644 index 33655bb..0000000 --- a/apps/cgs/views.py +++ /dev/null @@ -1,75 +0,0 @@ -from django.shortcuts import redirect, render, get_object_or_404 -from django.contrib import messages -from django.http import HttpResponse - -from apps.main.models import Experiment -from .models import CGSConfiguration - -from .forms import CGSConfigurationForm, UploadFileForm -from apps.main.views import sidebar - -import requests -import json -#from __builtin__ import None -# Create your views here. - -def cgs_conf(request, id_conf): - - conf = get_object_or_404(CGSConfiguration, pk=id_conf) - - ip=conf.device.ip_address - port=conf.device.port_address - - kwargs = {} - - kwargs['status'] = conf.device.get_status_display() - - kwargs['dev_conf'] = conf - kwargs['dev_conf_keys'] = ['label', - 'freq0', 'freq1', - 'freq2', 'freq3'] - - kwargs['title'] = 'CGS Configuration' - kwargs['suptitle'] = 'Details' - - kwargs['button'] = 'Edit Configuration' - - #kwargs['no_play'] = True - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf=conf)) - - return render(request, 'cgs_conf.html', kwargs) - -def cgs_conf_edit(request, id_conf): - - conf = get_object_or_404(CGSConfiguration, pk=id_conf) - - if request.method=='GET': - form = CGSConfigurationForm(instance=conf) - - if request.method=='POST': - form = CGSConfigurationForm(request.POST, instance=conf) - - if form.is_valid(): - if conf.freq0 == None: conf.freq0 = 0 - if conf.freq1 == None: conf.freq1 = 0 - if conf.freq2 == None: conf.freq2 = 0 - if conf.freq3 == None: conf.freq3 = 0 - - conf = form.save(commit=False) - - if conf.verify_frequencies(): - conf.save() - return redirect('url_cgs_conf', id_conf=conf.id) - - ##ERRORS - - kwargs = {} - kwargs['id_dev'] = conf.id - kwargs['form'] = form - kwargs['title'] = 'Device Configuration' - kwargs['suptitle'] = 'Edit' - kwargs['button'] = 'Save' - - return render(request, 'cgs_conf_edit.html', kwargs) diff --git a/apps/dds/__init__.py b/apps/dds/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/dds/__init__.py +++ /dev/null diff --git a/apps/dds/admin.py b/apps/dds/admin.py deleted file mode 100644 index ac325b6..0000000 --- a/apps/dds/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.contrib import admin -from .models import DDSConfiguration - -# Register your models here. - -admin.site.register(DDSConfiguration) diff --git a/apps/dds/forms.py b/apps/dds/forms.py deleted file mode 100644 index 61d617e..0000000 --- a/apps/dds/forms.py +++ /dev/null @@ -1,27 +0,0 @@ -from django import forms -from apps.main.models import Device -from .models import DDSConfiguration - -# from django.core.validators import MinValueValidator, MaxValueValidator - -class DDSConfigurationForm(forms.ModelForm): - - def __init__(self, *args, **kwargs): - - super(DDSConfigurationForm, self).__init__(*args, **kwargs) - - instance = getattr(self, 'instance', None) - - if instance and instance.pk: - - devices = Device.objects.filter(device_type__name='dds') - - #if instance.experiment: - # self.fields['experiment'].widget.attrs['disabled'] = 'disabled' - - self.fields['device'].widget.choices = [(device.id, device) for device in devices] - - - class Meta: - model = DDSConfiguration - exclude = ('type', 'parameters', 'status', 'author', 'hash') \ No newline at end of file diff --git a/apps/dds/migrations/__init__.py b/apps/dds/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/dds/migrations/__init__.py +++ /dev/null diff --git a/apps/dds/models.py b/apps/dds/models.py deleted file mode 100644 index 1edbe1c..0000000 --- a/apps/dds/models.py +++ /dev/null @@ -1,164 +0,0 @@ -from django.db import models -from apps.main.models import Configuration -from apps.main.utils import Params -# Create your models here. - -from django.core.validators import MinValueValidator, MaxValueValidator -from django.core.exceptions import ValidationError - -from devices.dds import api, data - -ENABLE_TYPE = ( - (False, 'Disabled'), - (True, 'Enabled'), - ) -MOD_TYPES = ( - (0, 'Single Tone'), - (1, 'FSK'), - (2, 'Ramped FSK'), - (3, 'Chirp'), - (4, 'BPSK'), - ) - -class DDSConfiguration(Configuration): - - DDS_NBITS = 48 - - clock = models.FloatField(verbose_name='Clock In (MHz)',validators=[MinValueValidator(5), MaxValueValidator(75)], null=True, default=60) - multiplier = models.PositiveIntegerField(verbose_name='Multiplier',validators=[MinValueValidator(1), MaxValueValidator(20)], default=4) - - frequencyA_Mhz = models.DecimalField(verbose_name='Frequency A (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, null=True, default=49.9200) - frequencyA = models.BigIntegerField(verbose_name='Frequency A (Decimal)',validators=[MinValueValidator(0), MaxValueValidator(2**DDS_NBITS-1)], blank=True, null=True) - - frequencyB_Mhz = models.DecimalField(verbose_name='Frequency B (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, blank=True, null=True) - frequencyB = models.BigIntegerField(verbose_name='Frequency B (Decimal)',validators=[MinValueValidator(0), MaxValueValidator(2**DDS_NBITS-1)], blank=True, null=True) - - phaseA_degrees = models.FloatField(verbose_name='Phase A (Degrees)', validators=[MinValueValidator(0), MaxValueValidator(360)], default=0) - - phaseB_degrees = models.FloatField(verbose_name='Phase B (Degrees)', validators=[MinValueValidator(0), MaxValueValidator(360)], blank=True, null=True) - - modulation = models.PositiveIntegerField(verbose_name='Modulation Type', choices = MOD_TYPES, default = 0) - - amplitude_enabled = models.BooleanField(verbose_name='Amplitude Control', choices=ENABLE_TYPE, default=False) - - amplitudeI = models.PositiveIntegerField(verbose_name='Amplitude CH1',validators=[MinValueValidator(0), MaxValueValidator(2**12-1)], blank=True, null=True) - amplitudeQ = models.PositiveIntegerField(verbose_name='Amplitude CH2',validators=[MinValueValidator(0), MaxValueValidator(2**12-1)], blank=True, null=True) - - - def get_nbits(self): - - return self.DDS_NBITS - - def clean(self): - - if self.modulation in [1,2,3]: - if self.frequencyB is None or self.frequencyB_Mhz is None: - raise ValidationError({ - 'frequencyB': 'Frequency modulation has to be defined when FSK or Chirp modulation is selected' - }) - - if self.modulation in [4,]: - if self.phaseB_degrees is None: - raise ValidationError({ - 'phaseB': 'Phase modulation has to be defined when BPSK modulation is selected' - }) - - self.frequencyA_Mhz = data.binary_to_freq(self.frequencyA, self.clock*self.multiplier) - self.frequencyB_Mhz = data.binary_to_freq(self.frequencyB, self.clock*self.multiplier) - - def verify_frequencies(self): - - return True - - def parms_to_text(self): - - my_dict = self.parms_to_dict()['configurations']['byId'][str(self.id)] - - text = data.dict_to_text(my_dict) - - return text - - def status_device(self): - - try: - answer = api.status(ip = self.device.ip_address, - port = self.device.port_address) - if 'clock' in answer: - self.device.status = 1 - else: - self.device.status = answer[0] - self.message = 'DDS - {}'.format(answer[2:]) - except Exception as e: - self.message = str(e) - self.device.status = 0 - - self.device.save() - - if self.device.status in (0, '0'): - return False - else: - return True - - def reset_device(self): - - answer = api.reset(ip = self.device.ip_address, - port = self.device.port_address) - - if answer[0] != "1": - self.message = 'DDS - {}'.format(answer[2:]) - return 0 - - self.message = 'DDS - {}'.format(answer[2:]) - return 1 - - def stop_device(self): - - try: - answer = api.disable_rf(ip = self.device.ip_address, - port = self.device.port_address) - - return self.status_device() - - except Exception as e: - self.message = str(e) - return False - - def start_device(self): - - try: - answer = api.enable_rf(ip = self.device.ip_address, - port = self.device.port_address) - - return self.status_device() - - except Exception as e: - self.message = str(e) - return False - - def read_device(self): - - parms = api.read_config(ip = self.device.ip_address, - port = self.device.port_address) - if not parms: - self.message = "Could not read DDS parameters from this device" - return parms - - self.message = "" - return parms - - - def write_device(self): - - try: - answer = api.write_config(ip = self.device.ip_address, - port = self.device.port_address, - parms = self.parms_to_dict()['configurations']['byId'][str(self.id)]) - - return self.status_device() - - except Exception as e: - self.message = str(e) - return False - - class Meta: - db_table = 'dds_configurations' diff --git a/apps/dds/static/js/dds_conversion.js b/apps/dds/static/js/dds_conversion.js deleted file mode 100644 index 196dd74..0000000 --- a/apps/dds/static/js/dds_conversion.js +++ /dev/null @@ -1,44 +0,0 @@ -function freq2Binary(mclock, frequency) { - - var freq_bin = parseInt(frequency * (Math.pow(2,48)/mclock)); - return freq_bin; - -} - -function binary2Freq(mclock, binary) { - - var frequency = (1.0*binary) / (Math.pow(2,48)/mclock); - return frequency; -} - - -function binary2FreqDelta(mclock, binary) { - - var frequency = (1.0*binary) / (Math.pow(2,48)/mclock); - return frequency; -} - -function freqDelta2Binary(mclock, frequency) { - - var freq_bin = parseInt(frequency * (Math.pow(2,48)/mclock)); - return freq_bin; -} - -function binary2Ramp(mclock, binary) { - - var frequency = (1.0*mclock) / (binary+1); - return frequency; -} - -function freqRamp2Binary(mclock, frequency) { - - var freq_bin = parseInt(mclock/frequency-1); - return freq_bin; -} - -function us2Ramp(step_us) { - //periodo_delpaso = sysclockperiod x (N+1) - // freqsys/(N+1)=freq_ddelpaso=1/step - var freq = (1.0/(step_us)); - return freq; -} \ No newline at end of file diff --git a/apps/dds/templates/dds_conf.html b/apps/dds/templates/dds_conf.html deleted file mode 100644 index 99944d7..0000000 --- a/apps/dds/templates/dds_conf.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "dev_conf.html" %} \ No newline at end of file diff --git a/apps/dds/templates/dds_conf_edit.html b/apps/dds/templates/dds_conf_edit.html deleted file mode 100644 index 0ce2ee5..0000000 --- a/apps/dds/templates/dds_conf_edit.html +++ /dev/null @@ -1,75 +0,0 @@ -{% extends "dev_conf_edit.html" %} -{% load bootstrap4 %} -{% load static %} -{% load main_tags %} - -{% block extra-js%} - - -{% endblock %} \ No newline at end of file diff --git a/apps/dds/templates/dds_conf_import.html b/apps/dds/templates/dds_conf_import.html deleted file mode 100644 index 3647997..0000000 --- a/apps/dds/templates/dds_conf_import.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "dev_conf_edit.html" %} -{% load bootstrap4 %} -{% load static %} -{% load main_tags %} - -{% block extra-js%} -{% endblock %} \ No newline at end of file diff --git a/apps/dds/tests.py b/apps/dds/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/apps/dds/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/dds/urls.py b/apps/dds/urls.py deleted file mode 100644 index 71392b5..0000000 --- a/apps/dds/urls.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.urls import path - -from . import views - -urlpatterns = ( - path('/', views.dds_conf, name='url_dds_conf'), - path('//', views.dds_conf, name='url_dds_conf'), - path('/edit/', views.dds_conf_edit, name='url_edit_dds_conf'), -) diff --git a/apps/dds/views.py b/apps/dds/views.py deleted file mode 100644 index 0c5a7c8..0000000 --- a/apps/dds/views.py +++ /dev/null @@ -1,74 +0,0 @@ -# Create your views here. -from django.shortcuts import redirect, render, get_object_or_404 - -# from apps.main.models import Experiment, Configuration -from apps.main.views import sidebar - -from .models import DDSConfiguration -from .forms import DDSConfigurationForm -# Create your views here. - -def dds_conf(request, id_conf): - - conf = get_object_or_404(DDSConfiguration, pk=id_conf) - - kwargs = {} - - kwargs['status'] = conf.device.get_status_display() - -# if not kwargs['connected']: -# messages.error(request, message=answer) - - kwargs['dev_conf'] = conf - kwargs['dev_conf_keys'] = [ - 'clock', - 'multiplier', - 'frequencyA_Mhz', - 'frequencyA', - 'frequencyB_Mhz', - 'frequencyB', - 'phaseA_degrees', - 'phaseB_degrees', - 'modulation', - 'amplitude_enabled', - 'amplitudeI', - 'amplitudeQ'] - - kwargs['title'] = 'DDS Configuration' - kwargs['suptitle'] = 'Details' - - kwargs['button'] = 'Edit Configuration' - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf=conf)) - - return render(request, 'dds_conf.html', kwargs) - -def dds_conf_edit(request, id_conf): - - conf = get_object_or_404(DDSConfiguration, pk=id_conf) - - if request.method=='GET': - form = DDSConfigurationForm(instance=conf) - - if request.method=='POST': - form = DDSConfigurationForm(request.POST, instance=conf) - - if form.is_valid(): - conf = form.save(commit=False) - - if conf.verify_frequencies(): - - conf.save() - return redirect('url_dds_conf', id_conf=conf.id) - - ##ERRORS - - kwargs = {} - kwargs['id_dev'] = conf.id - kwargs['form'] = form - kwargs['title'] = 'Device Configuration' - kwargs['suptitle'] = 'Edit' - kwargs['button'] = 'Save' - - return render(request, 'dds_conf_edit.html', kwargs) diff --git a/apps/dds_rest/__init__.py b/apps/dds_rest/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/dds_rest/__init__.py +++ /dev/null diff --git a/apps/dds_rest/admin.py b/apps/dds_rest/admin.py deleted file mode 100644 index b78fa05..0000000 --- a/apps/dds_rest/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.contrib import admin -from .models import DDSRestConfiguration - -# Register your models here. - -admin.site.register(DDSRestConfiguration) diff --git a/apps/dds_rest/forms.py b/apps/dds_rest/forms.py deleted file mode 100644 index de85ee1..0000000 --- a/apps/dds_rest/forms.py +++ /dev/null @@ -1,27 +0,0 @@ -from django import forms -from apps.main.models import Device -from .models import DDSRestConfiguration - -# from django.core.validators import MinValueValidator, MaxValueValidator - -class DDSRestConfigurationForm(forms.ModelForm): - - def __init__(self, *args, **kwargs): - - super(DDSRestConfigurationForm, self).__init__(*args, **kwargs) - - instance = getattr(self, 'instance', None) - - if instance and instance.pk: - - devices = Device.objects.filter(device_type__name='dds_rest') - - #if instance.experiment: - # self.fields['experiment'].widget.attrs['disabled'] = 'disabled' - - self.fields['device'].widget.choices = [(device.id, device) for device in devices] - - - class Meta: - model = DDSRestConfiguration - exclude = ('type', 'parameters', 'status', 'author', 'hash') diff --git a/apps/dds_rest/models.py b/apps/dds_rest/models.py deleted file mode 100644 index db05bbb..0000000 --- a/apps/dds_rest/models.py +++ /dev/null @@ -1,305 +0,0 @@ -import ast -import json -import requests -import numpy as np -from base64 import b64encode -from struct import pack - -from django.urls import reverse -from django.db import models -from apps.main.models import Configuration -from apps.main.utils import Params -# Create your models here. - -from django.core.validators import MinValueValidator, MaxValueValidator -from django.core.exceptions import ValidationError - -from devices.dds_rest import api, data - -ENABLE_TYPE = ( - (False, 'Disabled'), - (True, 'Enabled'), - ) -MOD_TYPES = ( - (0, 'Single Tone'), - (1, 'FSK'), - (2, 'Ramped FSK'), - (3, 'Chirp'), - (4, 'BPSK'), - ) - -class DDSRestConfiguration(Configuration): - - DDS_NBITS = 48 - - clock = models.FloatField(verbose_name='Clock In (MHz)',validators=[MinValueValidator(5), MaxValueValidator(75)], null=True, default=60) - multiplier = models.PositiveIntegerField(verbose_name='Multiplier',validators=[MinValueValidator(1), MaxValueValidator(20)], default=4) - - frequencyA_Mhz = models.DecimalField(verbose_name='Frequency A (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, null=True, default=49.9200) - frequencyA = models.BigIntegerField(verbose_name='Frequency A (Decimal)',validators=[MinValueValidator(0), MaxValueValidator(2**DDS_NBITS-1)], blank=True, null=True) - - frequencyB_Mhz = models.DecimalField(verbose_name='Frequency B (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, blank=True, null=True) - frequencyB = models.BigIntegerField(verbose_name='Frequency B (Decimal)',validators=[MinValueValidator(0), MaxValueValidator(2**DDS_NBITS-1)], blank=True, null=True) - - delta_frequency_Mhz = models.DecimalField(verbose_name='Delta frequency (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, blank=True, null=True) - delta_frequency = models.BigIntegerField(verbose_name='Delta frequency (Decimal)',validators=[MinValueValidator(0), MaxValueValidator(2**DDS_NBITS-1)], blank=True, null=True) - - update_clock_Mhz = models.DecimalField(verbose_name='Update clock (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, blank=True, null=True) - update_clock = models.BigIntegerField(verbose_name='Update clock (Decimal)',validators=[MinValueValidator(0), MaxValueValidator(2**32-1)], blank=True, null=True) - - ramp_rate_clock_Mhz = models.DecimalField(verbose_name='Ramp rate clock (MHz)', validators=[MinValueValidator(0), MaxValueValidator(150)], max_digits=19, decimal_places=16, blank=True, null=True) - ramp_rate_clock = models.BigIntegerField(verbose_name='Ramp rate clock (Decimal)',validators=[MinValueValidator(0), MaxValueValidator(2**18-1)], blank=True, null=True) - - phaseA_degrees = models.FloatField(verbose_name='Phase A (Degrees)', validators=[MinValueValidator(0), MaxValueValidator(360)], default=0) - - phaseB_degrees = models.FloatField(verbose_name='Phase B (Degrees)', validators=[MinValueValidator(0), MaxValueValidator(360)], blank=True, null=True) - - modulation = models.PositiveIntegerField(verbose_name='Modulation Type', choices = MOD_TYPES, default = 0) - - amplitude_enabled = models.BooleanField(verbose_name='Amplitude Control', choices=ENABLE_TYPE, default=False) - - amplitudeI = models.PositiveIntegerField(verbose_name='Amplitude CH1',validators=[MinValueValidator(0), MaxValueValidator(2**12-1)], blank=True, null=True) - amplitudeQ = models.PositiveIntegerField(verbose_name='Amplitude CH2',validators=[MinValueValidator(0), MaxValueValidator(2**12-1)], blank=True, null=True) - - - def get_nbits(self): - - return self.DDS_NBITS - - def clean(self): - - if self.modulation in [1,2,3]: - if self.frequencyB is None or self.frequencyB_Mhz is None: - raise ValidationError({ - 'frequencyB': 'Frequency modulation has to be defined when FSK or Chirp modulation is selected' - }) - - if self.modulation in [4,]: - if self.phaseB_degrees is None: - raise ValidationError({ - 'phaseB': 'Phase modulation has to be defined when BPSK modulation is selected' - }) - - self.frequencyA_Mhz = data.binary_to_freq(self.frequencyA, self.clock*self.multiplier) - self.frequencyB_Mhz = data.binary_to_freq(self.frequencyB, self.clock*self.multiplier) - - def verify_frequencies(self): - - return True - - def parms_to_text(self): - - my_dict = self.parms_to_dict()['configurations']['byId'][str(self.id)] - - text = data.dict_to_text(my_dict) - - return text - - def status_device(self): - print("Status ") - try: - self.device.status = 0 - payload = self.request('status') - if payload['status']=='generating RF': - self.device.status = 3 - elif payload['status']=='no generating RF': - self.device.status = 2 - else: - self.device.status = 1 - self.device.save() - self.message = 'DDS REST status: {}'.format(payload['status']) - return False - except Exception as e: - if 'No route to host' not in str(e): - self.device.status = 4 - self.device.save() - self.message = 'DDS REST status: {}'.format(str(e)) - return False - - self.device.save() - return True - - def reset_device(self): - - try: - payload = self.request('reset', 'post') - if payload['reset']=='ok': - self.message = 'DDS REST restarted OK' - self.device.status = 2 - self.device.save() - else: - self.message = 'DDS REST restart fail' - self.device.status = 4 - self.device.save() - except Exception as e: - self.message = 'DDS REST reset: {}'.format(str(e)) - return False - - return True - - def stop_device(self): - - try: - payload = self.request('stop', 'post',data=json.dumps({'_rf_enable':0})) - self.message = 'DDS REST: {}'.format(payload['stop']) - if payload['stop']=='ok': - self.device.status = 2 - self.device.save() - else: - self.device.status = 4 - self.device.save() - return False - except Exception as e: - if 'No route to host' not in str(e): - self.device.status = 4 - else: - self.device.status = 0 - self.message = 'DDS REST stop: {}'.format(str(e)) - self.device.save() - return False - - return True - - def start_device(self): - - try: - payload = self.request('start', 'post',data=json.dumps({'_rf_enable':1})) - self.message = 'DDS REST start: {}'.format(payload['start']) - if payload['start']=='ok': - self.device.status = 3 - self.device.save() - else: - self.device.status = 2 - self.device.save() - return False - except Exception as e: - if 'No route to host' not in str(e): - self.device.status = 4 - else: - self.device.status = 0 - self.message = 'DDS REST start: {}'.format(str(e)) - self.device.save() - return False - - return True - - def read_device(self): - - parms = self.request('read') - print(parms) - if not parms: - self.message = "Could not read DDS REST parameters from this device" - return parms - - self.message = "" - return parms - - def arma_control(self,l_clock,l_multiplier,l_modulation): - sysclock = l_clock*l_multiplier - pll_range = 0 - if(sysclock>=200): - pll_range = 1 - l_control = ((l_modulation<<9)+(pll_range<<22)+(l_multiplier<<16)).to_bytes(4,'little') - return l_control - - def conv_phase(self,l_phase): - - l_phase_2B = int((l_phase*(2**14)/360)).to_bytes(2,'little') - return l_phase_2B - - def arma_data_write(self): - #clock = RCClock.objects.get(rc_configuration=self) - clock = self.clock - print(clock) - multiplier = self.multiplier - print(multiplier) - frequencyA_Mhz = self.frequencyA_Mhz - print(frequencyA_Mhz) - frequencyA = self.frequencyA - print(frequencyA) - frequencyB_Mhz = self.frequencyB_Mhz - print(frequencyB_Mhz) - frequencyB = self.frequencyB - print(frequencyB) - phaseA_degrees = self.phaseA_degrees or 0 - print(phaseA_degrees) - phaseB_degrees = self.phaseB_degrees or 0 - print(phaseB_degrees) - modulation = self.modulation or 0 - print(modulation) - amplitude_enabled = self.amplitude_enabled or 0 - print(amplitude_enabled) - amplitudeI = self.amplitudeI or 0 - print(amplitudeI) - amplitudeQ = self.amplitudeQ or 0 - print(amplitudeQ) - delta_frequency = self.delta_frequency or 0 - print(delta_frequency) - update_clock = self.update_clock or 0 - print(update_clock) - ramp_rate_clock = self.ramp_rate_clock or 0 - print(ramp_rate_clock) - osrr = 0 - qdac = 0 - control = self.arma_control(clock,multiplier,modulation) - phase1 = self.conv_phase(phaseA_degrees) - phase2 = self.conv_phase(phaseB_degrees) - - cadena_json = {'clock': (b64encode(pack(' -
    - -
    - - {% bootstrap_field form.frequencyA_Mhz layout='horizontal' size='medium' %} -
    - {% bootstrap_field form.frequencyA layout='horizontal' size='medium' %} -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.frequencyB_Mhz layout='horizontal' size='medium' %} -
    - {% bootstrap_field form.frequencyB layout='horizontal' size='medium' %} -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.delta_frequency_Mhz layout='horizontal' size='medium' %} -
    - {% bootstrap_field form.delta_frequency layout='horizontal' size='medium' %} -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    - {% bootstrap_field form.ramp_rate_clock_Mhz layout='horizontal' size='medium' %} - {% bootstrap_field form.ramp_rate_clock layout='horizontal' size='medium' %} -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.phaseA_degrees layout='horizontal' size='medium' %} -
    -
    - 9282 -
    -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.phaseB_degrees layout='horizontal' size='medium' %} -
    -
    - 9876 -
    -
    - 1000 0000 -
    -
    -
    - - {% bootstrap_field form.modulation layout='horizontal' size='medium' %} - {% bootstrap_field form.amplitude_enabled layout='horizontal' size='medium' %} - - {% bootstrap_field form.amplitudeI layout='horizontal' size='medium' %} - {% bootstrap_field form.amplitudeQ layout='horizontal' size='medium' %} - - -{% endblock %} - -{% block extra-js%} - - -{% endblock %} \ No newline at end of file diff --git a/apps/dds_rest/templates/dds_rest_conf_edit.html b/apps/dds_rest/templates/dds_rest_conf_edit.html deleted file mode 100644 index 0be81fe..0000000 --- a/apps/dds_rest/templates/dds_rest_conf_edit.html +++ /dev/null @@ -1,576 +0,0 @@ -{% extends "dev_conf_edit.html" %} -{% load bootstrap4 %} -{% load static %} -{% load main_tags %} - -{% block dds_rest%} - - {% bootstrap_field form.template layout='horizontal' size='medium' %} - {% bootstrap_field form.device layout='horizontal' size='medium' %} - {% bootstrap_field form.label layout='horizontal' size='medium' %} - {% bootstrap_field form.experiment layout='horizontal' size='medium' %} - - {% bootstrap_field form.clock layout='horizontal' size='medium' %} - {% bootstrap_field form.multiplier layout='horizontal' size='medium' %} - -
    -
    - -
    -
    - {% bootstrap_field form.frequencyA_Mhz layout='horizontal' size='medium' %} -
    - {% bootstrap_field form.frequencyA layout='horizontal' size='medium' %} -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.frequencyB_Mhz layout='horizontal' size='medium' %} -
    - {% bootstrap_field form.frequencyB layout='horizontal' size='medium' %} -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.delta_frequency_Mhz layout='horizontal' size='medium' %} -
    - {% bootstrap_field form.delta_frequency layout='horizontal' size='medium' %} -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    - {% bootstrap_field form.ramp_rate_clock_Mhz layout='horizontal' size='medium' %} - {% bootstrap_field form.ramp_rate_clock layout='horizontal' size='medium' %} -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.phaseA_degrees layout='horizontal' size='medium' %} -
    -
    - 9282 -
    -
    - 1000 0000 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.phaseB_degrees layout='horizontal' size='medium' %} -
    -
    - 9876 -
    -
    - 1000 0000 -
    -
    -
    - - {% bootstrap_field form.modulation layout='horizontal' size='medium' %} - {% bootstrap_field form.amplitude_enabled layout='horizontal' size='medium' %} - -
    -
    - -
    -
    - {% bootstrap_field form.amplitudeI layout='horizontal' size='medium' %} -
    -
    - 35 -
    -
    - 0000 0000 0010 -
    -
    -
    - -
    -
    - -
    -
    - {% bootstrap_field form.amplitudeQ layout='horizontal' size='medium' %} -
    -
    - 40 -
    -
    - 0000 1010 1000 -
    -
    -
    - - -{% endblock %} - -{% block extra-js%} - - -{% endblock %} \ No newline at end of file diff --git a/apps/dds_rest/templates/dds_rest_conf_import.html b/apps/dds_rest/templates/dds_rest_conf_import.html deleted file mode 100644 index 3647997..0000000 --- a/apps/dds_rest/templates/dds_rest_conf_import.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "dev_conf_edit.html" %} -{% load bootstrap4 %} -{% load static %} -{% load main_tags %} - -{% block extra-js%} -{% endblock %} \ No newline at end of file diff --git a/apps/dds_rest/tests.py b/apps/dds_rest/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/apps/dds_rest/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/dds_rest/urls.py b/apps/dds_rest/urls.py deleted file mode 100644 index 502fc7f..0000000 --- a/apps/dds_rest/urls.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.urls import path - -from . import views - -urlpatterns = ( - path('/', views.dds_rest_conf, name='url_dds_rest_conf'), - path('//', views.dds_rest_conf, name='url_dds_rest_conf'), - path('/edit/', views.dds_rest_conf_edit, name='url_edit_dds_rest_conf'), -) diff --git a/apps/dds_rest/views.py b/apps/dds_rest/views.py deleted file mode 100644 index 743b714..0000000 --- a/apps/dds_rest/views.py +++ /dev/null @@ -1,80 +0,0 @@ -# Create your views here. -from django.shortcuts import redirect, render, get_object_or_404 - -# from apps.main.models import Experiment, Configuration -from apps.main.views import sidebar - -from .models import DDSRestConfiguration -from .forms import DDSRestConfigurationForm -# Create your views here. - -def dds_rest_conf(request, id_conf): - - conf = get_object_or_404(DDSRestConfiguration, pk=id_conf) - - kwargs = {} - - kwargs['status'] = conf.device.get_status_display() - -# if not kwargs['connected']: -# messages.error(request, message=answer) - - kwargs['dev_conf'] = conf - kwargs['dev_conf_keys'] = [ - 'clock', - 'multiplier', - 'frequencyA_Mhz', - 'frequencyA', - 'frequencyB_Mhz', - 'frequencyB', - 'delta_frequency_Mhz', - 'delta_frequency', - 'update_clock_Mhz', - 'update_clock', - 'ramp_rate_clock_Mhz', - 'ramp_rate_clock', - 'phaseA_degrees', - 'phaseB_degrees', - 'modulation', - 'amplitude_enabled', - 'amplitudeI', - 'amplitudeQ'] - - kwargs['title'] = 'DDS Rest Configuration' - kwargs['suptitle'] = 'Details' - - kwargs['button'] = 'Edit Configuration' - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf=conf)) - - return render(request, 'dds_rest_conf.html', kwargs) - -def dds_rest_conf_edit(request, id_conf): - - conf = get_object_or_404(DDSRestConfiguration, pk=id_conf) - - if request.method=='GET': - form = DDSRestConfigurationForm(instance=conf) - - if request.method=='POST': - form = DDSRestConfigurationForm(request.POST, instance=conf) - - if form.is_valid(): - conf = form.save(commit=False) - - if conf.verify_frequencies(): - - conf.save() - return redirect('url_dds_rest_conf', id_conf=conf.id) - - ##ERRORS - - kwargs = {} - kwargs['id_dev'] = conf.id - kwargs['form'] = form - kwargs['title'] = 'Device Configuration' - kwargs['suptitle'] = 'Edit' - kwargs['button'] = 'Save' - kwargs['device_dds'] = 'dds_rest' - return render(request, 'dds_rest_conf_edit.html', kwargs) diff --git a/apps/jars/__init__.py b/apps/jars/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/jars/__init__.py +++ /dev/null diff --git a/apps/jars/admin.py b/apps/jars/admin.py deleted file mode 100644 index a2bed62..0000000 --- a/apps/jars/admin.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.contrib import admin -from .models import JARSConfiguration, JARSFilter - -# Register your models here. - -admin.site.register(JARSConfiguration) -admin.site.register(JARSFilter) diff --git a/apps/jars/fixtures/initial_filters_data.json b/apps/jars/fixtures/initial_filters_data.json deleted file mode 100644 index 3f37fcf..0000000 --- a/apps/jars/fixtures/initial_filters_data.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "fields": { - "name": "49_92MHz_clock60MHz_F1KHz_12_25_2", - "clock": 60, - "multiplier": 5, - "frequency": 49.92, - "f_decimal": 721554506, - "fir": 2, - "cic_2": 12, - "cic_5": 25 - }, - "model": "jars.jarsfilter", - "pk": 1 - }, - { - "fields": { - "name": "49_920MHz_clock60MHz_F1MHz_10_1_6", - "clock": 60, - "multiplier": 5, - "frequency": 49.92, - "f_decimal": 721554506, - "fir": 6, - "cic_2": 10, - "cic_5": 1 - }, - "model": "jars.jarsfilter", - "pk": 2 - } -] \ No newline at end of file diff --git a/apps/jars/forms.py b/apps/jars/forms.py deleted file mode 100644 index 7403680..0000000 --- a/apps/jars/forms.py +++ /dev/null @@ -1,111 +0,0 @@ -import os - -from django import forms -from apps.main.models import Device, Experiment -from .models import JARSConfiguration, JARSFilter -from .widgets import SpectralWidget -from apps.main.forms import add_empty_choice - -def create_choices_from_model(model, filter_id=None): - - #instance = globals()[model] - choices = model.objects.all().values_list('pk', 'name') - choices = add_empty_choice(choices,label="New Choice") - return choices - -class JARSConfigurationForm(forms.ModelForm): - def __init__(self, *args, **kwargs): - super(JARSConfigurationForm, self).__init__(*args, **kwargs) - instance = getattr(self, 'instance', None) - - if instance and instance.pk: - devices = Device.objects.filter(device_type__name='jars') - self.fields['device'].widget.choices = [(device.id, device) for device in devices] - self.fields['spectral_number'].widget.attrs['readonly'] = True - self.fields['spectral'].widget = SpectralWidget() - - class Meta: - model = JARSConfiguration - exclude = ('type', 'parameters', 'status', 'filter_parms', 'author', 'hash', 'filter') - -class JARSFilterForm(forms.ModelForm): - def __init__(self, *args, **kwargs): - super(JARSFilterForm, self).__init__(*args, **kwargs) - instance = getattr(self, 'instance', None) - - self.fields['f_decimal'].widget.attrs['readonly'] = True - - if 'initial' in kwargs: - self.fields['filter_template'] = forms.ChoiceField( - choices = create_choices_from_model(JARSFilter), - initial = kwargs['initial']['id'] - ) - # self.fields['name'].initial = kwargs['initial']['id'] - - # filter_id = kwargs['initial']['filter_id'] - - # if filter_id == 0: - # for value in self.fields: - # if value != 'name': - # self.fields.pop(value) - # self.fields['name'].label = "Filter Template Name" - # else: - # self.fields['name'] = forms.ChoiceField(choices=create_choices_from_model(JARSFilter, kwargs['initial']['filter_id'])) - # jars_filter = JARSFilter.objects.get(pk=kwargs['initial']['filter_id']) - # labels = [f.name for f in jars_filter._meta.get_fields()] - - # for label in ['id']: - # labels.remove(label) - # for label in labels: - # self.fields['name'].initial = kwargs['initial']['filter_id'] - # self.fields[label].initial = getattr(jars_filter,label) - # self.fields['name'].label = "Filter Template Name" - - class Meta: - model = JARSFilter - exclude = ('name', ) - -class JARSFilterFormNew(forms.ModelForm): - def __init__(self, *args, **kwargs): - super(JARSFilterFormNew, self).__init__(*args, **kwargs) - - self.fields['f_decimal'].widget.attrs['readonly'] = True - - class Meta: - model = JARSFilter - exclude = () - -class ExtFileField(forms.FileField): - """ - Same as forms.FileField, but you can specify a file extension whitelist. - - >>> from django.core.files.uploadedfile import SimpleUploadedFile - >>> - >>> t = ExtFileField(ext_whitelist=(".pdf", ".txt")) - >>> - >>> t.clean(SimpleUploadedFile('filename.pdf', 'Some File Content')) - >>> t.clean(SimpleUploadedFile('filename.txt', 'Some File Content')) - >>> - >>> t.clean(SimpleUploadedFile('filename.exe', 'Some File Content')) - Traceback (most recent call last): - ... - ValidationError: [u'Not allowed filetype!'] - """ - def __init__(self, *args, **kwargs): - extensions = kwargs.pop("extensions") - self.extensions = [i.lower() for i in extensions] - - super(ExtFileField, self).__init__(*args, **kwargs) - - def clean(self, *args, **kwargs): - data = super(ExtFileField, self).clean(*args, **kwargs) - filename = data.name - ext = os.path.splitext(filename)[1] - ext = ext.lower() - if ext not in self.extensions: - raise forms.ValidationError('Not allowed file type: %s' % ext) - - -class JARSImportForm(forms.Form): - - file_name = ExtFileField(extensions=['.racp','.json']) diff --git a/apps/jars/migrations/__init__.py b/apps/jars/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/jars/migrations/__init__.py +++ /dev/null diff --git a/apps/jars/models.py b/apps/jars/models.py deleted file mode 100644 index a776704..0000000 --- a/apps/jars/models.py +++ /dev/null @@ -1,404 +0,0 @@ -import json -import requests - -from django.db import models -from django.core.validators import MinValueValidator, MaxValueValidator -from django.urls import reverse - -from apps.main.models import Configuration -from apps.main.utils import Params -from .utils import create_jarsfiles - -# Create your models here. - -EXPERIMENT_TYPE = ( - (0, 'RAW_DATA'), - (1, 'PDATA'), -) - -DATA_TYPE = ( - (0, 'SHORT'), - (1, 'FLOAT'), -) - -DECODE_TYPE = ( - (0, 'None'), - (1, 'TimeDomain'), - (2, 'FreqDomain'), - (3, 'InvFreqDomain'), -) - -FILTER = '{"id":1, "clock": 60, "multiplier": 5, "frequency": 49.92, "f_decimal": 721554506, "fir": 2, "cic_2": 12, "cic_5": 25}' - -class JARSFilter(models.Model): - - JARS_NBITS = 32 - - name = models.CharField(verbose_name='Name', max_length=60, unique=True, default='') - clock = models.FloatField(verbose_name='Clock In (MHz)', validators=[ - MinValueValidator(5), MaxValueValidator(75)], null=True, default=60) - multiplier = models.PositiveIntegerField(verbose_name='Multiplier', validators=[ - MinValueValidator(1), MaxValueValidator(20)], default=5) - frequency = models.FloatField(verbose_name='Frequency (MHz)', validators=[ - MaxValueValidator(150)], null=True, default=49.9200) - f_decimal = models.BigIntegerField(verbose_name='Frequency (Decimal)', validators=[ - MinValueValidator(-9223372036854775808), MaxValueValidator(2**JARS_NBITS-1)], null=True, default=721554505) - cic_2 = models.PositiveIntegerField(verbose_name='CIC2', validators=[ - MinValueValidator(2), MaxValueValidator(100)], default=10) - scale_cic_2 = models.PositiveIntegerField(verbose_name='Scale CIC2', validators=[ - MinValueValidator(0), MaxValueValidator(6)], default=1) - cic_5 = models.PositiveIntegerField(verbose_name='CIC5', validators=[ - MinValueValidator(1), MaxValueValidator(100)], default=1) - scale_cic_5 = models.PositiveIntegerField(verbose_name='Scale CIC5', validators=[ - MinValueValidator(0), MaxValueValidator(20)], default=5) - fir = models.PositiveIntegerField(verbose_name='FIR', validators=[ - MinValueValidator(1), MaxValueValidator(100)], default=6) - scale_fir = models.PositiveIntegerField(verbose_name='Scale FIR', validators=[ - MinValueValidator(0), MaxValueValidator(7)], default=3) - number_taps = models.PositiveIntegerField(verbose_name='Number of taps', validators=[ - MinValueValidator(1), MaxValueValidator(256)], default=4) - taps = models.CharField(verbose_name='Taps', max_length=1600, default='0') - - class Meta: - db_table = 'jars_filters' - - def __unicode__(self): - return u'%s' % (self.name) - - def jsonify(self): - - data = {} - ignored = () - - for field in self._meta.fields: - if field.name in ignored: - continue - data[field.name] = field.value_from_object(self) - - return data - - def parms_to_dict(self): - - parameters = {} - - parameters['name'] = self.name - parameters['clock'] = float(self.clock) - parameters['multiplier'] = int(self.multiplier) - parameters['frequency'] = float(self.frequency) - parameters['f_decimal'] = int(self.frequency) - parameters['fir'] = int(self.fir) - parameters['cic_2'] = int(self.cic_2) - parameters['cic_5'] = int(self.cic_5) - - return parameters - - def dict_to_parms(self, parameters): - - self.name = parameters['name'] - self.clock = parameters['clock'] - self.multiplier = parameters['multiplier'] - self.frequency = parameters['frequency'] - self.f_decimal = parameters['f_decimal'] - self.fir = parameters['fir'] - self.cic_2 = parameters['cic_2'] - self.cic_5 = parameters['cic_5'] - - def dict_to_parms_new(self, parameters): - - self.name = parameters['name'] - self.clock = parameters['clock'] - self.multiplier = parameters['multiplier'] - self.frequency = parameters['frequency'] - self.f_decimal = parameters['f_decimal'] - self.fir = parameters['fir'] - self.cic_2 = parameters['cic_2'] - self.cic_5 = parameters['cic_5'] - self.scale_fir = parameters['scale_fir'] - self.scale_cic_2 = parameters['scale_cic_2'] - self.scale_cic_5 = parameters['scale_cic_5'] - self.number_taps = parameters['number_taps'] - self.taps = parameters['taps'] - -class JARSConfiguration(Configuration): - - ADC_RESOLUTION = 8 - PCI_DIO_BUSWIDTH = 32 - HEADER_VERSION = 1103 - BEGIN_ON_START = True - REFRESH_RATE = 1 - - exp_type = models.PositiveIntegerField( - verbose_name='Experiment Type', choices=EXPERIMENT_TYPE, default=0) - cards_number = models.PositiveIntegerField(verbose_name='Number of Cards', validators=[ - MinValueValidator(1), MaxValueValidator(4)], default=1) - channels_number = models.PositiveIntegerField(verbose_name='Number of Channels', validators=[ - MinValueValidator(1), MaxValueValidator(8)], default=5) - channels = models.CharField( - verbose_name='Channels', max_length=15, default='1,2,3,4,5') - data_type = models.PositiveIntegerField( - verbose_name='Data Type', choices=DATA_TYPE, default=0) - raw_data_blocks = models.PositiveIntegerField( - verbose_name='Raw Data Blocks', validators=[MaxValueValidator(5000)], default=60) - profiles_block = models.PositiveIntegerField( - verbose_name='Profiles Per Block', default=400) - acq_profiles = models.PositiveIntegerField( - verbose_name='Acquired Profiles', default=400) - ftp_interval = models.PositiveIntegerField( - verbose_name='FTP Interval', default=60) - fftpoints = models.PositiveIntegerField( - verbose_name='FFT Points', default=16) - cohe_integr_str = models.PositiveIntegerField( - verbose_name='Coh. Int. Stride', validators=[MinValueValidator(1)], default=30) - cohe_integr = models.PositiveIntegerField( - verbose_name='Coherent Integrations', validators=[MinValueValidator(1)], default=30) - incohe_integr = models.PositiveIntegerField( - verbose_name='Incoherent Integrations', validators=[MinValueValidator(1)], default=30) - decode_data = models.PositiveIntegerField( - verbose_name='Decode Data', choices=DECODE_TYPE, default=0) - post_coh_int = models.BooleanField( - verbose_name='Post Coherent Integration', default=False) - spectral_number = models.PositiveIntegerField( - verbose_name='# Spectral Combinations', validators=[MinValueValidator(1)], default=1) - spectral = models.CharField( - verbose_name='Combinations', max_length=5000, default='[0, 0],') - create_directory = models.BooleanField( - verbose_name='Create Directory Per Day', default=True) - include_expname = models.BooleanField( - verbose_name='Experiment Name in Directory', default=False) - #view_raw_data = models.BooleanField(verbose_name='View Raw Data', default=True) - save_ch_dc = models.BooleanField( - verbose_name='Save Channels DC', default=True) - save_data = models.BooleanField(verbose_name='Save Data', default=True) - filter_parms = models.CharField( - max_length=10000, default=FILTER) - filter = models.ForeignKey( - 'JARSFilter', verbose_name='Filter', null=True, blank=True, on_delete=models.CASCADE) - - class Meta: - db_table = 'jars_configurations' - - def filter_resolution(self): - filter_parms = json.loads(self.filter_parms) - clock = float(filter_parms['clock']) - cic_2 = filter_parms['cic_2'] - cic_5 = filter_parms['cic_5'] - fir = filter_parms['fir'] - resolution = round((clock/(cic_2*cic_5*fir)), 2) - return resolution - - def dict_to_parms(self, params, id=None): - - if id is not None: - data = Params(params).get_conf(id_conf=id) - else: - data = Params(params).get_conf(dtype='jars') - data['filter_parms'] = params['filter_parms'] - - # self.name = data['name'] - self.exp_type = data['exp_type'] - #----PDATA---- - if self.exp_type == 1: - self.incohe_integr = data['incohe_integr'] - self.spectral_number = data['spectral_number'] - self.spectral = data['spectral'] - self.fftpoints = data['fftpoints'] - self.save_ch_dc = data['save_ch_dc'] - else: - self.raw_data_blocks = data['raw_data_blocks'] - #----PDATA---- - self.cards_number = data['cards_number'] - self.channels_number = data['channels_number'] - self.channels = data['channels'] - self.data_type = data['data_type'] - self.profiles_block = data['profiles_block'] - self.acq_profiles = data['acq_profiles'] - self.ftp_interval = data['ftp_interval'] - self.cohe_integr_str = data['cohe_integr_str'] - self.cohe_integr = data['cohe_integr'] - #----DECODE---- - self.decode_data = data['decode_data'] - self.post_coh_int = data['post_coh_int'] - #----DECODE---- - self.create_directory = data['create_directory'] - self.include_expname = data['include_expname'] - self.save_data = data['save_data'] - self.filter_parms = json.dumps(data['filter_parms']) - - self.save() - - def parms_to_text(self, file_format='jars'): - - data = self.experiment.parms_to_dict() - - for key in data['configurations']['allIds']: - if data['configurations']['byId'][key]['device_type'] in ('dds', 'cgs'): - data['configurations']['allIds'].remove(key) - data['configurations']['byId'].pop(key) - elif data['configurations']['byId'][key]['device_type'] == 'jars': - data['configurations']['byId'][key] = self.parms_to_dict( - )['configurations']['byId'][str(self.pk)] - elif data['configurations']['byId'][key]['device_type'] == 'rc': - data['configurations']['byId'][key]['pulses'] = '' - data['configurations']['byId'][key]['delays'] = '' - rc_ids = [pk for pk in data['configurations']['allIds'] - if data['configurations']['byId'][pk]['device_type'] == 'rc'] - mix_ids = [pk for pk in rc_ids if data['configurations'] - ['byId'][pk]['mix']] - - if mix_ids: - params = data['configurations']['byId'][mix_ids[0]]['parameters'] - rc = data['configurations']['byId'][params.split( - '-')[0].split('|')[0]] - rc['mix'] = True - data['configurations']['byId'][rc['id']] = rc - elif len(rc_ids) == 0: - self.message = 'File needs RC configuration' - return '' - - json_data = json.dumps(data) - racp_file, filter_file = create_jarsfiles(json_data) - if file_format == 'racp': - return racp_file - - return filter_file - - def request(self, cmd, method='get', **kwargs): - - req = getattr(requests, method)(self.device.url(cmd), **kwargs) - payload = req.json() - return payload - - def status_device(self): - - try: - payload = self.request('status', - params={'name': self.experiment.name}) - self.device.status = payload['status'] - self.device.save() - self.message = payload['message'] - except Exception as e: - self.device.status = 0 - self.message = str(e) - self.device.save() - return False - - return True - - def stop_device(self): - - try: - payload = self.request('stop', 'post') - self.device.status = payload['status'] - self.device.save() - self.message = payload['message'] - except Exception as e: - self.device.status = 0 - self.message = str(e) - self.device.save() - return False - - return True - - def read_device(self): - - try: - payload = self.request( - 'read', params={'name': self.experiment.name}) - self.message = 'Configuration loaded' - except: - self.device.status = 0 - self.device.save() - self.message = 'Could not read JARS configuration.' - return False - - return payload - - def write_device(self): - - if self.device.status == 3: - self.message = 'Could not configure device. Software Acquisition is running' - return False - - data = self.experiment.parms_to_dict() - #print(data) - for key in data['configurations']['allIds']: - if data['configurations']['byId'][key]['device_type'] in ('dds', 'cgs'): - data['configurations']['allIds'].remove(key) - data['configurations']['byId'].pop(key) - elif data['configurations']['byId'][key]['device_type'] == 'rc': - data['configurations']['byId'][key]['pulses'] = '' - data['configurations']['byId'][key]['delays'] = '' - rc_ids = [pk for pk in data['configurations']['allIds'] - if data['configurations']['byId'][pk]['device_type'] == 'rc'] - if len(rc_ids) == 0: - self.message = 'Missing RC configuration' - return False - - json_data = json.dumps(data) - - try: - payload = self.request('write', 'post', json=json_data) - self.device.status = payload['status'] - self.message = payload['message'] - self.device.save() - if self.device.status == 1: - return False - - except Exception as e: - self.device.status = 0 - self.message = str(e) - self.device.save() - return False - - return True - - def start_device(self): - - try: - payload = self.request('start', 'post', - json={'name': self.experiment.name}) - self.device.status = payload['status'] - self.message = payload['message'] - self.device.save() - if self.device.status == 1: - return False - - except Exception as e: - self.device.status = 0 - self.message = str(e) - self.device.save() - return False - - return True - - def get_log(self): - - payload = None - - try: - payload = requests.get(self.device.url('get_log'), params={ - 'name': self.experiment.name}) - except: - self.device.status = 0 - self.device.save() - self.message = 'Jars API is not running.' - return False - - self.message = 'Jars API is running' - - return payload - - def update_from_file(self, filename): - - f = JARSFile(filename) - self.dict_to_parms(f.data) - self.save() - - def get_absolute_url_import(self): - return reverse('url_import_jars_conf', args=[str(self.id)]) - - def get_absolute_url_read(self): - return reverse('url_read_jars_conf', args=[str(self.id)]) - - def get_absolute_url_log(self): - return reverse('url_get_jars_log', args=[str(self.id)]) diff --git a/apps/jars/static/js/filters.js b/apps/jars/static/js/filters.js deleted file mode 100644 index 2add350..0000000 --- a/apps/jars/static/js/filters.js +++ /dev/null @@ -1,29 +0,0 @@ -$("#id_frequency").change(function() { - updateParameters() -}); - -$("#id_clock").change(function() { - updateParameters() -}); - -$("#id_multiplier").change(function() { - updateParameters() -}); - -function updateParameters(){ - var clock = $("#id_clock").val(); // clock frequency (MHz) - var fch = $("#id_frequency").val(); // RF frequency (MHz) - var m_dds = $("#id_multiplier").val(); // DDS multiplier - - if (Math.abs(fch) < clock/2){ // Si se cumple nyquist - var nco = Math.pow(2,32)*((fch/clock)%1); - //var nco_i = Math.round(nco/m_dds)*m_dds; - var nco_i = Math.round(nco) - } - else { - nco = Math.pow(2,32)*(clock-fch)/(clock); - //nco_i = Math.round(nco/m_dds)*m_dds; - var nco_i = Math.round(nco) - } - $("#id_f_decimal").val(nco_i) -} diff --git a/apps/jars/static/js/jars.js b/apps/jars/static/js/jars.js deleted file mode 100644 index 4a07566..0000000 --- a/apps/jars/static/js/jars.js +++ /dev/null @@ -1,127 +0,0 @@ -$(document).ready(function() { - RawDataOrPData() - DecodeDataOrNone() -}); - -$("#id_exp_type").change(function() { - RawDataOrPData() -}); - -$("#id_decode_data").change(function() { - DecodeDataOrNone() -}); - -function RawDataOrPData(){ - var type = $("#id_exp_type").val(); - incohe_integr = $("#id_incohe_integr") - spectral = $("#id_spectral") - fftpoints = $("#id_fftpoints") - save_ch_dc = $("#id_save_ch_dc") - add_spec_button = $("#add_spectral_button") - del_spec_button = $("#delete_spectral_button") - sel_spec_button = $("#self_spectral_button") - cro_spec_button = $("#cross_spectral_button") - all_spec_button = $("#all_spectral_button") - - if (type == 0) { - $(incohe_integr).attr('readonly', true); - $(spectral).attr('readonly', true); - $(fftpoints).attr('readonly', true); - $(save_ch_dc).attr('disabled', true); - $(save_ch_dc).attr('readonly', true); - $(add_spec_button).attr('disabled', true); - $(del_spec_button).attr('disabled', true); - $(sel_spec_button).attr('disabled', true); - $(cro_spec_button).attr('disabled', true); - $(all_spec_button).attr('disabled', true); - } - else { - $(incohe_integr).attr('readonly', false); - $(spectral).attr('readonly', false); - $(fftpoints).attr('readonly', false); - $(save_ch_dc).attr('disabled', false); - $(save_ch_dc).attr('readonly', false); - $(add_spec_button).attr('disabled', false); - $(del_spec_button).attr('disabled', false); - $(sel_spec_button).attr('disabled', false); - $(cro_spec_button).attr('disabled', false); - $(all_spec_button).attr('disabled', false); - } -} - - -$("#id_cards_number").on('change', function() { - var cards_number = $("#id_cards_number").val(); - channels_number = $("#id_channels_number") - $(channels_number).val(cards_number*2) - updateChannelsNumber(); -}); - - -$("#id_channels_number").on('change', function() { - updateChannelsNumber(); -}); - - -$("#id_spectral").on('change', function() { - updateSpectralNumber(); -}); - -$("#id_cohe_integr").on('change', function() { - updateAcquiredProfiles(); -}); - -$("#id_profiles_block").on('change', function() { - updateAcquiredProfiles(); -}); - -function updateSpectralNumber(){ - var spectral_comb = $("#id_spectral").val(); - var num = spectral_comb.length; - var cont = 0 - for (i = 0; i < num; i++) { - if (spectral_comb[i] == "]"){ - cont = cont + 1 - } - } - $("#id_spectral_number").val(cont) -} - - -function updateChannelsNumber() { - - var channels_number = $("#id_channels_number").val(); - channels = $("#id_channels") - sequence = "" - - for (i = 1; i <= channels_number; i++) { - if (i==1){ - sequence = i.toString() - } - else{ - sequence = sequence + "," + i.toString() - } - } - $(channels).val(sequence) -} - - -function DecodeDataOrNone() { - var decode_data = $("#id_decode_data").val(); - post_coh_int = $("#id_post_coh_int") - if (decode_data != 0) { - $(post_coh_int).attr('readonly', false); - $(post_coh_int).attr('disabled', false); - } - else { - $(post_coh_int).attr('readonly', true); - $(post_coh_int).attr('disabled', true); - } -} - -function updateAcquiredProfiles() { - var profiles_block = $("#id_profiles_block").val(); - var cohe_integr = $("#id_cohe_integr").val(); - var acq_prof = profiles_block * cohe_integr; - $("#id_acq_profiles").val(acq_prof) -} diff --git a/apps/jars/templates/change_jars_filter.html b/apps/jars/templates/change_jars_filter.html deleted file mode 100644 index 01a062f..0000000 --- a/apps/jars/templates/change_jars_filter.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "dev_conf_edit.html" %} - -{% block extra-js%} - - -{% endblock %} diff --git a/apps/jars/templates/jars.html b/apps/jars/templates/jars.html deleted file mode 100644 index 4ccce23..0000000 --- a/apps/jars/templates/jars.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "base.html" %} -{% load bootstrap4 %} -{% block mainactive %}active{% endblock %} - -{% block content-title %}Acquisition System{% endblock %} -{% block content-suptitle %}JARS{% endblock %} - -{% block content %} - -{% csrf_token %} -{% bootstrap_form form layout='horizontal' size='medium' %} -
    -
    - - -{% endblock %} - -{% block sidebar%} -{% include "sidebar_devices.html" %} -{% endblock %} diff --git a/apps/jars/templates/jars_conf.html b/apps/jars/templates/jars_conf.html deleted file mode 100644 index ed1d497..0000000 --- a/apps/jars/templates/jars_conf.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "dev_conf.html" %} -{% load static %} -{% load bootstrap4 %} -{% load main_tags %} - -{% block extra-menu-actions %} -
  • - Get Log File
  • -{% endblock %} - -{% block extra-content %} - -
    -

    Filter: {{resolution}}

    -
    - - {% for key in filter_keys %} - - - - - {% endfor %} -
    {% get_verbose_field_name filter_obj key %}{{filter|attr:key}}
    - -{% endblock extra-content%} \ No newline at end of file diff --git a/apps/jars/templates/jars_conf_edit.html b/apps/jars/templates/jars_conf_edit.html deleted file mode 100644 index 8855609..0000000 --- a/apps/jars/templates/jars_conf_edit.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "dev_conf_edit.html" %} -{% load bootstrap4 %} -{% load static %} -{% load main_tags %} - -{% block content %} -
    - {% csrf_token %} - {% bootstrap_form form layout='horizontal' size='medium' %} -
    -

    Filter {{filter_name}}

    -
    - {% bootstrap_form filter_form layout='horizontal' size='medium' %} -
    -
    -
    - - -
    - -
    -
    -
    -{% endblock %} - -{% block extra-js%} - - - -{% endblock %} \ No newline at end of file diff --git a/apps/jars/templates/jars_new_filter.html b/apps/jars/templates/jars_new_filter.html deleted file mode 100644 index ae6827f..0000000 --- a/apps/jars/templates/jars_new_filter.html +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "base_edit.html" %} - diff --git a/apps/jars/tests.py b/apps/jars/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/apps/jars/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/jars/urls.py b/apps/jars/urls.py deleted file mode 100644 index 4ebdd46..0000000 --- a/apps/jars/urls.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.urls import path - -from . import views - -urlpatterns = ( - path('/', views.jars_conf, name='url_jars_conf'), - path('/edit/', views.jars_conf_edit, name='url_edit_jars_conf'), - path('/change_filter/', views.change_filter, name='url_change_jars_filter'), - path('/change_filter//', views.change_filter, name='url_change_jars_filter'), - path('/new_filter/', views.new_filter, name='url_new_jars_filter'), - path('/import/', views.import_file, name='url_import_jars_conf'), - path('/read/', views.read_conf, name='url_read_jars_conf'), - path('/get_log/', views.get_log, name='url_get_jars_log'), -) diff --git a/apps/jars/utils.py b/apps/jars/utils.py deleted file mode 100644 index a84071d..0000000 --- a/apps/jars/utils.py +++ /dev/null @@ -1,792 +0,0 @@ -''' -''' - -import json -import os -import math - -from apps.main.utils import Params - -DECODE_TYPE = {'DECODING_TIME_DOMAIN': 1, 'DECODING_FREQ_DOMAIN': 2, 'DECODING_INV_FREQ_DOMAIN': 3} - -def parse_range(s): - - vars = ('TXA,', 'A,', 'TXB,', 'B,', 'TXA', 'TXB', 'A', 'B') - - for var in vars: - if var in s: - s = s.replace(var, '') - if 'A' in var: - ref = 'TXA' - else: - ref = 'TXB' - return ref, s - - return '0', s - - -class RacpFile(object): - ''' - Class to handle Radar controller configuration files - ''' - - def __init__(self, f=None): - #print dir(f) - self.data = {} - self.lines = [] - self.line = '' - if isinstance(f, str): - self.f = open(f) - self.name = f.split('/')[-1] - elif hasattr(f, 'read'): - self.f = f - self.name = f.name.split('/')[-1] - else: - self.f = f - self.name = None - - if self.f: - if 'racp' in self.name: - self.parse_racp() - elif 'dat' in self.name: - self.parse_dat() - elif 'json' in self.name: - self.data = json.load(self.f) - - self.f.close() - - def get_line_parameters(self, data, line): - - line_params = {} - for label in data: - if 'L%d' % line in label or '(Line %d)' % line in label or 'Line%d' % line in label: - line_params[label] = data[label] - return line_params - - def parse_racp(self): - - data = {} - raw_data = [s.strip() for s in self.f.readlines()] - - for line in raw_data: - if line and '=' in line: - label, value = line.strip().split('=') - data[label] = value - - self.data['id'] = '1' - self.data['device_type'] = 'jars' - self.data['experiment_type'] = data['EXPERIMENT TYPE'] - self.data['header_version'] = data['HEADER VERSION'] - self.data['name'] = data['EXPERIMENT NAME'] - self.data['ipp'] = float(data['IPP']) - self.data['ntx'] = int(data['NTX']) - - #----Jars parameters---- - self.data['cards_number'] = data['Number of Cards'] - self.data['channels_number'] = data['Number of Channels'] - - channels = '' - for i in range(0,20): - try: - channels += data['Channel('+str(i)+')']+',' - except: - break - if channels: - channels = channels[:-1] - self.data['channels'] = channels - - data_type = 0 - if data['DATATYPE'] == 'FLOAT': - data_type = 1 - self.data['data_type'] = data_type - - self.data['profiles_block'] = data['PROFILES PER BLOCK'] - self.data['acq_profiles'] = data['ACQUIRED PROFILES'] - self.data['ftp_interval'] = data['FTP INTERVAL'] - self.data['cohe_integr_str'] = data['COHERENT INTEGRATION STRIDE'] - - self.data['cohe_integr'] = 1 - if 'COHERENT INTEGRATIONS' in data: - if int(data['COHERENT INTEGRATIONS']) != 0: - self.data['cohe_integr'] = int(data['COHERENT INTEGRATIONS']) - - self.data['exp_type'] = 0 - if data['EXPERIMENT TYPE'] != 'EXP_RAW_DATA': - self.data['exp_type'] = 1 - self.data['fftpoints'] = data['FFTPOINTS'] - self.data['incohe_integr'] = data['INCOHERENT INTEGRATIONS'] - self.data['spectral_number'] = data['TOTAL SPECTRAL COMBINATIONS'] - - spectral = '' - for i in range(0,100): - try: - spectral += '['+data['SPEC_COMB('+str(i)+')']+'],' - except: - break - self.data['spectral'] = spectral - self.data['save_ch_dc'] = False - if 'SAVE CHANNELS DC' in data: - self.data['save_ch_dc'] = True - else: - self.data['raw_data_blocks'] = data['RAW DATA BLOCKS'] - - self.data['decode_data'] = 0 - self.data['post_coh_int'] = False - if 'DECODE DATA' in data: - self.data['decode_data'] = DECODE_TYPE[data['DECODING TYPE']] - if 'POST COHERENT INTEGRATIONS' in data: - self.data['post_coh_int'] = True - - self.data['create_directory'] = False - if data['CREATE DIRECTORY PER DAY'] == 'YES': - self.data['create_directory'] = True - - self.data['include_expname'] = False - if data['INCLUDE EXPNAME IN DIRECTORY'] == 'YES': - self.data['include_expname'] = True - - self.data['save_data'] = False - if data['SAVE DATA'] == 'YES': - self.data['save_data'] = True - #----Jars parameters---- - - - if 'CLOCK DIVIDER' in data: - self.data['clock_divider'] = int(data['CLOCK DIVIDER']) - else: - self.data['clock_divider'] = 1 - self.data['clock_in'] = float(data['RELOJ'])*self.data['clock_divider'] - self.data['clock'] = float(data['RELOJ']) - self.data['time_before'] = int(data['TR_BEFORE']) - self.data['time_after'] = int(data['TR_AFTER']) - - if 'SYNCHRO DELAY' in data: - self.data['sync'] = int(data['SYNCHRO DELAY']) - else: - self.data['sync'] = 0 - - self.data['lines'] = [] - - if 'SAMPLING REFERENCE' in data: - if data['SAMPLING REFERENCE']=='MIDDLE OF FIRST BAUD': - self.data['sampling_reference'] = 'first_baud' - elif data['SAMPLING REFERENCE']=='MIDDLE OF FIRST SUB-BAUD': - self.data['sampling_reference'] = 'sub_baud' - else: - self.data['sampling_reference'] = 'none' - - self.data['lines'].append('10') - - #Add TX's lines - if 'TXA' in data: - line = {'line_type':'tx', 'id':'11', 'name':'TXA', - 'params':{'pulse_width':data['TXA'], 'delays':'0', 'range':'0'}} - if 'Pulse selection_TXA' in data: - line['params']['range'] = data['Pulse selection_TXA'] - self.data['lines'].append('11') - self.lines.append(line) - - if 'TXB' in data: - line = {'line_type':'tx', 'id':'12', 'name':'TXB', - 'params':{'pulse_width':data['TXB'], 'delays':'0', 'range':'0'}} - if 'Pulse selection_TXB' in data: - line['params']['range'] = data['Pulse selection_TXB'] - - if 'Number of Taus' in data: - delays = [data['TAU({0})'.format(i)] for i in range(int(data['Number of Taus']))] - line['params']['delays'] = ','.join(delays) - - self.data['lines'].append('12') - self.lines.append(line) - - #Add TR line - line = {'line_type':'tr', 'id':'10', 'name':'TR', - 'params':{'TX_ref':'0', 'range':'0'}} - if 'Pulse selection_TR' in data: - ref, rng = parse_range(data['Pulse selection_TR']) - line['params']['range'] = rng if rng else '0' - if ref=='TXA': - line['params']['TX_ref'] = '11' - elif ref=='TXB': - line['params']['TX_ref'] = '12' - - self.lines.append(line) - - #Add Other lines (4-6) - for n in range(4, 7): - id = '{:2d}'.format(10*n) - params = self.get_line_parameters(data, n) - labels = params.keys() - - if 'L%d_FLIP' % n in labels: - line = {'line_type':'flip', 'id':id, - 'params':{'number_of_flips':data['L%d_FLIP' % n]}} - elif 'Code Type' in data and n==4: - line = {'line_type':'codes', 'id':id, 'params':{'code':data['Code Type']}} - if data['L%d_REFERENCE' % n]=='TXA': - line['params']['TX_ref'] = '11' - else: - line['params']['TX_ref'] = '12' - if 'Number of Codes' in data: - line['params']['codes'] = [data['COD({})'.format(x)] for x in range(int(data['Number of Codes']))] - elif 'Code Type (Line %d)' % n in labels: - line = {'line_type':'codes', 'id':id, 'params':{'code':data['Code Type (Line %d)' % n]}} - if data['L%d_REFERENCE' % n]=='TXA': - line['params']['TX_ref'] = '11' - else: - line['params']['TX_ref'] = '12' - if 'Number of Codes (Line %d)' % n in data: - line['params']['codes'] = [data['L{}_COD({})'.format(n, x)] for x in range(int(data['Number of Codes (Line %d)' % n]))] - elif 'Sampling Windows (Line %d)' % n in data: - line = {'line_type':'windows', 'id':id, 'params':{}} - if data['L%d_REFERENCE' % n]=='TXA': - line['params']['TX_ref'] = '11' - else: - line['params']['TX_ref'] = '12' - windows = [] - for w in range(int(data['Sampling Windows (Line %d)' % n])): - windows.append({'first_height':float(data['L%d_H0(%d)' % (n, w)]), - 'resolution':float(data['L%d_DH(%d)' % (n, w)]), - 'number_of_samples':int(float(data['L%d_NSA(%d)' % (n, w)])), - 'last_height':float(data['L%d_DH(%d)' % (n, w)])*(int(float(data['L%d_NSA(%d)' % (n, w)]))-1)+float(data['L%d_H0(%d)' % (n, w)]) - } - ) - line['params']['params'] = windows - elif 'Line%d' % n in labels and data['Line%d' % n]=='Synchro': - line = {'line_type':'sync', 'id':id, 'params':{'invert':0}} - elif 'L%d Number Of Portions' % n in labels: - line = {'line_type':'prog_pulses', 'id':id, 'params':{}} - if 'L%s Portions IPP Periodic' % n in data: - line['params']['periodic'] = '1' if data['L%s Portions IPP Periodic' % n]=='YES' else '0' - portions = [] - x = raw_data.index('L%d Number Of Portions=%s' % (n, data['L%d Number Of Portions' % n])) - for w in range(int(data['L%d Number Of Portions' % n])): - begin = float(raw_data[x+1+2*w].split('=')[-1]) - end = float(raw_data[x+2+2*w].split('=')[-1]) - portions.append({'begin':int(begin), - 'end':int(end)} - ) - line['params']['params'] = portions - elif 'FLIP1' in data and n==5: - line = {'line_type':'flip', 'id':id, 'params':{'number_of_flips':data['FLIP1']}} - elif 'FLIP2' in data and n==6: - line = {'line_type':'flip', 'id':id, 'params':{'number_of_flips':data['FLIP2']}} - else: - line = {'line_type':'none', 'id':id, 'params':{}} - - self.data['lines'].append(id) - self.lines.append(line) - - #Add line 7 (windows) - if 'Sampling Windows' in data: - line = {'line_type':'windows', 'id':'17', 'params':{}} - if data['L7_REFERENCE']=='TXA': - line['params']['TX_ref'] = '11' - else: - line['params']['TX_ref'] = '12' - windows = [] - x = raw_data.index('Sampling Windows=%s' % data['Sampling Windows']) - for w in range(int(data['Sampling Windows'])): - h0 = raw_data[x+1+3*w].split('=')[-1] - nsa = raw_data[x+2+3*w].split('=')[-1] - dh = raw_data[x+3+3*w].split('=')[-1] - windows.append({'first_height':float(h0), - 'number_of_samples':int(nsa), - 'resolution':float(dh), - 'last_height':float(h0)+float(dh)*(int(nsa)-1)} - ) - line['params']['params'] = windows - self.data['lines'].append('17') - self.lines.append(line) - - #Add line 8 (synchro inverted) - self.data['lines'].append('18') - self.lines.append({'line_type':'sync', 'id':'18', 'params':{'invert':1}}) - - return - - def parse_dat(self): - pass - - def to_dict(self): - - out = Params() - out.add(self.data, 'configurations') - for line_data in self.lines: - out.add(line_data, 'lines') - - return out.data - - - - -def parse_line(n, data, lines): - - line_text = '' - line_type = data['lines']['byId'][lines[n]]['line_type'] - num = n+1 - if line_type == 'windows': - if num == 7: - reference = data['lines']['byId'][lines[n]]['params']['TX_ref'] - windows = data['lines']['byId'][lines[n]]['params']['params'] - if windows: - dh = str(float(windows[0]['resolution'])) - else: - dh = '' - - line_text = 'Sampling Windows={}\n'.format(len(windows)) - - cnt = 0 - for window in windows: - line_text += ('H0({cnt})={first_height}\n' - 'NSA({cnt})={number_of_samples}\n' - 'DH({cnt})={dh}\n'.format( - cnt=cnt, - first_height=window['first_height'], - number_of_samples=int(window['number_of_samples']), - dh=dh - ) - ) - cnt += 1 - - else: - reference = data['lines']['byId'][lines[n]]['params']['TX_ref'] - windows = data['lines']['byId'][lines[n]]['params']['params'] - if windows: - dh = str(float(windows[0]['resolution'])) - else: - dh = '' - - line_text = 'Sampling Windows (Line {})={}\n'.format(num, len(windows)) - - cnt = 0 - for window in windows: - line_text += ('L{num}_H0({cnt})={first_height}\n' - 'L{num}_NSA({cnt})={number_of_samples}\n' - 'L{num}_DH({cnt})={dh}\n'.format( - num=num, - cnt=cnt, - first_height=window['first_height'], - number_of_samples=int(window['number_of_samples']), - dh=dh - ) - ) - cnt += 1 - - line_text += 'L{}_REFERENCE={}\n'.format( - num, - data['lines']['byId'][reference]['name'] - ) - - elif line_type == 'sync': - line_text = 'Line{}=Synchro\n'.format(num) - - elif line_type == 'flip': - line_text = 'L{}_FLIP={}\n'.format( - num, - data['lines']['byId'][lines[n]]['params']['number_of_flips'] - ) - - elif line_type == 'prog_pulses': - periodic = data['lines']['byId'][lines[n]]['params']['periodic'] - if periodic == '0': - periodic = 'NO' - else: - periodic = 'YES' - - portions = data['lines']['byId'][lines[n]]['params']['params'] - line_text = 'L{} Number Of Portions={}\n'.format(num, len(portions)) - - for i, portion in enumerate(portions): - line_text += 'PORTION_BEGIN({cnt})={begin}\nPORTION_END({cnt})={end}\n'.format( - cnt=i, - begin=int(portion['begin']), - end=int(portion['end']), - ) - - line_text += 'L{} Portions IPP Periodic={}\n'.format(num, periodic) - - elif line_type == 'none': - line_text = '' - - else: - reference = data['lines']['byId'][lines[n]]['params']['TX_ref'] - code_type = data['lines']['byId'][lines[n]]['params']['code'] - codes = data['lines']['byId'][lines[n]]['params']['codes'] - - if num == 4: - line_text = 'Code Type={}\n'.format(code_type) - line_text += 'Number of Codes={}\nCode Width={}\n'.format( - len(codes), - len(codes[0]) - ) - cnt = 0 - for code in codes: - line_text += 'COD({})={}\n'.format(cnt, code) - cnt += 1 - else: - line_text = 'Code Type (Line {})={}\n'.format(num, code_type) - line_text += 'Number of Codes (Line {})={}\nCode Width (Line {})={}\n'.format( - num, - len(codes), - num, - len(codes[0]) - ) - cnt = 0 - for code in codes: - line_text += 'L{}_COD({})={}\n'.format(num,cnt, code) - cnt += 1 - - line_text += 'L{}_REFERENCE={}\n'.format( - num, - data['lines']['byId'][reference]['name'] - ) - - return line_text - -def create_jarsfiles(json_data): - """ - Function to create *.racp and *.jars files with json_data - """ - #global EXPNAME - - data = json.loads(json_data) - exp_id = data['experiments']['allIds'][0] - experiment = data['experiments']['byId'][exp_id] - name = experiment['name'] - #EXPNAME = name - folder_name = name#os.path.join(PATH, name) - #print 'Experiment: ' + name + ' received...' - #if not os.path.exists(folder_name): - # os.makedirs(folder_name) - #print 'Folder OK' - #if not os.path.exists(folder_name+'/DATA'): - # os.mkdir(folder_name+'/DATA') - - #try: - # racp_file = open(folder_name+'/'+name+'_jars.racp', 'w') - #except: - # return 0, 'Error creating .racp file' - - #try: - # json_file = open(folder_name+'/'+name+'_jars.json', 'w') - #except: - # return 0, 'Error creating .json file' - - #json_file.write(json_data) - #json_file.close() - - conf_ids = data['configurations']['allIds'] - - rc_id = [pk for pk in conf_ids \ - if data['configurations']['byId'][pk]['device_type'] == 'rc'][0] - - jars_id = [pk for pk in conf_ids \ - if data['configurations']['byId'][pk]['device_type'] == 'jars'][0] - - rc = data['configurations']['byId'][rc_id] - jars = data['configurations']['byId'][jars_id] - - if rc['mix'] == 'True': - mix_text = '*******Mixed Experiment*******************\n' - mix_text += '*******System parameters******************\n' - else: - mix_text = '' - - exp_type = jars['exp_type'] - if exp_type == 0: - exp_type = 'EXP_RAW_DATA' - else: - exp_type = 'EXP_PROCESS_SPECTRA' - - racp_text = 'EXPERIMENT TYPE={}\nEXPERIMENT NAME={}\nHEADER VERSION=1103\n'.format( - exp_type, - name - ) - - racp_text += '*****Radar Controller Parameters**********\n{}'.format(mix_text) - racp_text += 'IPP={}\n'.format(float(rc['ipp'])) - racp_text += 'NTX={}\n'.format(rc['ntx']) - racp_text += 'TXA={}\n'.format( - data['lines']['byId'][rc['lines'][1]]['params']['pulse_width'] - ) - - racp_text += 'TXB={}\n'.format( - data['lines']['byId'][rc['lines'][2]]['params']['pulse_width'] - ) - - idTR = data['lines']['byId'][rc['lines'][0]]['params']['TX_ref'] - rangeTR = data['lines']['byId'][rc['lines'][0]]['params']['range'] - - if rangeTR != '0': - racp_text += 'Pulse selection_TR={}\n'.format(rangeTR) - elif idTR != '0': - racp_text += 'Pulse selection_TR={}\n'.format( - data['lines']['byId'][idTR]['name'][-1] - ) - print ('TR OK') - - rangeTXA = data['lines']['byId'][rc['lines'][1]]['params']['range'] - if rangeTXA != '0': - racp_text += 'Pulse selection_TXA={}\n'.format(rangeTXA) - rangeTXB = data['lines']['byId'][rc['lines'][2]]['params']['range'] - if rangeTXB != '0': #if rangeTXB == '0': - racp_text += 'Pulse selection_TXB={}\n'.format(rangeTXB) - print ('Pulse selection OK') - - for n in range(3, 6): - racp_text += parse_line(n, data, rc['lines']) - - taus = data['lines']['byId'][rc['lines'][2]]['params']['delays'].split(',') - - if taus != '0': - racp_text += 'Number of Taus={}\n'.format(len(taus)) - for n, tau in enumerate(taus): - racp_text += 'TAU({})={}\n'.format(n, tau) - print ('Taus OK') - - racp_text += parse_line(6, data, rc['lines']) - racp_text += 'SAMPLING REFERENCE=MIDDLE OF FIRST SUB-BAUD\n' - racp_text += 'RELOJ={}\n'.format(int(rc['clock'])) - racp_text += 'CLOCK DIVIDER={}\n'.format(int(rc['clock_divider'])) - racp_text += 'TR_BEFORE={}\n'.format(rc['time_before']) - racp_text += 'TR_AFTER={}\n'.format(rc['time_after']) - racp_text += 'WINDOW IN LINE 5&6=NO\n' - racp_text += '******System Parameters*******************\n' - racp_text += 'Number of Cards={}\n'.format(jars['cards_number']) - - for i in range(jars['cards_number']): - racp_text += 'Card({})={}\n'.format(i, i) - - channels = jars['channels'].split(',') - - if channels: - racp_text += 'Number of Channels={}\n'.format(len(channels)) - for i, channel in enumerate(channels): - racp_text += 'Channel({})={}\n'.format(i, channel) - print ('Channels OK') - - if exp_type == 'EXP_RAW_DATA': - racp_text += 'RAW DATA DIRECTORY={}\n'.format(os.path.join(folder_name, 'DATA')) - else: - racp_text += 'PROCESS DATA DIRECTORY={}\n'.format(os.path.join(folder_name, 'DATA')) - - if jars['create_directory']: - racp_text += 'CREATE DIRECTORY PER DAY=YES'+'\n' - else: - racp_text += 'CREATE DIRECTORY PER DAY=NO'+'\n' - - if jars['include_expname']: - racp_text += 'INCLUDE EXPNAME IN DIRECTORY=YES'+'\n' - else: - racp_text += 'INCLUDE EXPNAME IN DIRECTORY=NO'+'\n' - - racp_text += '******System Parameters*******************\n' - racp_text += 'ADC Resolution=8\n' - racp_text += 'PCI DIO BusWidth=32\n' - - if exp_type == 'EXP_RAW_DATA': - racp_text += 'RAW DATA BLOCKS={}\n'.format(jars['raw_data_blocks']) - spectra_text = '' - else: - racp_text += 'PROCESS DATA BLOCKS=100\n' - spectra_text = '------------------------------------------\n' - - if jars['fftpoints'] > 1: - spectra_text += 'FFTPOINTS={}\n'.format(jars['fftpoints']) - - if jars['incohe_integr']: - spectra_text += 'INCOHERENT INTEGRATIONS={}\n'.format(jars['incohe_integr']) - - if jars['save_ch_dc']: - spectra_text += 'SAVE CHANNELS DC=YES\n' - - spectral = json.loads(jars['spectral'][:-1]) - if spectral: - spectra_text += '------------------------------------------\n' - spectra_text += 'TOTAL SPECTRAL COMBINATIONS={}\n'.format(len(spectral)) - for i, spc in enumerate(spectral): - spectra_text += 'SPEC_COMB({})={},{}\n'.format(i, *spc) - - racp_text += '******Process Parameters******************\n' - - data_type = jars['data_type'] - - if data_type == 0: - racp_text += 'DATATYPE=SHORT\n' - elif data_type == 1: - racp_text += 'DATATYPE=FLOAT\n' - print ('Datatype OK') - - racp_text += 'DATA ARRANGE=CONTIGUOUS_CH\n' - - if jars['cohe_integr'] > 1: - racp_text += 'COHERENT INTEGRATIONS={}\n'.format(jars['cohe_integr']) - - decode_text = '' - decode_data = jars['decode_data'] - if decode_data !=0: - decode_text = 'DECODE DATA=YES\n' - decode_text += 'DECODING TYPE={}\n'.format(DECODE_TYPE[decode_data]) - if jars['post_coh_int'] == True: - decode_text += 'POST COHERENT INTEGRATIONS=YES\n' - decode_text += '------------------------------------------\n' - print ('Decode OK') - - racp_text += 'COHERENT INTEGRATION STRIDE={}\n'.format(jars['cohe_integr_str']) - racp_text += '------------------------------------------\n' - racp_text += 'ACQUIRED PROFILES={}\n'.format(jars['acq_profiles']) - racp_text += 'PROFILES PER BLOCK={}\n'.format(jars['profiles_block']) - racp_text += spectra_text - racp_text += '------------------------------------------\n' - racp_text += decode_text - racp_text += 'BEGIN ON START=NO\n' - racp_text += 'BEGIN_TIME={}\n'.format(experiment['start_time'][:-3]) - racp_text += 'END_TIME={}\n'.format(experiment['end_time'][:-3]) - racp_text += 'GENERATE ACQUISITION LINK=YES\n' - racp_text += 'VIEW RAW DATA=YES\n' - racp_text += 'REFRESH RATE=1\n' - racp_text += '------------------------------------------\n' - racp_text += 'SEND STATUS TO FTP=YES\n' - racp_text += 'FTP SERVER=jro.igp.gob.pe\n' - racp_text += 'FTP USER=wmaster\n' - racp_text += 'FTP PASSWD=PKQLX20\n' - racp_text += 'FTP DIR=/users/database/on-line/\n' - racp_text += 'FTP FILE=status.txt\n' - racp_text += 'FTP INTERVAL={}\n'.format(jars['ftp_interval']) - racp_text += 'SAVE STATUS AND BLOCK=YES\n' - racp_text += 'GENERATE RTI=YES\n' - racp_text += 'RTI Inc.Int.=1\n' - racp_text += 'SEND RTI AND BLOCK=YES\n' - racp_text += '------------------------------------------\n' - racp_text += 'COMPORT CONFIG=Com1 CBR_9600 TWOSTOPBITS NOPARITY\n' - racp_text += 'JAM CONFIGURE FILE=dmasg_pprofiles_pch_64_pdigi_6clk.jam\n' - racp_text += 'ACQUISITION SYSTEM=JARS\n' - racp_text += '************JARS CONFIGURATION PARAMETERS************\n' - - #-------------------------JARS FILTER--------------------------------------- - filter_parms = jars['filter_parms'] - if filter_parms.__class__.__name__ == 'unicode': - filter_parms = eval(filter_parms) - elif filter_parms.__class__.__name__ == 'str': - filter_parms = eval(filter_parms) - if filter_parms.__class__.__name__ == 'str': - filter_parms = eval(filter_parms) - print ('Filter loaded OK') - try: - fclock = float(filter_parms['clock']) - fch = float(filter_parms['fch']) - m_dds = float(filter_parms['mult']) - M_CIC2 = float(filter_parms['filter_2']) - M_CIC5 = float(filter_parms['filter_5']) - M_RCF = float(filter_parms['filter_fir']) - print ('Filter parameters float OK') - except: - fclock = eval(filter_parms['clock']) - fch = eval(filter_parms['fch']) - m_dds = eval(filter_parms['mult']) - M_CIC2 = eval(filter_parms['filter_2']) - M_CIC5 = eval(filter_parms['filter_5']) - M_RCF = eval(filter_parms['filter_fir']) - print ('Filter parameters eval OK') - - filter_text = 'Loading\n' - filter_text += 'Impulse file found -> C:\jars\F1MHZ_8_MATCH.imp\n' - filter_text += 'Autoscale off\n' - filter_text += 'Initialize Printer Port\n' - filter_text += 'Chip Hardware Reset\n' - filter_text += '300h -> 1\n' - filter_text += '301h -> 6\n' - filter_text += '302h -> 11111111111111111111111111111111\n' - - if abs(fch) < (fclock/2): - nco = (2**32)*((fch/fclock))#%1) - nco_i = long(nco) - else: - nco = (2**32)*(fclock-fch)/(fclock) - nco_i = long(nco) - - filter_text += '303h -> {}\n'.format(nco_i) - filter_text += '304h -> 0\n' - - input_level = 1 - S_CIC2 = math.ceil(math.log((M_CIC2**2)*input_level)/math.log(2)) - if S_CIC2 < 0: - S_CIC2 = 0 - if S_CIC2 > 7: - S_CIC2 = 7 - - filter_text += '305h -> {}\n'.format(int(S_CIC2)) - filter_text += '306h -> {}\n'.format(int(M_CIC2-1)) - - OL_CIC2 = input_level/(2.0**S_CIC2) - - S_CIC5 = math.ceil(math.log((M_CIC5**5)*OL_CIC2)/math.log(2))-5 - if S_CIC5 < 0: - S_CIC5 = 0 - if S_CIC5 > 7: - S_CIC5 = 7 - - OL_CIC5 = ((M_CIC5**5)/(2**(S_CIC5+5)))*OL_CIC2 - - filter_text += '307h -> {}\n'.format(int(S_CIC5)) - filter_text += '308h -> {}\n'.format(int(M_CIC5-1)) - - Gain = 1 - S_RCF = int(4.0-math.log(Gain)/math.log(2)) - if S_RCF < 0: - S_RCF = 0 - if S_RCF > 7: - S_RCF = 7 - - filter_text += '309h -> {}\n'.format(S_RCF) - filter_text += '30Ah -> {}\n'.format(int(M_RCF-1)) - - Offset = 0 - filter_text += '30Bh -> {}\n'.format(Offset) - - ntaps = int(M_RCF) - filter_text += '30Ch -> {}\n'.format(ntaps-1) - filter_text += '30Dh -> 0\n' - - fsamp = fclock/(M_CIC2*M_CIC5*M_RCF) - - tap = int(2.0*((2**19)-1)/(ntaps*OL_CIC5)) - for p in range(0, ntaps): - filter_text += ' {} -> {}\n'.format(p, int(math.ceil(tap)))#filter_text += ' {} -> {}\n'.format(p, int(math.ceil(hn))) - - filter_text += 'RCF Gain -> .999996185302734\n' - filter_text += 'Chip Restarted:\n' - filter_text += '300h -> 1\n' - filter_text += '300h -> 0' - - filter_name = '{}_{}MHz_clock{}MHz_F{}MHz_{}_{}_{}.jars'.format( - abs(fch), - int((abs(fch)-abs(int(fch)))*1000), - fclock, - round(fsamp,3), - M_CIC2, - M_CIC5, - M_RCF - ) - - #jars_file = open(os.path.join(folder_name, filter_name), 'wb') - #jars_file.write(filter_text) - #jars_file.close() - print ('Filter .jars has been created') - racp_text += 'JARS_FILTER={}\n'.format(os.path.join(folder_name, filter_name)) - racp_text += 'MARK WIDTH=2\n' - racp_text += 'GENERATE OWN SAMPLING WINDOW=NO\n' - - if jars['save_data']: - racp_text += 'SAVE DATA=YES\n' - else: - racp_text += 'SAVE DATA=NO\n' - - racp_text += 'RC_STOP_SEQUENCE=255,0\n' - racp_text += 'RC_START_SEQUENCE=255,24\n' - - #racp_file.write(racp_text) - #racp_file.close() - - return racp_text, filter_text \ No newline at end of file diff --git a/apps/jars/views.py b/apps/jars/views.py deleted file mode 100644 index 8c78097..0000000 --- a/apps/jars/views.py +++ /dev/null @@ -1,221 +0,0 @@ -from django.shortcuts import render_to_response -from django.template import RequestContext -from django.shortcuts import redirect, render, get_object_or_404 -from django.contrib import messages -from django.http import HttpResponse - -from apps.main.models import Device -from apps.main.views import sidebar - -from .models import JARSConfiguration, JARSFilter -from .forms import JARSConfigurationForm, JARSFilterForm, JARSImportForm,JARSFilterFormNew - -import json -# Create your views here. - -def jars_conf(request, id_conf): - - conf = get_object_or_404(JARSConfiguration, pk=id_conf) - - filter_parms = json.loads(conf.filter_parms) - - kwargs = {} - kwargs['filter'] = filter_parms - kwargs['filter_obj'] = JARSFilter.objects.get(pk=1) - kwargs['filter_keys'] = ['clock', 'multiplier', 'frequency', 'f_decimal', - 'cic_2', 'scale_cic_2', 'cic_5', 'scale_cic_5', 'fir', - 'scale_fir', 'number_taps', 'taps'] - - filter_resolution = conf.filter_resolution() - kwargs['resolution'] = '{} (MHz)'.format(filter_resolution) - if filter_resolution < 1: - kwargs['resolution'] = '{} (kHz)'.format(filter_resolution*1000) - - kwargs['status'] = conf.device.get_status_display() - kwargs['dev_conf'] = conf - kwargs['dev_conf_keys'] = ['cards_number', 'channels_number', 'channels', - 'ftp_interval', 'data_type','acq_profiles', - 'profiles_block', 'raw_data_blocks', 'ftp_interval', - 'cohe_integr_str', 'cohe_integr', 'decode_data', 'post_coh_int', - 'incohe_integr', 'fftpoints', 'spectral_number', - 'spectral', 'create_directory', 'include_expname', - 'save_ch_dc', 'save_data'] - - if conf.exp_type == 0: - for field in ['incohe_integr','fftpoints','spectral_number', 'spectral', 'save_ch_dc']: - kwargs['dev_conf_keys'].remove(field) - - if conf.decode_data == 0: - kwargs['dev_conf_keys'].remove('decode_data') - kwargs['dev_conf_keys'].remove('post_coh_int') - - kwargs['title'] = 'JARS Configuration' - kwargs['suptitle'] = 'Details' - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf=conf)) - - return render(request, 'jars_conf.html', kwargs) - -def jars_conf_edit(request, id_conf): - - conf = get_object_or_404(JARSConfiguration, pk=id_conf) - filter_parms = json.loads(conf.filter_parms) - - if request.method=='GET': - form = JARSConfigurationForm(instance=conf) - filter_form = JARSFilterForm(initial=filter_parms) - - if request.method=='POST': - form = JARSConfigurationForm(request.POST, instance=conf) - filter_form = JARSFilterForm(request.POST) - - if filter_form.is_valid(): - jars_filter = filter_form.cleaned_data - jars_filter['id'] = request.POST['filter_template'] - else: - messages.error(request, filter_form.errors) - - if form.is_valid(): - conf = form.save(commit=False) - conf.filter_parms = json.dumps(jars_filter) - conf.save() - return redirect('url_jars_conf', id_conf=conf.id) - - kwargs = {} - - kwargs['id_dev'] = conf.id - kwargs['form'] = form - kwargs['filter_form'] = filter_form - kwargs['filter_name'] = JARSFilter.objects.get(pk=filter_parms['id']).name - kwargs['title'] = 'Device Configuration' - kwargs['suptitle'] = 'Edit' - kwargs['button'] = 'Save' - - return render(request, 'jars_conf_edit.html', kwargs) - -def import_file(request, conf_id): - - conf = get_object_or_404(JARSConfiguration, pk=conf_id) - if request.method=='POST': - form = JARSImportForm(request.POST, request.FILES) - if form.is_valid(): - try: - data = conf.import_from_file(request.FILES['file_name']) - conf.dict_to_parms(data) - messages.success(request, 'Configuration "%s" loaded succesfully' % request.FILES['file_name']) - return redirect(conf.get_absolute_url_edit()) - - except Exception as e: - messages.error(request, 'Error parsing file: "%s" - %s' % (request.FILES['file_name'], repr(e))) - else: - messages.warning(request, 'Your current configuration will be replaced') - form = JARSImportForm() - - kwargs = {} - kwargs['form'] = form - kwargs['title'] = 'JARS Configuration' - kwargs['suptitle'] = 'Import file' - kwargs['button'] = 'Upload' - kwargs['previous'] = conf.get_absolute_url() - - return render(request, 'jars_import.html', kwargs) - -def read_conf(request, conf_id): - - conf = get_object_or_404(JARSConfiguration, pk=conf_id) - #filter = get_object_or_404(JARSfilter, pk=filter_id) - - if request.method=='GET': - - parms = conf.read_device() - conf.status_device() - - if not parms: - messages.error(request, conf.message) - return redirect(conf.get_absolute_url()) - - form = JARSConfigurationForm(initial=parms, instance=conf) - - if request.method=='POST': - form = JARSConfigurationForm(request.POST, instance=conf) - - if form.is_valid(): - form.save() - return redirect(conf.get_absolute_url()) - - messages.error(request, "Parameters could not be saved") - - kwargs = {} - kwargs['id_dev'] = conf.id - kwargs['filter_id'] = conf.filter.id - kwargs['form'] = form - kwargs['title'] = 'Device Configuration' - kwargs['suptitle'] = 'Parameters read from device' - kwargs['button'] = 'Save' - - ###### SIDEBAR ###### - kwargs.update(sidebar(conf=conf)) - - return render(request, 'jars_conf_edit.html', kwargs) - -def new_filter(request, conf_id): - conf = get_object_or_404(JARSConfiguration, pk=conf_id) - form = JARSFilterFormNew() - - if request.method=='POST': - filter_form = JARSFilterFormNew(request.POST) - - if filter_form.is_valid(): - jars_filter = filter_form.cleaned_data - jars_filter['id'] = request.POST['name'] - else: - messages.error(request, filter_form.errors) - - #print(json.dumps(jars_filter)) - jars_filter_obj = JARSFilter() - jars_filter_obj.dict_to_parms_new(request.POST) - jars_filter_obj.save() - return redirect('url_edit_jars_conf', id_conf=conf.id) - - kwargs = {} - kwargs['id_dev'] = conf.id - kwargs['form'] = form - kwargs['title'] = 'New JARS Filter' - kwargs['suptitle'] = 'Parameters' - kwargs['button'] = 'Save' - - return render(request, 'jars_new_filter.html', kwargs) - -def change_filter(request, conf_id, filter_id): - - conf = get_object_or_404(JARSConfiguration, pk=conf_id) - filter = get_object_or_404(JARSFilter, pk=filter_id) - conf.filter_parms = json.dumps(filter.jsonify()) - conf.save() - - return redirect('url_edit_jars_conf', id_conf=conf.id) - -def get_log(request, conf_id): - - conf = get_object_or_404(JARSConfiguration, pk=conf_id) - response = conf.get_log() - - if not response: - message = conf.message - messages.error(request, message) - return redirect('url_jars_conf', id_conf=conf.id) - - try: - message = response.json()['message'] - messages.error(request, message) - return redirect('url_jars_conf', id_conf=conf.id) - except Exception as e: - message = 'Restarting Report.txt has been downloaded successfully.'+e - - content = response - filename = 'Log_%s_%s.txt' %(conf.experiment.name, conf.experiment.id) - response = HttpResponse(content,content_type='text/plain') - response['Content-Disposition'] = 'attachment; filename="%s"' %filename - - return response diff --git a/apps/jars/widgets.py b/apps/jars/widgets.py deleted file mode 100644 index 101471c..0000000 --- a/apps/jars/widgets.py +++ /dev/null @@ -1,165 +0,0 @@ - -import ast -import json -from itertools import chain - -from django import forms -from django.utils.safestring import mark_safe -from django.utils.html import conditional_escape - - -class SpectralWidget(forms.widgets.TextInput): - - def render(self, name, value, attrs=None, renderer=None): - label = name - readonly = 'readonly' if attrs.get('readonly', False) else '' - name = attrs.get('name', label) - if value == None: - value = '[0, 0],' - if '[' in value: - if value[len(value)-1] == ",": - value = ast.literal_eval(value) - else: - value = value + "," - value = ast.literal_eval(value) - - codes = value - if not isinstance(value, list): - text='' - #lista = [] - #if len(value) > 1: - for val in value: - text = text+str(val)+',' - #lista.append(val) - codes=text - else: - codes='' - - html = ''' - - - - - - - - '''.format(readonly, label, name, codes) - - script = ''' - - ''' - - return mark_safe(html+script) diff --git a/apps/main/forms.py b/apps/main/forms.py index 80804ed..0d0e14a 100644 --- a/apps/main/forms.py +++ b/apps/main/forms.py @@ -7,23 +7,6 @@ FILE_FORMAT = ( ('json', 'json'), ) -DDS_FILE_FORMAT = ( - ('json', 'json'), - ('text', 'dds') - ) - -RC_FILE_FORMAT = ( - ('json', 'json'), - ('text', 'racp'), - ('binary', 'dat'), - ) - -JARS_FILE_FORMAT = ( - ('json', 'json'), - ('racp', 'racp'), - ('text', 'jars'), - ) - def add_empty_choice(choices, pos=0, label='-----'): if len(choices)>0: choices = list(choices) @@ -133,18 +116,6 @@ class DownloadFileForm(forms.Form): self.fields['format'].choices = FILE_FORMAT - if device_type == 'dds': - self.fields['format'].choices = DDS_FILE_FORMAT - - if device_type == 'dds_rest': - self.fields['format'].choices = DDS_FILE_FORMAT - - if device_type == 'pedestal': - self.fields['format'].choices = RC_FILE_FORMAT - - if device_type == 'jars': - self.fields['format'].choices = JARS_FILE_FORMAT - class OperationForm(forms.Form): campaign = forms.ChoiceField(label="Campaign") diff --git a/apps/main/models.py b/apps/main/models.py index 52b6e8d..ebe7c5e 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -20,20 +20,11 @@ from django.db.models.signals import post_save from django.dispatch import receiver from apps.main.utils import Params -from apps.pedestal.utils import RCFile -from apps.jars.utils import RacpFile -from devices.dds import api as dds_api -from devices.dds import data as dds_data DEV_PORTS = { 'pedestal': 80, - 'dds' : 2000, - 'jars' : 2000, 'usrp_rx' : 2000, - 'cgs' : 8080, - 'abs' : 8080, - 'dds_rest': 80 } RADAR_STATES = ( @@ -70,9 +61,6 @@ DEV_TYPES = ( ('generator', 'Pulse Generator'), ('usrp_rx', 'Universal Software Radio Peripheral Rx'), ('usrp_tx', 'Universal Software Radio Peripheral Tx'), - ('cgs', 'Clock Generator System'), - ('abs', 'Automatic Beam Switching'), - ('dds_rest', 'Direct Digital Synthesizer_REST'), ) EXP_STATES = ( @@ -120,7 +108,7 @@ class Location(models.Model): class DeviceType(models.Model): - name = models.CharField(max_length = 10, choices = DEV_TYPES, default = 'dds_rest') + name = models.CharField(max_length = 10, choices = DEV_TYPES, default = 'pedestal') sequence = models.PositiveSmallIntegerField(default=55) description = models.TextField(blank=True, null=True) @@ -134,7 +122,7 @@ class Device(models.Model): device_type = models.ForeignKey('DeviceType', on_delete=models.CASCADE) location = models.ForeignKey('Location', on_delete=models.CASCADE) - ip_address = models.GenericIPAddressField(protocol='IPv4', default='0.0.0.0') + ip_address = models.GenericIPAddressField(verbose_name = 'IP address', protocol='IPv4', default='0.0.0.0') port_address = models.PositiveSmallIntegerField(default=2000) description = models.TextField(blank=True, null=True) status = models.PositiveSmallIntegerField(default=4, choices=DEV_STATES) @@ -144,8 +132,8 @@ class Device(models.Model): db_table = 'db_devices' def __str__(self): - ret = u'{} [{}]'.format(self.device_type.name.upper(), self.location.name) - + ret = self.device_type.name.upper() + #ret = u'{} [{}]'.format(self.device_type.name.upper(), self.location.name) return ret @property @@ -187,25 +175,7 @@ class Device(models.Model): def change_ip(self, ip_address, mask, gateway, dns, **kwargs): - if self.device_type.name=='dds': - try: - answer = dds_api.change_ip(ip = self.ip_address, - port = self.port_address, - new_ip = ip_address, - mask = mask, - gateway = gateway) - if answer[0]=='1': - self.message = '25|DDS - {}'.format(answer) - self.ip_address = ip_address - self.save() - else: - self.message = '30|DDS - {}'.format(answer) - return False - except Exception as e: - self.message = '40|{}'.format(str(e)) - return False - - elif self.device_type.name=='pedestal': + if self.device_type.name=='pedestal': headers = {'content-type': "application/json", 'cache-control': "no-cache"} @@ -287,9 +257,6 @@ class Campaign(models.Model): for conf in configurations: params.add(conf.jsonify(), 'configurations') - if conf.device.device_type.name=='pedestal': - for line in conf.get_lines(): - params.add(line.jsonify(), 'lines') return params.data @@ -363,7 +330,7 @@ class Experiment(models.Model): template = models.BooleanField(default=False) name = models.CharField(max_length=40, default='', unique=True) location = models.ForeignKey('Location', null=True, blank=True, on_delete=models.CASCADE) - freq = models.FloatField(verbose_name='Operating Freq. (MHz)', validators=[MinValueValidator(1), MaxValueValidator(10000)], default=49.9200) + #freq = models.FloatField(verbose_name='Operating Freq. (MHz)', validators=[MinValueValidator(1), MaxValueValidator(10000)], default=49.9200) start_time = models.TimeField(default='00:00:00') end_time = models.TimeField(default='23:59:59') task = models.CharField(max_length=36, default='', blank=True, null=True) @@ -422,27 +389,21 @@ class Experiment(models.Model): def start(self): ''' Configure and start experiments's devices - ABS-CGS-DDS-RC-JARS ''' confs = [] allconfs = Configuration.objects.filter(experiment=self, type = 0).order_by('-device__device_type__sequence') - rc_mix = [conf for conf in allconfs if conf.device.device_type.name=='pedestal' and conf.mix] - if rc_mix: - for conf in allconfs: - if conf.device.device_type.name == 'pedestal' and not conf.mix: - continue - confs.append(conf) - else: - confs = allconfs + confs = allconfs try: for conf in confs: conf.stop_device() - conf.write_device() + print("OK") + #conf.write_device() conf.device.conf_active = conf.pk conf.device.save() conf.start_device() + print("OK") time.sleep(1) except: return 0 @@ -452,11 +413,10 @@ class Experiment(models.Model): def stop(self): ''' Stop experiments's devices - DDS-JARS-RC-CGS-ABS + PEDESTAL, PULSE GENERATOR & USRP's ''' confs = Configuration.objects.filter(experiment=self, type = 0).order_by('device__device_type__sequence') - confs = confs.exclude(device__device_type__name='cgs') try: for conf in confs: conf.stop_device() @@ -508,9 +468,6 @@ class Experiment(models.Model): for conf in configurations: params.add(conf.jsonify(), 'configurations') - if conf.device.device_type.name=='pedestal': - for line in conf.get_lines(): - params.add(line.jsonify(), 'lines') return params.data @@ -623,29 +580,6 @@ class Configuration(PolymorphicModel): data[field.name] = field.value_from_object(self) data['device_type'] = self.device.device_type.name - - if self.device.device_type.name == 'pedestal': - data['lines'] = ['{}'.format(line.pk) for line in self.get_lines()] - data['delays'] = self.get_delays() - data['pulses'] = self.get_pulses() - - elif self.device.device_type.name == 'jars': - data['decode_type'] = DECODE_TYPE[self.decode_data][1] - - elif self.device.device_type.name == 'dds': - data['frequencyA_Mhz'] = float(data['frequencyA_Mhz']) - data['frequencyB_Mhz'] = float(data['frequencyB_Mhz']) - data['phaseA'] = dds_data.phase_to_binary(data['phaseA_degrees']) - data['phaseB'] = dds_data.phase_to_binary(data['phaseB_degrees']) - - elif self.device.device_type.name == 'dds_rest': - data['frequencyA_Mhz'] = float(data['frequencyA_Mhz']) - data['frequencyB_Mhz'] = float(data['frequencyB_Mhz']) - data['phaseA'] = dds_data.phase_to_binary(data['phaseA_degrees']) - data['phaseB'] = dds_data.phase_to_binary(data['phaseB_degrees']) - data['delta_frequency_Mhz'] = float(data['delta_frequency_Mhz'] or 0.00) - data['update_clock_Mhz'] = float(data['update_clock_Mhz'] or 0.00) - data['ramp_rate_clock_Mhz'] = float(data['ramp_rate_clock_Mhz'] or 0.0) return data def clone(self, **kwargs): @@ -663,11 +597,6 @@ class Configuration(PolymorphicModel): params = Params({}) params.add(self.jsonify(), 'configurations') - - if self.device.device_type.name=='pedestal': - for line in self.get_lines(): - params.add(line.jsonify(), 'lines') - return params.data def parms_to_text(self): @@ -689,16 +618,10 @@ class Configuration(PolymorphicModel): else: data = params.get_conf(dtype=self.device.device_type.name) - if data['device_type']=='pedestal': - self.clean_lines() - lines = data.pop('lines', None) - for line_id in lines: - pass - for key, value in data.items(): if key not in ('id', 'device_type'): setattr(self, key, value) - + self.save() @@ -742,17 +665,6 @@ class Configuration(PolymorphicModel): if ext == '.json': parms = json.load(fp) - if ext == '.dds': - lines = fp.readlines() - parms = dds_data.text_to_dict(lines) - - if ext == '.racp': - if self.device.device_type.name == 'jars': - parms = RacpFile(fp).to_dict() - parms['filter_parms'] = json.loads(self.filter_parms) - return parms - parms = RCFile(fp).to_dict() - return parms def status_device(self): @@ -773,7 +685,7 @@ class Configuration(PolymorphicModel): return False - def write_device(self, parms): + def write_device(self): self.message = 'Function not implemented' return False diff --git a/apps/main/templates/dev_conf.html b/apps/main/templates/dev_conf.html index 3ed0ec6..c17add9 100644 --- a/apps/main/templates/dev_conf.html +++ b/apps/main/templates/dev_conf.html @@ -18,7 +18,6 @@ {% block extra-menu-actions %} {% endblock %}
  • ----------------
  • -
  • Status
  • {% if not no_play %} {% if not only_stop %}
  • Start
  • diff --git a/apps/main/templates/device.html b/apps/main/templates/device.html index 13f1a9f..b38f09f 100644 --- a/apps/main/templates/device.html +++ b/apps/main/templates/device.html @@ -13,7 +13,6 @@
    -




    diff --git a/apps/main/templates/footer_igp.html b/apps/main/templates/footer_igp.html index 86e3708..2a9644a 100644 --- a/apps/main/templates/footer_igp.html +++ b/apps/main/templates/footer_igp.html @@ -12,8 +12,8 @@

    I Etapa Ate, Lima 15012 - PerĂº

  • - - roj@igp.gob.pe + + www.igp.gob.pe
  • @@ -28,7 +28,7 @@
  • - +
  • diff --git a/apps/main/templates/header_igp.html b/apps/main/templates/header_igp.html index a96ac39..2a1a4ed 100644 --- a/apps/main/templates/header_igp.html +++ b/apps/main/templates/header_igp.html @@ -13,7 +13,7 @@
  • @@ -79,9 +79,6 @@
    ') - return mark_safe(u'\n'.join(output)) diff --git a/apps/usrp(antes)/__init__.py b/apps/usrp(antes)/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/usrp(antes)/__init__.py +++ /dev/null diff --git a/apps/usrp(antes)/admin.py b/apps/usrp(antes)/admin.py deleted file mode 100644 index 2cb7979..0000000 --- a/apps/usrp(antes)/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.contrib import admin -from .models import USRPConfiguration - -# Register your models here. - -admin.site.register(USRPConfiguration) \ No newline at end of file diff --git a/apps/usrp(antes)/forms.py b/apps/usrp(antes)/forms.py deleted file mode 100644 index 1687a67..0000000 --- a/apps/usrp(antes)/forms.py +++ /dev/null @@ -1,8 +0,0 @@ -from django import forms -from .models import USRPConfiguration - -class USRPConfigurationForm(forms.ModelForm): - - class Meta: - model = USRPConfiguration - fields = ('device',) diff --git a/apps/usrp(antes)/models.py b/apps/usrp(antes)/models.py deleted file mode 100644 index 04f6418..0000000 --- a/apps/usrp(antes)/models.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.db import models -from apps.main.models import Configuration -# Create your models here. - -class USRPConfiguration(Configuration): - - frequency = models.FloatField( - verbose_name='Frequency (MHz)', - blank=False, - null=False - ) - - class Meta: - db_table = 'usrp_configurations' diff --git a/apps/usrp(antes)/tests.py b/apps/usrp(antes)/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/apps/usrp(antes)/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/usrp(antes)/urls.py b/apps/usrp(antes)/urls.py deleted file mode 100644 index 79c19b9..0000000 --- a/apps/usrp(antes)/urls.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.urls import path - -from apps.main import views - -urlpatterns = ( - path('/', views.dev_conf, name='url_usrp_conf'), - path('/edit/', views.dev_conf_edit, name='url_edit_usrp_conf'), - path('/write/', views.dev_conf_write, name='url_write_usrp_conf'), - path('/read/', views.dev_conf_read, name='url_read_usrp_conf'), - path('/import/', views.dev_conf_import, name='url_import_usrp_conf'), - path('/export/', views.dev_conf_export, name='url_export_usrp_conf'), -) diff --git a/apps/usrp(antes)/views.py b/apps/usrp(antes)/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/apps/usrp(antes)/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/apps/usrp_rx/admin.py b/apps/usrp_rx/admin.py index e34d9e0..144df27 100644 --- a/apps/usrp_rx/admin.py +++ b/apps/usrp_rx/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from .models import USRPConfiguration +from .models import USRPRXConfiguration # Register your models here. -admin.site.register(USRPConfiguration) +admin.site.register(USRPRXConfiguration) diff --git a/apps/usrp_rx/forms.py b/apps/usrp_rx/forms.py index cae6efc..fe3bf8f 100644 --- a/apps/usrp_rx/forms.py +++ b/apps/usrp_rx/forms.py @@ -5,8 +5,7 @@ from django import forms from django.utils.safestring import mark_safe from apps.main.models import Device from apps.main.forms import add_empty_choice -from .models import USRPConfiguration -from .widgets import KmUnitWidget, KmUnitHzWidget, KmUnitDcWidget, UnitKmWidget, DefaultWidget, CodesWidget, HiddenWidget, HCheckboxSelectMultiple +from .models import USRPRXConfiguration def create_choices_from_model(model, conf_id, all_choice=False): @@ -15,11 +14,10 @@ def create_choices_from_model(model, conf_id, all_choice=False): return choices - -class USRPConfigurationForm(forms.ModelForm): +class USRPRXConfigurationForm(forms.ModelForm): def __init__(self, *args, **kwargs): - super(USRPConfigurationForm, self).__init__(*args, **kwargs) + super(USRPRXConfigurationForm, self).__init__(*args, **kwargs) instance = getattr(self, 'instance', None) @@ -35,23 +33,48 @@ class USRPConfigurationForm(forms.ModelForm): self.fields['experiment'].widget.attrs['readonly'] = True class Meta: - model = USRPConfiguration - exclude = ('type', 'parameters', 'status', 'total_units', 'mix', 'author', 'hash', 'clock_in') + model = USRPRXConfiguration + exclude = ('type', 'parameters', 'status', 'total_units', 'author', 'hash') def clean(self): - form_data = super(USRPConfigurationForm, self).clean() - - if 'clock_divider' in form_data: - if form_data['clock_divider']<1: - self.add_error('clock_divider', 'Invalid Value') - #else: - # if form_data['ipp']*(20./3*(form_data['clock_in']/form_data['clock_divider']))%10!=0: - # self.add_error('ipp', 'Invalid IPP units={}'.format(form_data['ipp']*(20./3*(form_data['clock_in']/form_data['clock_divider'])))) - + form_data = super(USRPRXConfigurationForm, self).clean() return form_data def save(self, *args, **kwargs): - conf = super(USRPConfigurationForm, self).save(*args, **kwargs) + conf = super(USRPRXConfigurationForm, self).save(*args, **kwargs) conf.save() return conf +class ExtFileField(forms.FileField): + """ + Same as forms.FileField, but you can specify a file extension whitelist. + + >>> from django.core.files.uploadedfile import SimpleUploadedFile + >>> + >>> t = ExtFileField(ext_whitelist=(".pdf", ".txt")) + >>> + >>> t.clean(SimpleUploadedFile('filename.pdf', 'Some File Content')) + >>> t.clean(SimpleUploadedFile('filename.txt', 'Some File Content')) + >>> + >>> t.clean(SimpleUploadedFile('filename.exe', 'Some File Content')) + Traceback (most recent call last): + ... + ValidationError: [u'Not allowed filetype!'] + """ + def __init__(self, *args, **kwargs): + extensions = kwargs.pop("extensions") + self.extensions = [i.lower() for i in extensions] + + super(ExtFileField, self).__init__(*args, **kwargs) + + def clean(self, *args, **kwargs): + data = super(ExtFileField, self).clean(*args, **kwargs) + filename = data.name + ext = os.path.splitext(filename)[1] + ext = ext.lower() + if ext not in self.extensions: + raise forms.ValidationError('Not allowed file type: %s' % ext) + +class USRPRXImportForm(forms.Form): + + file_name = ExtFileField(extensions=['.racp', '.json', '.dat']) \ No newline at end of file diff --git a/apps/usrp_rx/models.py b/apps/usrp_rx/models.py index 355077b..b804e3d 100644 --- a/apps/usrp_rx/models.py +++ b/apps/usrp_rx/models.py @@ -1,5 +1,3 @@ - - import ast import json import requests @@ -12,8 +10,6 @@ from django.urls import reverse from django.core.validators import MinValueValidator, MaxValueValidator from apps.main.models import Configuration -from apps.main.utils import Params -from devices.rc import api BOARD_VALUE = ( ('1', 'A:AB'), @@ -21,8 +17,8 @@ BOARD_VALUE = ( ) ANT_VALUE = ( - ('1', 'Rx'), - ('2', 'Tx') + ('1', 'RX'), + ('2', 'TX') ) CLK_VALUE = ( @@ -35,60 +31,7 @@ TIME_VALUE = ( ('2', 'external') ) -LINE_TYPES = ( - ('none', 'Not used'), - ('tr', 'Transmission/reception selector signal'), - ('tx', 'A modulating signal (Transmission pulse)'), - ('codes', 'BPSK modulating signal'), - ('windows', 'Sample window signal'), - ('sync', 'Synchronizing signal'), - ('flip', 'IPP related periodic signal'), - ('prog_pulses', 'Programmable pulse'), - ('mix', 'Mixed line'), - ) - - -SAMPLING_REFS = ( - ('none', 'No Reference'), - ('begin_baud', 'Begin of the first baud'), - ('first_baud', 'Middle of the first baud'), - ('sub_baud', 'Middle of the sub-baud') - ) - -DAT_CMDS = { - # Pulse Design commands - 'DISABLE' : 0, # Disables pulse generation - 'ENABLE' : 24, # Enables pulse generation - 'DELAY_START' : 40, # Write delay status to memory - 'FLIP_START' : 48, # Write flip status to memory - 'SAMPLING_PERIOD' : 64, # Establish Sampling Period - 'TX_ONE' : 72, # Output '0' in line TX - 'TX_ZERO' : 88, # Output '0' in line TX - 'SW_ONE' : 104, # Output '0' in line SW - 'SW_ZERO' : 112, # Output '1' in line SW - 'RESTART': 120, # Restarts CR8 Firmware - 'CONTINUE' : 253, # Function Unknown - # Commands available to new controllers - # In Pulse Design Executable, the clock divisor code is written as 12 at the start, but it should be written as code 22(below) just before the final enable. - 'CLOCK_DIVISOR_INIT' : 12, # Specifies Clock Divisor. Legacy command, ignored in the actual .dat conversion - 'CLOCK_DIVISOR_LAST' : 22, # Specifies Clock Divisor (default 60 if not included) syntax: 255,22 254,N-1. - 'CLOCK_DIVIDER' : 8, - } - -MAX_BITS = 8 - -# Rotate left: 0b1001 --> 0b0011 -rol = lambda val, r_bits: \ - (val << r_bits%MAX_BITS) & (2**MAX_BITS-1) | \ - ((val & (2**MAX_BITS-1)) >> (MAX_BITS-(r_bits%MAX_BITS))) - -# Rotate right: 0b1001 --> 0b1100 -ror = lambda val, r_bits: \ - ((val & (2**MAX_BITS-1)) >> r_bits%MAX_BITS) | \ - (val << (MAX_BITS-(r_bits%MAX_BITS)) & (2**MAX_BITS-1)) - - -class USRPConfiguration(Configuration): +class USRPRXConfiguration(Configuration): daughterboard = models.CharField( verbose_name='Daughterboard', @@ -150,7 +93,7 @@ class USRPConfiguration(Configuration): class Meta: - db_table = 'usrp_configurations' + db_table = 'usrp_rx_configurations' def __str__(self): return self.label @@ -158,362 +101,6 @@ class USRPConfiguration(Configuration): def get_absolute_url_plot(self): return reverse('url_plot_usrp_rx_pulses', args=[str(self.id)]) - @property - def ipp_unit(self): - - return '{} ({})'.format(self.ipp, int(self.ipp*self.km2unit)) - - def clone(self, **kwargs): - - lines = self.get_lines() - self.pk = None - self.id = None - for attr, value in kwargs.items(): - setattr(self, attr, value) - self.save() - - for line in lines: - line.clone(rc_configuration=self) - - new_lines = self.get_lines() - for line in new_lines: - line_params = json.loads(line.params) - if 'TX_ref' in line_params and (line_params['TX_ref'] != '0'): - ref_line = RCLine.objects.get(pk=line_params['TX_ref']) - line_params['TX_ref'] = ['{}'.format(l.pk) for l in new_lines if l.get_name()==ref_line.get_name()][0] - line.params = json.dumps(line_params) - line.save() - - return self - - def get_lines(self, **kwargs): - ''' - Retrieve configuration lines - ''' - - return RCLine.objects.filter(rc_configuration=self.pk, **kwargs) - - - def clean_lines(self): - ''' - ''' - - empty_line = RCLineType.objects.get(name='none') - - for line in self.get_lines(): - line.line_type = empty_line - line.params = '{}' - line.save() - - def dict_to_parms(self, params, id=None): - ''' - ''' - - if id: - data = Params(params).get_conf(id_conf=id) - else: - data = Params(params).get_conf(dtype='usrp') - - #print(data) - # self.name = data['name'] - self.ipp = data['ipp'] - self.ntx = data['ntx'] - self.clock_in = data['clock_in'] - self.clock_divider = data['clock_divider'] - self.clock = data['clock'] - self.time_before = data['time_before'] - self.time_after = data['time_after'] - self.sync = data['sync'] - self.sampling_reference = data['sampling_reference'] - self.total_units = self.ipp*self.ntx*self.km2unit - self.save() - self.clean_lines() - - #print(params) - - positions = {'tx':0, 'tr':0} - for i, id in enumerate(data['lines']): - line_data = params['lines']['byId'][id] - line_type = RCLineType.objects.get(name=line_data['line_type']) - if line_type.name == 'codes': - code = RCLineCode.objects.get(name=line_data['params']['code']) - line_data['params']['code'] = code.pk - if line_type.name == 'tx': - position = positions['tx'] - positions['tx'] += 1 - elif line_type.name == 'tr': - position = positions['tr'] - positions['tr'] += 1 - else: - position = 0 - line, dum = RCLine.objects.update_or_create( - rc_configuration=self, - channel=i, - position=position, - defaults={ - 'line_type': line_type, - 'params': json.dumps(line_data['params']) - } - ) - - for i, line in enumerate(self.get_lines()): - line_params = json.loads(line.params) - if 'TX_ref' in line_params: - if line_params['TX_ref'] in (0, '0'): - line_params['TX_ref'] = '0' - else: - ref_id = '{}'.format(line_params['TX_ref']) - ref_line = params['lines']['byId'][ref_id] - line_params['TX_ref'] = RCLine.objects.get( - rc_configuration=self, - params=json.dumps(ref_line['params']) - ).pk - line.params = json.dumps(line_params) - print(line.params) - line.save() - print("Fin de dict to param") - - def get_delays(self): - - pulses = [line.pulses_as_points() for line in self.get_lines()] - points = [tup for tups in pulses for tup in tups] - points = set([x for tup in points for x in tup]) - points = list(points) - points.sort() - - if points[0]!=0: - points.insert(0, 0) - - return [points[i+1]-points[i] for i in range(len(points)-1)] - - - def get_pulses(self, binary=True): - - pulses = [line.pulses_as_points() for line in self.get_lines()] - tuples = [tup for tups in pulses for tup in tups] - points = set([x for tup in tuples for x in tup]) - points = list(points) - points.sort() - states = [] - last = [0 for x in pulses] - - for x in points: - dum = [] - for i, tups in enumerate(pulses): - ups = [tup[0] for tup in tups if tup!=(0,0)] - dws = [tup[1] for tup in tups if tup!=(0,0)] - if x in ups: - dum.append(1) - elif x in dws: - dum.append(0) - else: - dum.append(last[i]) - states.append(dum) - last = dum - - if binary: - ret = [] - for flips in states: - flips.reverse() - ret.append(int(''.join([str(x) for x in flips]), 2)) - states = ret - - return states[:-1] - - def add_cmd(self, cmd): - - if cmd in DAT_CMDS: - return (255, DAT_CMDS[cmd]) - - def add_data(self, value): - - return (254, value-1) - - def parms_to_binary(self, dat=True): - ''' - Create "dat" stream to be send to CR - ''' - - data = bytearray() - # create header - data.extend(self.add_cmd('DISABLE')) - data.extend(self.add_cmd('CONTINUE')) - data.extend(self.add_cmd('RESTART')) - - if self.control_sw: - data.extend(self.add_cmd('SW_ONE')) - else: - data.extend(self.add_cmd('SW_ZERO')) - - if self.control_tx: - data.extend(self.add_cmd('TX_ONE')) - else: - data.extend(self.add_cmd('TX_ZERO')) - - # write divider - data.extend(self.add_cmd('CLOCK_DIVIDER')) - data.extend(self.add_data(self.clock_divider)) - - # write delays - data.extend(self.add_cmd('DELAY_START')) - # first delay is always zero - data.extend(self.add_data(1)) - - delays = self.get_delays() - - for delay in delays: - while delay>252: - data.extend(self.add_data(253)) - delay -= 253 - data.extend(self.add_data(int(delay))) - - # write flips - data.extend(self.add_cmd('FLIP_START')) - - states = self.get_pulses(binary=True) - - - last = 0 - for flip, delay in zip(states, delays): - data.extend(self.add_data((flip^last)+1)) - last = flip - while delay>252: - data.extend(self.add_data(1)) - delay -= 253 - - # write sampling period - data.extend(self.add_cmd('SAMPLING_PERIOD')) - wins = self.get_lines(line_type__name='windows') - if wins: - win_params = json.loads(wins[0].params)['params'] - if win_params: - dh = int(win_params[0]['resolution']*self.km2unit) - else: - dh = 1 - else: - dh = 1 - data.extend(self.add_data(dh)) - - # write enable - data.extend(self.add_cmd('ENABLE')) - - if not dat: - return data - - return '\n'.join(['{}'.format(x) for x in data]) - - def update_pulses(self): - contador = 0 - for line in self.get_lines(): - contador=contador+1 - print(contador) - line.update_pulses() - - def plot_pulses2(self, km=False): - - import matplotlib - matplotlib.use('Agg') - import matplotlib.pyplot as plt - from bokeh.resources import CDN - from bokeh.embed import components - from bokeh.mpl import to_bokeh - from bokeh.models.tools import WheelZoomTool, ResetTool, PanTool, HoverTool, SaveTool - - lines = self.get_lines() - - N = len(lines) - npoints = self.total_units/self.km2unit if km else self.total_units - fig = plt.figure(figsize=(12, 2+N*0.5)) - ax = fig.add_subplot(111) - labels = ['IPP'] - - for i, line in enumerate(lines): - labels.append(line.get_name(channel=True)) - l = ax.plot((0, npoints),(N-i-1, N-i-1)) - points = [(tup[0], tup[1]-tup[0]) for tup in line.pulses_as_points(km=km) if tup!=(0,0)] - ax.broken_barh(points, (N-i-1, 0.5), - edgecolor=l[0].get_color(), facecolor='none') - - n = 0 - f = ((self.ntx+50)/100)*5 if ((self.ntx+50)/100)*10>0 else 2 - for x in np.arange(0, npoints, self.ipp if km else self.ipp*self.km2unit): - if n%f==0: - ax.text(x, N, '%s' % n, size=10) - n += 1 - - labels.reverse() - ax.set_yticks(range(len(labels))) - ax.set_yticklabels(labels) - ax.set_xlabel = 'Units' - plot = to_bokeh(fig, use_pandas=False) - plot.tools = [PanTool(dimensions="width"), WheelZoomTool(dimensions="width"), ResetTool(), SaveTool()] - plot.toolbar_location="above" - - return components(plot, CDN) - - def plot_pulses(self, km=False): - - from bokeh.plotting import figure - from bokeh.resources import CDN - from bokeh.embed import components - from bokeh.models import FixedTicker, PrintfTickFormatter - from bokeh.models.tools import WheelZoomTool, ResetTool, PanTool, HoverTool, SaveTool - from bokeh.models.sources import ColumnDataSource - - lines = self.get_lines().reverse() - - N = len(lines) - npoints = self.total_units/self.km2unit if km else self.total_units - ipp = self.ipp if km else self.ipp*self.km2unit - - hover = HoverTool(tooltips=[("Line", "@name"), - ("IPP", "@ipp"), - ("X", "@left")]) - - tools = [PanTool(dimensions="width"), - WheelZoomTool(dimensions="width"), - hover, SaveTool()] - - plot = figure(width=1000, - height=40+N*50, - y_range = (0, N), - tools=tools, - toolbar_location='above', - toolbar_sticky=False,) - - plot.xaxis.axis_label = 'Km' if km else 'Units' - plot.xaxis[0].formatter = PrintfTickFormatter(format='%d') - plot.yaxis.axis_label = 'Pulses' - plot.yaxis[0].ticker=FixedTicker(ticks=list(range(N))) - plot.yaxis[0].formatter = PrintfTickFormatter(format='Line %d') - - for i, line in enumerate(lines): - - points = [tup for tup in line.pulses_as_points(km=km) if tup!=(0,0)] - - source = ColumnDataSource(data = dict( - bottom = [i for tup in points], - top = [i+0.5 for tup in points], - left = [tup[0] for tup in points], - right = [tup[1] for tup in points], - ipp = [int(tup[0]/ipp) for tup in points], - name = [line.get_name() for tup in points] - )) - - plot.quad( - bottom = 'bottom', - top = 'top', - left = 'left', - right = 'right', - source = source, - fill_alpha = 0, - #line_color = 'blue', - ) - - plot.line([0, npoints], [i, i])#, color='blue') - - return components(plot, CDN) - def request(self, cmd, method='get', **kwargs): req = getattr(requests, method)(self.device.url(cmd), **kwargs) @@ -532,13 +119,13 @@ class USRPConfiguration(Configuration): else: self.device.status = 1 self.device.save() - self.message = 'RC status: {}'.format(payload['status']) + self.message = 'USRP Rx status: {}'.format(payload['status']) return False except Exception as e: if 'No route to host' not in str(e): self.device.status = 4 self.device.save() - self.message = 'RC status: {}'.format(str(e)) + self.message = 'USRP Rx status: {}'.format(str(e)) return False self.device.save() @@ -549,15 +136,15 @@ class USRPConfiguration(Configuration): try: payload = self.request('reset', 'post') if payload['reset']=='ok': - self.message = 'RC restarted OK' + self.message = 'USRP Rx restarted OK' self.device.status = 2 self.device.save() else: - self.message = 'RC restart fail' + self.message = 'USRP Rx restart fail' self.device.status = 4 self.device.save() except Exception as e: - self.message = 'RC reset: {}'.format(str(e)) + self.message = 'USRP Rx reset: {}'.format(str(e)) return False return True @@ -565,11 +152,12 @@ class USRPConfiguration(Configuration): def stop_device(self): try: - payload = self.request('stop', 'post') - self.message = 'RC stop: {}'.format(payload['stop']) - if payload['stop']=='ok': - self.device.status = 2 + command = self.device.url() + "stop" + r = requests.get(command) + if r: + self.device.status = 4 self.device.save() + self.message = 'USRP stopped' else: self.device.status = 4 self.device.save() @@ -579,7 +167,7 @@ class USRPConfiguration(Configuration): self.device.status = 4 else: self.device.status = 0 - self.message = 'RC stop: {}'.format(str(e)) + self.message = 'USRP Rx stop: {}'.format(str(e)) self.device.save() return False @@ -588,7 +176,7 @@ class USRPConfiguration(Configuration): def start_device(self): print("EntrĂ³ al start") try: - usrp = USRPConfiguration.objects.get(pk=self) + usrp = USRPRXConfiguration.objects.get(pk=self) usrp_daughterboard = usrp.get_daughterboard_display() usrp_antenna = usrp.get_antenna_display() usrp_clocksource = usrp.get_clocksource_display() @@ -597,14 +185,14 @@ class USRPConfiguration(Configuration): payload = {'daughterboard': usrp_daughterboard, 'antenna': usrp_antenna, 'sample_rate': usrp.samplerate, 'frequency': usrp.frequency, 'datadir': usrp.datadir, 'clock_source': usrp_clocksource, 'time_source': usrp_timesource, 'clock_rate':usrp.clockrate} print(payload) - x = requests.post(self.device.url("usrp"), json=payload) - print(x) + r = requests.post(self.device.url("usrp"), json=payload) + print(r.text) #payload = self.request('usrp', 'post', data=json.dumps(data)) #print(payload) - self.message = 'RC start: {}'.format(payload['start']) - if payload['start']=='ok': + if r: self.device.status = 3 self.device.save() + self.message = 'USRP Rx configured and started' else: return False except Exception as e: @@ -612,13 +200,13 @@ class USRPConfiguration(Configuration): self.device.status = 4 else: self.device.status = 0 - self.message = 'RC start: {}'.format(str(e)) + self.message = 'USRP Rx start: {}'.format(str(e)) self.device.save() return False return True - def write_device(self, raw=False): + #def write_device(self, raw=False): if not raw: clock = RCClock.objects.get(rc_configuration=self) @@ -630,11 +218,11 @@ class USRPConfiguration(Configuration): payload = self.request('setfreq', 'post', data=json.dumps(data)) print(payload) if payload['command'] != 'ok': - self.message = 'RC write: {}'.format(payload['command']) + self.message = 'USRP Rx write: {}'.format(payload['command']) else: self.message = payload['programming'] if payload['programming'] == 'fail': - self.message = 'RC write: error programming CGS chip' + self.message = 'USRP Rx write: error programming CGS chip' values = [] for pulse, delay in zip(self.get_pulses(), self.get_delays()): @@ -683,11 +271,11 @@ class USRPConfiguration(Configuration): if payload['write']=='ok': self.device.status = 3 self.device.save() - self.message = 'RC configured and started' + self.message = 'USRP Rx configured and started' else: self.device.status = 1 self.device.save() - self.message = 'RC write: {}'.format(payload['write']) + self.message = 'USRP Rx write: {}'.format(payload['write']) return False #payload = self.request('start', 'post') @@ -697,7 +285,7 @@ class USRPConfiguration(Configuration): self.device.status = 4 else: self.device.status = 0 - self.message = 'RC write: {}'.format(str(e)) + self.message = 'USRP Rx write: {}'.format(str(e)) self.device.save() return False diff --git a/apps/usrp_rx/templates/rc_add_line.html b/apps/usrp_rx/templates/rc_add_line.html deleted file mode 100644 index 5989f20..0000000 --- a/apps/usrp_rx/templates/rc_add_line.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "dev_conf_edit.html" %} - -{% block extra-js%} - - -{% endblock %} \ No newline at end of file diff --git a/apps/usrp_rx/templates/rc_edit_codes.html b/apps/usrp_rx/templates/rc_edit_codes.html deleted file mode 100644 index 84f07d4..0000000 --- a/apps/usrp_rx/templates/rc_edit_codes.html +++ /dev/null @@ -1,63 +0,0 @@ -{% extends "dev_conf_edit.html" %} - -{% block extra-js%} - - -{% endblock %} \ No newline at end of file diff --git a/apps/usrp_rx/templates/rc_edit_lines.html b/apps/usrp_rx/templates/rc_edit_lines.html deleted file mode 100644 index a9ad10f..0000000 --- a/apps/usrp_rx/templates/rc_edit_lines.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "dev_conf_edit.html" %} - -{% block content %} -
    - {% csrf_token %} - -
    - {% include "rc_lines.html" %} -
    - -
    -
    -
    - - -
    -
    -{% endblock %} \ No newline at end of file diff --git a/apps/usrp_rx/templates/rc_import.html b/apps/usrp_rx/templates/rc_import.html deleted file mode 100644 index b105cc5..0000000 --- a/apps/usrp_rx/templates/rc_import.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "dev_conf_edit.html" %} diff --git a/apps/usrp_rx/templates/rc_lines.html b/apps/usrp_rx/templates/rc_lines.html deleted file mode 100644 index f68083c..0000000 --- a/apps/usrp_rx/templates/rc_lines.html +++ /dev/null @@ -1,43 +0,0 @@ -{% load bootstrap4 %} -


    -{% for line in rc_lines %} -
    - -
    -
    - {% bootstrap_form line.form layout='horizontal' size='small' %} -

    - {% for f in line.subforms %} -
    - -
    {% if edit %}{% endif %}
    -
    - {% bootstrap_form f layout='horizontal' size='small' %} -
    - {% endfor %} - - {% if edit and line.subform %} -
    - - {% endif %} - - {% if edit and line.line_type.name == 'codes' %} -
    - - {% endif %} - -
    -
    -
    -{% endfor%} diff --git a/apps/usrp_rx/templates/rc_pulses.html b/apps/usrp_rx/templates/rc_pulses.html deleted file mode 100644 index e838ffe..0000000 --- a/apps/usrp_rx/templates/rc_pulses.html +++ /dev/null @@ -1,62 +0,0 @@ -{% extends "dev_conf.html" %} -{% load static %} -{% load bootstrap4 %} -{% load main_tags %} - -{% block extra-head %} - -{% endblock %} - -{% block content %} - -
    {{div}}
    -
    - -
    -
    - -
    - -
    -
    -
    - -
    -

    {{units}} Units

    -
    -
    -
    - -
    -

    {{kms}} Km

    -
    -
    -
    - -{% endblock %} - -{% block extra-js%} - - - - -{{script}} - -{% endblock %} diff --git a/apps/usrp_rx/templates/usrp_rx_conf.html b/apps/usrp_rx/templates/usrp_rx_conf.html index 1bd80a0..c42c8da 100644 --- a/apps/usrp_rx/templates/usrp_rx_conf.html +++ b/apps/usrp_rx/templates/usrp_rx_conf.html @@ -3,29 +3,9 @@ {% load bootstrap4 %} {% load main_tags %} -{% block extra-menu-actions %} -
  • View Pulses
  • -{% endblock %} - {% block content-detail %} -

    Clock

    - - - - - - - - - - - - - -
    Mode{{clock.get_mode_display}}
    Reference{{clock.get_reference_display}}
    Frequency{{clock.frequency}}
    -
    -

    RC

    +

    USRP Rx

    @@ -41,17 +21,6 @@
    Status
    {% endblock %} -{% block extra-content %} - -
    -

    RC Lines

    -
    -
    -{% include "rc_lines.html" %} -
    - -{% endblock extra-content%} - {% block extra-js%} - - - - -{% endblock %} \ No newline at end of file diff --git a/apps/jars/templates/jars_import.html b/apps/usrp_rx/templates/usrp_rx_import.html similarity index 100% rename from apps/jars/templates/jars_import.html rename to apps/usrp_rx/templates/usrp_rx_import.html diff --git a/apps/usrp_rx/tests.py b/apps/usrp_rx/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/apps/usrp_rx/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/usrp_rx/urls.py b/apps/usrp_rx/urls.py index 054df09..161e1bc 100644 --- a/apps/usrp_rx/urls.py +++ b/apps/usrp_rx/urls.py @@ -6,18 +6,6 @@ urlpatterns = ( path('/', views.conf, name='url_usrp_rx_conf'), path('/import/', views.import_file, name='url_import_usrp_rx_conf'), path('/edit/', views.conf_edit, name='url_edit_usrp_rx_conf'), - path('/plot/', views.plot_pulses, name='url_plot_usrp_rx_pulses'), - path('/plot2/', views.plot_pulses2, name='url_plot_usrp_rx_pulses2'), #url(r'^(?P-?\d+)/write/$', 'apps.main.views.dev_conf_write', name='url_write_rc_conf'), #url(r'^(?P-?\d+)/read/$', 'apps.main.views.dev_conf_read', name='url_read_rc_conf'), - path('/raw/', views.conf_raw, name='url_raw_usrp_rx_conf'), - path('/add_line/', views.add_line, name='url_add_usrp_rx_line'), - path('/add_line//', views.add_line, name='url_add_usrp_rx_line'), - path('/add_line//code//', views.add_line, name='url_add_usrp_rx_line_code'), - path('/update_position/', views.update_lines_position, name='url_update_usrp_rx_lines_position'), - path('/line//delete/', views.remove_line, name='url_remove_usrp_rx_line'), - path('/line//add_subline/', views.add_subline, name='url_add_usrp_rx_subline'), - path('/line//codes/', views.edit_codes, name='url_edit_usrp_rx_codes'), - path('/line//codes//', views.edit_codes, name='url_edit_usrp_rx_codes'), - path('/line//subline//delete/', views.remove_subline, name='url_remove_usrp_rx_subline'), ) diff --git a/apps/usrp_rx/utils.py b/apps/usrp_rx/utils.py deleted file mode 100644 index 7415e20..0000000 --- a/apps/usrp_rx/utils.py +++ /dev/null @@ -1,23 +0,0 @@ -''' -''' - -import json - -from apps.main.utils import Params - -def parse_range(s): - - vars = ('TXA,', 'A,', 'TXB,', 'B,', 'TXA', 'TXB', 'A', 'B') - - for var in vars: - if var in s: - s = s.replace(var, '') - if 'A' in var: - ref = 'TXA' - else: - ref = 'TXB' - return ref, s - - return '0', s - - diff --git a/apps/usrp_rx/views.py b/apps/usrp_rx/views.py index e7fecb7..11b0da2 100644 --- a/apps/usrp_rx/views.py +++ b/apps/usrp_rx/views.py @@ -9,13 +9,13 @@ from django.contrib.auth.decorators import login_required from apps.main.models import Experiment, Device from apps.main.views import sidebar -from .models import USRPConfiguration -from .forms import USRPConfigurationForm +from .models import USRPRXConfiguration +from .forms import USRPRXConfigurationForm, USRPRXImportForm def conf(request, conf_id): - conf = get_object_or_404(USRPConfiguration, pk=conf_id) + conf = get_object_or_404(USRPRXConfiguration, pk=conf_id) kwargs = {} kwargs['dev_conf'] = conf @@ -33,12 +33,12 @@ def conf(request, conf_id): @login_required def conf_edit(request, conf_id): print(conf_id) - conf = get_object_or_404(USRPConfiguration, pk=conf_id) + conf = get_object_or_404(USRPRXConfiguration, pk=conf_id) print(conf) #print("fin de carga de params") if request.method=='GET': print("GET case") - form = USRPConfigurationForm(instance=conf) + form = USRPRXConfigurationForm(instance=conf) print(form) elif request.method=='POST': @@ -79,14 +79,14 @@ def conf_edit(request, conf_id): #print(line_data[pk]) #update conf - form = USRPConfigurationForm(conf_data, instance=conf) + form = USRPRXConfigurationForm(conf_data, instance=conf) #print(request.POST.items()) if form.is_valid(): form.save() - messages.success(request, 'RC Configuration successfully updated') + messages.success(request, 'USRP Rx Configuration successfully updated') return redirect(conf.get_absolute_url()) @@ -95,7 +95,7 @@ def conf_edit(request, conf_id): kwargs['form'] = form kwargs['edit'] = True - kwargs['title'] = 'RC Configuration' + kwargs['title'] = 'USRP Rx Configuration' kwargs['suptitle'] = 'Edit' kwargs['button'] = 'Update' @@ -103,202 +103,11 @@ def conf_edit(request, conf_id): print(form) return render(request, 'usrp_rx_conf_edit.html', kwargs) - -def add_line(request, conf_id, line_type_id=None, code_id=None): - - conf = get_object_or_404(USRPConfiguration, pk=conf_id) - - if request.method=='GET': - if line_type_id: - line_type = get_object_or_404(RCLineType, pk=line_type_id) - - if code_id: - form = RCLineForm(initial={'rc_configuration':conf_id, 'line_type': line_type_id, 'code_id': code_id}, - extra_fields=json.loads(line_type.params)) - else: - form = RCLineForm(initial={'rc_configuration':conf_id, 'line_type': line_type_id}, - extra_fields=json.loads(line_type.params)) - else: - line_type = {'id':0} - form = RCLineForm(initial={'rc_configuration':conf_id}) - - if request.method=='POST': - - line_type = get_object_or_404(RCLineType, pk=line_type_id) - form = RCLineForm(request.POST, - extra_fields=json.loads(line_type.params)) - - if form.is_valid(): - form.save() - form.instance.update_pulses() - return redirect('url_edit_usrp_rx_conf', conf.id) - - kwargs = {} - kwargs['form'] = form - kwargs['title'] = 'RC Configuration' - kwargs['suptitle'] = 'Add Line' - kwargs['button'] = 'Add' - kwargs['previous'] = conf.get_absolute_url_edit() - kwargs['dev_conf'] = conf - kwargs['line_type'] = line_type - - return render(request, 'rc_add_line.html', kwargs) - -def edit_codes(request, conf_id, line_id, code_id=None): - - conf = get_object_or_404(USRPConfiguration, pk=conf_id) - line = get_object_or_404(RCLine, pk=line_id) - params = json.loads(line.params) - - if request.method=='GET': - if code_id: - code = get_object_or_404(RCLineCode, pk=code_id) - form = RCLineCodesForm(instance=code) - else: - initial = {'code': params['code'], - 'codes': params['codes'] if 'codes' in params else [], - 'number_of_codes': len(params['codes']) if 'codes' in params else 0, - 'bits_per_code': len(params['codes'][0]) if 'codes' in params else 0, - } - form = RCLineCodesForm(initial=initial) - - if request.method=='POST': - form = RCLineCodesForm(request.POST) - if form.is_valid(): - params['code'] = request.POST['code'] - params['codes'] = [s for s in request.POST['codes'].split('\r\n') if s] - line.params = json.dumps(params) - line.save() - messages.success(request, 'Line: "%s" has been updated.' % line) - return redirect('url_edit_usrp_rx_conf', conf.id) - - kwargs = {} - kwargs['form'] = form - kwargs['title'] = line - kwargs['suptitle'] = 'Edit' - kwargs['button'] = 'Update' - kwargs['dev_conf'] = conf - kwargs['previous'] = conf.get_absolute_url_edit() - kwargs['line'] = line - - return render(request, 'rc_edit_codes.html', kwargs) - -def add_subline(request, conf_id, line_id): - - conf = get_object_or_404(USRPConfiguration, pk=conf_id) - line = get_object_or_404(RCLine, pk=line_id) - - if request.method == 'POST': - if line: - params = json.loads(line.params) - subparams = json.loads(line.line_type.params) - if 'params' in subparams: - dum = {} - for key, value in subparams['params'].items(): - dum[key] = value['value'] - params['params'].append(dum) - line.params = json.dumps(params) - line.save() - return redirect('url_edit_usrp_rx_conf', conf.id) - - kwargs = {} - - kwargs['title'] = 'Add new' - kwargs['suptitle'] = '%s to %s' % (line.line_type.name, line) - - return render(request, 'confirm.html', kwargs) - -def remove_line(request, conf_id, line_id): - - conf = get_object_or_404(USRPConfiguration, pk=conf_id) - line = get_object_or_404(RCLine, pk=line_id) - - if request.method == 'POST': - if line: - try: - channel = line.channel - line.delete() - for ch in range(channel+1, RCLine.objects.filter(rc_configuration=conf).count()+1): - l = RCLine.objects.get(rc_configuration=conf, channel=ch) - l.channel = l.channel-1 - l.save() - messages.success(request, 'Line: "%s" has been deleted.' % line) - except: - messages.error(request, 'Unable to delete line: "%s".' % line) - - return redirect('url_edit_usrp_rx_conf', conf.id) - - kwargs = {} - - kwargs['object'] = line - kwargs['delete'] = True - kwargs['title'] = 'Delete' - kwargs['suptitle'] = 'Line' - kwargs['previous'] = conf.get_absolute_url_edit() - return render(request, 'confirm.html', kwargs) - - -def remove_subline(request, conf_id, line_id, subline_id): - - conf = get_object_or_404(USRPConfiguration, pk=conf_id) - line = get_object_or_404(RCLine, pk=line_id) - - if request.method == 'POST': - if line: - params = json.loads(line.params) - params['params'].remove(params['params'][int(subline_id)-1]) - line.params = json.dumps(params) - line.save() - - return redirect('url_edit_usrp_rx_conf', conf.id) - - kwargs = {} - - kwargs['object'] = line - kwargs['object_name'] = line.line_type.name - kwargs['delete_view'] = True - kwargs['title'] = 'Confirm delete' - - return render(request, 'confirm.html', kwargs) - - -def update_lines_position(request, conf_id): - - conf = get_object_or_404(USRPConfiguration, pk=conf_id) - print("ingreso a update_lines_position") - if request.method=='POST': - ch = 0 - for item in request.POST['items'].split('&'): - line = RCLine.objects.get(pk=item.split('=')[-1]) - line.channel = ch - line.save() - ch += 1 - - lines = RCLine.objects.filter(rc_configuration=conf).order_by('channel') - - for line in lines: - - params = json.loads(line.params) - print(params) - print("Fin de impresion_lines_position") - line.form = RCLineEditForm(conf=conf, line=line, extra_fields=params) - - if 'params' in params: - line.subform = True - line.subforms = [RCLineEditForm(extra_fields=fields, line=line, subform=i) for i, fields in enumerate(params['params'])] - - html = render(request, 'rc_lines.html', {'dev_conf':conf, 'rc_lines':lines, 'edit':True}) - data = {'html': html.content.decode('utf8')} - - return HttpResponse(json.dumps(data), content_type="application/json") - return redirect('url_edit_usrp_rx_conf', conf.id) - - def import_file(request, conf_id): - conf = get_object_or_404(USRPConfiguration, pk=conf_id) + conf = get_object_or_404(USRPRXConfiguration, pk=conf_id) if request.method=='POST': - form = RCImportForm(request.POST, request.FILES) + form = USRPRXImportForm(request.POST, request.FILES) if form.is_valid(): try: data = conf.import_from_file(request.FILES['file_name']) @@ -311,67 +120,18 @@ def import_file(request, conf_id): messages.error(request, 'Error parsing file: "%s" - %s' % (request.FILES['file_name'], repr(e))) else: messages.warning(request, 'Your current configuration will be replaced') - form = RCImportForm() + form = USRPRXImportForm() kwargs = {} kwargs['form'] = form - kwargs['title'] = 'RC Configuration' + kwargs['title'] = 'USRP Rx Configuration' kwargs['suptitle'] = 'Import file' kwargs['button'] = 'Upload' kwargs['previous'] = conf.get_absolute_url() - return render(request, 'rc_import.html', kwargs) - - -def plot_pulses(request, conf_id): - - conf = get_object_or_404(USRPConfiguration, pk=conf_id) - km = True if 'km' in request.GET else False - - script, div = conf.plot_pulses(km=km) - - kwargs = {} - kwargs['no_sidebar'] = True - kwargs['title'] = 'RC Pulses' - kwargs['suptitle'] = conf.name - kwargs['div'] = mark_safe(div) - kwargs['script'] = mark_safe(script) - kwargs['units'] = conf.km2unit - kwargs['kms'] = 1/conf.km2unit - - if km: - kwargs['km_selected'] = True - - if 'json' in request.GET: - return HttpResponse(json.dumps({'div':mark_safe(div), 'script':mark_safe(script)}), content_type="application/json") - else: - return render(request, 'rc_pulses.html', kwargs) - -def plot_pulses2(request, conf_id): - - conf = get_object_or_404(USRPConfiguration, pk=conf_id) - km = True if 'km' in request.GET else False - - script, div = conf.plot_pulses2(km=km) - - kwargs = {} - kwargs['no_sidebar'] = True - kwargs['title'] = 'RC Pulses' - kwargs['suptitle'] = conf.name - kwargs['div'] = mark_safe(div) - kwargs['script'] = mark_safe(script) - kwargs['units'] = conf.km2unit - kwargs['kms'] = 1/conf.km2unit - - if km: - kwargs['km_selected'] = True - - if 'json' in request.GET: - return HttpResponse(json.dumps({'div':mark_safe(div), 'script':mark_safe(script)}), content_type="application/json") - else: - return render(request, 'rc_pulses.html', kwargs) + return render(request, 'usrp_rx_import.html', kwargs) def conf_raw(request, conf_id): - conf = get_object_or_404(USRPConfiguration, pk=conf_id) + conf = get_object_or_404(USRPRXConfiguration, pk=conf_id) raw = conf.write_device(raw=True) return HttpResponse(raw, content_type='application/json') \ No newline at end of file diff --git a/radarsys/settings - mod.py b/radarsys/settings - mod.py deleted file mode 100644 index 4ed5203..0000000 --- a/radarsys/settings - mod.py +++ /dev/null @@ -1,143 +0,0 @@ -""" -Django settings for radarsys project. - -Generated by 'django-admin startproject' using Django 1.8.6. - -For more information on this file, see -https://docs.djangoproject.com/en/1.8/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.8/ref/settings/ -""" - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import os - -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'xshb$k5fc-+j16)cvyffj&9u__0q3$l!hieh#+tbzqg)*f^km0' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = ['*'] - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.forms', - 'bootstrap4', - 'polymorphic', - 'apps.accounts', - 'apps.main', - 'apps.misc', - 'apps.pedestal', - 'apps.dds', - 'apps.jars', - 'apps.usrp', - 'apps.abs', - 'apps.cgs', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - -] - -ROOT_URLCONF = 'radarsys.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, "templates")], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'apps.main.processors.radarsys_globals', - ], - }, - }, -] - -WSGI_APPLICATION = 'radarsys.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/1.8/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'radarsys.sqlite', - } - # 'default': { - # 'ENGINE': 'django.db.backends.postgresql_psycopg2', - # 'NAME': os.environ.get('DB_NAME', 'radarsys'), - # 'USER': os.environ.get('DB_USER', 'docker'), - # 'PASSWORD': os.environ.get('DB_PASSWORD', 'docker'), - # 'HOST': os.environ.get('POSTGRES_PORT_5432_TCP_ADDR', 'localhost'), - # 'PORT': os.environ.get('POSTGRES_PORT_5432_TCP_PORT', '5400'), - #} -} - -# Internationalization -# https://docs.djangoproject.com/en/1.8/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = os.environ.get('TZ', 'America/Lima') - -USE_I18N = True - -USE_L10N = True - -USE_TZ = False - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.8/howto/static-files/ - -MEDIA_URL = '/media/' -MEDIA_ROOT = os.path.join(BASE_DIR, 'media') - -STATIC_URL = '/static/' -STATIC_ROOT = os.path.join(BASE_DIR, 'static') - -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -) - -# Celery stuff -REDIS_HOST = os.environ.get('REDIS_HOST', '127.0.0.1') -REDIS_PORT = os.environ.get('REDIS_PORT', 6300) - -BROKER_TRANSPORT = 'redis' -BROKER_URL = 'redis://{}:{}/0'.format(REDIS_HOST, REDIS_PORT) - -CELERY_RESULT_BACKEND = 'redis://{}:{}/0'.format(REDIS_HOST, REDIS_PORT) -CELERY_BROKER_TRANSPORT = BROKER_URL -CELERY_ACCEPT_CONTENT = ['application/json'] -CELERY_TASK_SERIALIZER = 'json' -CELERY_RESULT_SERIALIZER = 'json' -CELERY_ENABLE_UTC = False -FORM_RENDERER = 'django.forms.renderers.DjangoTemplates' diff --git a/radarsys/settings.py b/radarsys/settings.py index cbe227e..3667ed2 100644 --- a/radarsys/settings.py +++ b/radarsys/settings.py @@ -39,14 +39,8 @@ INSTALLED_APPS = [ 'polymorphic', 'apps.accounts', 'apps.main', - 'apps.misc', 'apps.pedestal', - 'apps.dds', - 'apps.jars', 'apps.usrp_rx', - 'apps.abs', - 'apps.cgs', - 'apps.dds_rest', ] MIDDLEWARE = [ diff --git a/radarsys/urls.py b/radarsys/urls.py index 135ebd8..9e6c90b 100644 --- a/radarsys/urls.py +++ b/radarsys/urls.py @@ -7,14 +7,7 @@ urlpatterns = [ path('accounts/', include('apps.accounts.urls')), path('', include('apps.main.urls')), path('pedestal/', include('apps.pedestal.urls')), - path('dds/', include('apps.dds.urls')), - path('cgs/', include('apps.cgs.urls')), - path('jars/',include('apps.jars.urls')), path('usrp_rx/', include('apps.usrp_rx.urls')), - path('abs/', include('apps.abs.urls')), - path('misc/',include('apps.misc.urls')), - path('dds_rest/', include('apps.dds_rest.urls')), - ] #urlpatterns += staticfiles_urlpatterns()

    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 diff --git a/apps/abs/utils/resource/attenuation.txt b/apps/abs/utils/resource/attenuation.txt deleted file mode 100644 index 95264be..0000000 --- a/apps/abs/utils/resource/attenuation.txt +++ /dev/null @@ -1,18 +0,0 @@ - -attenuation = numpy.array([[[-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25], - [-21.25,-15.25,-9.25,-3.25,03.25,09.25,15.25,21.25]], - [[21.25,21.25,21.25,21.25,21.25,21.25,21.25,21.25], - [15.25,15.25,15.25,15.25,15.25,15.25,15.25,15.25], - [09.25,09.25,09.25,09.25,09.25,09.25,09.25,09.25], - [03.25,03.25,03.25,03.25,03.25,03.25,03.25,03.25], - [-03.25,-03.25,-03.25,-03.25,-03.25,-03.25,-03.25,-03.25], - [-09.25,-09.25,-09.25,-09.25,-09.25,-09.25,-09.25,-09.25], - [-15.25,-15.25,-15.25,-15.25,-15.25,-15.25,-15.25,-15.25], - [-21.25,-21.25,-21.25,-21.25,-21.25,-21.25,-21.25,-21.25]]]) - diff --git a/apps/abs/utils/resource/galaxy.txt b/apps/abs/utils/resource/galaxy.txt deleted file mode 100644 index 9ba726c..0000000 --- a/apps/abs/utils/resource/galaxy.txt +++ /dev/null @@ -1,99 +0,0 @@ -0000 13.00 -0015 13.00 -0030 13.00 -0045 13.00 -0060 13.00 -0075 12.50 -0090 12.50 -0105 12.50 -0120 11.50 -0135 11.00 -0150 10.50 -0165 10.00 -0180 10.00 -0195 10.00 -0210 9.00 -0225 8.50 -0240 8.50 -0255 8.50 -0270 8.50 -0285 8.00 -0300 8.00 -0315 8.50 -0330 9.00 -0345 10.00 -0360 11.00 -0375 12.50 -0390 14.50 -0405 17.00 -0420 18.00 -0435 17.00 -0450 15.50 -0465 15.00 -0480 14.00 -0495 12.50 -0510 11.00 -0525 10.00 -0540 9.50 -0555 9.00 -0570 23.00 -0585 8.00 -0600 10.00 -0615 10.50 -0630 10.00 -0645 9.00 -0660 8.50 -0675 9.00 -0690 10.00 -0705 11.00 -0720 12.00 -0735 12.50 -0750 13.50 -0765 13.00 -0780 13.00 -0795 13.00 -0810 13.00 -0825 12.50 -0840 12.00 -0855 12.50 -0870 13.00 -0885 14.00 -0900 15.00 -0915 17.00 -0930 18.00 -0945 17.50 -0960 16.50 -0975 16.50 -0990 17.00 -0990 17.00 -1005 17.00 -1020 20.00 -1035 26.00 -1050 30.00 -1065 36.00 -1080 47.00 -1095 71.00 -1102 60.00 -1110 77.00 -1115 87.00 -1120 83.00 -1130 60.00 -1140 50.00 -1155 35.00 -1170 28.00 -1185 21.00 -1200 18.00 -1215 16.00 -1237 15.50 -1260 15.00 -1275 15.50 -1290 16.00 -1305 15.50 -1320 15.00 -1335 14.50 -1350 14.00 -1365 13.00 -1380 12.00 -1395 12.50 -1410 13.00 -1425 12.50 diff --git a/apps/abs/utils/resource/igrf10coeffs.dat b/apps/abs/utils/resource/igrf10coeffs.dat deleted file mode 100644 index 9700b5b7314260404602b20c92b6ecee79021433..0000000000000000000000000000000000000000 GIT binary patch literal 0 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 diff --git a/apps/abs/utils/resource/igrf10coeffs.txt b/apps/abs/utils/resource/igrf10coeffs.txt deleted file mode 100644 index 3410721..0000000 --- a/apps/abs/utils/resource/igrf10coeffs.txt +++ /dev/null @@ -1,196 +0,0 @@ -g/h n m 1900.0 1905.0 1910.0 1915.0 1920.0 1925.0 1930.0 1935.0 1940.0 1945.0 1950.0 1955.0 1960.0 1965.0 1970.0 1975.0 1980.0 1985.0 1990.0 1995.0 2000.0 2005.0 SV -g 1 0 -31543 -31464 -31354 -31212 -31060 -30926 -30805 -30715 -30654 -30594 -30554 -30500 -30421 -30334 -30220 -30100 -29992 -29873 -29775 -29692 -29619.4 -29556.8 8.8 -g 1 1 -2298 -2298 -2297 -2306 -2317 -2318 -2316 -2306 -2292 -2285 -2250 -2215 -2169 -2119 -2068 -2013 -1956 -1905 -1848 -1784 -1728.2 -1671.8 10.8 -h 1 1 5922 5909 5898 5875 5845 5817 5808 5812 5821 5810 5815 5820 5791 5776 5737 5675 5604 5500 5406 5306 5186.1 5080.0 -21.3 -g 2 0 -677 -728 -769 -802 -839 -893 -951 -1018 -1106 -1244 -1341 -1440 -1555 -1662 -1781 -1902 -1997 -2072 -2131 -2200 -2267.7 -2340.5 -15.0 -g 2 1 2905 2928 2948 2956 2959 2969 2980 2984 2981 2990 2998 3003 3002 2997 3000 3010 3027 3044 3059 3070 3068.4 3047.0 -6.9 -h 2 1 -1061 -1086 -1128 -1191 -1259 -1334 -1424 -1520 -1614 -1702 -1810 -1898 -1967 -2016 -2047 -2067 -2129 -2197 -2279 -2366 -2481.6 -2594.9 -23.3 -g 2 2 924 1041 1176 1309 1407 1471 1517 1550 1566 1578 1576 1581 1590 1594 1611 1632 1663 1687 1686 1681 1670.9 1656.9 -1.0 -h 2 2 1121 1065 1000 917 823 728 644 586 528 477 381 291 206 114 25 -68 -200 -306 -373 -413 -458.0 -516.7 -14.0 -g 3 0 1022 1037 1058 1084 1111 1140 1172 1206 1240 1282 1297 1302 1302 1297 1287 1276 1281 1296 1314 1335 1339.6 1335.7 -0.3 -g 3 1 -1469 -1494 -1524 -1559 -1600 -1645 -1692 -1740 -1790 -1834 -1889 -1944 -1992 -2038 -2091 -2144 -2180 -2208 -2239 -2267 -2288.0 -2305.3 -3.1 -h 3 1 -330 -357 -389 -421 -445 -462 -480 -494 -499 -499 -476 -462 -414 -404 -366 -333 -336 -310 -284 -262 -227.6 -200.4 5.4 -g 3 2 1256 1239 1223 1212 1205 1202 1205 1215 1232 1255 1274 1288 1289 1292 1278 1260 1251 1247 1248 1249 1252.1 1246.8 -0.9 -h 3 2 3 34 62 84 103 119 133 146 163 186 206 216 224 240 251 262 271 284 293 302 293.4 269.3 -6.5 -g 3 3 572 635 705 778 839 881 907 918 916 913 896 882 878 856 838 830 833 829 802 759 714.5 674.4 -6.8 -h 3 3 523 480 425 360 293 229 166 101 43 -11 -46 -83 -130 -165 -196 -223 -252 -297 -352 -427 -491.1 -524.5 -2.0 -g 4 0 876 880 884 887 889 891 896 903 914 944 954 958 957 957 952 946 938 936 939 940 932.3 919.8 -2.5 -g 4 1 628 643 660 678 695 711 727 744 762 776 792 796 800 804 800 791 782 780 780 780 786.8 798.2 2.8 -h 4 1 195 203 211 218 220 216 205 188 169 144 136 133 135 148 167 191 212 232 247 262 272.6 281.4 2.0 -g 4 2 660 653 644 631 616 601 584 565 550 544 528 510 504 479 461 438 398 361 325 290 250.0 211.5 -7.1 -h 4 2 -69 -77 -90 -109 -134 -163 -195 -226 -252 -276 -278 -274 -278 -269 -266 -265 -257 -249 -240 -236 -231.9 -225.8 1.8 -g 4 3 -361 -380 -400 -416 -424 -426 -422 -415 -405 -421 -408 -397 -394 -390 -395 -405 -419 -424 -423 -418 -403.0 -379.5 5.9 -h 4 3 -210 -201 -189 -173 -153 -130 -109 -90 -72 -55 -37 -23 3 13 26 39 53 69 84 97 119.8 145.7 5.6 -g 4 4 134 146 160 178 199 217 234 249 265 304 303 290 269 252 234 216 199 170 141 122 111.3 100.2 -3.2 -h 4 4 -75 -65 -55 -51 -57 -70 -90 -114 -141 -178 -210 -230 -255 -269 -279 -288 -297 -297 -299 -306 -303.8 -304.7 0.0 -g 5 0 -184 -192 -201 -211 -221 -230 -237 -241 -241 -253 -240 -229 -222 -219 -216 -218 -218 -214 -214 -214 -218.8 -227.6 -2.6 -g 5 1 328 328 327 327 326 326 327 329 334 346 349 360 362 358 359 356 357 355 353 352 351.4 354.4 0.4 -h 5 1 -210 -193 -172 -148 -122 -96 -72 -51 -33 -12 3 15 16 19 26 31 46 47 46 46 43.8 42.7 0.1 -g 5 2 264 259 253 245 236 226 218 211 208 194 211 230 242 254 262 264 261 253 245 235 222.3 208.8 -3.0 -h 5 2 53 56 57 58 58 58 60 64 71 95 103 110 125 128 139 148 150 150 154 165 171.9 179.8 1.8 -g 5 3 5 -1 -9 -16 -23 -28 -32 -33 -33 -20 -20 -23 -26 -31 -42 -59 -74 -93 -109 -118 -130.4 -136.6 -1.2 -h 5 3 -33 -32 -33 -34 -38 -44 -53 -64 -75 -67 -87 -98 -117 -126 -139 -152 -151 -154 -153 -143 -133.1 -123.0 2.0 -g 5 4 -86 -93 -102 -111 -119 -125 -131 -136 -141 -142 -147 -152 -156 -157 -160 -159 -162 -164 -165 -166 -168.6 -168.3 0.2 -h 5 4 -124 -125 -126 -126 -125 -122 -118 -115 -113 -119 -122 -121 -114 -97 -91 -83 -78 -75 -69 -55 -39.3 -19.5 4.5 -g 5 5 -16 -26 -38 -51 -62 -69 -74 -76 -76 -82 -76 -69 -63 -62 -56 -49 -48 -46 -36 -17 -12.9 -14.1 -0.6 -h 5 5 3 11 21 32 43 51 58 64 69 82 80 78 81 81 83 88 92 95 97 107 106.3 103.6 -1.0 -g 6 0 63 62 62 61 61 61 60 59 57 59 54 47 46 45 43 45 48 53 61 68 72.3 72.9 -0.8 -g 6 1 61 60 58 57 55 54 53 53 54 57 57 57 58 61 64 66 66 65 65 67 68.2 69.6 0.2 -h 6 1 -9 -7 -5 -2 0 3 4 4 4 6 -1 -9 -10 -11 -12 -13 -15 -16 -16 -17 -17.4 -20.2 -0.4 -g 6 2 -11 -11 -11 -10 -10 -9 -9 -8 -7 6 4 3 1 8 15 28 42 51 59 68 74.2 76.6 -0.2 -h 6 2 83 86 89 93 96 99 102 104 105 100 99 96 99 100 100 99 93 88 82 72 63.7 54.7 -1.9 -g 6 3 -217 -221 -224 -228 -233 -238 -242 -246 -249 -246 -247 -247 -237 -228 -212 -198 -192 -185 -178 -170 -160.9 -151.1 2.1 -h 6 3 2 4 5 8 11 14 19 25 33 16 33 48 60 68 72 75 71 69 69 67 65.1 63.7 -0.4 -g 6 4 -58 -57 -54 -51 -46 -40 -32 -25 -18 -25 -16 -8 -1 4 2 1 4 4 3 -1 -5.9 -15.0 -2.1 -h 6 4 -35 -32 -29 -26 -22 -18 -16 -15 -15 -9 -12 -16 -20 -32 -37 -41 -43 -48 -52 -58 -61.2 -63.4 -0.4 -g 6 5 59 57 54 49 44 39 32 25 18 21 12 7 -2 1 3 6 14 16 18 19 16.9 14.7 -0.4 -h 6 5 36 32 28 23 18 13 8 4 0 -16 -12 -12 -11 -8 -6 -4 -2 -1 1 1 0.7 0.0 -0.2 -g 6 6 -90 -92 -95 -98 -101 -103 -104 -106 -107 -104 -105 -107 -113 -111 -112 -111 -108 -102 -96 -93 -90.4 -86.4 1.3 -h 6 6 -69 -67 -65 -62 -57 -52 -46 -40 -33 -39 -30 -24 -17 -7 1 11 17 21 24 36 43.8 50.3 0.9 -g 7 0 70 70 71 72 73 73 74 74 74 70 65 65 67 75 72 71 72 74 77 77 79.0 79.8 -0.4 -g 7 1 -55 -54 -54 -54 -54 -54 -54 -53 -53 -40 -55 -56 -56 -57 -57 -56 -59 -62 -64 -72 -74.0 -74.4 0.0 -h 7 1 -45 -46 -47 -48 -49 -50 -51 -52 -52 -45 -35 -50 -55 -61 -70 -77 -82 -83 -80 -69 -64.6 -61.4 0.8 -g 7 2 0 0 1 2 2 3 4 4 4 0 2 2 5 4 1 1 2 3 2 1 0.0 -1.4 -0.2 -h 7 2 -13 -14 -14 -14 -14 -14 -15 -17 -18 -18 -17 -24 -28 -27 -27 -26 -27 -27 -26 -25 -24.2 -22.5 0.4 -g 7 3 34 33 32 31 29 27 25 23 20 0 1 10 15 13 14 16 21 24 26 28 33.3 38.6 1.1 -h 7 3 -10 -11 -12 -12 -13 -14 -14 -14 -14 2 0 -4 -6 -2 -4 -5 -5 -2 0 4 6.2 6.9 0.1 -g 7 4 -41 -41 -40 -38 -37 -35 -34 -33 -31 -29 -40 -32 -32 -26 -22 -14 -12 -6 -1 5 9.1 12.3 0.6 -h 7 4 -1 0 1 2 4 5 6 7 7 6 10 8 7 6 8 10 16 20 21 24 24.0 25.4 0.2 -g 7 5 -21 -20 -19 -18 -16 -14 -12 -11 -9 -10 -7 -11 -7 -6 -2 0 1 4 5 4 6.9 9.4 0.4 -h 7 5 28 28 28 28 28 29 29 29 29 28 36 28 23 26 23 22 18 17 17 17 14.8 10.9 -0.9 -g 7 6 18 18 18 19 19 19 18 18 17 15 5 9 17 13 13 12 11 10 9 8 7.3 5.5 -0.5 -h 7 6 -12 -12 -13 -15 -16 -17 -18 -19 -20 -17 -18 -20 -18 -23 -23 -23 -23 -23 -23 -24 -25.4 -26.4 -0.3 -g 7 7 6 6 6 6 6 6 6 6 5 29 19 18 8 1 -2 -5 -2 0 0 -2 -1.2 2.0 0.9 -h 7 7 -22 -22 -22 -22 -22 -21 -20 -19 -19 -22 -16 -18 -17 -12 -11 -12 -10 -7 -4 -6 -5.8 -4.8 0.3 -g 8 0 11 11 11 11 11 11 11 11 11 13 22 11 15 13 14 14 18 21 23 25 24.4 24.8 -0.2 -g 8 1 8 8 8 8 7 7 7 7 7 7 15 9 6 5 6 6 6 6 5 6 6.6 7.7 0.2 -h 8 1 8 8 8 8 8 8 8 8 8 12 5 10 11 7 7 6 7 8 10 11 11.9 11.2 -0.2 -g 8 2 -4 -4 -4 -4 -3 -3 -3 -3 -3 -8 -4 -6 -4 -4 -2 -1 0 0 -1 -6 -9.2 -11.4 -0.2 -h 8 2 -14 -15 -15 -15 -15 -15 -15 -15 -14 -21 -22 -15 -14 -12 -15 -16 -18 -19 -19 -21 -21.5 -21.0 0.2 -g 8 3 -9 -9 -9 -9 -9 -9 -9 -9 -10 -5 -1 -14 -11 -14 -13 -12 -11 -11 -10 -9 -7.9 -6.8 0.2 -h 8 3 7 7 6 6 6 6 5 5 5 -12 0 5 7 9 6 4 4 5 6 8 8.5 9.7 0.2 -g 8 4 1 1 1 2 2 2 2 1 1 9 11 6 2 0 -3 -8 -7 -9 -12 -14 -16.6 -18.0 -0.2 -h 8 4 -13 -13 -13 -13 -14 -14 -14 -15 -15 -7 -21 -23 -18 -16 -17 -19 -22 -23 -22 -23 -21.5 -19.8 0.4 -g 8 5 2 2 2 3 4 4 5 6 6 7 15 10 10 8 5 4 4 4 3 9 9.1 10.0 0.2 -h 8 5 5 5 5 5 5 5 5 5 5 2 -8 3 4 4 6 6 9 11 12 15 15.5 16.1 0.2 -g 8 6 -9 -8 -8 -8 -7 -7 -6 -6 -5 -10 -13 -7 -5 -1 0 0 3 4 4 6 7.0 9.4 0.5 -h 8 6 16 16 16 16 17 17 18 18 19 18 17 23 23 24 21 18 16 14 12 11 8.9 7.7 -0.3 -g 8 7 5 5 5 6 6 7 8 8 9 7 5 6 10 11 11 10 6 4 2 -5 -7.9 -11.4 -0.7 -h 8 7 -5 -5 -5 -5 -5 -5 -5 -5 -5 3 -4 -4 1 -3 -6 -10 -13 -15 -16 -16 -14.9 -12.8 0.5 -g 8 8 8 8 8 8 8 8 8 7 7 2 -1 9 8 4 3 1 -1 -4 -6 -7 -7.0 -5.0 0.5 -h 8 8 -18 -18 -18 -18 -19 -19 -19 -19 -19 -11 -17 -13 -20 -17 -16 -17 -15 -11 -10 -4 -2.1 -0.1 0.4 -g 9 0 8 8 8 8 8 8 8 8 8 5 3 4 4 8 8 7 5 5 4 4 5.0 5.6 -g 9 1 10 10 10 10 10 10 10 10 10 -21 -7 9 6 10 10 10 10 10 9 9 9.4 9.8 -h 9 1 -20 -20 -20 -20 -20 -20 -20 -20 -21 -27 -24 -11 -18 -22 -21 -21 -21 -21 -20 -20 -19.7 -20.1 -g 9 2 1 1 1 1 1 1 1 1 1 1 -1 -4 0 2 2 2 1 1 1 3 3.0 3.6 -h 9 2 14 14 14 14 14 14 14 15 15 17 19 12 12 15 16 16 16 15 15 15 13.4 12.9 -g 9 3 -11 -11 -11 -11 -11 -11 -12 -12 -12 -11 -25 -5 -9 -13 -12 -12 -12 -12 -12 -10 -8.4 -7.0 -h 9 3 5 5 5 5 5 5 5 5 5 29 12 7 2 7 6 7 9 9 11 12 12.5 12.7 -g 9 4 12 12 12 12 12 12 12 11 11 3 10 2 1 10 10 10 9 9 9 8 6.3 5.0 -h 9 4 -3 -3 -3 -3 -3 -3 -3 -3 -3 -9 2 6 0 -4 -4 -4 -5 -6 -7 -6 -6.2 -6.7 -g 9 5 1 1 1 1 1 1 1 1 1 16 5 4 4 -1 -1 -1 -3 -3 -4 -8 -8.9 -10.8 -h 9 5 -2 -2 -2 -2 -2 -2 -2 -3 -3 4 2 -2 -3 -5 -5 -5 -6 -6 -7 -8 -8.4 -8.1 -g 9 6 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -5 1 -1 -1 0 -1 -1 -1 -2 -1 -1.5 -1.3 -h 9 6 8 8 8 8 9 9 9 9 9 9 8 10 9 10 10 10 9 9 9 8 8.4 8.1 -g 9 7 2 2 2 2 2 2 3 3 3 -4 -2 2 -2 5 3 4 7 7 7 10 9.3 8.7 -h 9 7 10 10 10 10 10 10 10 11 11 6 8 7 8 10 11 11 10 9 8 5 3.8 2.9 -g 9 8 -1 0 0 0 0 0 0 0 1 -3 3 2 3 1 1 1 2 1 1 -2 -4.3 -6.7 -h 9 8 -2 -2 -2 -2 -2 -2 -2 -2 -2 1 -11 -6 0 -4 -2 -3 -6 -7 -7 -8 -8.2 -7.9 -g 9 9 -1 -1 -1 -1 -1 -1 -2 -2 -2 -4 8 5 -1 -2 -1 -2 -5 -5 -6 -8 -8.2 -9.2 -h 9 9 2 2 2 2 2 2 2 2 2 8 -7 5 5 1 1 1 2 2 2 3 4.8 5.9 -g 10 0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -8 -3 1 -2 -3 -3 -4 -4 -3 -3 -2.6 -2.2 -g 10 1 -4 -4 -4 -4 -4 -4 -4 -4 -4 11 4 -5 -3 -3 -3 -3 -4 -4 -4 -6 -6.0 -6.3 -h 10 1 2 2 2 2 2 2 2 2 2 5 13 -4 4 2 1 1 1 1 2 1 1.7 2.4 -g 10 2 2 2 2 2 2 2 2 2 2 1 -1 -1 4 2 2 2 2 3 2 2 1.7 1.6 -h 10 2 1 1 1 1 1 1 1 1 1 1 -2 0 1 1 1 1 0 0 1 0 0.0 0.2 -g 10 3 -5 -5 -5 -5 -5 -5 -5 -5 -5 2 13 2 0 -5 -5 -5 -5 -5 -5 -4 -3.1 -2.5 -h 10 3 2 2 2 2 2 2 2 2 2 -20 -10 -8 0 2 3 3 3 3 3 4 4.0 4.4 -g 10 4 -2 -2 -2 -2 -2 -2 -2 -2 -2 -5 -4 -3 -1 -2 -1 -2 -2 -2 -2 -1 -0.5 -0.1 -h 10 4 6 6 6 6 6 6 6 6 6 -1 2 -2 2 6 4 4 6 6 6 5 4.9 4.7 -g 10 5 6 6 6 6 6 6 6 6 6 -1 4 7 4 4 6 5 5 5 4 4 3.7 3.0 -h 10 5 -4 -4 -4 -4 -4 -4 -4 -4 -4 -6 -3 -4 -5 -4 -4 -4 -4 -4 -4 -5 -5.9 -6.5 -g 10 6 4 4 4 4 4 4 4 4 4 8 12 4 6 4 4 4 3 3 3 2 1.0 0.3 -h 10 6 0 0 0 0 0 0 0 0 0 6 6 1 1 0 0 -1 0 0 0 -1 -1.2 -1.0 -g 10 7 0 0 0 0 0 0 0 0 0 -1 3 -2 1 0 1 1 1 1 1 2 2.0 2.1 -h 10 7 -2 -2 -2 -2 -2 -2 -2 -1 -1 -4 -3 -3 -1 -2 -1 -1 -1 -1 -2 -2 -2.9 -3.4 -g 10 8 2 2 2 1 1 1 1 2 2 -3 2 6 -1 2 0 0 2 2 3 5 4.2 3.9 -h 10 8 4 4 4 4 4 4 4 4 4 -2 6 7 6 3 3 3 4 4 3 1 0.2 -0.9 -g 10 9 2 2 2 2 3 3 3 3 3 5 10 -2 2 2 3 3 3 3 3 1 0.3 -0.1 -h 10 9 0 0 0 0 0 0 0 0 0 0 11 -1 0 0 1 1 0 0 -1 -2 -2.2 -2.3 -g 10 10 0 0 0 0 0 0 0 0 0 -2 3 0 0 0 -1 -1 0 0 0 0 -1.1 -2.2 -h 10 10 -6 -6 -6 -6 -6 -6 -6 -6 -6 -2 8 -3 -7 -6 -4 -5 -6 -6 -6 -7 -7.4 -8.0 -g 11 0 2.7 2.9 -g 11 1 -1.7 -1.6 -h 11 1 0.1 0.3 -g 11 2 -1.9 -1.7 -h 11 2 1.3 1.4 -g 11 3 1.5 1.5 -h 11 3 -0.9 -0.7 -g 11 4 -0.1 -0.2 -h 11 4 -2.6 -2.4 -g 11 5 0.1 0.2 -h 11 5 0.9 0.9 -g 11 6 -0.7 -0.7 -h 11 6 -0.7 -0.6 -g 11 7 0.7 0.5 -h 11 7 -2.8 -2.7 -g 11 8 1.7 1.8 -h 11 8 -0.9 -1.0 -g 11 9 0.1 0.1 -h 11 9 -1.2 -1.5 -g 11 10 1.2 1.0 -h 11 10 -1.9 -2.0 -g 11 11 4.0 4.1 -h 11 11 -0.9 -1.4 -g 12 0 -2.2 -2.2 -g 12 1 -0.3 -0.3 -h 12 1 -0.4 -0.5 -g 12 2 0.2 0.3 -h 12 2 0.3 0.3 -g 12 3 0.9 0.9 -h 12 3 2.5 2.3 -g 12 4 -0.2 -0.4 -h 12 4 -2.6 -2.7 -g 12 5 0.9 1.0 -h 12 5 0.7 0.6 -g 12 6 -0.5 -0.4 -h 12 6 0.3 0.4 -g 12 7 0.3 0.5 -h 12 7 0.0 0.0 -g 12 8 -0.3 -0.3 -h 12 8 0.0 0.0 -g 12 9 -0.4 -0.4 -h 12 9 0.3 0.3 -g 12 10 -0.1 0.0 -h 12 10 -0.9 -0.8 -g 12 11 -0.2 -0.4 -h 12 11 -0.4 -0.4 -g 12 12 -0.4 0.0 -h 12 12 0.8 1.0 -g 13 0 -0.2 -0.2 -g 13 1 -0.9 -0.9 -h 13 1 -0.9 -0.7 -g 13 2 0.3 0.3 -h 13 2 0.2 0.3 -g 13 3 0.1 0.3 -h 13 3 1.8 1.7 -g 13 4 -0.4 -0.4 -h 13 4 -0.4 -0.5 -g 13 5 1.3 1.2 -h 13 5 -1.0 -1.0 -g 13 6 -0.4 -0.4 -h 13 6 -0.1 0.0 -g 13 7 0.7 0.7 -h 13 7 0.7 0.7 -g 13 8 -0.4 -0.3 -h 13 8 0.3 0.2 -g 13 9 0.3 0.4 -h 13 9 0.6 0.6 -g 13 10 -0.1 -0.1 -h 13 10 0.3 0.4 -g 13 11 0.4 0.4 -h 13 11 -0.2 -0.2 -g 13 12 0.0 -0.1 -h 13 12 -0.5 -0.5 -g 13 13 0.1 -0.3 -h 13 13 -0.9 -1.0 diff --git a/apps/abs/utils/resource/igrf11coeffs.txt b/apps/abs/utils/resource/igrf11coeffs.txt deleted file mode 100644 index 3acabb2..0000000 --- a/apps/abs/utils/resource/igrf11coeffs.txt +++ /dev/null @@ -1,196 +0,0 @@ -g/h n m 1900.0 1905.0 1910.0 1915.0 1920.0 1925.0 1930.0 1935.0 1940.0 1945.0 1950.0 1955.0 1960.0 1965.0 1970.0 1975.0 1980.0 1985.0 1990.0 1995.0 2000.0 2005.0 2010.0 SV -g 1 0 -31543 -31464 -31354 -31212 -31060 -30926 -30805 -30715 -30654 -30594 -30554 -30500 -30421 -30334 -30220 -30100 -29992 -29873 -29775 -29692 -29619.4 -29554.63 -29496.5 11.4 -g 1 1 -2298 -2298 -2297 -2306 -2317 -2318 -2316 -2306 -2292 -2285 -2250 -2215 -2169 -2119 -2068 -2013 -1956 -1905 -1848 -1784 -1728.2 -1669.05 -1585.9 16.7 -h 1 1 5922 5909 5898 5875 5845 5817 5808 5812 5821 5810 5815 5820 5791 5776 5737 5675 5604 5500 5406 5306 5186.1 5077.99 4945.1 -28.8 -g 2 0 -677 -728 -769 -802 -839 -893 -951 -1018 -1106 -1244 -1341 -1440 -1555 -1662 -1781 -1902 -1997 -2072 -2131 -2200 -2267.7 -2337.24 -2396.6 -11.3 -g 2 1 2905 2928 2948 2956 2959 2969 2980 2984 2981 2990 2998 3003 3002 2997 3000 3010 3027 3044 3059 3070 3068.4 3047.69 3026.0 -3.9 -h 2 1 -1061 -1086 -1128 -1191 -1259 -1334 -1424 -1520 -1614 -1702 -1810 -1898 -1967 -2016 -2047 -2067 -2129 -2197 -2279 -2366 -2481.6 -2594.50 -2707.7 -23.0 -g 2 2 924 1041 1176 1309 1407 1471 1517 1550 1566 1578 1576 1581 1590 1594 1611 1632 1663 1687 1686 1681 1670.9 1657.76 1668.6 2.7 -h 2 2 1121 1065 1000 917 823 728 644 586 528 477 381 291 206 114 25 -68 -200 -306 -373 -413 -458.0 -515.43 -575.4 -12.9 -g 3 0 1022 1037 1058 1084 1111 1140 1172 1206 1240 1282 1297 1302 1302 1297 1287 1276 1281 1296 1314 1335 1339.6 1336.30 1339.7 1.3 -g 3 1 -1469 -1494 -1524 -1559 -1600 -1645 -1692 -1740 -1790 -1834 -1889 -1944 -1992 -2038 -2091 -2144 -2180 -2208 -2239 -2267 -2288.0 -2305.83 -2326.3 -3.9 -h 3 1 -330 -357 -389 -421 -445 -462 -480 -494 -499 -499 -476 -462 -414 -404 -366 -333 -336 -310 -284 -262 -227.6 -198.86 -160.5 8.6 -g 3 2 1256 1239 1223 1212 1205 1202 1205 1215 1232 1255 1274 1288 1289 1292 1278 1260 1251 1247 1248 1249 1252.1 1246.39 1231.7 -2.9 -h 3 2 3 34 62 84 103 119 133 146 163 186 206 216 224 240 251 262 271 284 293 302 293.4 269.72 251.7 -2.9 -g 3 3 572 635 705 778 839 881 907 918 916 913 896 882 878 856 838 830 833 829 802 759 714.5 672.51 634.2 -8.1 -h 3 3 523 480 425 360 293 229 166 101 43 -11 -46 -83 -130 -165 -196 -223 -252 -297 -352 -427 -491.1 -524.72 -536.8 -2.1 -g 4 0 876 880 884 887 889 891 896 903 914 944 954 958 957 957 952 946 938 936 939 940 932.3 920.55 912.6 -1.4 -g 4 1 628 643 660 678 695 711 727 744 762 776 792 796 800 804 800 791 782 780 780 780 786.8 797.96 809.0 2.0 -h 4 1 195 203 211 218 220 216 205 188 169 144 136 133 135 148 167 191 212 232 247 262 272.6 282.07 286.4 0.4 -g 4 2 660 653 644 631 616 601 584 565 550 544 528 510 504 479 461 438 398 361 325 290 250.0 210.65 166.6 -8.9 -h 4 2 -69 -77 -90 -109 -134 -163 -195 -226 -252 -276 -278 -274 -278 -269 -266 -265 -257 -249 -240 -236 -231.9 -225.23 -211.2 3.2 -g 4 3 -361 -380 -400 -416 -424 -426 -422 -415 -405 -421 -408 -397 -394 -390 -395 -405 -419 -424 -423 -418 -403.0 -379.86 -357.1 4.4 -h 4 3 -210 -201 -189 -173 -153 -130 -109 -90 -72 -55 -37 -23 3 13 26 39 53 69 84 97 119.8 145.15 164.4 3.6 -g 4 4 134 146 160 178 199 217 234 249 265 304 303 290 269 252 234 216 199 170 141 122 111.3 100.00 89.7 -2.3 -h 4 4 -75 -65 -55 -51 -57 -70 -90 -114 -141 -178 -210 -230 -255 -269 -279 -288 -297 -297 -299 -306 -303.8 -305.36 -309.2 -0.8 -g 5 0 -184 -192 -201 -211 -221 -230 -237 -241 -241 -253 -240 -229 -222 -219 -216 -218 -218 -214 -214 -214 -218.8 -227.00 -231.1 -0.5 -g 5 1 328 328 327 327 326 326 327 329 334 346 349 360 362 358 359 356 357 355 353 352 351.4 354.41 357.2 0.5 -h 5 1 -210 -193 -172 -148 -122 -96 -72 -51 -33 -12 3 15 16 19 26 31 46 47 46 46 43.8 42.72 44.7 0.5 -g 5 2 264 259 253 245 236 226 218 211 208 194 211 230 242 254 262 264 261 253 245 235 222.3 208.95 200.3 -1.5 -h 5 2 53 56 57 58 58 58 60 64 71 95 103 110 125 128 139 148 150 150 154 165 171.9 180.25 188.9 1.5 -g 5 3 5 -1 -9 -16 -23 -28 -32 -33 -33 -20 -20 -23 -26 -31 -42 -59 -74 -93 -109 -118 -130.4 -136.54 -141.2 -0.7 -h 5 3 -33 -32 -33 -34 -38 -44 -53 -64 -75 -67 -87 -98 -117 -126 -139 -152 -151 -154 -153 -143 -133.1 -123.45 -118.1 0.9 -g 5 4 -86 -93 -102 -111 -119 -125 -131 -136 -141 -142 -147 -152 -156 -157 -160 -159 -162 -164 -165 -166 -168.6 -168.05 -163.1 1.3 -h 5 4 -124 -125 -126 -126 -125 -122 -118 -115 -113 -119 -122 -121 -114 -97 -91 -83 -78 -75 -69 -55 -39.3 -19.57 0.1 3.7 -g 5 5 -16 -26 -38 -51 -62 -69 -74 -76 -76 -82 -76 -69 -63 -62 -56 -49 -48 -46 -36 -17 -12.9 -13.55 -7.7 1.4 -h 5 5 3 11 21 32 43 51 58 64 69 82 80 78 81 81 83 88 92 95 97 107 106.3 103.85 100.9 -0.6 -g 6 0 63 62 62 61 61 61 60 59 57 59 54 47 46 45 43 45 48 53 61 68 72.3 73.60 72.8 -0.3 -g 6 1 61 60 58 57 55 54 53 53 54 57 57 57 58 61 64 66 66 65 65 67 68.2 69.56 68.6 -0.3 -h 6 1 -9 -7 -5 -2 0 3 4 4 4 6 -1 -9 -10 -11 -12 -13 -15 -16 -16 -17 -17.4 -20.33 -20.8 -0.1 -g 6 2 -11 -11 -11 -10 -10 -9 -9 -8 -7 6 4 3 1 8 15 28 42 51 59 68 74.2 76.74 76.0 -0.3 -h 6 2 83 86 89 93 96 99 102 104 105 100 99 96 99 100 100 99 93 88 82 72 63.7 54.75 44.2 -2.1 -g 6 3 -217 -221 -224 -228 -233 -238 -242 -246 -249 -246 -247 -247 -237 -228 -212 -198 -192 -185 -178 -170 -160.9 -151.34 -141.4 1.9 -h 6 3 2 4 5 8 11 14 19 25 33 16 33 48 60 68 72 75 71 69 69 67 65.1 63.63 61.5 -0.4 -g 6 4 -58 -57 -54 -51 -46 -40 -32 -25 -18 -25 -16 -8 -1 4 2 1 4 4 3 -1 -5.9 -14.58 -22.9 -1.6 -h 6 4 -35 -32 -29 -26 -22 -18 -16 -15 -15 -9 -12 -16 -20 -32 -37 -41 -43 -48 -52 -58 -61.2 -63.53 -66.3 -0.5 -g 6 5 59 57 54 49 44 39 32 25 18 21 12 7 -2 1 3 6 14 16 18 19 16.9 14.58 13.1 -0.2 -h 6 5 36 32 28 23 18 13 8 4 0 -16 -12 -12 -11 -8 -6 -4 -2 -1 1 1 0.7 0.24 3.1 0.8 -g 6 6 -90 -92 -95 -98 -101 -103 -104 -106 -107 -104 -105 -107 -113 -111 -112 -111 -108 -102 -96 -93 -90.4 -86.36 -77.9 1.8 -h 6 6 -69 -67 -65 -62 -57 -52 -46 -40 -33 -39 -30 -24 -17 -7 1 11 17 21 24 36 43.8 50.94 54.9 0.5 -g 7 0 70 70 71 72 73 73 74 74 74 70 65 65 67 75 72 71 72 74 77 77 79.0 79.88 80.4 0.2 -g 7 1 -55 -54 -54 -54 -54 -54 -54 -53 -53 -40 -55 -56 -56 -57 -57 -56 -59 -62 -64 -72 -74.0 -74.46 -75.0 -0.1 -h 7 1 -45 -46 -47 -48 -49 -50 -51 -52 -52 -45 -35 -50 -55 -61 -70 -77 -82 -83 -80 -69 -64.6 -61.14 -57.8 0.6 -g 7 2 0 0 1 2 2 3 4 4 4 0 2 2 5 4 1 1 2 3 2 1 0.0 -1.65 -4.7 -0.6 -h 7 2 -13 -14 -14 -14 -14 -14 -15 -17 -18 -18 -17 -24 -28 -27 -27 -26 -27 -27 -26 -25 -24.2 -22.57 -21.2 0.3 -g 7 3 34 33 32 31 29 27 25 23 20 0 1 10 15 13 14 16 21 24 26 28 33.3 38.73 45.3 1.4 -h 7 3 -10 -11 -12 -12 -13 -14 -14 -14 -14 2 0 -4 -6 -2 -4 -5 -5 -2 0 4 6.2 6.82 6.6 -0.2 -g 7 4 -41 -41 -40 -38 -37 -35 -34 -33 -31 -29 -40 -32 -32 -26 -22 -14 -12 -6 -1 5 9.1 12.30 14.0 0.3 -h 7 4 -1 0 1 2 4 5 6 7 7 6 10 8 7 6 8 10 16 20 21 24 24.0 25.35 24.9 -0.1 -g 7 5 -21 -20 -19 -18 -16 -14 -12 -11 -9 -10 -7 -11 -7 -6 -2 0 1 4 5 4 6.9 9.37 10.4 0.1 -h 7 5 28 28 28 28 28 29 29 29 29 28 36 28 23 26 23 22 18 17 17 17 14.8 10.93 7.0 -0.8 -g 7 6 18 18 18 19 19 19 18 18 17 15 5 9 17 13 13 12 11 10 9 8 7.3 5.42 1.6 -0.8 -h 7 6 -12 -12 -13 -15 -16 -17 -18 -19 -20 -17 -18 -20 -18 -23 -23 -23 -23 -23 -23 -24 -25.4 -26.32 -27.7 -0.3 -g 7 7 6 6 6 6 6 6 6 6 5 29 19 18 8 1 -2 -5 -2 0 0 -2 -1.2 1.94 4.9 0.4 -h 7 7 -22 -22 -22 -22 -22 -21 -20 -19 -19 -22 -16 -18 -17 -12 -11 -12 -10 -7 -4 -6 -5.8 -4.64 -3.4 0.2 -g 8 0 11 11 11 11 11 11 11 11 11 13 22 11 15 13 14 14 18 21 23 25 24.4 24.80 24.3 -0.1 -g 8 1 8 8 8 8 7 7 7 7 7 7 15 9 6 5 6 6 6 6 5 6 6.6 7.62 8.2 0.1 -h 8 1 8 8 8 8 8 8 8 8 8 12 5 10 11 7 7 6 7 8 10 11 11.9 11.20 10.9 0.0 -g 8 2 -4 -4 -4 -4 -3 -3 -3 -3 -3 -8 -4 -6 -4 -4 -2 -1 0 0 -1 -6 -9.2 -11.73 -14.5 -0.5 -h 8 2 -14 -15 -15 -15 -15 -15 -15 -15 -14 -21 -22 -15 -14 -12 -15 -16 -18 -19 -19 -21 -21.5 -20.88 -20.0 0.2 -g 8 3 -9 -9 -9 -9 -9 -9 -9 -9 -10 -5 -1 -14 -11 -14 -13 -12 -11 -11 -10 -9 -7.9 -6.88 -5.7 0.3 -h 8 3 7 7 6 6 6 6 5 5 5 -12 0 5 7 9 6 4 4 5 6 8 8.5 9.83 11.9 0.5 -g 8 4 1 1 1 2 2 2 2 1 1 9 11 6 2 0 -3 -8 -7 -9 -12 -14 -16.6 -18.11 -19.3 -0.3 -h 8 4 -13 -13 -13 -13 -14 -14 -14 -15 -15 -7 -21 -23 -18 -16 -17 -19 -22 -23 -22 -23 -21.5 -19.71 -17.4 0.4 -g 8 5 2 2 2 3 4 4 5 6 6 7 15 10 10 8 5 4 4 4 3 9 9.1 10.17 11.6 0.3 -h 8 5 5 5 5 5 5 5 5 5 5 2 -8 3 4 4 6 6 9 11 12 15 15.5 16.22 16.7 0.1 -g 8 6 -9 -8 -8 -8 -7 -7 -6 -6 -5 -10 -13 -7 -5 -1 0 0 3 4 4 6 7.0 9.36 10.9 0.2 -h 8 6 16 16 16 16 17 17 18 18 19 18 17 23 23 24 21 18 16 14 12 11 8.9 7.61 7.1 -0.1 -g 8 7 5 5 5 6 6 7 8 8 9 7 5 6 10 11 11 10 6 4 2 -5 -7.9 -11.25 -14.1 -0.5 -h 8 7 -5 -5 -5 -5 -5 -5 -5 -5 -5 3 -4 -4 1 -3 -6 -10 -13 -15 -16 -16 -14.9 -12.76 -10.8 0.4 -g 8 8 8 8 8 8 8 8 8 7 7 2 -1 9 8 4 3 1 -1 -4 -6 -7 -7.0 -4.87 -3.7 0.2 -h 8 8 -18 -18 -18 -18 -19 -19 -19 -19 -19 -11 -17 -13 -20 -17 -16 -17 -15 -11 -10 -4 -2.1 -0.06 1.7 0.4 -g 9 0 8 8 8 8 8 8 8 8 8 5 3 4 4 8 8 7 5 5 4 4 5.0 5.58 5.4 0.0 -g 9 1 10 10 10 10 10 10 10 10 10 -21 -7 9 6 10 10 10 10 10 9 9 9.4 9.76 9.4 0.0 -h 9 1 -20 -20 -20 -20 -20 -20 -20 -20 -21 -27 -24 -11 -18 -22 -21 -21 -21 -21 -20 -20 -19.7 -20.11 -20.5 0.0 -g 9 2 1 1 1 1 1 1 1 1 1 1 -1 -4 0 2 2 2 1 1 1 3 3.0 3.58 3.4 0.0 -h 9 2 14 14 14 14 14 14 14 15 15 17 19 12 12 15 16 16 16 15 15 15 13.4 12.69 11.6 0.0 -g 9 3 -11 -11 -11 -11 -11 -11 -12 -12 -12 -11 -25 -5 -9 -13 -12 -12 -12 -12 -12 -10 -8.4 -6.94 -5.3 0.0 -h 9 3 5 5 5 5 5 5 5 5 5 29 12 7 2 7 6 7 9 9 11 12 12.5 12.67 12.8 0.0 -g 9 4 12 12 12 12 12 12 12 11 11 3 10 2 1 10 10 10 9 9 9 8 6.3 5.01 3.1 0.0 -h 9 4 -3 -3 -3 -3 -3 -3 -3 -3 -3 -9 2 6 0 -4 -4 -4 -5 -6 -7 -6 -6.2 -6.72 -7.2 0.0 -g 9 5 1 1 1 1 1 1 1 1 1 16 5 4 4 -1 -1 -1 -3 -3 -4 -8 -8.9 -10.76 -12.4 0.0 -h 9 5 -2 -2 -2 -2 -2 -2 -2 -3 -3 4 2 -2 -3 -5 -5 -5 -6 -6 -7 -8 -8.4 -8.16 -7.4 0.0 -g 9 6 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -5 1 -1 -1 0 -1 -1 -1 -2 -1 -1.5 -1.25 -0.8 0.0 -h 9 6 8 8 8 8 9 9 9 9 9 9 8 10 9 10 10 10 9 9 9 8 8.4 8.10 8.0 0.0 -g 9 7 2 2 2 2 2 2 3 3 3 -4 -2 2 -2 5 3 4 7 7 7 10 9.3 8.76 8.4 0.0 -h 9 7 10 10 10 10 10 10 10 11 11 6 8 7 8 10 11 11 10 9 8 5 3.8 2.92 2.2 0.0 -g 9 8 -1 0 0 0 0 0 0 0 1 -3 3 2 3 1 1 1 2 1 1 -2 -4.3 -6.66 -8.4 0.0 -h 9 8 -2 -2 -2 -2 -2 -2 -2 -2 -2 1 -11 -6 0 -4 -2 -3 -6 -7 -7 -8 -8.2 -7.73 -6.1 0.0 -g 9 9 -1 -1 -1 -1 -1 -1 -2 -2 -2 -4 8 5 -1 -2 -1 -2 -5 -5 -6 -8 -8.2 -9.22 -10.1 0.0 -h 9 9 2 2 2 2 2 2 2 2 2 8 -7 5 5 1 1 1 2 2 2 3 4.8 6.01 7.0 0.0 -g 10 0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -8 -3 1 -2 -3 -3 -4 -4 -3 -3 -2.6 -2.17 -2.0 0.0 -g 10 1 -4 -4 -4 -4 -4 -4 -4 -4 -4 11 4 -5 -3 -3 -3 -3 -4 -4 -4 -6 -6.0 -6.12 -6.3 0.0 -h 10 1 2 2 2 2 2 2 2 2 2 5 13 -4 4 2 1 1 1 1 2 1 1.7 2.19 2.8 0.0 -g 10 2 2 2 2 2 2 2 2 2 2 1 -1 -1 4 2 2 2 2 3 2 2 1.7 1.42 0.9 0.0 -h 10 2 1 1 1 1 1 1 1 1 1 1 -2 0 1 1 1 1 0 0 1 0 0.0 0.10 -0.1 0.0 -g 10 3 -5 -5 -5 -5 -5 -5 -5 -5 -5 2 13 2 0 -5 -5 -5 -5 -5 -5 -4 -3.1 -2.35 -1.1 0.0 -h 10 3 2 2 2 2 2 2 2 2 2 -20 -10 -8 0 2 3 3 3 3 3 4 4.0 4.46 4.7 0.0 -g 10 4 -2 -2 -2 -2 -2 -2 -2 -2 -2 -5 -4 -3 -1 -2 -1 -2 -2 -2 -2 -1 -0.5 -0.15 -0.2 0.0 -h 10 4 6 6 6 6 6 6 6 6 6 -1 2 -2 2 6 4 4 6 6 6 5 4.9 4.76 4.4 0.0 -g 10 5 6 6 6 6 6 6 6 6 6 -1 4 7 4 4 6 5 5 5 4 4 3.7 3.06 2.5 0.0 -h 10 5 -4 -4 -4 -4 -4 -4 -4 -4 -4 -6 -3 -4 -5 -4 -4 -4 -4 -4 -4 -5 -5.9 -6.58 -7.2 0.0 -g 10 6 4 4 4 4 4 4 4 4 4 8 12 4 6 4 4 4 3 3 3 2 1.0 0.29 -0.3 0.0 -h 10 6 0 0 0 0 0 0 0 0 0 6 6 1 1 0 0 -1 0 0 0 -1 -1.2 -1.01 -1.0 0.0 -g 10 7 0 0 0 0 0 0 0 0 0 -1 3 -2 1 0 1 1 1 1 1 2 2.0 2.06 2.2 0.0 -h 10 7 -2 -2 -2 -2 -2 -2 -2 -1 -1 -4 -3 -3 -1 -2 -1 -1 -1 -1 -2 -2 -2.9 -3.47 -4.0 0.0 -g 10 8 2 2 2 1 1 1 1 2 2 -3 2 6 -1 2 0 0 2 2 3 5 4.2 3.77 3.1 0.0 -h 10 8 4 4 4 4 4 4 4 4 4 -2 6 7 6 3 3 3 4 4 3 1 0.2 -0.86 -2.0 0.0 -g 10 9 2 2 2 2 3 3 3 3 3 5 10 -2 2 2 3 3 3 3 3 1 0.3 -0.21 -1.0 0.0 -h 10 9 0 0 0 0 0 0 0 0 0 0 11 -1 0 0 1 1 0 0 -1 -2 -2.2 -2.31 -2.0 0.0 -g 10 10 0 0 0 0 0 0 0 0 0 -2 3 0 0 0 -1 -1 0 0 0 0 -1.1 -2.09 -2.8 0.0 -h 10 10 -6 -6 -6 -6 -6 -6 -6 -6 -6 -2 8 -3 -7 -6 -4 -5 -6 -6 -6 -7 -7.4 -7.93 -8.3 0.0 -g 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2.7 2.95 3.0 0.0 -g 11 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.7 -1.60 -1.5 0.0 -h 11 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0.26 0.1 0.0 -g 11 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.9 -1.88 -2.1 0.0 -h 11 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.3 1.44 1.7 0.0 -g 11 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.5 1.44 1.6 0.0 -h 11 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.77 -0.6 0.0 -g 11 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.1 -0.31 -0.5 0.0 -h 11 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -2.6 -2.27 -1.8 0.0 -g 11 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0.29 0.5 0.0 -h 11 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0.90 0.9 0.0 -g 11 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.7 -0.79 -0.8 0.0 -h 11 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.7 -0.58 -0.4 0.0 -g 11 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0.53 0.4 0.0 -h 11 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -2.8 -2.69 -2.5 0.0 -g 11 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.7 1.80 1.8 0.0 -h 11 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -1.08 -1.3 0.0 -g 11 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0.16 0.2 0.0 -h 11 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.2 -1.58 -2.1 0.0 -g 11 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.2 0.96 0.8 0.0 -h 11 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.9 -1.90 -1.9 0.0 -g 11 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4.0 3.99 3.8 0.0 -h 11 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -1.39 -1.8 0.0 -g 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -2.2 -2.15 -2.1 0.0 -g 12 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.3 -0.29 -0.2 0.0 -h 12 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.55 -0.8 0.0 -g 12 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0.21 0.3 0.0 -h 12 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.23 0.3 0.0 -g 12 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0.89 1.0 0.0 -h 12 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2.5 2.38 2.2 0.0 -g 12 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.2 -0.38 -0.7 0.0 -h 12 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -2.6 -2.63 -2.5 0.0 -g 12 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0.96 0.9 0.0 -h 12 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0.61 0.5 0.0 -g 12 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.5 -0.30 -0.1 0.0 -h 12 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.40 0.6 0.0 -g 12 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.46 0.5 0.0 -h 12 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0 0.01 0.0 0.0 -g 12 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.3 -0.35 -0.4 0.0 -h 12 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0 0.02 0.1 0.0 -g 12 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.36 -0.4 0.0 -h 12 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.28 0.3 0.0 -g 12 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.1 0.08 0.2 0.0 -h 12 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.87 -0.9 0.0 -g 12 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.2 -0.49 -0.8 0.0 -h 12 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.34 -0.2 0.0 -g 12 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.08 0.0 0.0 -h 12 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.8 0.88 0.8 0.0 -g 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.2 -0.16 -0.2 0.0 -g 13 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.88 -0.9 0.0 -h 13 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.76 -0.8 0.0 -g 13 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.30 0.3 0.0 -h 13 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0.33 0.3 0.0 -g 13 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0.28 0.4 0.0 -h 13 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.8 1.72 1.7 0.0 -g 13 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.43 -0.4 0.0 -h 13 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.54 -0.6 0.0 -g 13 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.3 1.18 1.1 0.0 -h 13 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.0 -1.07 -1.2 0.0 -g 13 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.37 -0.3 0.0 -h 13 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.1 -0.04 -0.1 0.0 -g 13 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0.75 0.8 0.0 -h 13 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0.63 0.5 0.0 -g 13 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.26 -0.2 0.0 -h 13 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.21 0.1 0.0 -g 13 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.35 0.4 0.0 -h 13 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.6 0.53 0.5 0.0 -g 13 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.1 -0.05 0.0 0.0 -h 13 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.38 0.4 0.0 -g 13 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0.41 0.4 0.0 -h 13 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.2 -0.22 -0.2 0.0 -g 13 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0 -0.10 -0.3 0.0 -h 13 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.5 -0.57 -0.5 0.0 -g 13 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 -0.18 -0.3 0.0 -h 13 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.82 -0.8 0.0 diff --git a/apps/abs/utils/resource/skynoise_jro.dat b/apps/abs/utils/resource/skynoise_jro.dat deleted file mode 100644 index 86a159a4a9705bbcbe083d68c3cebaaa91120562..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@4ql 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 diff --git a/apps/abs/views.py b/apps/abs/views.py deleted file mode 100644 index a3254d6..0000000 --- a/apps/abs/views.py +++ /dev/null @@ -1,445 +0,0 @@ -from django.shortcuts import render_to_response -from django.template import RequestContext -from django.shortcuts import redirect, render, get_object_or_404 -from django.contrib import messages -from django.conf import settings -from django.http import HttpResponse -from django.urls import reverse -from django.views.decorators.csrf import csrf_exempt -from django.utils.safestring import mark_safe - -from datetime import datetime -from time import sleep -import os -import io - -from apps.main.models import Device, Configuration, Experiment -from apps.main.views import sidebar - -from .models import ABSConfiguration, ABSBeam -from .forms import ABSConfigurationForm, ABSBeamEditForm, ABSBeamAddForm, ABSImportForm - -from .utils.overJroShow import overJroShow -#from .utils.OverJRO import OverJRO -#Create your views here. -import json, ast - - -def get_values_from_form(form_data): - - sublistup = [] - sublistdown = [] - subtxlistup = [] - subtxlistdown = [] - subrxlistup = [] - subrxlistdown = [] - - up_values_list = [] - down_values_list = [] - up_txvalues_list = [] - down_txvalues_list = [] - up_rxvalues_list = [] - down_rxvalues_list = [] - - values_list = {} - cont = 1 - - for i in range(1,65): - x = float(form_data['abs_up'+str(i)]) - y = float(form_data['abs_down'+str(i)]) - sublistup.append(x) - sublistdown.append(y) - - if str(i) in form_data.getlist('uptx_checks'): - subtxlistup.append(1) - else: - subtxlistup.append(0) - if str(i) in form_data.getlist('downtx_checks'): - subtxlistdown.append(1) - else: - subtxlistdown.append(0) - - if str(i) in form_data.getlist('uprx_checks'): - subrxlistup.append(1) - else: - subrxlistup.append(0) - if str(i) in form_data.getlist('downrx_checks'): - subrxlistdown.append(1) - else: - subrxlistdown.append(0) - - cont = cont+1 - - if cont == 9: - up_values_list.append(sublistup) - down_values_list.append(sublistdown) - sublistup = [] - sublistdown = [] - - up_txvalues_list.append(subtxlistup) - down_txvalues_list.append(subtxlistdown) - subtxlistup = [] - subtxlistdown = [] - up_rxvalues_list.append(subrxlistup) - down_rxvalues_list.append(subrxlistdown) - subrxlistup = [] - subrxlistdown = [] - cont = 1 - - - list_uesup = [] - list_uesdown = [] - for i in range(1,5): - if form_data['ues_up'+str(i)] == '': - list_uesup.append(0.0) - else: - list_uesup.append(float(form_data['ues_up'+str(i)])) - - if form_data['ues_down'+str(i)] == '': - list_uesdown.append(0.0) - else: - list_uesdown.append(float(form_data['ues_down'+str(i)])) - - onlyrx_list = form_data.getlist('onlyrx') - only_rx = {} - if '1' in onlyrx_list: - only_rx['up'] = True - else: - only_rx['up'] = False - if '2' in onlyrx_list: - only_rx['down'] = True - else: - only_rx['down'] = False - - antenna = {'antenna_up': up_values_list, 'antenna_down': down_values_list} - tx = {'up': up_txvalues_list, 'down': down_txvalues_list} - rx = {'up': up_rxvalues_list, 'down': down_rxvalues_list} - ues = {'up': list_uesup, 'down': list_uesdown} - name = str(form_data['beam_name']) - - beam_data = {'name': name, 'antenna': antenna, 'tx': tx, 'rx': rx, 'ues': ues, 'only_rx': only_rx} - - return beam_data - - -def abs_conf(request, id_conf): - - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - beams = ABSBeam.objects.filter(abs_conf=conf) - #------------Colors for Active Beam:------------- - all_status = {} - module_messages = json.loads(conf.module_messages) - - color_status = {} - for i, status in enumerate(conf.module_status): - if status == '3': #Running background-color: #00cc00; - all_status['{}'.format(i+1)] = 2 - color_status['{}'.format(i+1)] = 'class=text-success'#'bgcolor=#00cc00' - elif status == '2': - all_status['{}'.format(i+1)] = 1 - color_status['{}'.format(i+1)] = 'class=text-info' - elif status == '1': #Connected background-color: #ee902c; - all_status['{}'.format(i+1)] = 1 - color_status['{}'.format(i+1)] = 'class=text-warning'#'bgcolor=#ee902c' - else: #Disconnected background-color: #ff0000; - all_status['{}'.format(i+1)] = 0 - color_status['{}'.format(i+1)] = 'class=text-danger'#'bgcolor=#FF0000' - #------------------------------------------------ - - kwargs = {} - kwargs['connected_modules'] = str(conf.connected_modules())+'/64' - kwargs['dev_conf'] = conf - - if conf.operation_mode == 0: - kwargs['dev_conf_keys'] = ['label', 'operation_mode'] - else: - kwargs['dev_conf_keys'] = ['label', 'operation_mode', 'operation_value'] - - kwargs['title'] = 'ABS Configuration' - kwargs['suptitle'] = 'Details' - kwargs['button'] = 'Edit Configuration' - - if conf.active_beam != 0: - kwargs['active_beam'] = int(conf.active_beam) - - kwargs['beams'] = beams - kwargs['modules_status'] = all_status - kwargs['color_status'] = color_status - kwargs['module_messages'] = module_messages - ###### SIDEBAR ###### - kwargs.update(sidebar(conf=conf)) - - return render(request, 'abs_conf.html', kwargs) - - -def abs_conf_edit(request, id_conf): - - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - - beams = ABSBeam.objects.filter(abs_conf=conf) - - if request.method=='GET': - form = ABSConfigurationForm(instance=conf) - - if request.method=='POST': - form = ABSConfigurationForm(request.POST, instance=conf) - - if form.is_valid(): - conf = form.save(commit=False) - conf.save() - return redirect('url_abs_conf', id_conf=conf.id) - - ###### SIDEBAR ###### - kwargs = {} - - kwargs['dev_conf'] = conf - #kwargs['id_dev'] = conf.id - kwargs['id_conf'] = conf.id - kwargs['form'] = form - kwargs['abs_beams'] = beams - kwargs['title'] = 'Device Configuration' - kwargs['suptitle'] = 'Edit' - kwargs['button'] = 'Save' - - kwargs['edit'] = True - - return render(request, 'abs_conf_edit.html', kwargs) - -@csrf_exempt -def abs_conf_alert(request): - - if request.method == 'POST': - print (request.POST) - return HttpResponse(json.dumps({'result':1}), content_type='application/json') - else: - return redirect('index') - - -def import_file(request, id_conf): - - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - if request.method=='POST': - form = ABSImportForm(request.POST, request.FILES) - if form.is_valid(): - try: - parms = conf.import_from_file(request.FILES['file_name']) - - if parms: - conf.update_from_file(parms) - messages.success(request, 'Configuration "%s" loaded succesfully' % request.FILES['file_name']) - return redirect(conf.get_absolute_url_edit()) - - except Exception as e: - messages.error(request, 'Error parsing file: "%s" - %s' % (request.FILES['file_name'], e)) - - else: - messages.warning(request, 'Your current configuration will be replaced') - form = ABSImportForm() - - kwargs = {} - kwargs['form'] = form - kwargs['title'] = 'ABS Configuration' - kwargs['suptitle'] = 'Import file' - kwargs['button'] = 'Upload' - kwargs['previous'] = conf.get_absolute_url() - - return render(request, 'abs_import.html', kwargs) - - -def send_beam(request, id_conf, id_beam): - - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - - abs = Configuration.objects.filter(pk=conf.device.conf_active).first() - if abs!=conf: - url = '#' if abs is None else abs.get_absolute_url() - label = 'None' if abs is None else abs.label - messages.warning( - request, - mark_safe('The current configuration has not been written in the modules, the active configuration is {}'.format( - url, - label - )) - ) - return redirect(conf.get_absolute_url()) - - beam = get_object_or_404(ABSBeam, pk=id_beam) - - if request.method == 'POST': - - beams_list = ABSBeam.objects.filter(abs_conf=conf) - conf.active_beam = id_beam - - i = 0 - for b in beams_list: - if b.id == int(id_beam): - break - else: - i += 1 - beam_pos = i + 1 #Estandarizar - print ('%s Position: %s') % (beam.name, str(beam_pos)) - conf.send_beam(beam_pos) - - return redirect('url_abs_conf', conf.id) - - kwargs = { - 'title': 'ABS', - 'suptitle': conf.label, - 'message': 'Are you sure you want to change ABS Beam to: {}?'.format(beam.name), - 'delete': False - } - kwargs['menu_configurations'] = 'active' - - return render(request, 'confirm.html', kwargs) - - -def add_beam(request, id_conf): - - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - confs = Configuration.objects.all() - - if request.method=='GET': - form = ABSBeamAddForm() - - if request.method=='POST': - form = ABSBeamAddForm(request.POST) - - beam_data = get_values_from_form(request.POST) - - new_beam = ABSBeam( - name = beam_data['name'], - antenna = json.dumps(beam_data['antenna']), - abs_conf = conf, - tx = json.dumps(beam_data['tx']), - rx = json.dumps(beam_data['rx']), - ues = json.dumps(beam_data['ues']), - only_rx = json.dumps(beam_data['only_rx']) - ) - new_beam.save() - messages.success(request, 'Beam: "%s" has been added.' % new_beam.name) - - return redirect('url_edit_abs_conf', conf.id) - - ###### SIDEBAR ###### - kwargs = {} - - #kwargs['dev_conf'] = conf.device - #kwargs['id_dev'] = conf.device - #kwargs['previous'] = conf.get_absolute_url_edit() - kwargs['id_conf'] = conf.id - kwargs['form'] = form - kwargs['title'] = 'ABS Beams' - kwargs['suptitle'] = 'Add Beam' - kwargs['button'] = 'Add' - kwargs['no_sidebar'] = True - kwargs['edit'] = True - - return render(request, 'abs_add_beam.html', kwargs) - - -def edit_beam(request, id_conf, id_beam): - - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - beam = get_object_or_404(ABSBeam, pk=id_beam) - - if request.method=='GET': - form = ABSBeamEditForm(initial={'beam': beam}) - - if request.method=='POST': - form = ABSBeamEditForm(request.POST) - - beam_data = get_values_from_form(request.POST) - - beam.dict_to_parms(beam_data) - beam.save() - - messages.success(request, 'Beam: "%s" has been updated.' % beam.name) - - return redirect('url_edit_abs_conf', conf.id) - - ###### SIDEBAR ###### - kwargs = {} - - kwargs['id_conf'] = conf.id - kwargs['form'] = form - kwargs['title'] = 'ABS Beams' - kwargs['suptitle'] = 'Edit Beam' - kwargs['button'] = 'Save' - kwargs['no_sidebar'] = True - - #kwargs['previous'] = conf.get_absolute_url_edit() - kwargs['edit'] = True - - return render(request, 'abs_edit_beam.html', kwargs) - - - -def remove_beam(request, id_conf, id_beam): - - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - beam = get_object_or_404(ABSBeam, pk=id_beam) - - if request.method=='POST': - if beam: - try: - beam.delete() - messages.success(request, 'Beam: "%s" has been deleted.' % beam) - except: - messages.error(request, 'Unable to delete beam: "%s".' % beam) - - return redirect('url_edit_abs_conf', conf.id) - - ###### SIDEBAR ###### - kwargs = {} - - kwargs['object'] = beam - kwargs['delete'] = True - kwargs['title'] = 'Delete' - kwargs['suptitle'] = 'Beam' - kwargs['previous'] = conf.get_absolute_url_edit() - return render(request, 'confirm.html', kwargs) - - - -def plot_patterns(request, id_conf, id_beam=None): - - kwargs = {} - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - beams = ABSBeam.objects.filter(abs_conf=conf) - - if id_beam: - beam = get_object_or_404(ABSBeam, pk=id_beam) - kwargs['beam'] = beam - - ###### SIDEBAR ###### - - kwargs['dev_conf'] = conf.device - kwargs['id_dev'] = conf.device - kwargs['id_conf'] = conf.id - kwargs['abs_beams'] = beams - kwargs['title'] = 'ABS Patterns' - kwargs['suptitle'] = conf.name - kwargs['no_sidebar'] = True - - return render(request, 'abs_patterns.html', kwargs) - - -def plot_pattern(request, id_conf, id_beam, antenna): - - if antenna=='down': - sleep(3) - - conf = get_object_or_404(ABSConfiguration, pk=id_conf) - beam = get_object_or_404(ABSBeam, pk=id_beam) - just_rx = 1 if json.loads(beam.only_rx)[antenna] else 0 - phases = json.loads(beam.antenna)['antenna_{}'.format(antenna)] - gain_tx = json.loads(beam.tx)[antenna] - gain_rx = json.loads(beam.rx)[antenna] - ues = json.loads(beam.ues)[antenna] - newOverJro = overJroShow(beam.name) - fig = newOverJro.plotPattern2(datetime.today(), phases, gain_tx, gain_rx, ues, just_rx) - buf = io.BytesIO() - fig.savefig(buf, format='png') - response = HttpResponse(buf.getvalue(), content_type='image/png') - return response - diff --git a/apps/abs/widgets.py b/apps/abs/widgets.py deleted file mode 100644 index c175fc6..0000000 --- a/apps/abs/widgets.py +++ /dev/null @@ -1,2093 +0,0 @@ -import ast -import json -from itertools import chain - -from django import forms -from django.utils.safestring import mark_safe -from django.utils.html import conditional_escape - -style = """""" - - -class EditUpDataWidget(forms.widgets.TextInput): - - def render(self, name, value, attrs=None, renderer=None): - - try: - beam = attrs.get('beam', value) - except: - return - - checked_tx = {} - for i in range(1,65): - checked_tx['']='' - - checked_onlyrx = '' - if beam.get_up_onlyrx == True: - checked_onlyrx = 'checked="True"' - - - html = ''' - -