Está en la página 1de 105

Proyecto Fin de Carrera

Trabajo Fin de Grado


Ingeniería
Grado deenTelecomunicación
Ingeniería de las Tecnologías de
Telecomunicación

Simulación en Python para la determinación


Formato de Publicación
de la intensidad de larecibida
de campo Escuelapor
Técnica
onda
Superior de Ingeniería
de superficie para frecuencias entre 10 kHz
y 30 MHz según la Rec. UIT-R P.368

Autor:Autor: Javier
F. Javier Ojeda
Payán Prieto
Somet
Tutor:Tutor:
Juan Susana Hornillo
José Murillo Mellado
Fuentes

Dpto.
Dep. Teoría
Teoría de ladeSeñal
la Señal y Comunicaciones
y Comunicaciones
Escuela
Escuela Técnica
Técnica Superior
Superior de Ingeniería
de Ingeniería
Universidad
Universidad de Sevilla
de Sevilla
Sevilla, 2020
Sevilla, 2013
Trabajo Fin de Grado
Grado en Ingeniería de las Tecnologías de Telecomunicación

Simulación en Python para la determinación de


la intensidad de campo recibida por onda de
superficie para frecuencias entre 10 kHz y 30
MHz según la Rec. UIT-R P.368

Autor:
Javier Ojeda Prieto

Tutor:
Susana Hornillo Mellado
Profesora Contratada Doctora

Dpto. Teoría de la Señal y Comunicaciones


Escuela Técnica Superior de Ingeniería
Universidad de Sevilla
Sevilla, 2020
Trabajo Fin de Grado: Simulación en Python para la determinación de la intensidad de campo
recibida por onda de superficie para frecuencias entre 10 kHz y 30 MHz
según la Rec. UIT-R P.368

Autor: Javier Ojeda Prieto


Tutor: Susana Hornillo Mellado

El tribunal nombrado para juzgar el trabajo arriba indicado, compuesto por los siguientes profesores:

Presidente:

Vocal/es:

Secretario:

acuerdan otorgarle la calificación de:

El Secretario del Tribunal

Fecha:
Agradecimientos

E ste proyecto no ha sido desarrollado en solitario, sino que ha necesitado colaboración en varios campos
que han agilizado y mejorardo el contenido.
La correcta orientación y motivación para completar este trabajo corresponde a Susana Hornillo, aludiendo
especialmente al conocimiento aportado sobre teoría de ondas superficiales.
A Fran agradezco su ayuda para el análisis realizado del código fuente mediante SonarQube, y asistencia
en lenguaje Python.
El diseño del documento en código LATEXse ha apoyado en la asistencia de Alex.
No obstante, bastante contenido de este trabajo perdería calidad de no haber contado con la aportación de
mi compañero Andrés.

Javier Ojeda Prieto


Sevilla, 2020

I
Resumen

E n este trabajo se ha desarrollado un simulador con interfaz gráfica en Python bajo el nombre GroundWave
Simulator, basado en el programa de computador GRWAVE que utiliza la Rec. ITU-R P.368 [1].
Sobre escenarios de transmisión de ondas superficiales, se estudia la intensidad de campo eléctrico que se
recibe a cierta distancia, en función de las características del terreno, para frecuencias entre 10 kHz y 30
MHz en una suposición de Tierra esférica lisa.

III
Índice Abreviado

Resumen III
Índice Abreviado V
Notación IX

1 Introducción 1

2 Teoría de ondas de superficie 3


2.1 Onda de superficie 3
2.2 Recomendación ITU-R P.368-9 6

3 Herramientas de desarrollo 13
3.1 GRWAVE 13
3.2 DOSBox 15
3.3 Anaconda 16
3.4 Python 3.6.5 17
3.5 Qt Designer 17

4 Funcionalidad de GroundWave Simulator 19


4.1 Interfaz de bienvenida 19
4.2 Ventana de cómputos 21

5 Deducciones y validez del simulador 31


5.1 SonarQube 31
5.2 Discusiones utilizando el simulador 32

6 Conclusiones y líneas futuras 37


Apéndice A Código fuente 39

Índice de Figuras 83
Índice de Tablas 85
Índice de Códigos 87
Bibliografía 89

V
Índice

Resumen III
Índice Abreviado V
Notación IX

1 Introducción 1

2 Teoría de ondas de superficie 3


2.1 Onda de superficie 3
2.1.1 Polarización 4
2.1.2 Atenuación y pérdidas 4
2.1.3 Simplificación de Tierra plana 5
2.1.4 Tierra esférica lisa 6
2.2 Recomendación ITU-R P.368-9 6
2.2.1 Profundidad de penetración 7
2.2.2 Método de Millington 9

3 Herramientas de desarrollo 13
3.1 GRWAVE 13
3.1.1 Parámetros de entrada y sus resultados 13
3.2 DOSBox 15
3.3 Anaconda 16
3.4 Python 3.6.5 17
3.4.1 Librerías añadidas 17
3.4.2 Producción del ejecutable con Pyinstaller 17
3.5 Qt Designer 17

4 Funcionalidad de GroundWave Simulator 19


4.1 Interfaz de bienvenida 19
4.2 Ventana de cómputos 21
4.2.1 Pestaña Terreno homogéneo 21
4.2.2 Pestaña Terreno mixto 26
4.2.3 Ventanas emergentes de error 27

5 Deducciones y validez del simulador 31


5.1 SonarQube 31
5.2 Discusiones utilizando el simulador 32

6 Conclusiones y líneas futuras 37

Apéndice A Código fuente 39

VII
VIII Índice

Índice de Figuras 83
Índice de Tablas 85
Índice de Códigos 87
Bibliografía 89
Notación

Z0 Impedancia intrínseca
Hϕ Componente azimutal de campo magnético
Kr Permitividad dieléctrica compleja del suelo
E Intensidad de campo eléctrico recibido
Ez Componente vertical de campo eléctrico
Eρ Componente radial de campo eléctrico
Et (x) Intensidad de campo eléctrico que penetra en superficie terres-
tre en función de distancia x
E0 Intensidad de campo eléctrico en la frontera entre aire y su-
perficie terrestre
Ecurva Intensidad de campo eléctrico recibido de la curva de propa-
gación GRWAVE
ER Intensidad de campo recibido cuando R es el receptor
ET Intensidad de campo recibido cuando T es el receptor
Lb Pérdidas básicas de propagación
e número e
h Altura de antena
PRA Potencia radiada por la antena
D Directividad de la antena
α Atenuación
δ Profundidad de penetración
εr Permitividad relativa
ε0 Permitividad en espacio libre
ε Permitividad eléctrica
λ Longitud de onda
µr Permeabilidad relativa
µ0 Permeabilidad en espacio libre
µ Permeabilidad magnética
π número pi
σ Conductividad eléctrica
ω Frecuencia angular
≈ aproximadamente igual
 mucho menor que

IX
1 Introducción

L a necesidad de modernizar la ejecución del programa GRWAVE, creado en 1985, hace que surja este
simulador en Python, que ofrecerá la posibilidad de conectarse con otros módulos creados en este
lenguaje para facilitar estudios sobre la propagación de ondas. GRWAVE fue desarrollado para el cálculo
de intensidad de campo eléctrico y sus pérdidas en función de valores de distancia, utilizando ondas de
superficie. Está escrito en código Fortran y funciona únicamente en entornos MS-DOS, que con el tiempo ha
supuesto un obstáculo por desuso.
Como primer objetivo, se persigue extraer el máximo rendimiento de las capacidades de GRWAVE,
haciendo un estudio sobre teoría de ondas de superficie. Siguiendo la Rec. ITU-R P.368 [1], se logra sacar
partido a la herramienta, permitiendo obtener resultados tanto para escenarios con un terreno homogéneo
como para terrenos mixtos mediante el Método de Millington.
Se proporciona además una interfaz gráfica al usuario simple y eficaz, desarrollada con la librería PyQt
de Python, concretamente la versión PyQt5, compaginado con el entorno de desarrollo Qt Designer para el
diseño, que permite al usuario desconocer detalles sobre la obtención de datos de GRWAVE en MS-DOS.
Finalmente, a raíz de la realización de pruebas mediante el simulador, se formulan conclusiones relacionadas
con los parámetros de entrada. En primer lugar, sobre la selección de alturas de antena se concluye la poca
influencia de éstas, salvo para casos de frecuencias cercanas a 30 MHz. Seguidamente se determina la
importancia de utilizar polarización vertical frente a la desventaja que supondría una polarización horizontal,
que atenúa rápidamente los valores de intensidad. Por último, se intenta comprobar los efectos de alterar
el orden de los terrenos en ejecuciones para terreno mixto, reflejando cambios notables para más de dos
superficies con características eléctricas distintas.

1
2 Teoría de ondas de superficie

L as bases para asentar la teoría de ondas de superficie fueron introducidas por Sommerfeld [2] en 1909
a través de un dipolo eléctrico vertical situado en la interfaz entre un plano aislante y otro conductor,
donde se trabajaba con un modelo de Tierra plana con una conductividad y permitividad finita. Este modelo
estudiaba la atenuación considerando pérdidas. Norton [3] amplió este desarrollo años después corrigiendo
algunos errores prácticos de Sommerfeld, también para Tierra plana [4]. En 1939, Van der Pol y Bremmer [5],
con el uso de fórmulas de serie de residuos, avanzaron a la consideración de Tierra esférica, con el resultado
final de un valor de intensidad de campo eléctrico a largas distancias. Dos años después, Norton [6] añadió
algunos matices a este último desarrollo haciéndolo más práctico, y del que saldría la terminación “Norton
Surface wave” usada para denotar ondas superficiales con antenas adyacentes a la superficie de la Tierra.
Sin embargo, todos estos trabajos no servían si tratabas de variar la caracterización del terreno a lo largo
del cálculo, principalmente la conductividad de éste y la permitividad relativa. En pos de solventar esta
incapacidad, Millington [7] en 1949 desarrolló un método aplicable a más de un terreno, aproximando
los efectos topográficos usando una conductividad equivalente. Este método generaría errores de cálculos
considerables para terrenos montañosos.

2.1 Onda de superficie

En la propagación electromagnética de onda media, cuando el objetivo es la comunicación a largas distancias,


la onda de suelo u onda de superficie juega un papel fundamental. Hablamos de la banda de 300 kHz a 3
MHz (MF).
La onda de superficie se propaga cerca de la capa de la superficie terrestre como se muestra en la figura
2.1. Debido a la radiación en linea recta de las ondas electromagnéticas, la curvatura de la Tierra limitará
la distancia de la línea de visión. En cambio, dependiendo de la dirección, algunas ondas son curvadas o
reflejadas de vuelta a la superficie por la ionosfera, que facilitará la transmisión por radio sobre la Tierra
haciendo llegar la onda más allá de la línea de visión.
Fue bastante común la onda superficial en sistemas de radionavegación marítima y aérea, o localización
por radio, hasta ser eclipsado por la comunicación por satélite. Sin embargo, a bajas frecuencias (LF) sigue
siendo una herramienta común. Para conseguir una máxima precisión en el cálculo, es muy necesario conocer
las variables que influyen, como la refracción de la atmósfera, los distintos tipos de terreno que conducen a
valores propios de conductividad y permitividad relativa, topografía, etc.

E
Hϕ = − (2.1)
Z0

La onda de superficie está compuesta por onda espacial y onda de suelo. Cuando las antenas transmisoras y
receptoras se encuentran cerca de la superficie terrestre, la onda de suelo es el principal modo de propagación.
Se debe a que las ondas directa y reflejada, que conforman la onda espacial, se contraponen en fase y cancelan
entre sí.
No obstante, cuando hablamos de frecuencias altas y antenas con elevación, el propio suelo producirá
reflexiones o difracciones que harán que no convenga utilizar la onda de superficie como modo de propagación.

3
4 Capítulo 2. Teoría de ondas de superficie

Figura 2.1 Transmisión de onda superficial y onda de cielo [8].

2.1.1 Polarización

Las ondas de tierra, que se propagan por la superficie terrestre, deberán estar polarizadas verticalmente. Solo
de esta manera no existirá el componente tangencial del campo eléctrico y por tanto, podrá propagarse a lo
largo de la superficie de la Tierra. Si se utilizara polarización horizontal, en una suposición de Tierra como
conductor perfecto, se haría imposible la propagación de cualquier componente horizontal, que, en contacto
con la Tierra, sería cortocirtuitado. Sabiendo que la Tierra no es conductor perfecto y su conductividad
depende de valores finitos (σ , εr ), sí podrá albergar un campo eléctrico en su interior, es decir, una parte de
esa componente horizontal podrá penetrar en la superficie terrestre. A pesar de esto, se disipará rápidamente
en forma de calor y la onda se atenuará, alcanzando distancias muy cortas (algo que en las comunicaciones
no es lo deseable).
Habitualmente se utilizan antenas formadas por dipolos eléctricos verticales, que radian con polarización
vertical, sobre tierra y con una altura desde 50 a 200 metros aproximadamente. El alcance que conseguirán
dependerá de la potencia con la que se radie y la frecuencia.

2.1.2 Atenuación y pérdidas

Como la onda propagada contiene un componente de campo magnético horizontal que afectará al campo
eléctrico según la fórmula 2.2, conforme la distancia va aumentando, la onda tiende a inclinarse a la horizontal,
y esa inclinación derivará pérdidas y atenuación.
Es decir, el vector de onda en las cercanías de la superficie terrestre es curvado ligeramente en el sentido
de la propagación. Dependerá de las características de conductividad del terreno y la frecuencia que se usa
en la transmisión.

E
Hϕ = − (2.2)
Z0

siendo Z0 la impedancia intrínseca de valor 120πΩ, E el módulo de intensidad de campo eléctrico recibido
y Hϕ la componente azimutal de campo magnético (trabajando en coordenadas cilíndricas).
2.1 Onda de superficie 5

Figura 2.2 Vector del frente de ondas en transmisión de onda de superficie.

Esa inclinación se puede medir mediante la permitividad dieléctrica compleja que actúa relacionada con
las componentes del campo eléctrico:
Eρ 1
≈u= √ (2.3)
Ez Kr

El procedimiento en detalle para este cálculo se puede consultar en el manual sobre propagación de ondas
superficiales de la ITU [9].
Además, la conductividad del terreno atenúa la intensidad del campo eléctrico cuando penetra en la
superficie terrestre de forma exponencial:
x
Et (x) = E0 e− δ (2.4)

Donde Et (x) es la intensidad de campo eléctrico que ha penetrado en la superficie terrestre en función de
la distancia x, E0 se toma como la intensidad de campo eléctrico en la frontera entre el aire y la superficie
terrestre, y δ es la profundidad de penetración expresada en metros:
s
1
δ= (2.5)
π f µσ

Aparece un nuevo parámetro µ, que se define como la permeabilidad del medio.

2.1.3 Simplificación de Tierra plana

En un escenario con distancias relativamente cortas entre transmisor y receptor que harían que se desprecie
la curvatura terrestre, con un terreno homogéneo, se puede usar un método de obtención de la intensidad de
campo eléctrico de Tierra plana, también llamado método de ecuación integral [10].
En el siguiente caso de propagación, se observa que el rayo directo es más corto que el rayo de la onda
reflejada, es decir, existirá un desfase entre las dos ondas que permitirá, como ya se ha comentado, que el
principal modo de propagación sea la onda de suelo.

Figura 2.3 Propagación con reflexión sobre tierra plana.


6 Capítulo 2. Teoría de ondas de superficie

Con la suposición de Tierra como conductor imperfecto, Richards [11] demostró el calculo de la intensidad
de campo incluyendo procedimientos que tenían en cuenta la atenuación por conductividad finita, a falta de
la ya mencionada atenuación por curvatura terrestre.

2.1.4 Tierra esférica lisa

Dejando a un lado el modelo de Sommerfeld para distancias cortas, se puede seguir considerando Tierra
plana para una distancia en kilómetros marcada por la expresión:
80
d= p
3
(2.6)
f (MHz)

Para distancias mayores, si se quiere obtener con mayor precisión la intensidad de campo, es necesario
considerar los efectos producidos por la difracción por curvatura terrestre, descritos por Bremmer [12].

2.2 Recomendación ITU-R P.368-9

El Sector de Radiocomunicaciones de la Unión Internacional de Telecomunicaciones, ITU-R, consideró útil


reunir distintas ejecuciones de curvas de propagación por onda de superficie, utilizando distintos terrenos y
frecuencias.
Para las frecuencias, conociendo que las bandas de LF y MF son provechosas y efectivas, marcan un rango
que va desde los 10 kHz hasta los 30 MHz, y el documento aporta multitud de casos en los que recomienda
basarse para la determinación de la intensidad de campo eléctrico recibida. Sin embargo, hay que asegurarse
de que los efectos por reflexiones ionosféricas sean despreciables para el caso que se esté analizando, así
como la altura de la antena receptora no puede colocarse a una altura superior a:
1 3
h = 1,2σ 2 λ 2 (2.7)

donde σ es la conductividad y λ la longitud de onda, en los casos en los que la permitividad relativa
cumpla:

εr  60πλ (2.8)

La recomendación se basa en la hipótesis de Tierra esférica homogénea lisa. Realmente la superficie


terrestre no suele ser homogénea, sino que se compone por varias capas, cada una con distinta característica
eléctrica y de ahí nace el concepto parámetro efectivo que representa al suelo homogéneo en una zona. Los
cálculos de dichos parámetros efectivos vienen descritos en la Recomendación P. 527 de la ITU-R [13]. Esta
hipótesis se basa en un modelo aproximado que pierde exactitud en cuanto el terreno se vuelve montañoso o se
manifiestan edificios considerables en zonas urbanas, es decir, aparece el fenómeno fading o desvanecimiento
[14].
Para la estimación del índice de refracción radioeléctrica se ha seguido la Recomendación P. 453 de la
ITU-R [15], donde se describe que en la troposféra disminuye de forma exponencial con dependencia de la
altura.
Tanto antenas transmisora como receptora se encuentran instaladas en tierra, con un monopolo vertical
corto (comparado con λ /4) como elemento radiante, PRA de 1 kW y fuerza ciclomotriz 300 V. Y los resultados
se han calculado para región de campo lejano de la antena.
Las curvas del Anexo 1 de ese documento se han generado con el programa GRWAVE, que determina el
valor de campo E (dB(µV /m)), y del que se pueden derivar las pérdidas básicas de propagación:

Lb [dB] = 142,0 + 20log fMHz − E (2.9)

Las curvas representadas en función de los valores de GRWAVE, y cuya semejanza se tiene que imitar en
el simulador propuesto para el proyecto, serían del estilo a la figura 2.4.
Se han incluido curvas para el rango entre 10 kHz hasta 30 MHz, expresando en la leyenda también la
longitud de onda correspondiente.
2.2 Recomendación ITU-R P.368-9 7

Figura 2.4 Ejemplo de curva de la Rec. P.368-9 para terreno homogéneo agua de mar (salinidad baja).

Sin embargo, para obtener el resultado final teniendo en cuenta los factores de la antena emisora, es
necesario utilizar la directividad y potencia radiada de la antena.
r
PRA · D
E = Ecurva (2.10)
3
Donde D es la directividad y PRA la potencia radiada por la antena. Como hemos comentado, la PRA se
corresponde con 1 kW. Respecto a la directividad, en una antena vertical corta es de 1,5, sin embargo, este
valor se duplica debido a la consideración de la Tierra como conductor (teoría de imágenes aplicadas a
antenas [16]). Sustituyendo estos valores, Ecurva se puede escribir como el resultado final.

2.2.1 Profundidad de penetración

Los parámetros que caracterizan el terreno influyen en la transmisión de las ondas superficiales a través de la
profundidad de penetración δ en el suelo. La determinación de la profundidad de penetración en un material
se utiliza la siguiente fórmula:

− 1
√ ! s  2 2
2 σ
δ= √  1+ −1  (2.11)
ω µr µ0 εr ε0 ωεr ε0

donde ω=2πf, µr la permeabilidad relativa, µ0 la permeabilidad en espacio libre y ε0 la permitividad en


espacio libre, además de las ya mencionadas δ , σ y εr .
A la pregunta de qué parámetro influye más en la propagación, DeMinco respondió con un estudio
realizado con el Modelo de Campo sin Perturbaciones [17]. Con alturas de antenas fijadas a 0 metros se
realizaban mediciones para determinar, según la frecuencia, qué distancias era capaz de cubrir la emisión.
En frecuencias desde 30 a 915 MHz todavía se identificaba la onda de suelo hasta unos 250 metros y las
pérdidas se producían en mayor medida por el efecto de la permitividad relativa y la conductividad era casi
despreciable. Sin embargo, para rangos de frecuencias menores a 150 MHz, la conclusión se invertía, siendo
la conductividad la característica predominante.
8 Capítulo 2. Teoría de ondas de superficie

Figura 2.5 Pérdidas de propagación en función de epsilon, con σ =0.001 y distancia=10 m [17].

Figura 2.6 Pérdidas de propagación en función de sigma, con εr =4.0 y distancia=10 m [17].

En la figura 2.5 extraída del estudio se comprueba como para valores por encima de 30 MHz de frecuencia,
εr aumenta su efecto sobre las pérdidas conforme la frecuencia crece. En la figura 2.6 la dependencia de σ es
prácticamente despreciable, y solo se muestra una pequeña influencia en valores muy cercanos a 30 MHz.
En cuanto a la atenuación que sufre la onda, Sommerfeld [2] y Norton [3] [4] obtuvieron un valor de factor
de atenuación F para Tierra plana de conductividad finita que variaba en función de las características del
2.2 Recomendación ITU-R P.368-9 9

terreno y distancia:


 q 
(2.12)

F = 1 − j (πw)exp(−w) er f c( j w)

donde
− j2kr2 u2 (1 − u2 cos2 ψ2 )
w= (2.13)
(1 − Rv )

2
u2 = (2.14)
(εr − jx)

σ σ
x= = 1,8 · 104 (2.15)
(wε0 ) fMHz

y er f c la función error complementaria.


Este factor estará presente en las expresiones de intensidad de campo de la onda de superficie, cuyas
componentes en coordenadas cilíndricas son:
 
exp(− jkr1 ) exp(− jkr2 )
Ez = − j30kIdl cos2 ψ1 + cos2 ψ2 +
r1 r2
  (2.16)
exp(− jkr2 )
(1 − Rv )(1 − u2 + u4 cos2 ψ2 )F
r2


exp(− jkr1 ) exp(− jkr2 )
Eρ = − j30kIdl sinψ1 cosψ1 + sinψ2 cosψ2
r1 r2
 2 2  (2.17)
u sin ψ2 exp(− jkr2 )
q
−cosψ2 (1 − Rv )u (1 − u2 cos2 ψ2 ) 1 − (1 − u2 cos2 ψ2 ) + F
2 2 r2

donde ψ1 y ψ2 se detallan en la figura 2.3, Idl es el producto de la corriente de la fuente y la longitud, Rv


el coeficiente de reflexión de Fresnel de onda plana para la polarización vertical,

2.2.2 Método de Millington

Para el caso de terrenos mixtos, se detalla el procedimiento de obtención de la intensidad de campo eléctrico
con rigurosidad, utilizando el Método semi-empírico de Millington, que supone tierra esférica. Sigue el
principio de reciprocidad, el campo total recibido será una media aritmética de ER y ET , que se corresponden
con el módulo de intensidad de campo recibido cuando R es el receptor y cuando T es el receptor respec-
tivamente, obtenidos tras una serie de cálculos invirtiendo los emplazamientos. Existirá mayor precisión
mientras más parecidos sean los valores ER y ET .
En términos generales, las fórmulas a utilizar serían:
N N
ER = ∑ Ek (Sk ) − ∑ Ek (Sk−1 ) (2.18)
k=1 k=2

k
Sk = ∑ dn (2.19)
n=1

N N
ET = ∑ EN−k+1 (rk ) − ∑ EN−k+1 (rk−1 ) (2.20)
k=1 k=2
10 Capítulo 2. Teoría de ondas de superficie

k
rk = ∑ dN−n+1 (2.21)
n=1

ET + ER
E= (dB(µV /m)) (2.22)
2
En escenarios de transmisión donde el terreno se divide en tres de distintas características, se procede de
la siguiente manera:

ER = E1 (d1 ) − E2 (d1 ) + E2 (d1 + d2 ) − E3 (d1 + d2 ) + E3 (d1 + d2 + d3 ) (2.23)

ET = E3 (d3 ) − E2 (d3 ) + E2 (d3 + d2 ) − E1 (d3 + d2 ) + E1 (d1 + d2 + d3 ) (2.24)

Y se finalizaría con la ecuación 2.22


La gráficas que aporta el documento y que hay que tratar de imitar serían del estilo a la figura 2.7.

Figura 2.7 Ejemplo de curva de la Rec. P.368-9 para terreno mixto con distintos valores de σ y εr .

En la figura 2.8 podemos ver la curva resultante de combinar dos terrenos en la misma transmisión.
Tanto las características de la antena, como las hipótesis de Tierra esférica homogénea lisa y estimación
del índice de refracción radioeléctrica siguen vigentes en este apartado.
2.2 Recomendación ITU-R P.368-9 11

Figura 2.8 Curva resultante tras aplicar Método Millington para dos terrenos.
3 Herramientas de desarrollo

E l simulador se apoya en diferentes software para conseguir una mayor simplicidad de uso de cara al
usuario y sacar el máximo rendimiento posible. Todos los utensilios son libres y no requieren licencia, y
han sido incluidos en el archivo comprimido del programa, con lo cual, el usuario no tendrá que preocuparse
por la instalación de éstos.

3.1 GRWAVE
Se trata de un software libre que aporta la ITU desarrollado por Gill [18] para el cálculo de intensidad de
campo eléctrico. Está escrito en código fortran y preparado para ejecutarse en un entorno MSDOS, sin
embargo, es posible hacerlo funcionar en el terminal de Windows, dependiendo de la versión.
El cálculo de intensidad de campo eléctrico dependerá de la frecuencia, altura de antenas y características
del terreno utilizados entre otros parámetros. Admite un rango de frecuencias entre 10 KHz y 10 GHz,
considera un modelo de tierra esférica lisa con un monopolo vertical corto de 1 kW como elemento radiante
y fuerza ciclomotriz 300 V.

3.1.1 Parámetros de entrada y sus resultados

• ANS, refractividad de la troposfera en la superficie de la tierra en N-unidades. Por defecto 315.


• HSCALE, altura de la escala de la troposfera en kilómetros. Por defecto 7.35.
• IPOLRN, polarización. 1 si es vertical, 2 si es horizontal. Por defecto, 1.
• FREQ, Frecuencia en MHz. Por defecto, 1
• EPSLON, permitividad relativa de la superficie de la tierra. Por defecto, 70.0 (mar).
• SIGMA, conductividad de la superficie de la tierra expresada en S/m. Por defecto, 5.0 (mar).
• DMIN, rango mínimo de distancia en kilómetros. Por defecto, 10.0.
• DMAX, rango máximo de distancia en kilómetros. Por defecto, 200.0.
• DSTEP, los pasos en el rango, en kilómetros. Por defecto, 10.0.
• IG. Si IG=-1 o 0, se utilizará el cálculo de largas distancias con serie de residuos. Si IG=0 o 1, se
utilizará el método de distancias cortas. Por defecto, 0.
• HTT , array de 20 alturas de la antena transmisora en metros. Por defecto, 100.0.
• HRR, array de 20 alturas de la antena receptora en metros. Por defecto, 100.0.
• JHT , combinación de la altura de antenas, en caso de que se haya ingresado más de un valor. Si JHT=1,
se utilizan todas las combinaciones posibles. Si JHT=2, hace combinación de pares. Por defecto: 1.
• GO, empezar con la ejecución.
• STOP, cierra el programa.
Es posible introducir estos parámetros en tiempo de ejecución, o a través de un fichero de entrada de extensión
.dat. De la misma manera, los datos de salida se pueden volcar en un fichero .out.

Código 3.1 Ejecución de GRWAVE con ficheros de entrada y salida.


grwave.exe <entrada.dat >salida.out

13
14 Capítulo 3. Herramientas de desarrollo

Donde entrada.dat es un fichero que debe existir, estar rellenado correctamente con los parámetros de entrada
deseados y precedido por el símbolo <. Salida.out se generará automáticamente si no existe, con la salida del
programa siempre que vaya precedido del símbolo >.

A continuación, se muestran ejemplos de ficheros de entrada y salida.

Figura 3.1 Ejemplo de fichero de entrada para GRWAVE.

Figura 3.2 Ejemplo de fichero de salida para GRWAVE.


3.2 DOSBox 15

En los datos de salida, las primeras 32 lineas corresponden a los parámetros de entrada introducidos, y los
cálculos se presentan en tres columnas. Distancia (en kilómetros), intensidad de campo (en dB(µV/m)), y
pérdidas básicas de transmisión (en dB). De la interpretación se encargará Python.
GRWAVE viene acompañado de un manual en el fichero grwusr.man con información útil.

3.2 DOSBox
DOSBox es un emulador capaz de recrear un entorno para la ejecución de
programas y videojuegos escritos para el sistema operativo MS-DOS, en formato
.exe .com y .bat. Este sistema operativo fue el principal para computadores en
la década de los 80, hasta que otros como Windows lo sustituyeron gracias a su
interfaz gráfica de usuario incluida, que relegaron a MS-DOS a la obsolescencia.
Todavía es posible usar algunos de los comandos de MS-DOS mediante
el terminal de Windows, sin embargo, para el programa usado en este pro-
yecto GRWAVE, no suele funcionar en algunos ordenadores, y se ofrecerá la
posibilidad de ejecutarlo mediante este simulador.
Para el proyecto se ha utilizado la versión 0.74-3, y al ejecutarlo, muestra
una ventana para comenzar a escribir comandos:

Figura 3.3 Interfaz inicial DOSBox.

En lo que concierne a este trabajo, los comandos que conviene conocer para la ejecución de GRWAVE
serán:
• MOUNT Drive-Letter Local-Directory. Esto montará el directorio indicado dentro de la unidad especi-
ficada.

Código 3.2 Ejemplo para montar directorio en DOSBox.

mount c d:\dosprog

Montará el directorio d:\dosprog en la unidad C:, dentro de DOSBox.


• CD. Se usará para acceder a otro directorio dentro del actual.
• DIR. Ofrecerá el listado de directorios dentro del actual.
16 Capítulo 3. Herramientas de desarrollo

• HELP. Lista los comandos básicos con una breve descripción


• EXIT . Cierra el simulador.

Un ejemplo de los pasos para hacer funcionar GRWAVE en DOSBox se muestra en la siguiente figura:

Figura 3.4 Ejecución de GRWAVE en DOSBox.

El emulador ha ejecutado correctamente grwave.exe, ubicado en la carpeta en la que se ha montado la


unidad (C:\GWSim), se ha cambiado a dicha unidad C:, y los datos de salida se ven reflejados en el fichero de
salida grwave.out. En caso de ejecutar grwave.exe sin especificar ficheros de entrada ni salida, los parámetros
se irían introduciendo en tiempo de ejecución, y la salida se volcaría en la interfaz de DOSBox.
Además, es posible modificar el fichero de configuración para ejecutar comandos al iniciar el emulador
automáticamente. Como se explica en el manual de DOSBox, en su primera ejecución creará, si no existe,
un fichero .conf en la ruta C:\%USERPROFILE%\AppData\Local\DOSBox, con el nombre “DOSBox 0.74-3
Manual.conf”.
Al archivo de configuración original se le ha añadido en la parte de autoejecución el siguiente código:

Código 3.3 Código para llamada automática de GRWAVE en DOSBox.


mount c C:\GWSim
c:
grwave <entrada.dat >grwave.out
exit

3.3 Anaconda

Anaconda es una distribución de software libre para Python que funciona


como un gestor de entorno. Desde un principio se utilizó por su comodidad
de instalación de librerías y paquetes, e incorpora una interfaz Navigator para
escoger sus herramientas, entre las que se encuentra el IDE Spyder que se ha
usado para escribir todo el código del simulador.
3.4 Python 3.6.5 17

3.4 Python 3.6.5


Todas las lineas de código se escribieron en Spyder, pero se necesitó un intérprete de
código Python para que, ejecutando los archivos .py en el terminal de Windows, se
pudiera entender todo el código aportado y generar una respuesta. La versión usada
ha sido la 3.6.5. Se han añadido paquetes para el desarrollo de nuestro objetivo. En
primer lugar, el instalador de paquetes pip permitía añadir nuevas librerías sin la
necesidad de descargarlas navegando en internet:

Código 3.4 Ejemplo de uso del instalador de paquetes pip.


pip install [package]

3.4.1 Librerías añadidas

Es muy habitual en la programación tener que incluir nuevas librerías que no aparecen entre las predetermi-
nadas, para permitir el uso de nuevas funciones. Para este proyecto se han agregado las siguientes:

• Numpy, para soporte sobre arrays y funciones matemáticas.


• Math, para otras funciones matemáticas que no incorpora Python.
• Pandas, para estructuras de datos.
• Matplotlib, para la generación de gráficas.
• Pyinstaller, para crear un ejecutable a partir de un archivo Python .py.

Uno de los objetivos más importantes de este proyecto buscaba presentar una interfaz gráfica al Usuario con
aspecto moderno y llamativo, para poder recoger los datos de entrada y presentar los de salida de la manera
más clara y eficaz posible. Python integra la librería Tkinter para generar una GUI, pero por su poco potencial
se creyó conveniente usar PyQt5 (previa instalación a través de pip). Realmente es una biblioteca que permite
usar la GUI de Qt (escrita en C++), y para agilizar el proceso se decidió utilizar la herramienta Qt Designer.
PyQt5 utiliza fundamentalmente una jerarquía de herencia múltiple, que se detallará en capitulos posteriores.

3.4.2 Producción del ejecutable con Pyinstaller

La función de esta librería, Pyinstaller, es agrupar todos los archivos de código fuente, librerías utilizadas, y
demás dependencias, en un solo paquete. Se genera un ejecutable que funciona como aplicación empaquetada
sin la necesidad de tener Python y sus módulos instalados.
Por defecto, el ejecutable abriría simultáneamente el terminal de Windows. Nuestro proyecto evitará esto,
además de incluir una opción que añade icono de aplicación. También sería posible añadir –onefile para
que todo quede comprimido en un solo fichero ejecutable, pero sería más pesado y aumentaría el tiempo de
apertura de la aplicación. Al no ser necesario, se ignora esa posibilidad.

Código 3.5 Comando para generar ejecutable del Simulador.


pyinstaller.exe --windowed --icon=iconn.ico grmain.py

3.5 Qt Designer
Qt Designer es una herramienta que facilita la construcción de ventanas con
widgets de Qt. Se puede ver en tiempo real la posición y estilo de cada elemento
que conforma base de la ventana que se está creando, además de permitir dar
forma al estilo con lenguaje CSS.
Las ventanas se pueden configurar inicialmente de dos tipos:

• QMainWindow, como ventana principal de la aplicación. Clase para


construir la interfaz principal.
18 Capítulo 3. Herramientas de desarrollo

• QDialog, ventana secundaria preparada para presentar al usuario ciertas


opciones o notificaciones.
Los elementos más usados han sido:

• QLabel, para etiquetas de texto.


• QPushButton, para botones a los que aplicar eventos.
• QLineEdit, para casillas de texto.
• QRadioButton, boton circular con opción para marcar.
• QTabWidget, para incluir varias pestañas dentro de la misma ventana.
• QFrame, que aportará un cuadro vacío que otro software aprovechará (en el caso del proyecto, se ha
utilizado para la implementación de gráficas).
• QComboBox, para desplegables.

Figura 3.5 Interfaz de Qt Designer para la construcción de ventanas de la GUI.

Qt Designer genera un archivo .ui que se convertirá en .py con código Python utilizando pyuic5 en el
terminal de Windows.

Código 3.6 Ejemplo traducción de archivo de Qt Designer para Python.


pyuic5 -x [archivo.ui] -o [archivo.py]

La modalidad –x genera código extra para testear las clases, y se corresponden con los __init__ que aparecen
al final de los ficheros convertidos con esta opción. La opción –o vuelca la salida en un fichero en concreto y
no en la salida estándar.
4 Funcionalidad de GroundWave Simulator

E l simulador se basa en una interfaz gráfica presentada al usuario para interactuar con él, que se
inicia mediante el ejecutable “GroundWave Simulator.exe”. Está disponible en https://github.com/Ssuki27/GroundWave-
Simulator y su código fuente en https://github.com/Ssuki27/GWSim-sourceCode.
Para su correcta instalación, se debe descomprimir el archivo y reubicar la carpeta GWSim en el disco
duro principal de la computadora, Disco Local (C:). Como ya ha sido comentado, incluye todas las librerías
necesarias para su funcionamiento, intérprete Python, emulador DOSBox y GRWAVE.

4.1 Interfaz de bienvenida

La ventana principal, considerada de bienvenida, de la que heredará la ventana encargada de los cálculos del
programa, es de tamaño fijo 867x604 px. Por consiguiente, se cancela el botón de maximizar, para evitar
posible desestructuración de los elementos que conforman la ventana y mantener la apariencia deseada.

Figura 4.1 Ventana de Bienvenida de GroundWave Simulator.

19
20 Capítulo 4. Funcionalidad de GroundWave Simulator

Se ha diseñado con Adobe Photoshop un logo central en específico para el simulador, por mera cuestión
visual, que integra una figura de la Tierra simulando un efecto de transmisión de ondas, en referencia a la
superficie terrestre como conductor para propagar ondas de superficie. Esta figura se ha utilizado también
como icono de ventana y de aplicación en la barra de tareas.
En la esquina inferior izquierda aparece el correo universitario del autor, para posibles problemas o dudas,
y en la inferior derecha un botón que abrirá el archivo Guia.txt donde se aporta información básica para
comenzar.

Figura 4.2 Guía sobre uso e instalación del simulador.

Lo más peculiar de esta ventana es la inclusión de un botón deslizante para elegir el entorno de ejecución,
entre el terminal de Windows o MS-DOS mediante DOSBox.

Figura 4.3 Botón deslizante de la interfaz principal.

Cuenta con las siguientes funciones:

• Se mueve sobre el ancho de la casilla de fondo.


• Modifica su hoja de estilos cuando el ratón pasa por encima (al igual que el resto de los QPushButton
del simulador).
• Posee dos estados. Desde el centro del ancho hasta cada uno de los extremos: Windows y MSDOS.
• Al traspasar el centro del ancho total hacia el lado contrario, actualiza en tiempo real el texto de la
etiqueta principal, y el color de fondo (actualizarEstado(self)).
• No es necesario arrastrar hasta el extremo de la casilla. Al soltar el botón, se desliza automáticamente
hacia el extremo más cercano (mousePressEvent(self,event)).
• También es posible cambiar el estado sin deslizar, pinchando directamente en el ancho contrario
reservado para el otro estado. Además, haciendo click en el mismo botón, se deslizara automáticamente
hacia el otro estado (mousePressEvent(self,event)).
• Una vez modificado el valor del botón, se actualizará la etiqueta inferior indicando en que entorno se
ejecutará el simulador (estadoInterruptor(self,texto)). Así, el simulador escogerá un camino u otro a la
4.2 Ventana de cómputos 21

hora de llamar a GRWAVE y realizar los cálculos.

Por último, el QPushButton verde con etiqueta Empezar, avanzaría hacia la segunda ventana del programa
para configurar los cálculos.

4.2 Ventana de cómputos


Construida a partir de la clase interruptorPalanca de tipo QDialog, correspondiente a la ventana de bienvenida.
Python no tiene la potencia para generar aplicaciones como podría tener un lenguaje como Java, que permitiría
fácilmente avanzar de una ventana a otra, actualizando el aspecto de la interfaz. En este simulador, la técnica
que se ha utilizado ha sido ocultar (hide) la ventana de bienvenida y seguidamente mostrar (show) la ventana
de cálculos, y a simple vista el efecto se corresponde con el deseado.
Destaca la presencia de dos QTabWidget (pestañas). Cada una es un cajón con bloques totalmente distintos
a los de su homóloga, aunque el aspecto pueda parecer muy similar.

Figura 4.4 QTabWidget Terreno mixto seleccionado.

El QTabWidget seleccionado, cambiará su hoja de estilos para presentar su fondo negro y letra blanca,
además de incrementar su tamaño ligeramente para facilitar la intuición visual del resalte que está seleccionado.
El objetivo en el interior de estas solapas es introducir los parámetros de entrada que serán enviados a
GRWAVE, mostrar gráficamente los resultados obtenidos en curvas de propagación, y obtener un valor para
un punto (distancia) en específico de éstas.

4.2.1 Pestaña Terreno homogéneo

Figura 4.5 Ventana para los cálculos de un Terreno homogéneo.


22 Capítulo 4. Funcionalidad de GroundWave Simulator

Para la altura de las antenas, características del terreno y frecuencias, se utilizan elementos QLineEdit.
Casillas que guardan el texto introducido en variables de tipo String. Como lo introducido debe ser un número,
cuando sea necesario operar con ellos se transformará a tipo Float.

La polarización consta de dos QRadioButton con etiquetas Vertical (marcada por defecto) y Horizontal.
Al marcar una, se deselecciona la otra, siendo imposible señalar ambas o ninguna de forma sincrónica.

Para la selección rápida de características del terreno se ha utilizado un QComboBox. Un desplegable con
distintos ítems ya configurados según valores de la curva de la Figura 17 de la ITU-R Rec. P.527-5 [13].

Figura 4.6 Desplegable ComboBox para la selección de terreno.

Al seleccionar una opción, se introducirá en las casillas de texto su valor de conductividad y constante
dieléctrica. Además, se cambiarán al modo solo lectura (setReadOnly), para evitar confusiones sobre las
características de un terreno. Solo estará disponible en la opción Personalizado, modificar manualmente las
magnitudes de σ y εr .

El QPushButton con etiqueta Ayuda, abre una nueva ventana mostrando una tabla de valores predetermina-
dos para cada medio.

Figura 4.7 Ventana de ayuda para selección de terreno.

En esta pestaña esta permitido el uso de varias frecuencias. Esto no significa que los cálculos se hagan
teniendo en cuenta todas en su conjunto, sino que por separado se muestran tantas curvas en la gráfica como
frecuencias se hayan escogido.

Otro QComboBox facilitará esta utilidad, nuevamente con otro botón de Ayuda para disipar posibles dudas
sobre la ejecución con varios niveles de frecuencia.

El número máximo de frecuencias simultáneas es de ocho. Se ha considerado que una cantidad mayor
dificulta la lectura y análisis de la gráfica, además de complicar el aspecto del simulador.
4.2 Ventana de cómputos 23

Figura 4.8 Desplegable QComboBox para la selección de número de frecuencias.

Conociendo todos estos parámetros a rellenar, es posible generar una primera gráfica de prueba, mediante
el botón verde Ejecutar, visible en la Figura 4.9.
El simulador recogerá los campos completados y rellenará el fichero de entrada para GRWAVE de extensión
.dat. Se comprobará cuál es el número de frecuencias escogido, y llamará a GRWAVE esa cantidad de veces.

Figura 4.9 Ejemplo de ejecución de gráfica para terreno homogéneo.

Para una representación gráfica como la de la Figura ?? se ha creado la clase MplWidget de tipo QWidget.
Se ha instanciado un objeto de dicha clase, incrustado dentro del Frame que se colocó mediante Qt Designer,
y ejecutado usando las funciones de la librería matplotlib.

Código 4.1 Fragmentos de código en referencia a la creación y rellenado de gráfica. No todas las líneas
pertenecen al mismo fichero ni son las únicas para ésto..
class MplWidget(QWidget):
24 Capítulo 4. Funcionalidad de GroundWave Simulator

def __init__(self, parent = None):


QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111,position=[0.15,
0.17, 0.75, 0.75])
self.setLayout(vertical_layout)
self.tab = QtWidgets.QWidget()
self.MplWidget = MplWidget(self.tab)
self.MplWidget.canvas.axes.plot(distancia, intensidad,label=etiqueta,color=
colorGrafica)
self.MplWidget.canvas.draw()

El eje de abcisas está expresado en escala logarítmica. Para tener un equilibrio de puntos en cada división
de potencia de 10, se optó por llamar a GRWAVE en cuatro ocasiones (de 0 a 10, de 10 a 100, de 100 a 1000
y de 1000 a 10000) por cada frecuencia. Esto consumiría más recursos de la cuenta y alargaría el tiempo
de ejecución (siendo notable cuando ejecutas ocho frecuencias simultáneamente), pero se conseguiría tener
el mismo número de puntos por división: 100. Por cada una de esas cuatro ejecuciones, los resultados de
salida se van escribiendo en otro fichero distinto al que hemos llamado “grwave2.out”, únicamente con las
tres columnas de resultados que GRWAVE ofrece.
Cada curva tendrá un color asociado con el que será dibujada en la gráfica, y será etiquetada en la leyenda
de la figura junto a la frecuencia que la distingue. Se ha evitado la generación aleatoria de tonos para eludir
posibles parecidos, siendo escogidos suaves y distantes.

Figura 4.10 Gama de colores para la generación de curvas expresados en hexadecimal.

El QPushButton con etiqueta Limpiar, borraría todos los elementos añadidos sobre la figura.
Existe la posibilidad de exportar la gráfica con el botón específico para ello, abriendo un selector de
carpetas donde guardar el archivo. Las opciones de extensión que ofrece matplotlib para exportar gráficas
aparecen en la Figura 4.11
El último módulo implementado en esta pestaña, el QGroupBox para calcular la intensidad de campo
eléctrico recibido, solo está disponible una vez generada una gráfica, para una única frecuencia.
En los instantes en que la gráfica se encuentra vacía o completada con más de una frecuencia, no tiene
sentido realizar este cálculo en función de una distancia introducida.
4.2 Ventana de cómputos 25

Figura 4.11 Ventana selector de carpetas para exportar gráfica.

Figura 4.12 Ejemplo de cálculo de intensidad de campo eléctrico para terreno homogéneo.
26 Capítulo 4. Funcionalidad de GroundWave Simulator

4.2.2 Pestaña Terreno mixto

Figura 4.13 Ventana para los cálculos de un Terreno mixto..

Presenta una apariencia similar a la de Terreno homogéneo. Las alturas y polarización se introducen de
la misma manera. La frecuencia esta vez será única, y lo que variará será el número de terrenos, que se
elige mediante un desplegable desde 2 hasta 4 terrenos (véase la figura 4.14). El mínimo es 2 por cuestiones
lógicas, sino, sería homogéneo.

Figura 4.14 Desplegable ComboBox para la selección del número de terrenos.

El máximo está configurado para 4 terrenos distintos, por considerarlo suficiente teniendo en cuenta la
4.2 Ventana de cómputos 27

cantidad de aproximaciones tomadas en el método y el acarreo de errores que conllevaría mezclar más
tipos. Se complementa con un botón de información, que abre un documento de texto para explicar el
funcionamiento del método Millington y disipar algunas posibles dudas que pueda dejar la interfaz destinada
a este cálculo.
Una vez seleccionado el número de terrenos, se podrá introducir en cada uno de ellos, al igual que en la
parte de Terreno homogéneo, un tipo de terreno predeterminado, o bien escoger Personalizado para escribir
manualmente las características eléctricas. Además, es necesario especificar la longitud en kilómetros de
cada terreno, cuya suma será la distancia total de la transmisión.

Figura 4.15 Ejemplo de producción de gráfica para terreno mixto..

Para la ejecución de los resultados de la figura 4.15 se han escogido alturas de 5 metros de altura,
polarización vertical a frecuencia 500 kHz, y cuatro terrenos distintos (Agua dulce con salinidad baja, Tierra,
Tierra moderadamente seca y Tierra seca), con distinta longitud. Al pulsar en el botón verde Ejecutar, la
gráfica se rellena con los cuatro colores preestablecidos para cada terreno, incluyendo una leyenda explicativa.
Además, es instantáneo el cálculo de la intensidad de campo eléctrico recibido a la distancia de 1050 km (suma
total de longitudes), que es de -14.7 dB(µV/m). El botón con borde rojo y etiqueta "Regresar" retrocedería a
la interfaz de bienvenida, y los botones Limpiar y Exportar terminarían por completar las funciones de esta
pestaña, limpiando o guardando la gráfica en el formato deseado.

4.2.3 Ventanas emergentes de error

Es posible que el usuario no conozca todas las especificaciones y regulaciones que presenta el simulador, o
simplemente que cometa alguna equivocación en la introducción de los parámetros de entrada. Cuando ello
ocurra, y para evitar que el programa se quede colgado, se ha previsto la apertura de ventanas emergentes
que informarán del fallo producido para su corrección y detendrán la ejecución de los cálculos.
Estas ventanas se han creado como objetos heredados construidos a partir de la clase QMainWindow,
inicializándola con los parámetros y funciones creados en específico para dicha ventana emergente.
Cuando uno de los parámetros de entrada tanto para terreno homogéneo como para terreno mixto queda
sin rellenar, se informa de un parámetro incompleto al pulsar para la ejecución de la gráfica (figura 4.16). Ni
siquiera la llamada a GRWAVE habrá tenido lugar, el programa detiene los cálculos.
28 Capítulo 4. Funcionalidad de GroundWave Simulator

Figura 4.16 Ventana de error generado por falta de parámetros.

Figura 4.17 Ventana de error generado por frecuencia fuera de rango.

Aunque GRWAVE permite frecuencias desde 10 kHz hasta 10 GHz, el simulador está preparado para
admitir el rango entre 10 kHz y 30 MHz, por considerar que fuera de éste los resultados dejan de ser útiles o
cercanos a la realidad. En la figura 4.17 se abre una ventana al introducir una frecuencia de 33 MHz.

Figura 4.18 Ventana de error producido por incumplir restricciones de altura de antena receptora.
4.2 Ventana de cómputos 29

En los casos en los que la altura receptora no cumpla las restricciones impuestas por las ecuaciones 2.7
y 2.8, no es recomendable tener en cuenta los resultados según la ITU-R, así que se contempla como otro
mensaje de error más en el programa mostrado en la figura 4.18.

Figura 4.19 Ventana de error producido por distancia fuera de rango.

Una vez ejecutado correctamente el simulador y habiendo generado una gráfica para terreno homogéneo
con una sola frecuencia, si al introducir la distancia para la cual queremos calcular el campo eléctrico el valor
suministrado al simulador no contiene ningún resultado de campo eléctrico (por considerarse despreciable a
esos niveles), se ha preferido mostrar un error informando que la distancia introducida está fuera del rango
permitido para esa ejecución. Además se muestra el último valor admisible que contiene resultados de la
intensidad, como se puede apreciar en la figura 4.19.
De la misma manera, cuando se trata de obtener resultados para terreno mixto, con independencia del
número de terrenos, si la suma de longitudes introducidas es alto como para que su valor de intensidad de
campo sea despreciable, se considera que la suma de longitudes está fuera de rango. La gráfica es generada
(pues es independiente de las longitudes) pero no se puede obtener un valor de campo E (véase la figura
4.20).
30 Capítulo 4. Funcionalidad de GroundWave Simulator

Figura 4.20 Ventana de error producido por suma de longitudes fuera de rango.
5 Deducciones y validez del simulador

E n este Capítulo se realizarán pruebas para obtener conclusiones sobre el comportamiento de las ondas
superficiales y de los métodos y aproximaciones seguidas para desarrollar este simulador. En definitiva,
comprobar la validez de éste.

5.1 SonarQube

Se ha utilizado la plataforma de código abierto SonarQube, que realiza un


examen de calidad usando diversas herramientas de análisis estático de código
fuente como Checkstyle, PMD o FindBugs para obtener métricas que pueden
ayudar a mejorar la calidad del código de un programa.
Es una herramienta esencial para la fase de pruebas y auditoría de código
dentro del ciclo de desarrollo de la aplicación. Existen muchos problemas que
se pueden encontrar en un código, las reglas difieren y pueden clasificarse en 5
grupos según su severidad: Bloqueador, crítico, grave, menor e informativo. Así que, si existe un error de
software (bug), va a ser clasificado como problema bloqueador o crítico, y algunos problemas como “los
números mágicos no se deben utilizar” van a ser clasificados con severidad menor o informativa. Y deberá
volver a la fase de desarrollo para corregir esas partes de código.

Figura 5.1 Análisis de calidad del código del simulador con SonarQube.

31
32 Capítulo 5. Deducciones y validez del simulador

Una vez finalizado el desarrollo de la aplicación, ha sido analizada completamente con el fin de obtener
los resultados de calidad estática que posee. De esta manera se puede garantizar que esta producida de la
manera más eficiente y garantizando la seguridad y robustez del código.
Como se muestra en la figura 5.1, el código desarrollado posee nivel A (el de mejor calidad) en los
umbrales de fiabilidad, seguridad, mantenibilidad y revisión de seguridad. No posee ningún error de software,
vulnerabilidades ni hotspots de seguridad, con lo que podemos garantizar que el desarrollo de la aplicación
es lo más eficiente, robusto y seguro posible.
Presenta 158 code smells, los cuales afectan a la mantenibilidad del programa. Siguiendo indicaciones de
nomenclatura, los code smells aconsejan renombrar algunos nombres de variables y funciones, pero no atañe
negativamente en el rendimiento, eficiencia, robustez y seguridad del software.
En la figura 5.2 se detalla la información dividida por ficheros.

Figura 5.2 Informe de análisis desglosado de SonarQube.

5.2 Discusiones utilizando el simulador

El programa, que basa sus resultados en los cálculos de GRWAVE y las fórmulas del método de Millington,
realiza hipótesis derivadas de dichos criterios para simplificar los procedimientos, intentando alejarse lo
mínimo posible de la realidad. En esta sección se tratará de demostrar alguna de esas aproximaciones, además
de las consecuencias de estas y de la teoría de ondas de superficie.
Además, la recomendación de la ITU-R P.368 que sigue este proyecto proporciona una amplia variedad
de figuras a las que recurrir en caso de necesidad. Sin embargo, este simulador proporciona esas mismas
gráficas sin el inconveniente que suponen los valores discretos de frecuencia o características del terreno
fijados en ellas. Es decir, se proporciona independencia y libertad de análisis.

Figura 5.3 Curva de propagación por GroundWave Simulator en tierra húmeda (σ =0.01, εr =30).
5.2 Discusiones utilizando el simulador 33

La figura 5.3, extraída del simulador, presenta hasta ocho frecuencias para una ejecución de terreno
homogéneo con tierra húmeda, haciendo apreciable la similitud con la figura 5.4 perteneciente al documento
de la ITU-R P.368

Figura 5.4 Curva de propagación ITU-R de onda de superficie en tierra húmeda (σ =0.01, εr =30).

Uno de los primeros parámetros a configurar en la aplicación es la altura de antena, tanto para transmisor
como para receptor. En la recomendación de referencia se alude a una condición de altura límite para la
receptora, sin embargo, es inevitable pensar de qué manera puede afectar la altura de antenas en la transmisión.
Se han analizado los datos obtenidos para campo eléctrico de la figura 5.5, para los cuales se ha usado
una polarización vertical con un terreno de agua dulce, haciendo variar simultáneamente la frecuencia y las
alturas de antenas HTT y HRR (altura antena transmisora y receptora respectivamente).
5.5 perteneciente al documento de la ITU-R P.368

Figura 5.5 Comparación de intensidad de campo eléctrico según la altura de antenas para agua dulce y
distancia de 50 km.

Se observa que para las frecuencias más altas dentro del rango permitido, se produce una fluctuación de la
34 Capítulo 5. Deducciones y validez del simulador

intensidad del campo en función de la altura de las antenas. Mientras más alta, más notable es este efecto.
Por el contrario, para frecuencias más bajas, esa alteración es casi despreciable, además de conseguir una
mejora en la intensidad de campo recibida para estos niveles (debido a longitudes de onda más largas).
Otra condición a la que se hace referencia en varias ocasiones es la polarización. En las subsecciones
2.1.1 y 2.1.2 se habla la importancia que tiene realizar la transmisión con polarización vertical, y de las
consecuencias de pérdidas y atenuación que tiene la polarización horizontal. Se han realizado ejecuciones
en el simulador para comparar los resultados con distintas polarizaciones, reflejado en la tabla 5.1, donde
el terreno es homogéneo con Tierra húmeda durante todo el trayecto y alturas de antenas a 5 m cada una.
Se han realizado tres simulaciones con distinta frecuencia, y en ellas se ha ido aumentando la distancia a
la que se calcula la intensidad del campo eléctrico recibido. Se aprecia rápidamente la rápida atenuación
del caso con polarización horizontal respecto a la vertical. Además, conforme disminuye la frecuencia, la
polarización vertical mejora la intensidad y la horizontal la empeora.

Tabla 5.1 Comparación de intensidad según la polarización para Tierra húmeda


(σ =0.01 S/m, εr =30).

Frecuencia Distancia Polarización vertical Polarización horizontal

10 MHz 10 km 51.98 dBu 22.93 dBu


10 MHz 40 km 28.26 dBu -0.81 dBu
10 MHz 70 km 16.53 dBu -12.68 dBu
10 MHz 100 km 7.81 dBu -21.56 dBu
1 MHz 10 km 85.30 dBu 9.80 dBu
1 MHz 40 km 67.43 dBu -13.52 dBu
1 MHz 70 km 57.80 dBu -23.42 dBu
1 MHz 100 km 50.41 dBu -30.51 dBu
0.1 MHz 10 km 88.60 dBu 1.46 dBu
0.1 MHz 40 km 77.03 dBu -21.60 dBu
0.1 MHz 70 km 72.07 dBu -31.43 dBu
0.1 MHz 100 km 68.95 dBu -37.91 dBu

Visualmente se aprecia en la figura 5.6. Las curvas en color azul y verde corresponden a la misma
frecuencia (10 MHz) pero distinta polarización. La horizontal claramente tiene niveles menores de intensidad.
Al disminuir la frecuencia hasta 1 MHz, la línea verde de polarización vertical evoluciona a la de color
morado, mejorando los resultados. La azul, de polarización horizontal, se transformaría en la amarilla, con
menor intensidad de campo eléctrico.

Figura 5.6 Curva de propagación en tierra húmeda (σ =0.01, εr =30) variando polarización y frecuencia.

Para escenarios con terreno mixto, surgía la duda de hasta qué punto el orden de los terrenos podía alterar
los resultados. Para ello, se realizaron mediciones de intensidad de campo eléctrico con antenas situadas a 0
5.2 Discusiones utilizando el simulador 35

metros de altura.
En primer lugar, se tomaba un escenario ficticio como el de la figura 5.7, con un primer terreno de Tierra
de 200 km de longitud y otro de agua del mar con salinidad baja de 100 km de longitud. Aunque se representa
con relieve, los cálculos se eximen de tener en cuenta dichos efectos.

Figura 5.7 Escenario de transmisión de dos terrenos.

En la primera ejecución de la tabla 5.2 se toma la Antena 1 como transmisora, y Antena 2 como receptora.
Para la segunda medición se invierten los roles de transmisor y receptor, obteniendo la misma intensidad de
campo eléctrico, 48.86 dBu. Se concluye así que para dos terrenos no importa el orden en una transmisión,
incluso cuando las distancias no son idénticas.

Tabla 5.2 Comparación de intensidad recibida en Método Millington según


el orden de dos terrenos con distinta longitud y f =1 MHz.

Terreno 1 Terreno 2 Campo eléctrico

Agua del mar, sal. baja Tierra 48.86 dBu


(σ =1, εr =80) (σ =0.03, εr =40)
Longitud=200 km Longitud = 100 km
Tierra Agua del mar, sal. baja 48.86 dBu
(σ =0.03, εr =40) (σ =1, εr =80)
Longitud=100 km Longitud = 200 km

A continuación se intenta comprobar los mismos efectos para la combinación de tres terrenos con 100
kilómetros de longitud cada uno.

Figura 5.8 Escenarios de transmisión de tres terrenos.


36 Capítulo 5. Deducciones y validez del simulador

Se realizan ejecuciones ordenando los terrenos de tres maneras diferentes. En la tabla 5.3 se comprueba
como los valores recibidos de campo eléctrico varían. De la primera a la segunda medida existe una pequeña
variación, pero sorprende bastante más el tercer dato que se espacia del resto en más de 20 dBu. La deducción
que se obtiene es que en la fase final de la transmisión, que es donde más pérdidas se generan, que haya
un tipo de terreno con buena conductividad hace que la intensidad se mantenga a buen nivel durante mas
distancia, tarda mas en atenuarse. En los primeros terrenos, que la conductividad sea mas alta o menos alta
no influye tanto. Por eso, la tercera ejecución presenta como tercer trayecto agua de mar con salinidad baja,
con una conductividad mucho mayor que el resto de terrenos. En definitiva, el orden a partir de tres terrenos
importa, incluso cuando las longitudes sean idénticas.

Tabla 5.3 Comparación de intensidad recibida en Método Millington según el orden de tres terrenos
con 100km de longitud cada uno y f =1 MHz.

Terreno 1 Terreno 2 Terreno 3 Campo eléctrico

Tierra muy seca Agua del mar, sal. baja Tierra 18.62 dBu
(σ =0.0001, εr =3) (σ =1, εr =80) (σ =0.03, εr =40)
Agua del mar, sal. baja Tierra Tierra muy seca 19.01 dBu
(σ =1, εr =80) (σ =0.03, εr =40) (σ =0.0001, εr =3)
Tierra Tierra muy seca Agua del mar, sal. baja 40.86 dBu
(σ =0.03, εr =40) (σ =0.0001, εr =3) (σ =1, εr =80)
6 Conclusiones y líneas futuras

E l desarrollo del proyecto implicó inicialmente el aprendizaje del lenguaje Python unido a la dificultad
de entender su librería PyQt5 para interfaces gráficas. Multitud de módulos han sido sustituidos en
pleno proceso por otros con mayores garantías, como los reemplazos de Tkinter por PyQt5 para la interfaz
gráfica, que supondría un salto de calidad en ésta, o la librería pqytgraph por matplotlib para la generación de
gráficas entre los más destacados. Sin embargo, el aprendizaje en pleno camino ha penalizado la posibilidad
en ocasiones de remodelar la estructura básica del programa, y por ello, es necesario listar modificaciones
factibles a realizar en una continuación de este trabajo.

En primer lugar, se ha comentado la forzosa opción escogida para representar los datos en la gráfica en
escala logarítmica, que pasaba por realizar cuatro llamadas a GRWAVE por cada curva. Cuando el entorno
utilizado es el terminal de Windows, el retardo no llega a ser molesto, incluso para la ejecución de ocho
mediciones distintas. No obstante, la obligatoriedad de utilizar MS-DOS hace que por cada llamada se inicie
en cuatro ocasiones el emulador DOSBox, proceso que sí ocupa bastante tiempo de ejecución, más cuando
varias curvas son generadas. Sería conveniente buscar un método más sencillo para representar la curva
deseada con una única llamada a GRWAVE, sin la necesidad de dividir las secciones de la escala del eje de
abcisas.
En acorde con esto, el programa realiza llamadas a GRWAVE de una manera u otra en función de qué
entorno se escoja para ello: terminal de Windows o emulador DOSBox para MS-DOS. No se ha logrado
reconocer cual es el factor que determina por qué GRWAVE puede ser ejecutado en algunos sistemas
operativos Windows mediante su propio terminal, y en otros ordenadores con dicho sistema operativo fuera
necesario un emulador para recrear MS-DOS puro. GroundWave Simulator será más productivo cuando se
ejecute en el terminal de Windows por la razón ya comentada de prescindir de la apertura de un emulador,
con lo cual, descubrir esa causa puede resultar útil para simplificar el uso del simulador.
Otro punto pendiente del programa atañe al trazado de gráficas para el método Millington en terrenos
mixtos. Resultaría cómodo poder expresar el resultado final en una curva combinación de todos los terrenos
teniendo en cuenta las fórmulas del método que se sigue, como sucede en la figura 2.8. Sin embargo, la
obtención de datos se hace de tal manera que se machacan estos valores con el paso de una curva a otra, con
lo cual, con la actual estructura, no se ha encontrado forma de realizar la unión de las curvas que componen
el terreno mixto en una exclusiva.

Por otro lado, varias ideas quedaron en el tintero por cuestiones de tiempo. Amoldar el simulador a
mediciones reales hubiera sido un gran paso. El plan para esto pasaba por acoplar una opción de selección de
terrenos usando la Recomendación P.832 [19] de la ITU-R que proporciona un Atlas Mundial, con mapas de
conductividad del terreno en todo el planeta. Un paso todavía más profesional sería incluir una selección
de distancias atractiva y real, mediante una aplicación externa como Google Earth u otra similar que pueda
unirse a una base de datos con características eléctricas del terreno en función de las coordenadas geográficas
escogidas (que conllevaría la necesidad de calcular longitudes mediante dos pares de coordenadas). Es decir,
el usuario seleccionaría ubicaciones reales para las antenas mediante una aplicación de cartografía global.
Todo esto dejaría a un lado la elección manual de terreno homogéneo o mixto, pues el propio simulador
debería ser capaz de reconocer cuantos terrenos atraviesa la transmisión escogida mediante puntos de latitud
y longitud.

37
38 Capítulo 6. Conclusiones y líneas futuras

Respecto a la validez de los cálculos, los modelos utilizados por GRWAVE ignoran grandes efectos de
relieve montañoso o edificios como obstáculos. Una línea de estudio podría avanzar en este sentido, haciendo
los resultados obtenidos mucho más cercanos a la realidad, cuando se consiga completar un método de
predicción en zonas urbanas con altos edificios y relieve intensificado.

El último análisis corresponde al objetivo marcado para el proyecto. A la necesidad de adaptar el uso
del arcaico programa GRWAVE a los ordenadores actuales, se ha respondido con una interfaz moderna y
sencilla que facilita tanto la introducción de variables como la representación y extracción de datos y figuras,
incluyendo resultados para el método Millington. Se concluye, por tanto, que el principal propósito del trabajo
se ha cumplido colmadamente.
Apéndice A
Código fuente

GRMain.py
1 from PyQt5.QtGui import QIcon, QFont, QPalette, QColor, QPixmap
2 from PyQt5.QtCore import Qt, QPoint, pyqtSignal, QTimer, QPropertyAnimation, QAbstractAnimation
3 from PyQt5.QtWidgets import QApplication, QPushButton, QDialog, QFrame, QLabel
4 from PyQt5 import QtCore, QtGui, QtWidgets
5 from ventanaCalculos import ∗
6 import os
7
8 # ========================= CLASE Frame ===========================
9
10 class Frame(QFrame):
11 clicked = pyqtSignal( str )
12 MIN_VALOR = 1
13 MAX_VALOR = 121
14 VALOR = MAX_VALOR + MIN_VALOR
15
16 def __init__ ( self , parent=None):
17 super(Frame, self ) . __init__ (parent)
18 self . parent = parent
19 self . mover = QPoint()
20 self . habilitado = False
21
22 def actualizarEstado( self ) :
23 # Si la posicion x del boton mas la mitad de su ancho
24 # es menor que la mitad del ancho del widget padre,
25 # entonces esta apagado (NO)
26 if ( self . parent.button.x() + ( self . parent.button.width() / 2)) < Frame.VALOR / 2:
27 self . habilitado = False
28 # Si la posicion x del boton mas la mitad de su ancho
29 # es mayor que la mitad del ancho del widget padre,
30 # entonces esta encendido (SI)
31 if ( self . parent.button.x() + ( self . parent.button.width() / 2)) > Frame.VALOR / 2:
32 self . habilitado = True
33
34 if self . habilitado :
35 self . parent.button. setText ("MSDOS")
36 color = QColor(250, 185, 78)
37 elif not self . habilitado :
38 self . parent.button. setText ("Windows")
39 color = QColor(78, 172, 250)
40
41 colorFrame = self . palette ()
42 colorFrame.setColor(QPalette.Background, color)
43 self . setPalette (colorFrame)
44
45 def mousePressEvent(self, event) :
46 if event . button() == Qt.LeftButton:
47 self . mover.setY(1)
48 if event . pos() . x() < Frame.VALOR / 2:
49 self . mover.setX(Frame.MIN_VALOR)
50 elif event . pos() . x() > Frame.VALOR / 2:

39
40 Appendix A. Código fuente

51 self . mover.setX(Frame.MAX_VALOR − self.parent.button.width())


52
53 self . animacion = QPropertyAnimation(self.parent.button, b"pos")
54 self . animacion.setDuration(150)
55 self . animacion.setEndValue(self.mover)
56 self . animacion.valueChanged.connect(self.actualizarEstado)
57 self . animacion.finished . connect( self . emitirEstado)
58 self . animacion.start(QAbstractAnimation.DeleteWhenStopped)
59
60 def emitirEstado( self ) :
61 self . clicked . emit( self . parent.button. text () )
62
63 # ====================== CLASE PushButton =========================
64
65 class PushButton(QPushButton):
66 clicked = pyqtSignal( str )
67
68 MIN_VALOR = 1
69 MAX_VALOR = 121
70 VALOR = MAX_VALOR + MIN_VALOR
71
72 def __init__ ( self , parent=None):
73 super(PushButton, self ) . __init__ (parent)
74 self . parent = parent
75 self . pressing = False
76 self . inicio = QPoint()
77 self . mover = QPoint()
78 self . habilitado = False
79 self . arrastrado = False
80
81 self . actualizarEstado ()
82 self . actualizarPosicion ()
83
84 def mousePressEvent(self, event) :
85 if event . button() == Qt.LeftButton:
86 self . ultimo = True
87 self . inicio = event . pos()
88 self . pressing = True
89 self . actualizarEstado ()
90
91 def actualizarEstado( self ) :
92 # Si la posicion x del boton mas la mitad de su ancho
93 # es menor que la mitad del ancho del widget padre,
94 # entonces esta apagado (NO)
95 if ( self . x() + ( self . width() / 2)) < PushButton.VALOR / 2:
96 self . habilitado = False
97
98 # Si la posicion x del boton mas la mitad de su ancho
99 # es mayor que la mitad del ancho del widget padre,
100 # entonces esta encendido (SI)
101 if ( self . x() + ( self . width() / 2)) > PushButton.VALOR / 2:
102 self . habilitado = True
103
104 if self . habilitado :
105 self . setText ("MSDOS")
106 color = QColor(250, 185, 78)
107 elif not self . habilitado :
108 self . setText ("Windows")
109 color = QColor(78, 172, 250)
110
111 colorFrame = self . palette ()
112 colorFrame.setColor(QPalette.Background, color)
113 self . parent. setPalette (colorFrame)
114
115 def mouseMoveEvent(self, event):
116 if self . pressing:
117 self . mover = self . mapToParent(event.pos() − self . inicio )
118 self . mover.setY(1)
119 self . move(self . mover)
120 self . arrastrado = True
121 self . restringirMovimiento()
41

122 self . actualizarEstado ()


123
124 def actualizarPosicion ( self ) :
125 self . mover.setY(1)
126 if self . habilitado :
127 self . mover.setX(PushButton.MAX_VALOR − self.width())
128 else :
129 self . mover.setX(PushButton.MIN_VALOR)
130
131 self . animacion = QPropertyAnimation(self, b"pos")
132 self . animacion.setDuration(150)
133 self . animacion.setEndValue(self.mover)
134 self . animacion.finished . connect( self . emitirEstado)
135 self . animacion.start(QAbstractAnimation.DeleteWhenStopped)
136
137 def restringirMovimiento( self ) :
138 self . mover.setY(1)
139
140 # Restringir lado izquierdo
141 if self . x() < PushButton.MIN_VALOR:
142 self . mover.setX(PushButton.MIN_VALOR)
143 self . move(self . mover)
144 return
145
146 # Restringir lado derecho
147 if ( self . x() + self . width()) > PushButton.MAX_VALOR:
148 self . mover.setX(PushButton.MAX_VALOR − self.width())
149 self . move(self . mover)
150 return
151
152 def mouseReleaseEvent(self, event) :
153 if self . pressing:
154 self . pressing = False
155 self . actualizarEstado ()
156 self . actualizarPosicion ()
157
158 if not self . arrastrado and self . ultimo:
159 # QApplication.instance() . doubleClickInterval ()
160 QTimer.singleShot(100, self . performSingleClickAction)
161 else :
162 self . arrastrado = False
163
164 def mouseDoubleClickEvent(self, event):
165 if event . button() == Qt.LeftButton:
166 self . ultimo = False
167
168 def performSingleClickAction(self) :
169 if self . ultimo:
170 self . mover.setY(1)
171 if self . habilitado :
172 self . mover.setX(PushButton.MIN_VALOR)
173 else :
174 self . mover.setX(PushButton.MAX_VALOR − self.width())
175
176 self . animacion = QPropertyAnimation(self, b"pos")
177 self . animacion.setDuration(150)
178 self . animacion.setEndValue(self.mover)
179 self . animacion.valueChanged.connect(self.actualizarEstado)
180 self . animacion.finished . connect( self . emitirEstado)
181 self . animacion.start(QAbstractAnimation.DeleteWhenStopped)
182
183 def emitirEstado( self ) :
184 self . clicked . emit( self . text () )
185
186 # =================== CLASE interruptorPalanca ====================
187
188 class interruptorPalanca(QDialog):
189 def __init__ ( self , parent=None):
190 super(interruptorPalanca, self ) . __init__ (parent)
191 self . setWindowIcon(QIcon("iconn.png"))
192 self . setWindowTitle("GroundWave Simulator")
42 Appendix A. Código fuente

193 self . setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint)


194 # self . setWindowFlags(Qt.WindowCloseButtonHint | Qt.MSWindowsFixedSizeDialogHint)
195 self . setFixedSize (867, 604)
196 self . initUI ()
197
198 def initUI ( self ) :
199 # ========================= WIDGETS =========================
200
201 fuenteLabel = self . font ()
202 fuenteLabel.setBold(True)
203 fuenteLabel.setFamily("Bahnschrift Light")
204 fuenteLabel. setPointSize (10)
205
206 self . frame = Frame(self)
207 self . frame.setFrameShape(QFrame.StyledPanel)
208 self . frame.setFixedSize (122, 32)
209 self . frame.setAutoFillBackground(True)
210 self . frame.move(370, 334)
211
212 self . button = PushButton(self.frame)
213 self . button. setFixedSize (64, 30)
214 self . button.setAutoDefault(False)
215 self . button.move(1, 1)
216
217 self . labelEstado = QLabel("El simulador se ejecutará en: Windows", self)
218 self . labelTitulo = QLabel(self)
219 self . labelTitulo . setGeometry(QtCore.QRect(0, 40, 865, 111))
220 font = QtGui.QFont()
221 font . setFamily("Roboto")
222 font . setPointSize (28)
223 self . labelTitulo . setAlignment(QtCore.Qt.AlignCenter)
224
225 self . label_2 = QLabel("Desarrollado para la representación y determinación de intensidad de campo elé
ctrico recibido \n"
226 "por onda de superficie para frecuencias entre 10 kHZ y 30 MHz según la Rec. ITU−R P.368−9.",self)
227 self . label_2 . setGeometry(QtCore.QRect(0, 153, 861, 61))
228 #font = QtGui.QFont()
229 font . setFamily("Roboto")
230 font . setPointSize (11)
231 self . label_2 . setFont( font )
232 self . label_2 . setStyleSheet ("color: rgb(146, 146, 146);")
233 self . label_2 . setAlignment(QtCore.Qt.AlignCenter)
234 self . label_2 . setObjectName("label_2")
235 self . labelEstado.setFont(fuenteLabel)
236 self . labelEstado.move(304, 373)
237 self . labelEstado. setStyleSheet ("color: rgb(53, 53, 53);")
238
239 self . label_3 = QLabel("Elige ejecutar el simulador en el terminal de Windows, o en MSDOS mediante el
emulador DOSBox:",self)
240 self . label_3 . setGeometry(QtCore.QRect(0, 290, 861, 41))
241 self . label_3 . setFont( font )
242 self . label_3 . setStyleSheet ("color: rgb(146, 146, 146);")
243 self . label_3 . setAlignment(QtCore.Qt.AlignCenter)
244 self . label_3 . setObjectName("label_3")
245
246 pixmap = QPixmap(’Logo2.png’)
247 self . labelTitulo . setPixmap(pixmap)
248
249 buttoon=QPushButton("Empezar",self)
250 buttoon.setGeometry(QtCore.QRect(365, 460, 131, 35))
251 buttoon.setObjectName("botoncito")
252 # self . Empezar = PushButton("Empezar")
253 # self . Empezar.move(304, 363)
254 buttoon. setStyleSheet ("#botoncito {\ n"
255 " border: 1px solid ;\ n"
256 " background−color: rgb(0, 200, 60) ;\ n"
257 " color : #FFF;\n"
258 " font−weight: bolder;\n"
259 " border−color: rgb(65, 65, 65) ;\ n"
260 " font−family: \’ Roboto \’;\ n"
261 " font−size: 17px;\n"
43

262 " }"


263 "#botoncito:hover {\ n"
264 " background−color: rgb(16, 214, 96) ;\ n"
265 " }"
266 )
267
268 buttonGUIA = QPushButton("Abrir",self)
269 buttonGUIA.setGeometry(QtCore.QRect(785, 570, 55, 21))
270 buttonGUIA.setObjectName("buttonGUIA")
271 buttonGUIA.setStyleSheet("color: rgb(80,80,80)")
272
273 self . mail = QLabel(self)
274 self . mail.setGeometry(QtCore.QRect(20, 565, 30, 30))
275 pixmail = QPixmap(’mail.png’)
276 self . mail.setPixmap(pixmail)
277
278 self . labelMail = QLabel(’javojepri(ARROBA)alum.us.es’,self)
279 self . labelMail.setGeometry(QtCore.QRect(52, 563, 361, 31))
280 self . labelMail. setFont( font )
281 self . labelMail. setStyleSheet ("color: rgb(146, 146, 146);")
282
283 self . labelInfo = QLabel("Para más información, consulta la guía:", self )
284 self . labelInfo . setAlignment(QtCore.Qt.AlignRight)
285 self . labelInfo . setGeometry(QtCore.QRect(477, 571, 301, 31))
286 self . labelInfo . setFont( font )
287 self . labelInfo . setStyleSheet ("color: rgb(146, 146, 146);")
288
289 self . etsi = QLabel(self)
290 self . etsi . setGeometry(QtCore.QRect(2, 490, 200, 77))
291 pixetsi = QPixmap(’etsi.png’)
292 self . etsi . setPixmap(pixetsi )
293
294 # =============== EVENTOS QFRAME − QPUSHBUTTON ==============
295
296 self . frame.clicked . connect( self . estadoInterruptor)
297 self . button. clicked . connect( self . estadoInterruptor)
298 buttoon. clicked . connect( self . openWindow)
299 buttonGUIA.clicked.connect(self . openGuia)
300
301 # ======================== FUNCIONES ============================
302
303 def estadoInterruptor( self , texto ) :
304 self . labelEstado. setText ("El simulador se ejecutará en: {}".format(texto) )
305
306 def openWindow(self):
307 ui=MainWindow(self)
308 ui . show()
309
310 def openGuia(self):
311 os . startfile ("Guia.txt")
312
313 # =================================================================
314
315 if __name__ == ’__main__’:
316 import sys
317 aplicacion = QApplication(sys.argv)
318 ventana = interruptorPalanca()
319 ventana.show()
320 sys . exit ( aplicacion . exec_ () )

interfazCalculosStyle.py
1 # Created by: PyQt5 UI code generator 5.13.0
2 #
3 # WARNING! All changes made in this file will be lost !
4
5 ## Archivo .py transformado de un .ui que genera QtDesigner. Facilita el diseo y estilo de la interfaz .
6
44 Appendix A. Código fuente

7 from PyQt5 import QtCore, QtGui, QtWidgets


8
9
10 class Ui_MainWindow(object):
11 def setupUi( self , MainWindow):
12 MainWindow.setObjectName("MainWindow")
13 MainWindow.resize(867, 604)
14 MainWindow.setMinimumSize(QtCore.QSize(867, 604))
15 MainWindow.setMaximumSize(QtCore.QSize(867, 604))
16 MainWindow.setStyleSheet("/∗Cambiamos el color de la ventana∗/\n"
17 " #MainWindow{\n"
18 " background−color: rgb(255, 255, 255);\n"
19 " }\ n"
20 "\n"
21 " /∗ Estilos para el botón∗/\n"
22 " #button_iniciar , #button_iniciar_2 {\ n"
23 " border: 1px solid ;\ n"
24 " background−color: rgb(0, 200, 60) ;\ n"
25 " color : # fff ;\ n"
26 " border−color: rgb(65, 65, 65) ;\ n"
27 " font−family: \’ Roboto \’;\ n"
28 " font−size: 17px;\n"
29 " }\ n"
30 " \n"
31 " /∗Definimos el estilo para un efecto hover sobre el botón,\n"
32 " este cambiará su background cuando pasemos el mouse por\n"
33 " encima∗/\n"
34 " #button_iniciar :hover, #button_iniciar_2:hover{\n"
35 " background−color: rgb(16, 214, 96) ;\ n"
36 " }\ n"
37 "\n"
38 "\n"
39 " /∗Definimos los estilos para los QLineEdit∗/\n"
40 " QLineEdit{\n"
41 " border: 1px solid ;\ n"
42 " border−color: rgb(150,150, 150) ;\ n"
43 " }\ n"
44 "\n"
45 " /∗Definimos los estilos para los QLabel∗/\n"
46 " QLabel{\n"
47 " font−family: \’ Roboto \’;\ n"
48 " }\ n"
49 "\n"
50 " /∗Definimos los estilos para los QLabels cuyos nombres son\n"
51 " \’ label_usuario \’ y \’ label−password\’∗/\n"
52 " #label_usuario , #label_password{\n"
53 " font−size: 17px;\n"
54 " color : #212121;\n"
55 " }\ n"
56 " \n"
57 " /∗ Estilo para el QLable cuyo nombre es #label_login∗/\n"
58 " # label_login {\ n"
59 " font−size:30px;\n"
60 " color : # fff ;\ n"
61 " }\ n"
62 "\n"
63 " #button_limpiar, #button_limpiar_2{\n"
64 " border: 1px solid ;\ n"
65 " background−color: rgb(239, 239, 239);\n"
66 " color : rgb(65, 65, 65) ;\ n"
67 " border−color: rgb(65, 65, 65) ;\ n"
68 " font−family: \’ Roboto \’;\ n"
69 " font−size: 17px;\n"
70 " }\ n"
71 " #button_limpiar:hover,#button_limpiar_2:hover{\n"
72 " background−color: rgb(209, 209, 209);\n"
73 " }\ n"
74 "\n"
75 " #button_exportar,#button_exportar_2{\n"
76 " border: 1px solid ;\ n"
77 " background−color: rgb(239, 239, 239);\n"
45

78 " color: rgb(65, 65, 65) ;\ n"


79 " border−color: rgb(65, 65, 65) ;\ n"
80 " font−family: \’ Roboto \’;\ n"
81 " font−size: 17px;\n"
82 " }\ n"
83 " #button_exportar:hover, #button_exportar_2:hover{\n"
84 " background−color: rgb(209, 209, 209);\n"
85 " }\ n"
86 "\n"
87 " QGroupBox {\n"
88 " border: 1px solid ;\ n"
89 " border−color: rgb(166, 166, 166) ;\ n"
90 " margin−top: 0.5em;\n"
91 " }\ n"
92 "\n"
93 " QGroupBox::title {\n"
94 " top: −6px;\n"
95 " left : 10px;\n"
96 " }\ n"
97 " #button_cerrar, #button_cerrar_2{\n"
98 " border: 1px solid ;\ n"
99 " background−color: rgb(239, 239, 239);\n"
100 " color: rgb(65, 65, 65) ;\ n"
101 " border−color: rgb(223, 22, 22) ;\ n"
102 " font−family: \’ Roboto \’;\ n"
103 " font−size: 17px;\n"
104 " }\ n"
105 " #button_cerrar:hover,#button_cerrar_2:hover{\n"
106 " background−color: rgb(209, 209, 209);\n"
107 " }")
108 self . centralwidget = QtWidgets.QWidget(MainWindow)
109 self . centralwidget . setObjectName("centralwidget")
110 self . tabWidget = QtWidgets.QTabWidget(self.centralwidget)
111 self . tabWidget.setGeometry(QtCore.QRect(0, 0, 861, 581))
112 self . tabWidget.setStyleSheet("QTabWidget::pane {\n"
113 " border: 1px solid ;\ n"
114 " border−color: rgb(65, 65, 65) ;\ n"
115 " background: white;\n"
116 "}\n"
117 "QTabWidget::tab−bar:top {\n"
118 " top: 1px;\n"
119 "}\n"
120 "\n"
121 "QTabWidget::tab−bar:bottom {\n"
122 " bottom: 1px;\n"
123 "}\n"
124 "\n"
125 "QTabWidget::tab−bar:left {\n"
126 " right : 1px;\n"
127 "}\n"
128 "\n"
129 "QTabWidget::tab−bar:right {\n"
130 " left : 1px;\n"
131 "}\n"
132 "\n"
133 "QTabBar::tab {\n"
134 " border: 1px solid ;\ n"
135 " border−color: rgb(65, 65, 65) ;\ n"
136 "}\n"
137 "\n"
138 "QTabBar::tab:selected {\n"
139 " background: rgb(34, 34, 34) ;\ n"
140 " color : white ;\ n"
141 "}\n"
142 "\n"
143 "QTabBar::tab:!selected {\n"
144 " background: white;\n"
145 " color : rgb(34, 34, 34) ;\ n"
146 "}\n"
147 "\n"
148 "QTabBar::tab:!selected:hover {\n"
46 Appendix A. Código fuente

149 " background:rgb(232, 232, 232) ;\ n"


150 "}\n"
151 "\n"
152 "QTabBar::tab:top:!selected {\n"
153 " margin−top: 3px;\n"
154 "}\n"
155 "\n"
156 "QTabBar::tab:bottom:!selected {\n"
157 " margin−bottom: 3px;\n"
158 "}\n"
159 "\n"
160 "QTabBar::tab:top, QTabBar::tab:bottom {\n"
161 " min−width: 8ex;\n"
162 " margin−right: −1px;\n"
163 " padding: 5px 10px 5px 10px;\n"
164 "}\n"
165 "\n"
166 "QTabBar::tab:top:selected {\n"
167 " border−bottom−color: none;\n"
168 "}\n"
169 "\n"
170 "QTabBar::tab:bottom:selected {\n"
171 " border−top−color: none;\n"
172 "}\n"
173 "\n"
174 "QTabBar::tab:top:last, QTabBar::tab:bottom:last,\n"
175 "QTabBar::tab:top:only−one, QTabBar::tab:bottom:only−one {\n"
176 " margin−right: 0;\n"
177 "}\n"
178 "\n"
179 "QTabBar::tab:left:! selected {\ n"
180 " margin−right: 3px;\n"
181 "}\n"
182 "\n"
183 "QTabBar::tab:right:!selected {\ n"
184 " margin−left: 3px;\n"
185 "}\n"
186 "\n"
187 "QTabBar::tab:left, QTabBar::tab:right {\n"
188 " min−height: 8ex;\n"
189 " margin−bottom: −1px;\n"
190 " padding: 10px 5px 10px 5px;\n"
191 "}\n"
192 "\n"
193 "QTabBar::tab:left:selected {\ n"
194 " border−left−color: none;\n"
195 "}\n"
196 "\n"
197 "QTabBar::tab:right:selected {\ n"
198 " border−right−color: none;\n"
199 "}\n"
200 "\n"
201 "QTabBar::tab:left:last , QTabBar::tab:right:last ,\ n"
202 "QTabBar::tab:left:only−one, QTabBar::tab:right:only−one {\n"
203 " margin−bottom: 0;\n"
204 "}")
205 self . tabWidget.setObjectName("tabWidget")
206 self . tab = QtWidgets.QWidget()
207 self . tab.setObjectName("tab")
208 self . button_limpiar = QtWidgets.QPushButton(self.tab)
209 self . button_limpiar.setGeometry(QtCore.QRect(420, 370, 101, 31))
210 self . button_limpiar.setObjectName("button_limpiar")
211 self . groupBox = QtWidgets.QGroupBox(self.tab)
212 self . groupBox.setGeometry(QtCore.QRect(10, 110, 281, 51))
213 self . groupBox.setObjectName("groupBox")
214 self . pushButton = QtWidgets.QPushButton(self.groupBox)
215 self . pushButton.setGeometry(QtCore.QRect(210, 20, 61, 21))
216 self . pushButton.setObjectName("pushButton")
217 self . comboBox = QtWidgets.QComboBox(self.groupBox)
218 self . comboBox.setGeometry(QtCore.QRect(10, 20, 191, 22))
219 self . comboBox.setStyleSheet("backgroundr: white;")
47

220 self . comboBox.setDuplicatesEnabled(False)


221 self . comboBox.setFrame(True)
222 self . comboBox.setObjectName("comboBox")
223 self . comboBox.addItem("")
224 self . comboBox.addItem("")
225 self . comboBox.addItem("")
226 self . comboBox.addItem("")
227 self . comboBox.addItem("")
228 self . comboBox.addItem("")
229 self . comboBox.addItem("")
230 self . comboBox.addItem("")
231 self . comboBox.addItem("")
232 self . comboBox.addItem("")
233 self . comboBox.addItem("")
234 self . label_7 = QtWidgets.QLabel(self.tab)
235 self . label_7 . setGeometry(QtCore.QRect(20, 200, 141, 16))
236 self . label_7 . setObjectName("label_7")
237 self . Vertical = QtWidgets.QRadioButton(self.tab)
238 self . Vertical . setGeometry(QtCore.QRect(131, 80, 91, 16))
239 self . Vertical . setChecked(True)
240 self . Vertical . setObjectName("Vertical")
241 self . groupBox_2 = QtWidgets.QGroupBox(self.tab)
242 self . groupBox_2.setGeometry(QtCore.QRect(560, 310, 241, 141))
243 self . groupBox_2.setFlat(False)
244 self . groupBox_2.setCheckable(False)
245 self . groupBox_2.setObjectName("groupBox_2")
246 self . label_14 = QtWidgets.QLabel(self.groupBox_2)
247 self . label_14 . setGeometry(QtCore.QRect(10, 50, 61, 20))
248 self . label_14 . setObjectName("label_14")
249 self . pushButton_2 = QtWidgets.QPushButton(self.groupBox_2)
250 self . pushButton_2.setGeometry(QtCore.QRect(90, 110, 71, 23))
251 self . pushButton_2.setCheckable(False)
252 self . pushButton_2.setAutoDefault(False)
253 self . pushButton_2.setDefault(False)
254 self . pushButton_2.setFlat(False)
255 self . pushButton_2.setObjectName("pushButton_2")
256 self . lineEdit_2 = QtWidgets.QLineEdit(self.groupBox_2)
257 self . lineEdit_2 . setEnabled(True)
258 self . lineEdit_2 . setGeometry(QtCore.QRect(80, 50, 81, 20))
259 self . lineEdit_2 . setStyleSheet ("")
260 self . lineEdit_2 . setReadOnly(True)
261 self . lineEdit_2 . setObjectName("lineEdit_2")
262 self . label_13 = QtWidgets.QLabel(self.groupBox_2)
263 self . label_13 . setGeometry(QtCore.QRect(10, 20, 71, 21))
264 self . label_13 . setObjectName("label_13")
265 self . lineEdit = QtWidgets.QLineEdit(self.groupBox_2)
266 self . lineEdit . setGeometry(QtCore.QRect(80, 20, 81, 21))
267 self . lineEdit . setStyleSheet ("")
268 self . lineEdit . setObjectName("lineEdit")
269 self . lineEdit_3 = QtWidgets.QLineEdit(self.groupBox_2)
270 self . lineEdit_3 . setGeometry(QtCore.QRect(80, 80, 81, 20))
271 self . lineEdit_3 . setStyleSheet ("")
272 self . lineEdit_3 . setReadOnly(True)
273 self . lineEdit_3 . setObjectName("lineEdit_3")
274 self . label_27 = QtWidgets.QLabel(self.groupBox_2)
275 self . label_27 . setGeometry(QtCore.QRect(10, 80, 61, 20))
276 self . label_27 . setObjectName("label_27")
277 self . label_28 = QtWidgets.QLabel(self.groupBox_2)
278 self . label_28 . setGeometry(QtCore.QRect(170, 20, 31, 21))
279 self . label_28 . setObjectName("label_28")
280 self . label_29 = QtWidgets.QLabel(self.groupBox_2)
281 self . label_29 . setGeometry(QtCore.QRect(170, 50, 61, 21))
282 self . label_29 . setObjectName("label_29")
283 self . label_30 = QtWidgets.QLabel(self.groupBox_2)
284 self . label_30 . setGeometry(QtCore.QRect(170, 80, 31, 21))
285 self . label_30 . setObjectName("label_30")
286 self . label_3 = QtWidgets.QLabel(self.tab)
287 self . label_3 . setGeometry(QtCore.QRect(20, 50, 171, 16))
288 self . label_3 . setObjectName("label_3")
289 self . label_10 = QtWidgets.QLabel(self.tab)
290 self . label_10 . setGeometry(QtCore.QRect(250, 200, 41, 21))
48 Appendix A. Código fuente

291 self . label_10 . setAlignment(QtCore.Qt.AlignCenter)


292 self . label_10 . setObjectName("label_10")
293 self . button_iniciar = QtWidgets.QPushButton(self.tab)
294 self . button_iniciar . setGeometry(QtCore.QRect(420, 320, 101, 31))
295 self . button_iniciar . setObjectName("button_iniciar")
296 self . label = QtWidgets.QLabel(self.tab)
297 self . label . setGeometry(QtCore.QRect(250, 20, 41, 21))
298 self . label . setAlignment(QtCore.Qt.AlignCenter)
299 self . label . setObjectName("label")
300 self . label_8 = QtWidgets.QLabel(self.tab)
301 self . label_8 . setGeometry(QtCore.QRect(250, 50, 41, 21))
302 self . label_8 . setAlignment(QtCore.Qt.AlignCenter)
303 self . label_8 . setObjectName("label_8")
304 self . label_6 = QtWidgets.QLabel(self.tab)
305 self . label_6 . setGeometry(QtCore.QRect(20, 170, 151, 16))
306 self . label_6 . setObjectName("label_6")
307 self . label_5 = QtWidgets.QLabel(self.tab)
308 self . label_5 . setGeometry(QtCore.QRect(20, 80, 81, 16))
309 self . label_5 . setObjectName("label_5")
310 self . Horizontal = QtWidgets.QRadioButton(self.tab)
311 self . Horizontal.setGeometry(QtCore.QRect(210, 80, 82, 17))
312 self . Horizontal.setObjectName("Horizontal")
313 self . label_2 = QtWidgets.QLabel(self.tab)
314 self . label_2 . setGeometry(QtCore.QRect(20, 20, 171, 16))
315 self . label_2 . setObjectName("label_2")
316 self . groupBox_3 = QtWidgets.QGroupBox(self.tab)
317 self . groupBox_3.setGeometry(QtCore.QRect(10, 230, 281, 291))
318 self . groupBox_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
319 self . groupBox_3.setFlat(False)
320 self . groupBox_3.setCheckable(False)
321 self . groupBox_3.setObjectName("groupBox_3")
322 self . label_9 = QtWidgets.QLabel(self.groupBox_3)
323 self . label_9 . setGeometry(QtCore.QRect(240, 50, 41, 21))
324 self . label_9 . setAlignment(QtCore.Qt.AlignCenter)
325 self . label_9 . setObjectName("label_9")
326 self . label_4 = QtWidgets.QLabel(self.groupBox_3)
327 self . label_4 . setGeometry(QtCore.QRect(10, 50, 121, 16))
328 self . label_4 . setObjectName("label_4")
329 self . FREQ = QtWidgets.QLineEdit(self.groupBox_3)
330 self . FREQ.setGeometry(QtCore.QRect(170, 50, 71, 20))
331 self . FREQ.setStyleSheet("")
332 self . FREQ.setObjectName("FREQ")
333 self . comboBox_2 = QtWidgets.QComboBox(self.groupBox_3)
334 self . comboBox_2.setGeometry(QtCore.QRect(10, 20, 61, 22))
335 self . comboBox_2.setObjectName("comboBox_2")
336 self . comboBox_2.addItem("")
337 self . comboBox_2.addItem("")
338 self . comboBox_2.addItem("")
339 self . comboBox_2.addItem("")
340 self . comboBox_2.addItem("")
341 self . comboBox_2.addItem("")
342 self . comboBox_2.addItem("")
343 self . comboBox_2.addItem("")
344 self . label_11 = QtWidgets.QLabel(self.groupBox_3)
345 self . label_11 . setGeometry(QtCore.QRect(10, 80, 121, 16))
346 self . label_11 . setObjectName("label_11")
347 self . label_12 = QtWidgets.QLabel(self.groupBox_3)
348 self . label_12 . setGeometry(QtCore.QRect(240, 80, 41, 21))
349 self . label_12 . setAlignment(QtCore.Qt.AlignCenter)
350 self . label_12 . setObjectName("label_12")
351 self . label_15 = QtWidgets.QLabel(self.groupBox_3)
352 self . label_15 . setGeometry(QtCore.QRect(10, 110, 121, 16))
353 self . label_15 . setObjectName("label_15")
354 self . label_16 = QtWidgets.QLabel(self.groupBox_3)
355 self . label_16 . setGeometry(QtCore.QRect(240, 140, 41, 21))
356 self . label_16 . setAlignment(QtCore.Qt.AlignCenter)
357 self . label_16 . setObjectName("label_16")
358 self . label_17 = QtWidgets.QLabel(self.groupBox_3)
359 self . label_17 . setGeometry(QtCore.QRect(240, 110, 41, 21))
360 self . label_17 . setAlignment(QtCore.Qt.AlignCenter)
361 self . label_17 . setObjectName("label_17")
49

362 self . label_18 = QtWidgets.QLabel(self.groupBox_3)


363 self . label_18 . setGeometry(QtCore.QRect(10, 140, 121, 16))
364 self . label_18 . setObjectName("label_18")
365 self . label_19 = QtWidgets.QLabel(self.groupBox_3)
366 self . label_19 . setGeometry(QtCore.QRect(10, 170, 121, 16))
367 self . label_19 . setObjectName("label_19")
368 self . label_20 = QtWidgets.QLabel(self.groupBox_3)
369 self . label_20 . setGeometry(QtCore.QRect(240, 200, 41, 21))
370 self . label_20 . setAlignment(QtCore.Qt.AlignCenter)
371 self . label_20 . setObjectName("label_20")
372 self . label_21 = QtWidgets.QLabel(self.groupBox_3)
373 self . label_21 . setGeometry(QtCore.QRect(240, 170, 41, 21))
374 self . label_21 . setAlignment(QtCore.Qt.AlignCenter)
375 self . label_21 . setObjectName("label_21")
376 self . label_22 = QtWidgets.QLabel(self.groupBox_3)
377 self . label_22 . setGeometry(QtCore.QRect(240, 260, 41, 21))
378 self . label_22 . setAlignment(QtCore.Qt.AlignCenter)
379 self . label_22 . setObjectName("label_22")
380 self . label_23 = QtWidgets.QLabel(self.groupBox_3)
381 self . label_23 . setGeometry(QtCore.QRect(10, 200, 121, 16))
382 self . label_23 . setObjectName("label_23")
383 self . label_24 = QtWidgets.QLabel(self.groupBox_3)
384 self . label_24 . setGeometry(QtCore.QRect(10, 260, 121, 16))
385 self . label_24 . setObjectName("label_24")
386 self . label_25 = QtWidgets.QLabel(self.groupBox_3)
387 self . label_25 . setGeometry(QtCore.QRect(240, 230, 41, 21))
388 self . label_25 . setAlignment(QtCore.Qt.AlignCenter)
389 self . label_25 . setObjectName("label_25")
390 self . label_26 = QtWidgets.QLabel(self.groupBox_3)
391 self . label_26 . setGeometry(QtCore.QRect(10, 230, 121, 16))
392 self . label_26 . setObjectName("label_26")
393 self . pushButton_3 = QtWidgets.QPushButton(self.groupBox_3)
394 self . pushButton_3.setGeometry(QtCore.QRect(80, 20, 61, 23))
395 self . pushButton_3.setObjectName("pushButton_3")
396 self . FREQ_2 = QtWidgets.QLineEdit(self.groupBox_3)
397 self . FREQ_2.setGeometry(QtCore.QRect(170, 80, 71, 20))
398 self . FREQ_2.setStyleSheet("")
399 self . FREQ_2.setObjectName("FREQ_2")
400 self . FREQ_3 = QtWidgets.QLineEdit(self.groupBox_3)
401 self . FREQ_3.setGeometry(QtCore.QRect(170, 110, 71, 20))
402 self . FREQ_3.setStyleSheet("")
403 self . FREQ_3.setObjectName("FREQ_3")
404 self . FREQ_4 = QtWidgets.QLineEdit(self.groupBox_3)
405 self . FREQ_4.setGeometry(QtCore.QRect(170, 140, 71, 20))
406 self . FREQ_4.setStyleSheet("")
407 self . FREQ_4.setObjectName("FREQ_4")
408 self . FREQ_5 = QtWidgets.QLineEdit(self.groupBox_3)
409 self . FREQ_5.setGeometry(QtCore.QRect(170, 170, 71, 20))
410 self . FREQ_5.setStyleSheet("")
411 self . FREQ_5.setObjectName("FREQ_5")
412 self . FREQ_6 = QtWidgets.QLineEdit(self.groupBox_3)
413 self . FREQ_6.setGeometry(QtCore.QRect(170, 200, 71, 20))
414 self . FREQ_6.setStyleSheet("")
415 self . FREQ_6.setObjectName("FREQ_6")
416 self . FREQ_7 = QtWidgets.QLineEdit(self.groupBox_3)
417 self . FREQ_7.setGeometry(QtCore.QRect(170, 230, 71, 20))
418 self . FREQ_7.setStyleSheet("")
419 self . FREQ_7.setObjectName("FREQ_7")
420 self . FREQ_8 = QtWidgets.QLineEdit(self.groupBox_3)
421 self . FREQ_8.setGeometry(QtCore.QRect(170, 260, 71, 20))
422 self . FREQ_8.setStyleSheet("")
423 self . FREQ_8.setObjectName("FREQ_8")
424 self . button_exportar = QtWidgets.QPushButton(self.tab)
425 self . button_exportar.setGeometry(QtCore.QRect(420, 420, 101, 31))
426 self . button_exportar.setObjectName("button_exportar")
427 self . HTT = QtWidgets.QLineEdit(self.tab)
428 self . HTT.setGeometry(QtCore.QRect(180, 20, 71, 20))
429 self . HTT.setObjectName("HTT")
430 self . HRR = QtWidgets.QLineEdit(self.tab)
431 self . HRR.setGeometry(QtCore.QRect(180, 50, 71, 20))
432 self . HRR.setObjectName("HRR")
50 Appendix A. Código fuente

433 self . EPSLON = QtWidgets.QLineEdit(self.tab)


434 self . EPSLON.setGeometry(QtCore.QRect(180, 170, 71, 20))
435 self . EPSLON.setObjectName("EPSLON")
436 self . SIGMA = QtWidgets.QLineEdit(self.tab)
437 self . SIGMA.setGeometry(QtCore.QRect(180, 200, 71, 20))
438 self . SIGMA.setObjectName("SIGMA")
439 self . MplWidget = MplWidget(self.tab)
440 self . MplWidget.setGeometry(QtCore.QRect(320, 0, 541, 281))
441 self . MplWidget.setStyleSheet("background−color:white;")
442 self . MplWidget.setObjectName("MplWidget")
443 self . button_cerrar = QtWidgets.QPushButton(self.tab)
444 self . button_cerrar.setGeometry(QtCore.QRect(700, 490, 101, 31))
445 self . button_cerrar.setObjectName("button_cerrar")
446 self . tabWidget.addTab(self.tab, "")
447 self . tab_2 = QtWidgets.QWidget()
448 self . tab_2.setObjectName("tab_2")
449 self . label_31 = QtWidgets.QLabel(self.tab_2)
450 self . label_31 . setGeometry(QtCore.QRect(250, 20, 41, 21))
451 self . label_31 . setAlignment(QtCore.Qt.AlignCenter)
452 self . label_31 . setObjectName("label_31")
453 self . Vertical_2 = QtWidgets.QRadioButton(self.tab_2)
454 self . Vertical_2 . setGeometry(QtCore.QRect(131, 80, 91, 16))
455 self . Vertical_2 . setChecked(True)
456 self . Vertical_2 . setObjectName("Vertical_2")
457 self . label_32 = QtWidgets.QLabel(self.tab_2)
458 self . label_32 . setGeometry(QtCore.QRect(20, 80, 81, 16))
459 self . label_32 . setObjectName("label_32")
460 self . label_33 = QtWidgets.QLabel(self.tab_2)
461 self . label_33 . setGeometry(QtCore.QRect(250, 50, 41, 21))
462 self . label_33 . setAlignment(QtCore.Qt.AlignCenter)
463 self . label_33 . setObjectName("label_33")
464 self . HTT_2 = QtWidgets.QLineEdit(self.tab_2)
465 self . HTT_2.setGeometry(QtCore.QRect(180, 20, 71, 20))
466 self . HTT_2.setObjectName("HTT_2")
467 self . label_34 = QtWidgets.QLabel(self.tab_2)
468 self . label_34 . setGeometry(QtCore.QRect(20, 50, 121, 16))
469 self . label_34 . setObjectName("label_34")
470 self . Horizontal_2 = QtWidgets.QRadioButton(self.tab_2)
471 self . Horizontal_2.setGeometry(QtCore.QRect(210, 80, 82, 17))
472 self . Horizontal_2.setObjectName("Horizontal_2")
473 self . label_35 = QtWidgets.QLabel(self.tab_2)
474 self . label_35 . setGeometry(QtCore.QRect(20, 20, 131, 16))
475 self . label_35 . setObjectName("label_35")
476 self . label_36 = QtWidgets.QLabel(self.tab_2)
477 self . label_36 . setGeometry(QtCore.QRect(20, 110, 121, 16))
478 self . label_36 . setObjectName("label_36")
479 self . label_37 = QtWidgets.QLabel(self.tab_2)
480 self . label_37 . setGeometry(QtCore.QRect(250, 110, 41, 21))
481 self . label_37 . setAlignment(QtCore.Qt.AlignCenter)
482 self . label_37 . setObjectName("label_37")
483 self . MplWidget_2 = MplWidget(self.tab_2)
484 self . MplWidget_2.setGeometry(QtCore.QRect(320, 0, 541, 281))
485 self . MplWidget_2.setStyleSheet("background−color:white;")
486 self . MplWidget_2.setObjectName("MplWidget_2")
487 self . button_cerrar_2 = QtWidgets.QPushButton(self.tab_2)
488 self . button_cerrar_2.setGeometry(QtCore.QRect(700, 490, 101, 31))
489 self . button_cerrar_2.setObjectName("button_cerrar_2")
490 self . groupBox_4 = QtWidgets.QGroupBox(self.tab_2)
491 self . groupBox_4.setGeometry(QtCore.QRect(560, 310, 241, 61))
492 self . groupBox_4.setFlat(False)
493 self . groupBox_4.setCheckable(False)
494 self . groupBox_4.setObjectName("groupBox_4")
495 self . label_38 = QtWidgets.QLabel(self.groupBox_4)
496 self . label_38 . setGeometry(QtCore.QRect(80, 30, 81, 20))
497 self . label_38 . setObjectName("label_38")
498 self . lineEdit_4 = QtWidgets.QLineEdit(self.groupBox_4)
499 self . lineEdit_4 . setEnabled(True)
500 self . lineEdit_4 . setGeometry(QtCore.QRect(170, 30, 61, 20))
501 self . lineEdit_4 . setStyleSheet ("")
502 self . lineEdit_4 . setReadOnly(True)
503 self . lineEdit_4 . setObjectName("lineEdit_4")
51

504 self . button_exportar_2 = QtWidgets.QPushButton(self.tab_2)


505 self . button_exportar_2.setGeometry(QtCore.QRect(420, 420, 101, 31))
506 self . button_exportar_2.setObjectName("button_exportar_2")
507 self . button_limpiar_2 = QtWidgets.QPushButton(self.tab_2)
508 self . button_limpiar_2.setGeometry(QtCore.QRect(420, 370, 101, 31))
509 self . button_limpiar_2.setObjectName("button_limpiar_2")
510 self . button_iniciar_2 = QtWidgets.QPushButton(self.tab_2)
511 self . button_iniciar_2 . setGeometry(QtCore.QRect(420, 320, 101, 31))
512 self . button_iniciar_2 . setObjectName("button_iniciar_2")
513 self . groupBox_5 = QtWidgets.QGroupBox(self.tab_2)
514 self . groupBox_5.setGeometry(QtCore.QRect(10, 140, 281, 51))
515 self . groupBox_5.setObjectName("groupBox_5")
516 self . comboBox_3 = QtWidgets.QComboBox(self.groupBox_5)
517 self . comboBox_3.setGeometry(QtCore.QRect(10, 20, 61, 22))
518 self . comboBox_3.setStyleSheet("backgroundr: white;")
519 self . comboBox_3.setDuplicatesEnabled(False)
520 self . comboBox_3.setFrame(True)
521 self . comboBox_3.setObjectName("comboBox_3")
522 self . comboBox_3.addItem("")
523 self . comboBox_3.addItem("")
524 self . comboBox_3.addItem("")
525 self . pushButton_7 = QtWidgets.QPushButton(self.groupBox_5)
526 self . pushButton_7.setGeometry(QtCore.QRect(160, 20, 111, 21))
527 self . pushButton_7.setObjectName("pushButton_7")
528 self . groupBox_6 = QtWidgets.QGroupBox(self.tab_2)
529 self . groupBox_6.setGeometry(QtCore.QRect(10, 200, 281, 331))
530 self . groupBox_6.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
531 self . groupBox_6.setFlat(False)
532 self . groupBox_6.setCheckable(False)
533 self . groupBox_6.setObjectName("groupBox_6")
534 self . label_44 = QtWidgets.QLabel(self.groupBox_6)
535 self . label_44 . setGeometry(QtCore.QRect(210, 10, 61, 20))
536 self . label_44 . setAlignment(QtCore.Qt.AlignCenter)
537 self . label_44 . setObjectName("label_44")
538 self . label_45 = QtWidgets.QLabel(self.groupBox_6)
539 self . label_45 . setGeometry(QtCore.QRect(160, 10, 41, 20))
540 self . label_45 . setAlignment(QtCore.Qt.AlignCenter)
541 self . label_45 . setObjectName("label_45")
542 self . label_46 = QtWidgets.QLabel(self.groupBox_6)
543 self . label_46 . setGeometry(QtCore.QRect(120, 60, 81, 21))
544 self . label_46 . setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
545 self . label_46 . setObjectName("label_46")
546 self . comboBox_4 = QtWidgets.QComboBox(self.groupBox_6)
547 self . comboBox_4.setGeometry(QtCore.QRect(10, 30, 141, 22))
548 self . comboBox_4.setStyleSheet("backgroundr: white;")
549 self . comboBox_4.setDuplicatesEnabled(False)
550 self . comboBox_4.setFrame(True)
551 self . comboBox_4.setObjectName("comboBox_4")
552 self . comboBox_4.addItem("")
553 self . comboBox_4.addItem("")
554 self . comboBox_4.addItem("")
555 self . comboBox_4.addItem("")
556 self . comboBox_4.addItem("")
557 self . comboBox_4.addItem("")
558 self . comboBox_4.addItem("")
559 self . comboBox_4.addItem("")
560 self . comboBox_4.addItem("")
561 self . comboBox_4.addItem("")
562 self . comboBox_4.addItem("")
563 self . label_47 = QtWidgets.QLabel(self.groupBox_6)
564 self . label_47 . setGeometry(QtCore.QRect(160, 90, 41, 20))
565 self . label_47 . setAlignment(QtCore.Qt.AlignCenter)
566 self . label_47 . setObjectName("label_47")
567 self . label_48 = QtWidgets.QLabel(self.groupBox_6)
568 self . label_48 . setGeometry(QtCore.QRect(210, 90, 61, 20))
569 self . label_48 . setAlignment(QtCore.Qt.AlignCenter)
570 self . label_48 . setObjectName("label_48")
571 self . comboBox_5 = QtWidgets.QComboBox(self.groupBox_6)
572 self . comboBox_5.setGeometry(QtCore.QRect(10, 110, 141, 22))
573 self . comboBox_5.setStyleSheet("backgroundr: white;")
574 self . comboBox_5.setDuplicatesEnabled(False)
52 Appendix A. Código fuente

575 self . comboBox_5.setFrame(True)


576 self . comboBox_5.setObjectName("comboBox_5")
577 self . comboBox_5.addItem("")
578 self . comboBox_5.addItem("")
579 self . comboBox_5.addItem("")
580 self . comboBox_5.addItem("")
581 self . comboBox_5.addItem("")
582 self . comboBox_5.addItem("")
583 self . comboBox_5.addItem("")
584 self . comboBox_5.addItem("")
585 self . comboBox_5.addItem("")
586 self . comboBox_5.addItem("")
587 self . comboBox_5.addItem("")
588 self . label_49 = QtWidgets.QLabel(self.groupBox_6)
589 self . label_49 . setGeometry(QtCore.QRect(120, 140, 81, 21))
590 self . label_49 . setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
591 self . label_49 . setObjectName("label_49")
592 self . label_50 = QtWidgets.QLabel(self.groupBox_6)
593 self . label_50 . setGeometry(QtCore.QRect(160, 170, 41, 20))
594 self . label_50 . setAlignment(QtCore.Qt.AlignCenter)
595 self . label_50 . setObjectName("label_50")
596 self . label_51 = QtWidgets.QLabel(self.groupBox_6)
597 self . label_51 . setGeometry(QtCore.QRect(210, 170, 61, 20))
598 self . label_51 . setAlignment(QtCore.Qt.AlignCenter)
599 self . label_51 . setObjectName("label_51")
600 self . comboBox_6 = QtWidgets.QComboBox(self.groupBox_6)
601 self . comboBox_6.setGeometry(QtCore.QRect(10, 190, 141, 22))
602 self . comboBox_6.setStyleSheet("backgroundr: white;")
603 self . comboBox_6.setDuplicatesEnabled(False)
604 self . comboBox_6.setFrame(True)
605 self . comboBox_6.setObjectName("comboBox_6")
606 self . comboBox_6.addItem("")
607 self . comboBox_6.addItem("")
608 self . comboBox_6.addItem("")
609 self . comboBox_6.addItem("")
610 self . comboBox_6.addItem("")
611 self . comboBox_6.addItem("")
612 self . comboBox_6.addItem("")
613 self . comboBox_6.addItem("")
614 self . comboBox_6.addItem("")
615 self . comboBox_6.addItem("")
616 self . comboBox_6.addItem("")
617 self . label_52 = QtWidgets.QLabel(self.groupBox_6)
618 self . label_52 . setGeometry(QtCore.QRect(120, 220, 81, 21))
619 self . label_52 . setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
620 self . label_52 . setObjectName("label_52")
621 self . label_53 = QtWidgets.QLabel(self.groupBox_6)
622 self . label_53 . setGeometry(QtCore.QRect(120, 300, 81, 21))
623 self . label_53 . setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
624 self . label_53 . setObjectName("label_53")
625 self . label_54 = QtWidgets.QLabel(self.groupBox_6)
626 self . label_54 . setGeometry(QtCore.QRect(210, 250, 61, 20))
627 self . label_54 . setAlignment(QtCore.Qt.AlignCenter)
628 self . label_54 . setObjectName("label_54")
629 self . comboBox_7 = QtWidgets.QComboBox(self.groupBox_6)
630 self . comboBox_7.setGeometry(QtCore.QRect(10, 270, 141, 22))
631 self . comboBox_7.setStyleSheet("backgroundr: white;")
632 self . comboBox_7.setDuplicatesEnabled(False)
633 self . comboBox_7.setFrame(True)
634 self . comboBox_7.setObjectName("comboBox_7")
635 self . comboBox_7.addItem("")
636 self . comboBox_7.addItem("")
637 self . comboBox_7.addItem("")
638 self . comboBox_7.addItem("")
639 self . comboBox_7.addItem("")
640 self . comboBox_7.addItem("")
641 self . comboBox_7.addItem("")
642 self . comboBox_7.addItem("")
643 self . comboBox_7.addItem("")
644 self . comboBox_7.addItem("")
645 self . comboBox_7.addItem("")
53

646 self . label_55 = QtWidgets.QLabel(self.groupBox_6)


647 self . label_55 . setGeometry(QtCore.QRect(160, 250, 41, 20))
648 self . label_55 . setAlignment(QtCore.Qt.AlignCenter)
649 self . label_55 . setObjectName("label_55")
650 self . line = QtWidgets.QFrame(self.groupBox_6)
651 self . line . setGeometry(QtCore.QRect(0, 240, 281, 16))
652 self . line . setFrameShape(QtWidgets.QFrame.HLine)
653 self . line . setFrameShadow(QtWidgets.QFrame.Sunken)
654 self . line . setObjectName("line")
655 self . line_2 = QtWidgets.QFrame(self.groupBox_6)
656 self . line_2 . setGeometry(QtCore.QRect(0, 160, 281, 16))
657 self . line_2 . setFrameShape(QtWidgets.QFrame.HLine)
658 self . line_2 . setFrameShadow(QtWidgets.QFrame.Sunken)
659 self . line_2 . setObjectName("line_2")
660 self . line_3 = QtWidgets.QFrame(self.groupBox_6)
661 self . line_3 . setGeometry(QtCore.QRect(0, 80, 281, 16))
662 self . line_3 . setFrameShape(QtWidgets.QFrame.HLine)
663 self . line_3 . setFrameShadow(QtWidgets.QFrame.Sunken)
664 self . line_3 . setObjectName("line_3")
665 self . Epsilon1 = QtWidgets.QLineEdit(self.groupBox_6)
666 self . Epsilon1.setGeometry(QtCore.QRect(160, 30, 41, 20))
667 self . Epsilon1.setObjectName("Epsilon1")
668 self . Sigma1 = QtWidgets.QLineEdit(self.groupBox_6)
669 self . Sigma1.setGeometry(QtCore.QRect(210, 30, 61, 20))
670 self . Sigma1.setObjectName("Sigma1")
671 self . Long1 = QtWidgets.QLineEdit(self.groupBox_6)
672 self . Long1.setGeometry(QtCore.QRect(210, 60, 61, 20))
673 self . Long1.setObjectName("Long1")
674 self . Epsilon2 = QtWidgets.QLineEdit(self.groupBox_6)
675 self . Epsilon2.setGeometry(QtCore.QRect(160, 110, 41, 20))
676 self . Epsilon2.setObjectName("Epsilon2")
677 self . Sigma2 = QtWidgets.QLineEdit(self.groupBox_6)
678 self . Sigma2.setGeometry(QtCore.QRect(210, 110, 61, 20))
679 self . Sigma2.setObjectName("Sigma2")
680 self . Long2 = QtWidgets.QLineEdit(self.groupBox_6)
681 self . Long2.setGeometry(QtCore.QRect(210, 140, 61, 20))
682 self . Long2.setObjectName("Long2")
683 self . Epsilon3 = QtWidgets.QLineEdit(self.groupBox_6)
684 self . Epsilon3.setGeometry(QtCore.QRect(160, 190, 41, 20))
685 self . Epsilon3.setObjectName("Epsilon3")
686 self . Sigma3 = QtWidgets.QLineEdit(self.groupBox_6)
687 self . Sigma3.setGeometry(QtCore.QRect(210, 190, 61, 20))
688 self . Sigma3.setObjectName("Sigma3")
689 self . Long3 = QtWidgets.QLineEdit(self.groupBox_6)
690 self . Long3.setGeometry(QtCore.QRect(210, 220, 61, 20))
691 self . Long3.setObjectName("Long3")
692 self . Epsilon4 = QtWidgets.QLineEdit(self.groupBox_6)
693 self . Epsilon4.setGeometry(QtCore.QRect(160, 270, 41, 20))
694 self . Epsilon4.setObjectName("Epsilon4")
695 self . Sigma4 = QtWidgets.QLineEdit(self.groupBox_6)
696 self . Sigma4.setGeometry(QtCore.QRect(210, 270, 61, 20))
697 self . Sigma4.setObjectName("Sigma4")
698 self . Long4 = QtWidgets.QLineEdit(self.groupBox_6)
699 self . Long4.setGeometry(QtCore.QRect(210, 300, 61, 20))
700 self . Long4.setObjectName("Long4")
701 self . HRR_2 = QtWidgets.QLineEdit(self.tab_2)
702 self . HRR_2.setGeometry(QtCore.QRect(180, 50, 71, 20))
703 self . HRR_2.setObjectName("HRR_2")
704 self . FREQmixto = QtWidgets.QLineEdit(self.tab_2)
705 self . FREQmixto.setGeometry(QtCore.QRect(180, 110, 71, 20))
706 self . FREQmixto.setObjectName("FREQmixto")
707 self . tabWidget.addTab(self.tab_2, "")
708 MainWindow.setCentralWidget(self.centralwidget)
709 self . menubar = QtWidgets.QMenuBar(MainWindow)
710 self . menubar.setGeometry(QtCore.QRect(0, 0, 867, 21))
711 self . menubar.setObjectName("menubar")
712 MainWindow.setMenuBar(self.menubar)
713 self . statusbar = QtWidgets.QStatusBar(MainWindow)
714 self . statusbar . setObjectName("statusbar")
715 MainWindow.setStatusBar(self.statusbar)
716
54 Appendix A. Código fuente

717 self . MplWidget.setGeometry(QtCore.QRect(320, 0, 541, 309)) #SUSTITUCION


718 self . MplWidget_2.setGeometry(QtCore.QRect(320, 0, 541, 309)) #SUSTITUCION
719 self . button_iniciar . setIcon (QtGui.QIcon("media2.png")) #INCRUSTADO
720 self . button_iniciar . setIconSize (QtCore.QSize(20,20)) #INCRUSTADO
721 self . button_limpiar.setIcon (QtGui.QIcon("goma.png")) #INCRUSTADO
722 self . button_limpiar.setIconSize (QtCore.QSize(20,20)) #INCRUSTADO
723 self . button_exportar.setIcon(QtGui.QIcon("export.png")) #INCRUSTADO
724 self . button_exportar.setIconSize (QtCore.QSize(15,15)) #INCRUSTADO
725 self . button_cerrar.setIcon (QtGui.QIcon("back.png")) #INCRUSTADO
726 self . button_cerrar.setIconSize (QtCore.QSize(20,20)) #INCRUSTADO
727
728 self . button_iniciar_2 . setIcon (QtGui.QIcon("media2.png")) #INCRUSTADO
729 self . button_iniciar_2 . setIconSize (QtCore.QSize(20,20)) #INCRUSTADO
730 self . button_limpiar_2.setIcon(QtGui.QIcon("goma.png")) #INCRUSTADO
731 self . button_limpiar_2.setIconSize (QtCore.QSize(20,20)) #INCRUSTADO
732 self . button_exportar_2.setIcon(QtGui.QIcon("export.png")) #INCRUSTADO
733 self . button_exportar_2.setIconSize(QtCore.QSize(15,15)) #INCRUSTADO
734 self . button_cerrar_2.setIcon(QtGui.QIcon("back.png")) #INCRUSTADO
735 self . button_cerrar_2.setIconSize(QtCore.QSize(20,20)) #INCRUSTADO
736
737
738 self . MplWidget.canvas.axes. set_title ( ’Curva de propagación’,fontsize=10)#INCRUSTADO
739 self . MplWidget.canvas.axes.set_xlabel(’Distancia [km]’, fontsize =8)#INCRUSTADO
740 self . MplWidget.canvas.axes.set_ylabel( ’Intensidad de campo [dBu]’,fontsize=8)#INCRUSTADO
741 self . MplWidget.canvas.axes.set_ylim(−20,120) #INCRUSTADO
742 self . MplWidget.canvas.axes.semilogx() #INCRUSTADO
743 self . MplWidget.canvas.axes.set_xlim(1,10000) #INCRUSTADO
744 self . MplWidget.canvas.axes.grid(True,which="both",linewidth=0.5) #INCRUSTADO
745 self . MplWidget_2.canvas.axes. set_title ( ’Curva de propagación’,fontsize=10)#INCRUSTADO
746 self . MplWidget_2.canvas.axes.set_xlabel(’Distancia [km]’, fontsize =8)#INCRUSTADO
747 self . MplWidget_2.canvas.axes.set_ylabel(’Intensidad de campo [dBu]’,fontsize=8)#INCRUSTADO
748 self . MplWidget_2.canvas.axes.set_ylim(−20,120) #INCRUSTADO
749 self . MplWidget_2.canvas.axes.semilogx() #INCRUSTADO
750 self . MplWidget_2.canvas.axes.set_xlim(1,10000) #INCRUSTADO
751 self . MplWidget_2.canvas.axes.grid(True,which="both",linewidth=0.5) #INCRUSTADO
752 self . FREQ_2.setVisible(False) #INCRUSTADO
753 self . FREQ_3.setVisible(False) #INCRUSTADO
754 self . FREQ_4.setVisible(False) #INCRUSTADO
755 self . FREQ_5.setVisible(False) #INCRUSTADO
756 self . FREQ_6.setVisible(False) #INCRUSTADO
757 self . FREQ_7.setVisible(False) #INCRUSTADO
758 self . FREQ_8.setVisible(False) #INCRUSTADO
759 self . label_11 . setVisible (False) #INCRUSTADO
760 self . label_12 . setVisible (False) #INCRUSTADO
761 self . label_15 . setVisible (False) #INCRUSTADO
762 self . label_16 . setVisible (False) #INCRUSTADO
763 self . label_17 . setVisible (False) #INCRUSTADO
764 self . label_18 . setVisible (False) #INCRUSTADO
765 self . label_19 . setVisible (False) #INCRUSTADO
766 self . label_20 . setVisible (False) #INCRUSTADO
767 self . label_21 . setVisible (False) #INCRUSTADO
768 self . label_22 . setVisible (False) #INCRUSTADO
769 self . label_23 . setVisible (False) #INCRUSTADO
770 self . label_24 . setVisible (False) #INCRUSTADO
771 self . label_25 . setVisible (False) #INCRUSTADO
772 self . label_26 . setVisible (False) #INCRUSTADO
773 self . lineEdit . setReadOnly(True) #INCRUSTADO
774 self . lineEdit_4 . setReadOnly(True) #INCRUSTADO
775 self . lineEdit . setStyleSheet ("background−color: rgb(235, 235, 235);")#INCRUSTADO
776 self . lineEdit_2 . setStyleSheet ("background−color: rgb(235, 235, 235);")#INCRUSTADO
777 self . lineEdit_3 . setStyleSheet ("background−color: rgb(235, 235, 235);")#INCRUSTADO
778
779 self . label_50 . setVisible (False) #INCRUSTADO
780 self . label_51 . setVisible (False) #INCRUSTADO
781 self . label_52 . setVisible (False) #INCRUSTADO
782 self . comboBox_6.setVisible(False) #INCRUSTADO
783 self . Epsilon3. setVisible (False) #INCRUSTADO
784 self . Sigma3.setVisible (False) #INCRUSTADO
785 self . Long3.setVisible (False) #INCRUSTADO
786
787 self . label_53 . setVisible (False) #INCRUSTADO
55

788 self . label_54 . setVisible (False) #INCRUSTADO


789 self . label_55 . setVisible (False) #INCRUSTADO
790 self . comboBox_7.setVisible(False) #INCRUSTADO
791 self . Epsilon4. setVisible (False) #INCRUSTADO
792 self . Sigma4.setVisible (False) #INCRUSTADO
793 self . Long4.setVisible (False) #INCRUSTADO
794 self . line . setVisible (False) #INCRUSTADO
795
796 self . retranslateUi (MainWindow)
797 self . tabWidget.setCurrentIndex(0)
798 QtCore.QMetaObject.connectSlotsByName(MainWindow)
799
800 def retranslateUi ( self , MainWindow):
801 _translate = QtCore.QCoreApplication.translate
802 MainWindow.setWindowTitle(_translate("MainWindow", "GroundWave Simulator"))
803 self . button_limpiar.setText ( _translate ("MainWindow", "Limpiar"))
804 self . groupBox.setTitle( _translate ("MainWindow", "Elegir tipo de terreno"))
805 self . pushButton.setText(_translate ("MainWindow", "Ayuda"))
806 self . comboBox.setItemText(0, _translate("MainWindow", "Personalizado"))
807 self . comboBox.setItemText(1, _translate("MainWindow", "Agua del mar, salinidad baja"))
808 self . comboBox.setItemText(2, _translate("MainWindow", "Agua del mar, salinidad media"))
809 self . comboBox.setItemText(3, _translate("MainWindow", "Agua dulce"))
810 self . comboBox.setItemText(4, _translate("MainWindow", "Tierra"))
811 self . comboBox.setItemText(5, _translate("MainWindow", "Tierra húmeda"))
812 self . comboBox.setItemText(6, _translate("MainWindow", "Tierra moderadamente seca"))
813 self . comboBox.setItemText(7, _translate("MainWindow", "Tierra seca"))
814 self . comboBox.setItemText(8, _translate("MainWindow", "Tierra muy seca"))
815 self . comboBox.setItemText(9, _translate("MainWindow", "Hielo de agua dulce, −1C"))
816 self . comboBox.setItemText(10, _translate("MainWindow", "Hielo de agua dulce, −10C"))
817 self . label_7 . setText ( _translate ("MainWindow", "(Conductividad)"))
818 self . Vertical . setText ( _translate ("MainWindow", "Vertical"))
819 self . groupBox_2.setTitle( _translate ("MainWindow", "Calcular intensidad de campo eléctrico"))
820 self . label_14 . setText ( _translate ("MainWindow", "E"))
821 self . pushButton_2.setText(_translate ("MainWindow", "Calcular"))
822 self . label_13 . setText ( _translate ("MainWindow", "Distancia"))
823 self . label_27 . setText ( _translate ("MainWindow", "Lb"))
824 self . label_28 . setText ( _translate ("MainWindow", "km"))
825 self . label_29 . setText ( _translate ("MainWindow", "dBu"))
826 self . label_30 . setText ( _translate ("MainWindow", "dB"))
827 self . label_3 . setText ( _translate ("MainWindow", "Altura antena receptora"))
828 self . label_10 . setText ( _translate ("MainWindow", "S/m"))
829 self . button_iniciar . setText ( _translate ("MainWindow", "Ejecutar"))
830 self . label . setText ( _translate ("MainWindow", "m"))
831 self . label_8 . setText ( _translate ("MainWindow", "m"))
832 self . label_6 . setText ( _translate ("MainWindow", "(Constante dieléctrica)"))
833 self . label_5 . setText ( _translate ("MainWindow", "Polarización"))
834 self . Horizontal. setText ( _translate ("MainWindow", "Horizontal"))
835 self . label_2 . setText ( _translate ("MainWindow", "Altura antena transmisora"))
836 self . groupBox_3.setTitle( _translate ("MainWindow", "Número de frecuencias"))
837 self . label_9 . setText ( _translate ("MainWindow", "MHz"))
838 self . label_4 . setText ( _translate ("MainWindow", "Frecuencia 1"))
839 self . comboBox_2.setItemText(0, _translate("MainWindow", "1"))
840 self . comboBox_2.setItemText(1, _translate("MainWindow", "2"))
841 self . comboBox_2.setItemText(2, _translate("MainWindow", "3"))
842 self . comboBox_2.setItemText(3, _translate("MainWindow", "4"))
843 self . comboBox_2.setItemText(4, _translate("MainWindow", "5"))
844 self . comboBox_2.setItemText(5, _translate("MainWindow", "6"))
845 self . comboBox_2.setItemText(6, _translate("MainWindow", "7"))
846 self . comboBox_2.setItemText(7, _translate("MainWindow", "8"))
847 self . label_11 . setText ( _translate ("MainWindow", "Frecuencia 2"))
848 self . label_12 . setText ( _translate ("MainWindow", "MHz"))
849 self . label_15 . setText ( _translate ("MainWindow", "Frecuencia 3"))
850 self . label_16 . setText ( _translate ("MainWindow", "MHz"))
851 self . label_17 . setText ( _translate ("MainWindow", "MHz"))
852 self . label_18 . setText ( _translate ("MainWindow", "Frecuencia 4"))
853 self . label_19 . setText ( _translate ("MainWindow", "Frecuencia 5"))
854 self . label_20 . setText ( _translate ("MainWindow", "MHz"))
855 self . label_21 . setText ( _translate ("MainWindow", "MHz"))
856 self . label_22 . setText ( _translate ("MainWindow", "MHz"))
857 self . label_23 . setText ( _translate ("MainWindow", "Frecuencia 6"))
858 self . label_24 . setText ( _translate ("MainWindow", "Frecuencia 8"))
56 Appendix A. Código fuente

859 self . label_25 . setText ( _translate ("MainWindow", "MHz"))


860 self . label_26 . setText ( _translate ("MainWindow", "Frecuencia 7"))
861 self . pushButton_3.setText(_translate ("MainWindow", "Ayuda"))
862 self . button_exportar.setText( _translate ("MainWindow", "Exportar"))
863 self . button_cerrar.setText ( _translate ("MainWindow", "Regresar"))
864 self . tabWidget.setTabText(self . tabWidget.indexOf(self.tab) , _translate ("MainWindow", "Terreno homogé
neo"))
865 self . label_31 . setText ( _translate ("MainWindow", "m"))
866 self . Vertical_2 . setText ( _translate ("MainWindow", "Vertical"))
867 self . label_32 . setText ( _translate ("MainWindow", "Polarización"))
868 self . label_33 . setText ( _translate ("MainWindow", "m"))
869 self . label_34 . setText ( _translate ("MainWindow", "Altura antena receptora"))
870 self . Horizontal_2.setText ( _translate ("MainWindow", "Horizontal"))
871 self . label_35 . setText ( _translate ("MainWindow", "Altura antena transmisora"))
872 self . label_36 . setText ( _translate ("MainWindow", "Frecuencia"))
873 self . label_37 . setText ( _translate ("MainWindow", "MHz"))
874 self . button_cerrar_2.setText( _translate ("MainWindow", "Regresar"))
875 self . groupBox_4.setTitle( _translate ("MainWindow", "Intensidad de campo eléctrico recibido"))
876 self . label_38 . setText ( _translate ("MainWindow", "E [dBu"))
877 self . button_exportar_2.setText( _translate ("MainWindow", "Exportar"))
878 self . button_limpiar_2.setText( _translate ("MainWindow", "Limpiar"))
879 self . button_iniciar_2 . setText ( _translate ("MainWindow", "Ejecutar"))
880 self . groupBox_5.setTitle( _translate ("MainWindow", "Elegir número de terrenos"))
881 self . comboBox_3.setItemText(0, _translate("MainWindow", "2"))
882 self . comboBox_3.setItemText(1, _translate("MainWindow", "3"))
883 self . comboBox_3.setItemText(2, _translate("MainWindow", "4"))
884 self . pushButton_7.setText(_translate ("MainWindow", "Información"))
885 self . groupBox_6.setTitle( _translate ("MainWindow", "Parámetros de cada terreno"))
886 self . label_44 . setText ( _translate ("MainWindow", "Cond. [S/m]"))
887 self . label_45 . setText ( _translate ("MainWindow", "Epsilon_r"))
888 self . label_46 . setText ( _translate ("MainWindow", "Longitud [km]"))
889 self . comboBox_4.setItemText(0, _translate("MainWindow", "Personalizado"))
890 self . comboBox_4.setItemText(1, _translate("MainWindow", "Agua del mar, salinidad baja"))
891 self . comboBox_4.setItemText(2, _translate("MainWindow", "Agua del mar, salinidad media"))
892 self . comboBox_4.setItemText(3, _translate("MainWindow", "Agua dulce"))
893 self . comboBox_4.setItemText(4, _translate("MainWindow", "Tierra"))
894 self . comboBox_4.setItemText(5, _translate("MainWindow", "Tierra húmeda"))
895 self . comboBox_4.setItemText(6, _translate("MainWindow", "Tierra moderadamente seca"))
896 self . comboBox_4.setItemText(7, _translate("MainWindow", "Tierra seca"))
897 self . comboBox_4.setItemText(8, _translate("MainWindow", "Tierra muy seca"))
898 self . comboBox_4.setItemText(9, _translate("MainWindow", "Hielo de agua dulce, −1C"))
899 self . comboBox_4.setItemText(10, _translate("MainWindow", "Hielo de agua dulce, −10C"))
900 self . label_47 . setText ( _translate ("MainWindow", "Epsilon_r"))
901 self . label_48 . setText ( _translate ("MainWindow", "Cond. [S/m]"))
902 self . comboBox_5.setItemText(0, _translate("MainWindow", "Personalizado"))
903 self . comboBox_5.setItemText(1, _translate("MainWindow", "Agua del mar, salinidad baja"))
904 self . comboBox_5.setItemText(2, _translate("MainWindow", "Agua del mar, salinidad media"))
905 self . comboBox_5.setItemText(3, _translate("MainWindow", "Agua dulce"))
906 self . comboBox_5.setItemText(4, _translate("MainWindow", "Tierra"))
907 self . comboBox_5.setItemText(5, _translate("MainWindow", "Tierra húmeda"))
908 self . comboBox_5.setItemText(6, _translate("MainWindow", "Tierra moderadamente seca"))
909 self . comboBox_5.setItemText(7, _translate("MainWindow", "Tierra seca"))
910 self . comboBox_5.setItemText(8, _translate("MainWindow", "Tierra muy seca"))
911 self . comboBox_5.setItemText(9, _translate("MainWindow", "Hielo de agua dulce, −1C"))
912 self . comboBox_5.setItemText(10, _translate("MainWindow", "Hielo de agua dulce, −10C"))
913 self . label_49 . setText ( _translate ("MainWindow", "Longitud [km]"))
914 self . label_50 . setText ( _translate ("MainWindow", "Epsilon_r"))
915 self . label_51 . setText ( _translate ("MainWindow", "Cond. [S/m]"))
916 self . comboBox_6.setItemText(0, _translate("MainWindow", "Personalizado"))
917 self . comboBox_6.setItemText(1, _translate("MainWindow", "Agua del mar, salinidad baja"))
918 self . comboBox_6.setItemText(2, _translate("MainWindow", "Agua del mar, salinidad media"))
919 self . comboBox_6.setItemText(3, _translate("MainWindow", "Agua dulce"))
920 self . comboBox_6.setItemText(4, _translate("MainWindow", "Tierra"))
921 self . comboBox_6.setItemText(5, _translate("MainWindow", "Tierra húmeda"))
922 self . comboBox_6.setItemText(6, _translate("MainWindow", "Tierra moderadamente seca"))
923 self . comboBox_6.setItemText(7, _translate("MainWindow", "Tierra seca"))
924 self . comboBox_6.setItemText(8, _translate("MainWindow", "Tierra muy seca"))
925 self . comboBox_6.setItemText(9, _translate("MainWindow", "Hielo de agua dulce, −1C"))
926 self . comboBox_6.setItemText(10, _translate("MainWindow", "Hielo de agua dulce, −10C"))
927 self . label_52 . setText ( _translate ("MainWindow", "Longitud [km]"))
928 self . label_53 . setText ( _translate ("MainWindow", "Longitud [km]"))
57

929 self . label_54 . setText ( _translate ("MainWindow", "Cond. [S/m]"))


930 self . comboBox_7.setItemText(0, _translate("MainWindow", "Personalizado"))
931 self . comboBox_7.setItemText(1, _translate("MainWindow", "Agua del mar, salinidad baja"))
932 self . comboBox_7.setItemText(2, _translate("MainWindow", "Agua del mar, salinidad media"))
933 self . comboBox_7.setItemText(3, _translate("MainWindow", "Agua dulce"))
934 self . comboBox_7.setItemText(4, _translate("MainWindow", "Tierra"))
935 self . comboBox_7.setItemText(5, _translate("MainWindow", "Tierra húmeda"))
936 self . comboBox_7.setItemText(6, _translate("MainWindow", "Tierra moderadamente seca"))
937 self . comboBox_7.setItemText(7, _translate("MainWindow", "Tierra seca"))
938 self . comboBox_7.setItemText(8, _translate("MainWindow", "Tierra muy seca"))
939 self . comboBox_7.setItemText(9, _translate("MainWindow", "Hielo de agua dulce, −1C"))
940 self . comboBox_7.setItemText(10, _translate("MainWindow", "Hielo de agua dulce, −10C"))
941 self . label_55 . setText ( _translate ("MainWindow", "Epsilon_r"))
942 self . tabWidget.setTabText(self . tabWidget.indexOf(self.tab_2), _translate ("MainWindow", "Terreno mixto"
))
943 from mplwidget import MplWidget
944
945
946 if __name__ == "__main__":
947 import sys
948 app = QtWidgets.QApplication(sys.argv)
949 MainWindow = QtWidgets.QMainWindow()
950 ui = Ui_MainWindow()
951 ui . setupUi(MainWindow)
952 MainWindow.show()
953 sys . exit (app.exec_() )

lectura2.py
1 # −− coding: utf−8 −−
2 """
3 Created on Mon Mar 30 17:28:31 2020
4
5 author: Javier Ojeda Prieto
6 """
7 from matplotlib.pyplot import figure , show
8 import pandas as pd
9 import subprocess
10 import os.path
11 import os
12 import shutil
13
14 def ejecucion( self ) :
15 ENTORNO=self.parent().labelEstado.text() #Obtenemos de la interfaz de bienvenida el estado del interruptor
16 if ’Windows’ in ENTORNO:
17 subprocess. call ( ’grwave <entrada.dat >grwave.out’, shell =True)
18 return True
19 elif ’MSDOS’ in ENTORNO:
20 homedir = os.path.expanduser("~") #Obtenemos nombre de carpeta de usuario extendida
21 direccion=homedir+"\AppData\Local\DOSBox" #le aadimos el resto de direccion donde necesitamos
22 #que exista un fichero de configuracion de DOSBox
23 os . makedirs(direccion, exist_ok=True) #Creamos la carpeta, pero que no de fallo si ya existe
24 shutil . copy("dosbox−0.74−3.conf", direccion) #copiamos el archivo a la direccion
25 subprocess. call ( ’DOSBox−0.74−3\DOSBox’, shell=True) #llamamos al programa DOSBox
26 fnew=open("grwave.out","r")
27 contenedor=fnew.read()
28 if ("mode" in contenedor):
29 self . lanzarWarning("No disponible en MS−DOS. \n Elija terminal de Windows.")
30 return False
31 else :
32 return True
33 fnew.close ()
34
35 def lecturaSalida ( self , inicial ) :
36 #lee el archivo, salta primeras 31 filas
37 fo = open("grwave.out", "r")
38 if ( inicial ==0): #se llama a GRWAVE 4 veces por cada grafica (por temas de escala logaritmica)
58 Appendix A. Código fuente

39 #asi que si no es la primera de esas 4, se abre el archivo continuar escribiendo al final , sin borrar
nada
40 f = open ("grwave2.out", "a")
41 elif ( inicial ==1): #si es la primera de esas 4, abre el archivo para empezar a escribir desde cero
42 f = open ("grwave2.out", "w")
43 iteracion =(linea for i , linea in enumerate(fo) if i>=32) #descarta del grwave.out las primeras 31 lineas
porque ese texto no vale
44 for linea in iteracion :
45 f . write( linea )
46
47 f . close ()
48 fo . close ()
49
50
51
52 def leerGRW2(archivoSal,tipo):
53 #se lee el archivo y se separa en columnas: index, fs y pathloss , tratando los datos como data, con libreria
pandas
54 data = pd.read_csv(archivoSal, sep=r’\s+’, index_col=0, names=[’fs’, ’ pathloss ’ ])
55 data.dropna(how=’all’, axis=0, inplace=True) #elimina los renglones que contengan columna vacia
56 data = data.drop(data[data[’ fs ’]=="∗∗∗∗∗∗∗"].index) #elimina renglones que tengan alguna columna con
asteriscos
57 data = data.drop(data[data[’ fs ’]=="0.00"].index) #elimina los elementos cuyo campo electrico sea 0.00, porque
a veces GRWAVE devolvia eso y no lo queremos
58 data = data.drop(data[data[’pathloss ’]=="0.00"].index) #elimina los elementos cuyas perdidas sea 0.00
59 if ( tipo==2):
60 data = data.drop(data[data.index=="∗∗∗∗∗∗∗"].index) #a veces tambien aparecian asteriscos en el indice ,
solo cuando calculamos un punto de distancia especifico demasiado alto
61 data = data[~data.index.duplicated(keep=’ first ’ ) ] #elimina los renglones duplicados, manteniendo el primero (
a veces GRWAVE repetia varios)
62 data.index = data.index.astype( float ) #convierte columna indice a float
63 d_km = data.index.values.astype( float ) #convierte columna distancia a float
64 data[’ fs ’ ] = data[’ fs ’ ]. astype( float )
65 intensidad=data[’fs ’ ]. values . astype( float ) #convierte columna campo electrico a float
66 perdidas=data[’pathloss’ ]. values . astype( float ) #convierte perdidas a float
67 return d_km, intensidad, perdidas

popUpWarning.py
1 # −− coding: utf−8 −−
2
3 # Form implementation generated from reading ui file ’popUpWarning.ui’
4 #
5 # Created by: PyQt5 UI code generator 5.13.0
6 #
7 # WARNING! All changes made in this file will be lost !
8
9
10 from PyQt5 import QtCore, QtGui, QtWidgets
11 from PyQt5.QtGui import QIcon
12
13 class Ui_popUp(object):
14 def setupUi( self , popUp):
15 popUp.setObjectName("popUp")
16 popUp.resize(271, 112)
17 popUp.setStyleSheet(" #popUp{\n"
18 " background−color: rgb(255, 255, 255);\n"
19 " }")
20 self . centralwidget = QtWidgets.QWidget(popUp)
21 self . centralwidget . setObjectName("centralwidget")
22 self . labelError = QtWidgets.QLabel(self.centralwidget)
23 self . labelError.setGeometry(QtCore.QRect(10, 40, 251, 41))
24 self . labelError.setAlignment(QtCore.Qt.AlignCenter)
25 self . labelError.setObjectName("labelError")
26 self . iconWarning = QtWidgets.QLabel(self.centralwidget)
27 self . iconWarning.setGeometry(QtCore.QRect(120, 10, 31, 31))
28 self . iconWarning.setText("")
29 self . iconWarning.setPixmap(QtGui.QPixmap("warning.png"))
59

30 self . iconWarning.setScaledContents(True)
31 self . iconWarning.setObjectName("iconWarning")
32 popUp.setCentralWidget(self.centralwidget)
33 self . menubar = QtWidgets.QMenuBar(popUp)
34 self . menubar.setGeometry(QtCore.QRect(0, 0, 271, 21))
35 self . menubar.setObjectName("menubar")
36 popUp.setMenuBar(self.menubar)
37 self . statusbar = QtWidgets.QStatusBar(popUp)
38 self . statusbar . setObjectName("statusbar")
39 popUp.setStatusBar(self . statusbar)
40
41 self . retranslateUi (popUp)
42 QtCore.QMetaObject.connectSlotsByName(popUp)
43
44 def retranslateUi ( self , popUp):
45 _translate = QtCore.QCoreApplication.translate
46 popUp.setWindowTitle(_translate("popUp", "Warning"))
47 self . labelError. setText ( _translate ("popUp", "Distancia fuera de rango"))
48
49
50 if __name__ == "__main__":
51 import sys
52 app = QtWidgets.QApplication(sys.argv)
53 popUp = QtWidgets.QMainWindow()
54 ui = Ui_popUp()
55 ui . setupUi(popUp)
56 popUp.show()
57 sys . exit (app.exec_() )

mplwidget.py
1 # −− coding: utf−8 −−
2 """
3 Created on Sat Apr 4 22:04:59 2020
4
5 author: Javier Ojeda Prieto
6 """
7 from PyQt5 import QtCore, QtGui, QtWidgets
8 from PyQt5.QtWidgets import∗
9 from matplotlib.backends.backend_qt5agg import FigureCanvas
10
11 from matplotlib. figure import Figure
12
13
14 class MplWidget(QWidget):
15
16 def __init__ ( self , parent = None):
17
18 QWidget.__init__(self , parent)
19
20 self . canvas = FigureCanvas(Figure())
21
22 vertical_layout = QVBoxLayout()
23 vertical_layout . addWidget(self.canvas)
24
25 self . canvas.axes = self . canvas. figure . add_subplot(111,position =[0.15, 0.17, 0.75, 0.75])
26 self . setLayout( vertical_layout )

info.py
1 # −− coding: utf−8 −−
2
3 # Form implementation generated from reading ui file ’popUpWarning.ui’
4 #
5 # Created by: PyQt5 UI code generator 5.13.0
60 Appendix A. Código fuente

6 #
7 # WARNING! All changes made in this file will be lost !
8
9
10 from PyQt5 import QtCore, QtGui, QtWidgets
11 from PyQt5.QtGui import QIcon
12
13 class Ui_Info(object ) :
14 def setupUi( self , popUp):
15 popUp.setObjectName("popUp2")
16 popUp.resize(546, 249)
17 popUp.setStyleSheet(" #popUp2{\n"
18 " background−color: rgb(255, 255, 255);\n"
19 " }")
20 self . centralwidget = QtWidgets.QWidget(popUp)
21 self . centralwidget . setObjectName("centralwidget")
22 self . iconWarning = QtWidgets.QLabel(self.centralwidget)
23 self . iconWarning.setGeometry(QtCore.QRect(0, 0, 546, 232))
24 self . iconWarning.setText("")
25 self . iconWarning.setPixmap(QtGui.QPixmap("TablaTerrenos.png"))
26 self . iconWarning.setScaledContents(True)
27 popUp.setCentralWidget(self.centralwidget)
28 self . menubar = QtWidgets.QMenuBar(popUp)
29 self . menubar.setGeometry(QtCore.QRect(0, 0, 271, 21))
30 self . menubar.setObjectName("menubar")
31 popUp.setMenuBar(self.menubar)
32 self . statusbar = QtWidgets.QStatusBar(popUp)
33 self . statusbar . setObjectName("statusbar")
34 popUp.setStatusBar(self . statusbar)
35
36 self . retranslateUi (popUp)
37 QtCore.QMetaObject.connectSlotsByName(popUp)
38
39 def retranslateUi ( self , popUp):
40 _translate = QtCore.QCoreApplication.translate
41 popUp.setWindowTitle(_translate("popUp2", "Características de los terrenos"))
42
43
44 if __name__ == "__main__":
45 import sys
46 app = QtWidgets.QApplication(sys.argv)
47 popUp = QtWidgets.QMainWindow()
48 ui = Ui_Info()
49 ui . setupUi(popUp)
50 popUp.show()
51 sys . exit (app.exec_() )

freqWindow.py
1 # −− coding: utf−8 −−
2
3 # Form implementation generated from reading ui file ’popUpWarning.ui’
4 #
5 # Created by: PyQt5 UI code generator 5.13.0
6 #
7 # WARNING! All changes made in this file will be lost !
8
9
10 from PyQt5 import QtCore, QtGui, QtWidgets
11 from PyQt5.QtGui import QIcon
12
13 class Ui_Freq(object):
14 def setupUi( self , popUp):
15 popUp.setObjectName("popUpFreq")
16 popUp.resize(316, 189)
17 popUp.setStyleSheet(" #popUp2{\n"
18 " background−color: rgb(255, 255, 255);\n"
19 " }")
61

20 self . centralwidget = QtWidgets.QWidget(popUp)


21 self . centralwidget . setObjectName("centralwidget")
22 self . freqLabel = QtWidgets.QLabel(self.centralwidget)
23 self . freqLabel.setGeometry(QtCore.QRect(0, 0, 316, 179))
24 self . freqLabel.setAlignment(QtCore.Qt.AlignCenter)
25 self . freqLabel.setText ("El cálculo de una curva se realiza con \n un único valor de frecuencia , con lo
cual , \n"
26 "la selecci ón de múltiples frecuencias \ngenerará varias curvas independientes. \n
\n"
27 "Es posible que la ejecuci ón de una gráfica \n con más de una distinci ón contenga
retardo. \n \n"
28 "El uso de frecuencias está restringido \n para el rango entre 10 kHz y 30 MHz.")
29 popUp.setCentralWidget(self.centralwidget)
30 self . menubar = QtWidgets.QMenuBar(popUp)
31 self . menubar.setGeometry(QtCore.QRect(0, 0, 271, 21))
32 self . menubar.setObjectName("menubar")
33 popUp.setMenuBar(self.menubar)
34 self . statusbar = QtWidgets.QStatusBar(popUp)
35 self . statusbar . setObjectName("statusbar")
36 popUp.setStatusBar(self . statusbar)
37
38 self . retranslateUi (popUp)
39 QtCore.QMetaObject.connectSlotsByName(popUp)
40
41 def retranslateUi ( self , popUp):
42 _translate = QtCore.QCoreApplication.translate
43 popUp.setWindowTitle(_translate("popUpFreq", "Información sobre frecuencias"))
44
45
46 if __name__ == "__main__":
47 import sys
48 app = QtWidgets.QApplication(sys.argv)
49 popUp = QtWidgets.QMainWindow()
50 ui = Ui_Freq()
51 ui . setupUi(popUp)
52 popUp.show()
53 sys . exit (app.exec_() )

ventanaCalculos.py
1 # −− coding: utf−8 −−
2 """
3 Created on Wed Apr 1 00:16:31 2020
4
5 author: Javier Ojeda Prieto
6 """
7 import sys
8 import math
9 import os
10 import subprocess
11 import numpy as np
12 from interfazCalculosStyle import ∗
13 from lectura2 import ∗
14 import pyqtgraph as pg
15 from popUpWarning import Ui_popUp
16 from info import Ui_Info
17 from freqWindow import Ui_Freq
18 from matplotlib.backends.backend_qt5agg import (NavigationToolbar2QT as NavigationToolbar)
19 from matplotlib. figure import Figure
20 from matplotlib.backends.backend_qt5agg import FigureCanvas
21 from PyQt5 import QtWidgets, QtCore, QtGui
22 from PyQt5.QtGui import QIcon, QSystemTrayIcon, QMenu
23 from PyQt5.QtWidgets import ∗
24 from PyQt5.QtCore import QFileInfo
25
26 class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
27 def __init__ ( self , parent, ∗args, ∗∗kwargs):
28 super(MainWindow, self).__init__(parent)
62 Appendix A. Código fuente

29 ultimo=9998
30 # ensure this window gets garbage−collected when closed
31 self . setAttribute (QtCore.Qt.WA_DeleteOnClose)
32 QtWidgets.QMainWindow.__init__(self, parent, ∗args, ∗∗kwargs)
33 self . setupUi( self )
34 self . setWindowIcon(QIcon(’iconn.png’))
35
36 self . setWindowFlags(QtCore.Qt.WindowCloseButtonHint) #Elimina las flags (boton cerrar, minimizar,
maximizar superior)
37 self . comboBox_2.activated.connect(self.comprobarFrecuencias)
38 self . comboBox_3.activated.connect(self.comprobarTerrenos)
39 self . comboBox_4.activated.connect(self.actualizaTerreno1)
40 self . comboBox_5.activated.connect(self.actualizaTerreno2)
41 self . comboBox_6.activated.connect(self.actualizaTerreno3)
42 self . comboBox_7.activated.connect(self.actualizaTerreno4)
43 self . comboBox.activated.connect(self . actualizaTerreno)
44 self . button_iniciar . clicked . connect( self . secuencia)
45 self . button_iniciar_2 . clicked . connect( self . secuenciaMixta)
46 self . button_limpiar. clicked . connect( self . clearr )
47 self . button_limpiar_2.clicked . connect( self . clearr_2)
48 self . pushButton_2.clicked.connect( self . calculaDistancia )
49 self . button_cerrar. clicked . connect( self . regresar)
50 self . button_cerrar_2.clicked . connect( self . regresar)
51 self . button_exportar.clicked . connect( self . saveFig1)
52 self . button_exportar_2.clicked . connect( self . saveFig2)
53 self . pushButton_7.clicked.connect( self . informacion)
54 self . pushButton.clicked.connect( self . informacion)
55 self . pushButton_3.clicked.connect( self . infoFreq)
56
57
58 ##################################################################################
59 # FUNCIONES TERRENO HOMOGENEO
60
61 def infoFreq( self ) : #Abre informacion sobre uso de multiples frecuencias
62 self . ventanaFreq=QtWidgets.QMainWindow()
63 self . ui=Ui_Freq()
64 self . ui . setupUi( self . ventanaFreq)
65 self . ventanaFreq.setWindowFlags(QtCore.Qt.WindowCloseButtonHint);
66 self . ventanaFreq.setWindowIcon(QIcon("iconn.png"))
67 self . ventanaFreq.show()
68
69 def regresar( self ) : #Funcion usada en ambas pestaas
70 self . close () #cierra ventana actual de calculos
71 self . parent() . show() #vuelve a mostrar ventana de bienvenida, padre de la actual
72
73 def inhabilitaSigEps ( self ) : #cuando se ha seleccionado un terreno predeterminado, las casillas de sigma y
epsilon se configuran para solo lectura
74 self . SIGMA.setReadOnly(True)
75 self . EPSLON.setReadOnly(True)
76 self . SIGMA.setStyleSheet("background−color: rgb(235, 235, 235);")
77 self . EPSLON.setStyleSheet("background−color: rgb(235, 235, 235);")
78
79 def actualizaTerreno( self ) : #En funcion del valor seleccionado del desplegable de terrenos , actualiza casillas
de sigma y epsilon
80 terreno = self . comboBox.currentText()
81 if (terreno=="Agua del mar, salinidad baja"):
82 self . SIGMA.setText("1")
83 self . EPSLON.setText("80")
84 self . inhabilitaSigEps () #como es un terreno predeterminado, se ponen casillas de sigma y epsilon en
modo solo lectura
85 elif (terreno=="Agua del mar, salinidad media"):
86 self . SIGMA.setText("5")
87 self . EPSLON.setText("70")
88 self . inhabilitaSigEps ()
89 elif (terreno=="Agua dulce"):
90 self . SIGMA.setText("0.003")
91 self . EPSLON.setText("80")
92 self . inhabilitaSigEps ()
93 elif (terreno=="Tierra"):
94 self . SIGMA.setText("0.03")
95 self . EPSLON.setText("40")
63

96 self . inhabilitaSigEps ()
97 elif (terreno=="Tierra húmeda"):
98 self . SIGMA.setText("0.01")
99 self . EPSLON.setText("30")
100 self . inhabilitaSigEps ()
101 elif (terreno=="Tierra moderadamente seca"):
102 self . SIGMA.setText("0.001")
103 self . EPSLON.setText("15")
104 self . inhabilitaSigEps ()
105 elif (terreno=="Tierra seca"):
106 self . SIGMA.setText("0.0003")
107 self . EPSLON.setText("7")
108 self . inhabilitaSigEps ()
109 elif (terreno=="Tierra muy seca"):
110 self . SIGMA.setText("0.0001")
111 self . EPSLON.setText("3")
112 self . inhabilitaSigEps ()
113 elif (terreno=="Hielo de agua dulce, −1C"):
114 self . SIGMA.setText("0.00003")
115 self . EPSLON.setText("3")
116 self . inhabilitaSigEps ()
117 elif (terreno=="Hielo de agua dulce, −10C"):
118 self . SIGMA.setText("−0.00001")
119 self . EPSLON.setText("3")
120 self . inhabilitaSigEps ()
121 elif (terreno=="Personalizado"):
122 self . SIGMA.setText("")
123 self . EPSLON.setText("")
124 self . SIGMA.setReadOnly(False) #se desactiva modo solo lectura para sigma y epsilon
125 self . EPSLON.setReadOnly(False)
126 self . SIGMA.setStyleSheet("background−color: rgb(255, 255, 255);")
127 self . EPSLON.setStyleSheet("background−color: rgb(255, 255, 255);")
128
129 def comprobarFrecuencias(self): #comprueba que numero de frecuencias se ha seleccionado en el desplegable , y
oculta o muestra las casillas necesarias para ello
130 comboText = self . comboBox_2.currentText()
131 if (comboText=="1"):
132 self . FREQ_2.setVisible(False) #oculta casilla de frecuencia 2
133 self . label_11 . setVisible (False) #oculta etiqueta de frecuencia 2
134 self . label_12 . setVisible (False) #oculta etiqueta de MHz de la frecuencia 2
135 self . FREQ_3.setVisible(False) #igual , para frecuencia 3
136 self . label_15 . setVisible (False)
137 self . label_17 . setVisible (False)
138 self . FREQ_4.setVisible(False)
139 self . label_16 . setVisible (False)
140 self . label_18 . setVisible (False)
141 self . FREQ_5.setVisible(False)
142 self . label_19 . setVisible (False)
143 self . label_21 . setVisible (False)
144 self . FREQ_6.setVisible(False)
145 self . label_20 . setVisible (False)
146 self . label_23 . setVisible (False)
147 self . FREQ_7.setVisible(False)
148 self . label_25 . setVisible (False)
149 self . label_26 . setVisible (False)
150 self . FREQ_8.setVisible(False)
151 self . label_24 . setVisible (False)
152 self . label_22 . setVisible (False)
153 self . lineEdit . setReadOnly(False) #como es solo una frecuencia , activa lo necesario para calcular la
distancia
154 self . lineEdit . setStyleSheet ("background−color: rgb(255, 255, 255);")
155 self . lineEdit_2 . setStyleSheet ("background−color: rgb(255, 255, 255);")
156 self . lineEdit_3 . setStyleSheet ("background−color: rgb(255, 255, 255);")
157 elif (comboText=="2"):
158 self . FREQ_2.setVisible(True)
159 self . label_11 . setVisible (True)
160 self . label_12 . setVisible (True)
161 self . FREQ_3.setVisible(False)
162 self . label_15 . setVisible (False)
163 self . label_17 . setVisible (False)
164 self . FREQ_4.setVisible(False)
64 Appendix A. Código fuente

165 self . label_16 . setVisible (False)


166 self . label_18 . setVisible (False)
167 self . FREQ_5.setVisible(False)
168 self . label_19 . setVisible (False)
169 self . label_21 . setVisible (False)
170 self . FREQ_6.setVisible(False)
171 self . label_20 . setVisible (False)
172 self . label_23 . setVisible (False)
173 self . FREQ_7.setVisible(False)
174 self . label_25 . setVisible (False)
175 self . label_26 . setVisible (False)
176 self . FREQ_8.setVisible(False)
177 self . label_24 . setVisible (False)
178 self . label_22 . setVisible (False)
179 self . ocultaDistancia () #oculta el calculo de distancia porque a mas de una frecuencia no esta
disponible
180 elif (comboText=="3"):
181 self . FREQ_2.setVisible(True)
182 self . label_11 . setVisible (True)
183 self . label_12 . setVisible (True)
184 self . FREQ_3.setVisible(True)
185 self . label_15 . setVisible (True)
186 self . label_17 . setVisible (True)
187 self . FREQ_4.setVisible(False)
188 self . label_16 . setVisible (False)
189 self . label_18 . setVisible (False)
190 self . FREQ_5.setVisible(False)
191 self . label_19 . setVisible (False)
192 self . label_21 . setVisible (False)
193 self . FREQ_6.setVisible(False)
194 self . label_20 . setVisible (False)
195 self . label_23 . setVisible (False)
196 self . FREQ_7.setVisible(False)
197 self . label_25 . setVisible (False)
198 self . label_26 . setVisible (False)
199 self . FREQ_8.setVisible(False)
200 self . label_24 . setVisible (False)
201 self . label_22 . setVisible (False)
202 self . ocultaDistancia ()
203 elif (comboText=="4"):
204 self . FREQ_2.setVisible(True)
205 self . label_11 . setVisible (True)
206 self . label_12 . setVisible (True)
207 self . FREQ_3.setVisible(True)
208 self . label_15 . setVisible (True)
209 self . label_17 . setVisible (True)
210 self . FREQ_4.setVisible(True)
211 self . label_16 . setVisible (True)
212 self . label_18 . setVisible (True)
213 self . FREQ_5.setVisible(False)
214 self . label_19 . setVisible (False)
215 self . label_21 . setVisible (False)
216 self . FREQ_6.setVisible(False)
217 self . label_20 . setVisible (False)
218 self . label_23 . setVisible (False)
219 self . FREQ_7.setVisible(False)
220 self . label_25 . setVisible (False)
221 self . label_26 . setVisible (False)
222 self . FREQ_8.setVisible(False)
223 self . label_24 . setVisible (False)
224 self . label_22 . setVisible (False)
225 self . ocultaDistancia ()
226 elif (comboText=="5"):
227 self . FREQ_2.setVisible(True)
228 self . label_11 . setVisible (True)
229 self . label_12 . setVisible (True)
230 self . FREQ_3.setVisible(True)
231 self . label_15 . setVisible (True)
232 self . label_17 . setVisible (True)
233 self . FREQ_4.setVisible(True)
234 self . label_16 . setVisible (True)
65

235 self . label_18 . setVisible (True)


236 self . FREQ_5.setVisible(True)
237 self . label_19 . setVisible (True)
238 self . label_21 . setVisible (True)
239 self . FREQ_6.setVisible(False)
240 self . label_20 . setVisible (False)
241 self . label_23 . setVisible (False)
242 self . FREQ_7.setVisible(False)
243 self . label_25 . setVisible (False)
244 self . label_26 . setVisible (False)
245 self . FREQ_8.setVisible(False)
246 self . label_24 . setVisible (False)
247 self . label_22 . setVisible (False)
248 self . ocultaDistancia ()
249 elif (comboText=="6"):
250 self . FREQ_2.setVisible(True)
251 self . label_11 . setVisible (True)
252 self . label_12 . setVisible (True)
253 self . FREQ_3.setVisible(True)
254 self . label_15 . setVisible (True)
255 self . label_17 . setVisible (True)
256 self . FREQ_4.setVisible(True)
257 self . label_16 . setVisible (True)
258 self . label_18 . setVisible (True)
259 self . FREQ_5.setVisible(True)
260 self . label_19 . setVisible (True)
261 self . label_21 . setVisible (True)
262 self . FREQ_6.setVisible(True)
263 self . label_20 . setVisible (True)
264 self . label_23 . setVisible (True)
265 self . FREQ_7.setVisible(False)
266 self . label_25 . setVisible (False)
267 self . label_26 . setVisible (False)
268 self . FREQ_8.setVisible(False)
269 self . label_24 . setVisible (False)
270 self . label_22 . setVisible (False)
271 self . ocultaDistancia ()
272 elif (comboText=="7"):
273 self . FREQ_2.setVisible(True)
274 self . label_11 . setVisible (True)
275 self . label_12 . setVisible (True)
276 self . FREQ_3.setVisible(True)
277 self . label_15 . setVisible (True)
278 self . label_17 . setVisible (True)
279 self . FREQ_4.setVisible(True)
280 self . label_16 . setVisible (True)
281 self . label_18 . setVisible (True)
282 self . FREQ_5.setVisible(True)
283 self . label_19 . setVisible (True)
284 self . label_21 . setVisible (True)
285 self . FREQ_6.setVisible(True)
286 self . label_20 . setVisible (True)
287 self . label_23 . setVisible (True)
288 self . FREQ_7.setVisible(True)
289 self . label_25 . setVisible (True)
290 self . label_26 . setVisible (True)
291 self . FREQ_8.setVisible(False)
292 self . label_24 . setVisible (False)
293 self . label_22 . setVisible (False)
294 self . ocultaDistancia ()
295 elif (comboText=="8"):
296 self . FREQ_2.setVisible(True)
297 self . label_11 . setVisible (True)
298 self . label_12 . setVisible (True)
299 self . FREQ_3.setVisible(True)
300 self . label_15 . setVisible (True)
301 self . label_17 . setVisible (True)
302 self . FREQ_4.setVisible(True)
303 self . label_16 . setVisible (True)
304 self . label_18 . setVisible (True)
305 self . FREQ_5.setVisible(True)
66 Appendix A. Código fuente

306 self . label_19 . setVisible (True)


307 self . label_21 . setVisible (True)
308 self . FREQ_6.setVisible(True)
309 self . label_20 . setVisible (True)
310 self . label_23 . setVisible (True)
311 self . FREQ_7.setVisible(True)
312 self . label_25 . setVisible (True)
313 self . label_26 . setVisible (True)
314 self . FREQ_8.setVisible(True)
315 self . label_24 . setVisible (True)
316 self . label_22 . setVisible (True)
317 self . ocultaDistancia ()
318
319 def comprobarWarning(self): #se comprueba que ningun parametro introducido esta vacio, a excepcion de las
frecuencias 2−3−4−5−6−7−8
320 if ( self . HTT.text()=="" or self . HRR.text()=="" or self . SIGMA.text()==""
321 or self . EPSLON.text()=="" or self.FREQ.text()==""):
322 parar=True #si hay alguno vacio , se debe parar la ejecucion
323 self . lanzarWarning("Es necesario rellenar todos los campos") #se lanza ventana emergente con error
especificado
324 #Se comprueba que todos los valores introducidos estan en formato float , a excepcion de frecuencias
2−3−4−5−6−7−8
325 elif ( self . is_float ( self . HTT.text())==False or self . is_float ( self . HRR.text())==False or
326 self . is_float ( self . SIGMA.text())==False or self . is_float ( self . EPSLON.text())==False or
327 self . is_float ( self . FREQ.text())==False):
328 parar=True
329 self . lanzarWarning("Los parámetros deben ser formato float")
330
331 elif ( float ( self . FREQ.text())>30 or float ( self . FREQ.text())<0.01): #se comprueba que la frecuencia 1 esta
dentro del rango (10 kHz−30 MHz)
332 self . lanzarWarning("La frecuencia debe estar entre \n 10 KHz y 30 MHz")
333 parar=True
334 else : #si no hay errores , no se para la ejecucion
335 parar=False
336 return parar
337
338 def lanzarWarning(self,texto ) : #Usada en ambas pestaas . Muestra ventanas emergentes para notificar errores .
339 self . ventana=QtWidgets.QMainWindow() #crea un QMainWindow
340 self . ui=Ui_popUp() #crea un objeto con el constructor de Ui_popUp
341 self . ui . setupUi( self . ventana) #a ese objeto le aplica la configuracion setupUi, pasandole la ventana a la
que se lo aplicará
342 self . ui . labelError. setText ( texto ) #a la etiqueta de error le introduce el texto que se quiere mostrar
como error
343 self . ventana.setWindowFlags(QtCore.Qt.WindowCloseButtonHint); #solo el boton de cerrar esta disponible, ni
minimizar ni maximizar
344 self . ventana.setWindowIcon(QIcon("warning.png")) #icono de ventana, el de la aplicacion
345 self . ventana.show() #muestra la ventana
346
347 def calculaRango(self , frecuencia) : #Usada en ambas pestaas . Comprueba que el rango de frecuencia contiene
a la frecuencia introducida
348 if ( float (frecuencia)>30 or float (frecuencia)<0.01):
349 return 1
350 else :
351 return 0
352
353 def compruebaAltura(self,HRR,SIGMA,EPSLON,FREQ): #Usada en ambas pestaas
354 #Comprueba que la altura de antena receptora cumpla dos condiciones. Vease pagina 3 de Rec. P.368−9 de la
ITU−R
355 c=300
356 landa=c/FREQ
357 cond1=60∗landa∗SIGMA
358 cond2=1.2∗math.sqrt(SIGMA)∗math.sqrt(landa∗landa∗landa)
359 if (100∗EPSLON<cond1 and HRR>=cond2):
360 return True
361 else :
362 return False
363
364 def is_float ( self , cadena): #Usada en ambas pestaas
365 try:
366 float (cadena)
367 return True #si ha ido bien la conversion, es float y se devuelve True
67

368 except ValueError:


369 return False #si ha ido mal la conversion, no es float y se devuelve False
370
371 def secuencia( self ) : #en funcion de cuantas frecuencias se hayan seleccionado, se obtendran los valores de
esas frecuencias y se llamara a Generar datos para grafica
372 #tantas veces como numero de frecuencias se hayan seleccionado. Para que cada curva sea independiente
373 self . clearr () #se limpia grafica anterior , para evitar mezclar resultados y que el usuario no tenga que
limpiarla manualmente
374 comboText = self . comboBox_2.currentText()
375 parar=self . comprobarWarning() #comprueba que no existen errores principales de rango de frecuencia,
parametros incompletos ...
376 if (parar==False): #si todo ha ido bien en la comprobacion anterior
377 if (comboText=="1"): #si el numero de frecuencias es 1
378 self . lineEdit . setReadOnly(False) #activa el calculo de distancia
379 self . lineEdit . setStyleSheet ("background−color: rgb(255, 255, 255);")
380 self . lineEdit_2 . setStyleSheet ("background−color: rgb(255, 255, 255);")
381 self . lineEdit_3 . setStyleSheet ("background−color: rgb(255, 255, 255);")
382 FREQ=self.FREQ.text() #selecciona valor de frecuencia
383 Label1=’f=’+FREQ+’ MHz’ #prepara etiqueta para leyenda de grafica
384 self . generar(FREQ,’#0093FF’,Label1) #se llama a funcion que genera datos para grafica y su
representacion, con etiqueta y un color para esta frecuencia
385 elif (comboText=="2"): #si numero de frecuencias es 2
386 if ( self . FREQ_2.text()==""): #comprueba que la freq2 no esta vacia
387 self . lanzarWarning("Es necesario rellenar todos los campos")
388 elif ( self . is_float ( self . FREQ_2.text())==False): #comprueba que es un valor entero o decimal
389 self . lanzarWarning("Los parámetros deben ser formato float")
390 else :
391 if ( self . calculaRango(self . FREQ_2.text())==1): #comprueba su rango
392 self . lanzarWarning("Las frecuencias deben estar entre \n 10 KHz y 30 MHz")
393 else :
394 FREQ=self.FREQ.text() #obtiene valores de frecuencias 1 y 2, con sus etiquetas y colores ,
y se generan sus curvas en la grafica
395 Label1=’f=’+FREQ+’ MHz’
396 self . generar(FREQ,’#0093FF’,Label1)
397 FREQ=self.FREQ_2.text()
398 Label1=’f=’+FREQ+’ MHz’
399 self . generar(FREQ, ’#FFB726’,Label1)
400 elif (comboText=="3"): #se repite todo el proceso recogiendo y comprobando los datos de tres
frecuencias
401 if ( self . FREQ_2.text()=="" or self.FREQ_3.text()==""):
402 self . lanzarWarning("Es necesario rellenar todos los campos")
403 elif ( self . is_float ( self . FREQ_2.text())==False or self . is_float ( self . FREQ_3.text())==False):
404 self . lanzarWarning("Los parámetros deben ser formato float")
405 else :
406 if ( self . calculaRango(self . FREQ_2.text())==1 or self . calculaRango(self . FREQ_3.text())==1):
407 self . lanzarWarning("Las frecuencias deben estar entre \n 10 KHz y 30 MHz")
408 else :
409 FREQ=self.FREQ.text()
410 Label1=’f=’+FREQ+’ MHz’
411 self . generar(FREQ,’#0093FF’,Label1)
412 FREQ=self.FREQ_2.text()
413 Label1=’f=’+FREQ+’ MHz’
414 self . generar(FREQ, ’#FFB726’,Label1)
415 FREQ=self.FREQ_3.text()
416 Label1=’f=’+FREQ+’ MHz’
417 self . generar(FREQ,’#60C146’,Label1)
418 elif (comboText=="4"):
419 if ( self . FREQ_2.text()=="" or self.FREQ_3.text()=="" or self.FREQ_4.text()==""):
420 self . lanzarWarning("Es necesario rellenar todos los campos")
421 elif ( self . is_float ( self . FREQ_2.text())==False or self . is_float ( self . FREQ_3.text())==False
422 or self . is_float ( self . FREQ_4.text())==False):
423 self . lanzarWarning("Los parámetros deben ser formato float")
424 else :
425 if ( self . calculaRango(self . FREQ_2.text())==1 or self . calculaRango(self . FREQ_3.text())==1
426 or self . calculaRango(self . FREQ_4.text())==1):
427 self . lanzarWarning("Las frecuencias deben estar entre \n 10 KHz y 30 MHz")
428 else :
429 FREQ=self.FREQ.text()
430 Label1=’f=’+FREQ+’ MHz’
431 self . generar(FREQ,’#0093FF’,Label1)
432 FREQ=self.FREQ_2.text()
68 Appendix A. Código fuente

433 Label1=’f=’+FREQ+’ MHz’


434 self . generar(FREQ, ’#FFB726’,Label1)
435 FREQ=self.FREQ_3.text()
436 Label1=’f=’+FREQ+’ MHz’
437 self . generar(FREQ,’#60C146’,Label1)
438 FREQ=self.FREQ_4.text()
439 Label1=’f=’+FREQ+’ MHz’
440 self . generar(FREQ,’#BE54C7’,Label1)
441 elif (comboText=="5"):
442 if ( self . FREQ_2.text()=="" or self.FREQ_3.text()=="" or self.FREQ_4.text()==""
443 or self . FREQ_5.text()==""):
444 self . lanzarWarning("Es necesario rellenar todos los campos")
445 elif ( self . is_float ( self . FREQ_2.text())==False or self . is_float ( self . FREQ_3.text())==False
446 or self . is_float ( self . FREQ_4.text())==False or self . is_float ( self . FREQ_5.text())==False):
447 self . lanzarWarning("Los parámetros deben ser formato float")
448 else :
449 if ( self . calculaRango(self . FREQ_2.text())==1 or self . calculaRango(self . FREQ_3.text())==1
450 or self . calculaRango(self . FREQ_4.text())==1 or self . calculaRango(self . FREQ_5.text())==1):
451 self . lanzarWarning("Las frecuencias deben estar entre \n 10 KHz y 30 MHz")
452 else :
453 FREQ=self.FREQ.text()
454 Label1=’f=’+FREQ+’ MHz’
455 self . generar(FREQ,’#0093FF’,Label1)
456 FREQ=self.FREQ_2.text()
457 Label1=’f=’+FREQ+’ MHz’
458 self . generar(FREQ, ’#FFB726’,Label1)
459 FREQ=self.FREQ_3.text()
460 Label1=’f=’+FREQ+’ MHz’
461 self . generar(FREQ,’#60C146’,Label1)
462 FREQ=self.FREQ_4.text()
463 Label1=’f=’+FREQ+’ MHz’
464 self . generar(FREQ,’#BE54C7’,Label1)
465 FREQ=self.FREQ_5.text()
466 Label1=’f=’+FREQ+’ MHz’
467 self . generar(FREQ,’#50DCA5’,Label1)
468 elif (comboText=="6"):
469 if ( self . FREQ_2.text()=="" or self.FREQ_3.text()=="" or self.FREQ_4.text()==""
470 or self . FREQ_5.text()=="" or self.FREQ_6.text()==""):
471 self . lanzarWarning("Es necesario rellenar todos los campos")
472 elif ( self . is_float ( self . FREQ_2.text())==False or self . is_float ( self . FREQ_3.text())==False
473 or self . is_float ( self . FREQ_4.text())==False or self . is_float ( self . FREQ_5.text())==False
474 or self . is_float ( self . FREQ_6.text())==False):
475 self . lanzarWarning("Los parámetros deben ser formato float")
476 else :
477 if ( self . calculaRango(self . FREQ_2.text())==1 or self . calculaRango(self . FREQ_3.text())==1
478 or self . calculaRango(self . FREQ_4.text())==1 or self . calculaRango(self . FREQ_5.text())==1
479 or self . calculaRango(self . FREQ_6.text())==1):
480 self . lanzarWarning("Las frecuencias deben estar entre \n 10 KHz y 30 MHz")
481 else :
482 FREQ=self.FREQ.text()
483 Label1=’f=’+FREQ+’ MHz’
484 self . generar(FREQ,’#0093FF’,Label1)
485 FREQ=self.FREQ_2.text()
486 Label1=’f=’+FREQ+’ MHz’
487 self . generar(FREQ, ’#FFB726’,Label1)
488 FREQ=self.FREQ_3.text()
489 Label1=’f=’+FREQ+’ MHz’
490 self . generar(FREQ,’#60C146’,Label1)
491 FREQ=self.FREQ_4.text()
492 Label1=’f=’+FREQ+’ MHz’
493 self . generar(FREQ,’#BE54C7’,Label1)
494 FREQ=self.FREQ_5.text()
495 Label1=’f=’+FREQ+’ MHz’
496 self . generar(FREQ,’#50DCA5’,Label1)
497 FREQ=self.FREQ_6.text()
498 Label1=’f=’+FREQ+’ MHz’
499 self . generar(FREQ,’#808080’,Label1)
500 elif (comboText=="7"):
501 if ( self . FREQ_2.text()=="" or self.FREQ_3.text()=="" or self.FREQ_4.text()==""
502 or self . FREQ_5.text()=="" or self.FREQ_6.text()=="" or self.FREQ_7.text()==""):
503 self . lanzarWarning("Es necesario rellenar todos los campos")
69

504 else :
505 if ( self . calculaRango(self . FREQ_2.text())==1 or self . calculaRango(self . FREQ_3.text())==1
506 or self . calculaRango(self . FREQ_4.text())==1 or self . calculaRango(self . FREQ_5.text())==1
507 or self . calculaRango(self . FREQ_6.text())==1 or self . calculaRango(self . FREQ_7.text())==1):
508 self . lanzarWarning("Las frecuencias deben estar entre \n 10 KHz y 30 MHz")
509 elif ( self . is_float ( self . FREQ_2.text())==False or self . is_float ( self . FREQ_3.text())==False
510 or self . is_float ( self . FREQ_4.text())==False or self . is_float ( self . FREQ_5.text())==False
511 or self . is_float ( self . FREQ_6.text())==False or self . is_float ( self . FREQ_7.text())==False)
:
512 self . lanzarWarning("Los parámetros deben ser formato float")
513 else :
514 FREQ=self.FREQ.text()
515 Label1=’f=’+FREQ+’ MHz’
516 self . generar(FREQ,’#0093FF’,Label1)
517 FREQ=self.FREQ_2.text()
518 Label1=’f=’+FREQ+’ MHz’
519 self . generar(FREQ, ’#FFB726’,Label1)
520 FREQ=self.FREQ_3.text()
521 Label1=’f=’+FREQ+’ MHz’
522 self . generar(FREQ,’#60C146’,Label1)
523 FREQ=self.FREQ_4.text()
524 Label1=’f=’+FREQ+’ MHz’
525 self . generar(FREQ,’#BE54C7’,Label1)
526 FREQ=self.FREQ_5.text()
527 Label1=’f=’+FREQ+’ MHz’
528 self . generar(FREQ,’#50DCA5’,Label1)
529 FREQ=self.FREQ_6.text()
530 Label1=’f=’+FREQ+’ MHz’
531 self . generar(FREQ,’#808080’,Label1)
532 FREQ=self.FREQ_7.text()
533 Label1=’f=’+FREQ+’ MHz’
534 self . generar(FREQ,’#7C1A2F’,Label1)
535 elif (comboText=="8"):
536 if ( self . FREQ_2.text()=="" or self.FREQ_3.text()=="" or self.FREQ_4.text()==""
537 or self . FREQ_5.text()=="" or self.FREQ_6.text()==""
538 or self . FREQ_7.text()=="" or self.FREQ_8.text()==""):
539 self . lanzarWarning("Es necesario rellenar todos los campos")
540 elif ( self . is_float ( self . FREQ_2.text())==False or self . is_float ( self . FREQ_3.text())==False
541 or self . is_float ( self . FREQ_4.text())==False or self . is_float ( self . FREQ_5.text())==False
542 or self . is_float ( self . FREQ_6.text())==False or self . is_float ( self . FREQ_7.text())==False
543 or self . is_float ( self . FREQ_8.text())==False):
544 self . lanzarWarning("Los parámetros deben ser formato float")
545 else :
546 if ( self . calculaRango(self . FREQ_2.text())==1 or self . calculaRango(self . FREQ_3.text())==1
547 or self . calculaRango(self . FREQ_4.text())==1 or self . calculaRango(self . FREQ_5.text())==1
548 or self . calculaRango(self . FREQ_6.text())==1 or self . calculaRango(self . FREQ_7.text())==1
549 or self . calculaRango(self . FREQ_8.text())==1):
550 self . lanzarWarning("Las frecuencias deben estar entre \n 10 KHz y 30 MHz")
551 else :
552 FREQ=self.FREQ.text()
553 Label1=’f=’+FREQ+’ MHz’
554 self . generar(FREQ,’#0093FF’,Label1)
555 FREQ=self.FREQ_2.text()
556 Label1=’f=’+FREQ+’ MHz’
557 self . generar(FREQ, ’#FFB726’,Label1)
558 FREQ=self.FREQ_3.text()
559 Label1=’f=’+FREQ+’ MHz’
560 self . generar(FREQ,’#60C146’,Label1)
561 FREQ=self.FREQ_4.text()
562 Label1=’f=’+FREQ+’ MHz’
563 self . generar(FREQ,’#BE54C7’,Label1)
564 FREQ=self.FREQ_5.text()
565 Label1=’f=’+FREQ+’ MHz’
566 self . generar(FREQ,’#50DCA5’,Label1)
567 FREQ=self.FREQ_6.text()
568 Label1=’f=’+FREQ+’ MHz’
569 self . generar(FREQ,’#808080’,Label1)
570 FREQ=self.FREQ_7.text()
571 Label1=’f=’+FREQ+’ MHz’
572 self . generar(FREQ,’#7C1A2F’,Label1)
573 FREQ=self.FREQ_8.text()
70 Appendix A. Código fuente

574 Label1=’f=’+FREQ+’ MHz’


575 self . generar(FREQ,’#FF4711’,Label1)
576
577 def generar( self , FREQ,color,label):
578 global ultimo
579 HTT=self.HTT.text() #recoge textos de los parametros introducidos
580 HRR=self.HRR.text()
581 SIGMA=self.SIGMA.text()
582 EPSLON=self.EPSLON.text()
583 if ( self . compruebaAltura(float(HRR), float(SIGMA),float(EPSLON), float(FREQ)) == True): #comprueba que
la altura receptora cumple una restriccion de la ITU−R P.368
584 self . lanzarWarning("Cuando 60 , debe elegir altura \n antena receptora tal que h<
\n Fallo para frecuencia:"+FREQ+" MHz")
585 else :
586 if self . Horizontal.isChecked():
587 IPOLRN=’2’ #Para GRWAVE, 2 es horizontal y 1 es vertical en el parametro IPOLRN
588 elif self . Vertical . isChecked():
589 IPOLRN=’1’
590 #Se itera durante 4 veces . Cada curva será consecuencia de 4 llamadas a GRWAVE. Para que la escala
logaritmica del eje X, segmentada en 4 divisiones , tenga en cada
591 #una de ellas los mismos puntos (100), se ha hecho eso. Cada uno tiene un dstep distinto para
asegurar que hay 100 puntos en cada division .
592 for i in [0, 1, 2, 3]:
593 if i==0:
594 dmin=’1’
595 dmax=’10’
596 dstep=’0.1’
597 inicial =1; #marca si en el fichero de salida se debe empezar a escribir desde cero (por ser la
primera vez), o continuando al final del fichero sin borrar lo ya escrito
598 elif i==1:
599 dmin=’10’
600 dmax=’100’
601 dstep=’1’
602 inicial =0;
603 elif i==2:
604 dmin=’100’
605 dmax=’1000’
606 dstep=’10’
607 inicial =0;
608 elif i==3:
609 dmin=’1000’
610 dmax=’10000’
611 dstep=’100’
612 inicial =0;
613 self . rellenarEntrada(HTT,HRR,FREQ,SIGMA,EPSLON,IPOLRN,dmin,dmax,dstep) #se rellena
fichero de entrada para GRWAVE, entrada.dat
614 seguir= self . empezar(inicial ) # realiza llamada a grwave y recoge datos de salida . Variable
SEGUIR indica si ha habido fallo en la seleccion del entorno Windows o MSDOS.
615 if ( seguir==True): #no ha habido fallo ejecutando GRWAVE
616 d_km, intensidad,perdidas=leerGRW2("grwave2.out",2) #el numero 2 viene explicado en dicha funcion,
solo elimina un fallo que se contemplaba para esta funcion
617 ultimo=d_km[−1] #coge el ultimo valor que podrá usarse para calcular la distancia , metiendolo en
una variable global
618 self . update_graph(d_km,intensidad,color,label) #se llama a la funcion que genera la grafica
619
620 def rellenarEntrada( self , HTT,HRR,FREQ,SIGMA,EPSLON,IPOLRN,dmin,dmax,dstep): #Usada en ambas
pestaas. Rellena fichero de entrada.dat para GRWAVE
621 fentrada = open(’entrada.dat’ , ’w’)
622 fentrada.write( ’HTT ’+HTT+’\n’+’HRR ’+HRR+’\n’+’FREQ ’+FREQ+’\n’+’SIGMA ’
623 +SIGMA+’\n’+’EPSLON ’+EPSLON+’\n’+’IPOLRN ’+IPOLRN+’\n’+’dmin ’+dmin+’\n’+
624 ’dmax ’+dmax+’\n’+’dstep ’+dstep+’\n’+’GO’+’\n’+’STOP’+’\n’)
625 fentrada. close ()
626
627 def empezar(self, inicial ) : #Usada en ambas pestaas
628 seguir=ejecucion( self ) #hace llamada a GRWAVE
629 lecturaSalida ( self , inicial ) #procesa los datos de salida
630 return seguir # notifica si ha habido fallo en llamada a GRWAVE por seleccion de entorno equivocado
631
632 def update_graph(self, distancia , intensidad , colorGrafica , etiqueta ) : #Dibuja gráfica terreno homogéneo
633 self . MplWidget.canvas.axes. set_title ( ’Curva de propagación’,fontsize=10) #Titulo grafica
634 self . MplWidget.canvas.axes.set_xlabel(’Distancia [km]’, fontsize =8) #Etiqueta eje X
71

635 self . MplWidget.canvas.axes.set_ylabel( ’Intensidad de campo [dB(V /m)]’, fontsize =8) #Etiqueta eje Y
636 self . MplWidget.canvas.axes.set_ylim(−20,120) #Limite superior e inferior de eje Y
637 self . MplWidget.canvas.axes.set_xlim(1,10000) #Limite superior e inferior de eje X
638 self . MplWidget.canvas.axes.grid(True,which="both",linewidth=0.5) #Activar cuadrícula en ambos ejes con
anchura 0.5
639 #se introduce en la grafica la curva con valores de distancia e intensidad para cada eje . Las etiquetas y
color
640 #se pasan por parámetros para diferenciar una curva de otra en la misma grafica
641 self . MplWidget.canvas.axes.plot(distancia , intensidad , label=etiqueta , color=colorGrafica,linewidth=1)
642 self . MplWidget.canvas.axes.legend(loc=’upper right’,prop={’size’ : 7}) #leyenda situada arriba a la
derecha, con tamao de fuente 7.
643 self . MplWidget.canvas.axes.semilogx(distancia,intensidad , color=colorGrafica) #aplica escala logaritmica a
eje X. Se vuelve a pasar datos de ejes X e Y, y el color .
644 self . MplWidget.canvas.draw() #Se dibuja en la grá fica , es decir , se aplican y muestran los cambios.
645
646 def saveFig1( self ) : #Abre QDialog para guardar archivo seleccionando carpeta, en las extensiones especificadas
.
647 fileName = QFileDialog.getSaveFileName(self,
648 self . tr ("Exportar gráfica"),
649 "", self . tr ("PNG (∗.png);;PDF (∗.pdf);;EPS (∗.eps);;RAW (∗.raw);;PGF (∗.pgf);; PS (∗.ps);;RAW (∗.raw
);;"
650 "RGBA (∗.rgba);;SVG (∗.svg);;SVGZ (∗.svgz)"))[0]
651 if fileName: #si se ha seleccionado el boton Guardar, fileName estara relleno y contiene el nombre del
archivo con la extension
652 self . MplWidget.canvas.figure. savefig (fileName, dpi=300, quality=80, optimize=True, progressive=True)
653
654 def calculaDistancia ( self ) : #calculara el valor de intensidad para distancia introducida una vez generada
grafica
655 global ultimo #se coge la variable ultimo haciendola global , para usarla en otra funcion
656 distancia = self . lineEdit . text () #se toma el texto de la distancia
657 if ( distancia ==""): # se comprueba que no se ha dejado vacio una vez pulsado Calcular, sino , error
658 self . lanzarWarning("Es necesario especificar una distancia")
659 elif ( float ( distancia )>ultimo): #se comprueba que la distancia no supera el rango maximo de distancia que
se puede obtener
660 self . lanzarWarning("Distancia fuera de rango. \n Valor máximo: "+str(ultimo))
661 else :
662 dmin=distancia #pone el valor que queremos calcular como dmin, que se metera en entrada.dat
663 distancia2= float ( distancia ) #lo convierte a float para realizarle una suma
664 dmax=distancia2+1
665 dmax2=str(dmax) #distancia+1 se convierte a cadena para introducirse en entrada.dat, ya que en
ficheros solo se pueden escribir strings .
666 dstep="1" #el salto será solo de 1, es decir , de dmin a dmax solo habra 1 paso, solo 2 valores
667 HTT=self.HTT.text()
668 HRR=self.HRR.text()
669 FREQ=self.FREQ.text()
670 SIGMA=self.SIGMA.text()
671 EPSLON=self.EPSLON.text()
672 if self . Horizontal.isChecked(): #comprueba si esta marcada la polarizacion horizontal
673 IPOLRN=’2’ #el valor 2 es porque GRWAVE entiende 2 como horizontal y 1 como vertical
674 elif self . Vertical . isChecked(): #comprueba si es polarizacion vertical
675 IPOLRN=’1’
676 self . rellenarEntrada(HTT,HRR,FREQ,SIGMA,EPSLON,IPOLRN,dmin,dmax2,dstep) #rellena fichero
entrada.dat
677 seguir= self . empezar(1) #llama a funcion empezar, que ejecutará llamada a GRWAVE y recogerá datos de
salida
678 if ( seguir==True): #no ha habido fallo ejecutando GRWAVE
679 d_km, intensidad,perdidas=leerGRW2("grwave2.out",1) #se recogen los valores de grwave2.out ( solo
habra 2 pares)
680 if (d_km.size==0): #comprobar que no esta vacio, es decir , que no se ha metido un valor
demasiado alto o nulo
681 self . lanzarWarning("Distancia fuera de rango")
682 self . lineEdit_2 . setText ("") #se ponen los campos de E y Lb vacios, para evitar confusiones .
683 self . lineEdit_3 . setText ("")
684
685 else :
686 self . lineEdit_2 . setText ( str (intensidad . item(0) ) ) #si todo esta bien, se mete en E y Lb el
primer par de valores correspondientes a dmin, que es el valor que queremos
687 self . lineEdit_3 . setText ( str (perdidas.item(0) ) )
688
689 def clearr ( self ) : #limpia la grá fica homogenea, pero vuelve a asignar valores de configuración de grá fica vací
a
72 Appendix A. Código fuente

690 self . MplWidget.canvas.axes.clear()


691 self . MplWidget.canvas.axes. set_title ( ’Curva de propagación’,fontsize=10)
692 self . MplWidget.canvas.axes.set_xlabel(’Distancia [km]’, fontsize =8)
693 self . MplWidget.canvas.axes.set_ylabel( ’Intensidad de campo [dB(V /m)]’, fontsize =8)
694 self . MplWidget.canvas.axes.set_ylim(−20,120)
695 self . MplWidget.canvas.axes.set_xlim(1,10000)
696 self . MplWidget.canvas.axes.grid(True,which="both",linewidth=0.5)
697 self . MplWidget.canvas.axes.semilogx()
698 self . MplWidget.canvas.draw()
699 self . ocultaDistancia ()
700
701 def ocultaDistancia ( self ) : #cuando se limpia una grafica , o se selecciona mas de una frecuencia, se desactivan
las casillas para calcular la distancia
702 self . lineEdit . setReadOnly(True) #Distancia en modo solo lectura , para no admitir que se pueda escribir .
703 self . lineEdit . setStyleSheet ("background−color: rgb(235, 235, 235);")
704 self . lineEdit_2 . setStyleSheet ("background−color: rgb(235, 235, 235);")
705 self . lineEdit_3 . setStyleSheet ("background−color: rgb(235, 235, 235);")
706 self . lineEdit . setText ("")
707 self . lineEdit_2 . setText ("")
708 self . lineEdit_3 . setText ("")
709
710 ##################################################################################
711 ##################################################################################
712 ##################################################################################
713 ##################################################################################
714 # FUNCIONES TERRENO MIXTO
715
716 def informacion( self ) : #Abre informacion con la tabla de caracteristicas de terrenos
717 self . ventanaInfo=QtWidgets.QMainWindow()
718 self . ui=Ui_Info()
719 self . ui . setupUi( self . ventanaInfo)
720 self . ventanaInfo.setWindowFlags(QtCore.Qt.WindowCloseButtonHint);
721 self . ventanaInfo.setWindowIcon(QIcon("iconn.png"))
722 self . ventanaInfo.show()
723
724 def comprobarWarning_2(self):
725 #Se comprueba que ningun parametro introducido esta vacio, a excepcion de los terrenos 3 y 4 que se hara
726 #mas adelante
727 if ( self . HTT_2.text()=="" or self . HRR_2.text()=="" or self.Sigma1.text()==""
728 or self . Epsilon1. text () =="" or self . Sigma2.text()==""
729 or self . Epsilon2. text () =="" or self . Long1.text()==""
730 or self . Long2.text()=="" or self . FREQmixto.text()==""):
731 parar=True #si hay alguno vacio , se debe parar la ejecucion
732 self . lanzarWarning("Es necesario rellenar todos los campos")#se lanza ventana emergente con error
especificado
733
734 #Se comprueba que todos los valores introducidos estan en formato float , a excepcion de terrenos 3 y 4
735 elif ( self . is_float ( self . HTT_2.text())==False or self . is_float ( self . HRR_2.text())==False or
736 self . is_float ( self . Sigma2.text() )==False or self . is_float ( self . Epsilon1. text () )==False or
737 self . is_float ( self . Sigma1.text() )==False or self . is_float ( self . Long1.text() )==False or
738 self . is_float ( self . Epsilon2. text () )==False or self . is_float ( self . Long2.text() )==False or
739 self . is_float ( self . FREQmixto.text())==False):
740 parar=True
741 self . lanzarWarning("Los parámetros deben ser formato float")
742
743 #Se comprueba que la frecuencia esta entre 10 kHz y 30 MHz
744 elif ( float ( self . FREQmixto.text())>30 or float ( self . FREQmixto.text())<0.01): #se comprueba que la
frecuencia esta dentro del rango (10 kHz−30 MHz)
745 self . lanzarWarning("La frecuencia debe estar entre \n 10 KHz y 30 MHz")
746 parar=True
747 else : #si no hay errores , no se para la ejecucion
748 parar=False
749 return parar
750
751 def inhabilitaSigEps1( self ) : #cuando se ha seleccionado un terreno predeterminado, las casillas de sigma y
epsilon se configuran para solo lectura
752 self . Sigma1.setReadOnly(True)
753 self . Epsilon1.setReadOnly(True)
754 self . Sigma1.setStyleSheet("background−color: rgb(235, 235, 235);")
755 self . Epsilon1. setStyleSheet ("background−color: rgb(235, 235, 235);")
756
73

757 def inhabilitaSigEps2( self ) : #igual , pero para el terreno 2


758 self . Sigma2.setReadOnly(True)
759 self . Epsilon2.setReadOnly(True)
760 self . Sigma2.setStyleSheet("background−color: rgb(235, 235, 235);")
761 self . Epsilon2. setStyleSheet ("background−color: rgb(235, 235, 235);")
762
763 def inhabilitaSigEps3( self ) : #igual para terreno 3
764 self . Sigma3.setReadOnly(True)
765 self . Epsilon3.setReadOnly(True)
766 self . Sigma3.setStyleSheet("background−color: rgb(235, 235, 235);")
767 self . Epsilon3. setStyleSheet ("background−color: rgb(235, 235, 235);")
768
769 def inhabilitaSigEps4( self ) : #igual para terreno 4
770 self . Sigma4.setReadOnly(True)
771 self . Epsilon4.setReadOnly(True)
772 self . Sigma4.setStyleSheet("background−color: rgb(235, 235, 235);")
773 self . Epsilon4. setStyleSheet ("background−color: rgb(235, 235, 235);")
774
775 def comprobarTerrenos(self): #numero de terrenos, desplegable 1 2 3 4
776 comboText = self . comboBox_3.currentText()
777 if (comboText=="2"): #si el numero de terrenos seleccionados es 2, oculta los parametros y etiquetas de
los bloques de terrenos 3 y 4
778 self . label_50 . setVisible (False) #Correspondiente al bloque 3
779 self . label_51 . setVisible (False)
780 self . label_52 . setVisible (False)
781 self . comboBox_6.setVisible(False)
782 self . Epsilon3. setVisible (False)
783 self . Sigma3.setVisible (False)
784 self . Long3.setVisible (False)
785 self . label_53 . setVisible (False) #Correspondiente al bloque 4
786 self . label_54 . setVisible (False)
787 self . label_55 . setVisible (False)
788 self . comboBox_7.setVisible(False)
789 self . Epsilon4. setVisible (False)
790 self . Sigma4.setVisible (False)
791 self . Long4.setVisible (False)
792 self . line . setVisible (False)
793 elif (comboText=="3"):
794 self . label_50 . setVisible (True) #Correspondiente al bloque 3
795 self . label_51 . setVisible (True)
796 self . label_52 . setVisible (True)
797 self . comboBox_6.setVisible(True)
798 self . Epsilon3. setVisible (True)
799 self . Sigma3.setVisible (True)
800 self . Long3.setVisible (True)
801 self . label_53 . setVisible (False) #Correspondiente al bloque 4
802 self . label_54 . setVisible (False)
803 self . label_55 . setVisible (False)
804 self . comboBox_7.setVisible(False)
805 self . Epsilon4. setVisible (False)
806 self . Sigma4.setVisible (False)
807 self . Long4.setVisible (False)
808 self . line . setVisible (True)
809 elif (comboText=="4"):
810 self . label_50 . setVisible (True) #Correspondiente al bloque 3
811 self . label_51 . setVisible (True)
812 self . label_52 . setVisible (True)
813 self . comboBox_6.setVisible(True)
814 self . Epsilon3. setVisible (True)
815 self . Sigma3.setVisible (True)
816 self . Long3.setVisible (True)
817 self . label_53 . setVisible (True) #Correspondiente al bloque 4
818 self . label_54 . setVisible (True)
819 self . label_55 . setVisible (True)
820 self . comboBox_7.setVisible(True)
821 self . Epsilon4. setVisible (True)
822 self . Sigma4.setVisible (True)
823 self . Long4.setVisible (True)
824 self . line . setVisible (True)
825
74 Appendix A. Código fuente

826 def actualizaTerreno1( self ) : #si en el desplegable de terreno 1 se ha tomado valor predefinido , se actualizan
las casillas de sigma y epsilon , poniendo modo lectura si toca
827 terreno = self . comboBox_4.currentText()
828 if (terreno=="Agua del mar, salinidad baja"):
829 self . Sigma1.setText("1")
830 self . Epsilon1. setText ("80")
831 self . inhabilitaSigEps1 ()
832 elif (terreno=="Agua del mar, salinidad media"):
833 self . Sigma1.setText("5")
834 self . Epsilon1. setText ("70")
835 self . inhabilitaSigEps1 ()
836 elif (terreno=="Agua dulce"):
837 self . Sigma1.setText("0.003")
838 self . Epsilon1. setText ("80")
839 self . inhabilitaSigEps1 ()
840 elif (terreno=="Tierra"):
841 self . Sigma1.setText("0.03")
842 self . Epsilon1. setText ("40")
843 self . inhabilitaSigEps1 ()
844 elif (terreno=="Tierra húmeda"):
845 self . Sigma1.setText("0.01")
846 self . Epsilon1. setText ("30")
847 self . inhabilitaSigEps1 ()
848 elif (terreno=="Tierra moderadamente seca"):
849 self . Sigma1.setText("0.001")
850 self . Epsilon1. setText ("15")
851 self . inhabilitaSigEps1 ()
852 elif (terreno=="Tierra seca"):
853 self . Sigma1.setText("0.0003")
854 self . Epsilon1. setText ("7")
855 self . inhabilitaSigEps1 ()
856 elif (terreno=="Tierra muy seca"):
857 self . Sigma1.setText("0.0001")
858 self . Epsilon1. setText ("3")
859 self . inhabilitaSigEps1 ()
860 elif (terreno=="Hielo de agua dulce, −1C"):
861 self . Sigma1.setText("0.00003")
862 self . Epsilon1. setText ("3")
863 self . inhabilitaSigEps1 ()
864 elif (terreno=="Hielo de agua dulce, −10C"):
865 self . Sigma1.setText("−0.00001")
866 self . Epsilon1. setText ("3")
867 self . inhabilitaSigEps1 ()
868 elif (terreno=="Personalizado"): #cuando es personalizado, se desactiva modo solo lectura para introducir
manualmente los parametros
869 self . Sigma1.setText("")
870 self . Epsilon1. setText ("")
871 self . Sigma1.setReadOnly(False)
872 self . Epsilon1.setReadOnly(False)
873 self . Sigma1.setStyleSheet("background−color: rgb(255, 255, 255);")
874 self . Epsilon1. setStyleSheet ("background−color: rgb(255, 255, 255);")
875
876 def actualizaTerreno2( self ) :
877 terreno = self . comboBox_5.currentText()
878 if (terreno=="Agua del mar, salinidad baja"):
879 self . Sigma2.setText("1")
880 self . Epsilon2. setText ("80")
881 self . inhabilitaSigEps2 ()
882 elif (terreno=="Agua del mar, salinidad media"):
883 self . Sigma2.setText("5")
884 self . Epsilon2. setText ("70")
885 self . inhabilitaSigEps2 ()
886 elif (terreno=="Agua dulce"):
887 self . Sigma2.setText("0.003")
888 self . Epsilon2. setText ("80")
889 self . inhabilitaSigEps2 ()
890 elif (terreno=="Tierra"):
891 self . Sigma2.setText("0.03")
892 self . Epsilon2. setText ("40")
893 self . inhabilitaSigEps2 ()
894 elif (terreno=="Tierra húmeda"):
75

895 self . Sigma2.setText("0.01")


896 self . Epsilon2. setText ("30")
897 self . inhabilitaSigEps2 ()
898 elif (terreno=="Tierra moderadamente seca"):
899 self . Sigma2.setText("0.001")
900 self . Epsilon2. setText ("15")
901 self . inhabilitaSigEps2 ()
902 elif (terreno=="Tierra seca"):
903 self . Sigma2.setText("0.0003")
904 self . Epsilon2. setText ("7")
905 self . inhabilitaSigEps2 ()
906 elif (terreno=="Tierra muy seca"):
907 self . Sigma2.setText("0.0001")
908 self . Epsilon2. setText ("3")
909 self . inhabilitaSigEps2 ()
910 elif (terreno=="Hielo de agua dulce, −1C"):
911 self . Sigma2.setText("0.00003")
912 self . Epsilon2. setText ("3")
913 self . inhabilitaSigEps2 ()
914 elif (terreno=="Hielo de agua dulce, −10C"):
915 self . Sigma2.setText("−0.00001")
916 self . Epsilon2. setText ("3")
917 self . inhabilitaSigEps2 ()
918 elif (terreno=="Personalizado"):
919 self . Sigma2.setText("")
920 self . Epsilon2. setText ("")
921 self . Sigma2.setReadOnly(False)
922 self . Epsilon2.setReadOnly(False)
923 self . Sigma2.setStyleSheet("background−color: rgb(255, 255, 255);")
924 self . Epsilon2. setStyleSheet ("background−color: rgb(255, 255, 255);")
925
926 def actualizaTerreno3( self ) :
927 terreno = self . comboBox_6.currentText()
928 if (terreno=="Agua del mar, salinidad baja"):
929 self . Sigma3.setText("1")
930 self . Epsilon3. setText ("80")
931 self . inhabilitaSigEps3 ()
932 elif (terreno=="Agua del mar, salinidad media"):
933 self . Sigma3.setText("5")
934 self . Epsilon3. setText ("70")
935 self . inhabilitaSigEps3 ()
936 elif (terreno=="Agua dulce"):
937 self . Sigma3.setText("0.003")
938 self . Epsilon3. setText ("80")
939 self . inhabilitaSigEps3 ()
940 elif (terreno=="Tierra"):
941 self . Sigma3.setText("0.03")
942 self . Epsilon3. setText ("40")
943 self . inhabilitaSigEps3 ()
944 elif (terreno=="Tierra húmeda"):
945 self . Sigma3.setText("0.01")
946 self . Epsilon3. setText ("30")
947 self . inhabilitaSigEps3 ()
948 elif (terreno=="Tierra moderadamente seca"):
949 self . Sigma3.setText("0.001")
950 self . Epsilon3. setText ("15")
951 self . inhabilitaSigEps3 ()
952 elif (terreno=="Tierra seca"):
953 self . Sigma3.setText("0.0003")
954 self . Epsilon3. setText ("7")
955 self . inhabilitaSigEps3 ()
956 elif (terreno=="Tierra muy seca"):
957 self . Sigma3.setText("0.0001")
958 self . Epsilon3. setText ("3")
959 self . inhabilitaSigEps3 ()
960 elif (terreno=="Hielo de agua dulce, −1C"):
961 self . Sigma3.setText("0.00003")
962 self . Epsilon3. setText ("3")
963 self . inhabilitaSigEps3 ()
964 elif (terreno=="Hielo de agua dulce, −10C"):
965 self . Sigma3.setText("−0.00001")
76 Appendix A. Código fuente

966 self . Epsilon3. setText ("3")


967 self . inhabilitaSigEps3 ()
968 elif (terreno=="Personalizado"):
969 self . Sigma3.setText("")
970 self . Epsilon3. setText ("")
971 self . Sigma3.setReadOnly(False)
972 self . Epsilon3.setReadOnly(False)
973 self . Sigma3.setStyleSheet("background−color: rgb(255, 255, 255);")
974 self . Epsilon3. setStyleSheet ("background−color: rgb(255, 255, 255);")
975
976 def actualizaTerreno4( self ) :
977 terreno = self . comboBox_7.currentText()
978 if (terreno=="Agua del mar, salinidad baja"):
979 self . Sigma4.setText("1")
980 self . Sigma4.setText("80")
981 self . inhabilitaSigEps4 ()
982 elif (terreno=="Agua del mar, salinidad media"):
983 self . Sigma4.setText("5")
984 self . Epsilon4. setText ("70")
985 self . inhabilitaSigEps4 ()
986 elif (terreno=="Agua dulce"):
987 self . Sigma4.setText("0.003")
988 self . Epsilon4. setText ("80")
989 self . inhabilitaSigEps4 ()
990 elif (terreno=="Tierra"):
991 self . Sigma4.setText("0.03")
992 self . Epsilon4. setText ("40")
993 self . inhabilitaSigEps4 ()
994 elif (terreno=="Tierra húmeda"):
995 self . Sigma4.setText("0.01")
996 self . Epsilon4. setText ("30")
997 self . inhabilitaSigEps4 ()
998 elif (terreno=="Tierra moderadamente seca"):
999 self . Sigma4.setText("0.001")
1000 self . Epsilon4. setText ("15")
1001 self . inhabilitaSigEps4 ()
1002 elif (terreno=="Tierra seca"):
1003 self . Sigma4.setText("0.0003")
1004 self . Epsilon4. setText ("7")
1005 self . inhabilitaSigEps4 ()
1006 elif (terreno=="Tierra muy seca"):
1007 self . Sigma4.setText("0.0001")
1008 self . Epsilon4. setText ("3")
1009 self . inhabilitaSigEps4 ()
1010 elif (terreno=="Hielo de agua dulce, −1C"):
1011 self . Sigma4.setText("0.00003")
1012 self . Epsilon4. setText ("3")
1013 self . inhabilitaSigEps4 ()
1014 elif (terreno=="Hielo de agua dulce, −10C"):
1015 self . Sigma4.setText("−0.00001")
1016 self . Epsilon4. setText ("3")
1017 self . inhabilitaSigEps4 ()
1018 elif (terreno=="Personalizado"):
1019 self . Sigma4.setText("")
1020 self . Epsilon4. setText ("")
1021 self . Sigma4.setReadOnly(False)
1022 self . Epsilon4.setReadOnly(False)
1023 self . Sigma4.setStyleSheet("background−color: rgb(255, 255, 255);")
1024 self . Epsilon4. setStyleSheet ("background−color: rgb(255, 255, 255);")
1025
1026 def secuenciaMixta(self ) : #se comprueba si el numero de terrenos seleccionado es 2, 3 o 4
1027 self . clearr_2 () #se limpia grafica anterior
1028 comboText = self . comboBox_3.currentText()
1029 parar=self . comprobarWarning_2() #comprueba una serie de posibles errores iniciales , parametros
incompletos, rango frecuencia , etc .
1030 if (parar==False): #si todo ha ido bien
1031 if (comboText=="2"): #si el numero de terrenos es 2
1032 SIGMA1=self.Sigma1.text() #recoge datos de sigma y epsilon
1033 SIGMA2=self.Sigma2.text()
1034 EPSLON1=self.Epsilon1.text()
1035 EPSLON2=self.Epsilon2.text()
77

1036 Label1=’ =’+EPSLON1+’, =’+SIGMA1 #prepara sus etiquetas para la leyenda


1037 Label2=’ =’+EPSLON2+’, =’+SIGMA2
1038 self . generar_2(SIGMA1,EPSLON1,0,’#0093FF’,Label1) #genera sus curvas en la grafica con ciertos
colores, el 0 indica que es la primera vez
1039 self . generar_2(SIGMA2,EPSLON2,1, ’#FF9B00’,Label2) #el 1 indica que se continue solapando
curvas
1040 Long1=self.Long1.text() #se recogen valores de longitudes y se calculan valores de intensidad en
esos puntos siguiendo Metodo Millington
1041 E1=self. calculaDistancia11 (Long1)
1042 Long2=self.Long2.text()
1043 Long12=str(float(Long1)+float(Long2))
1044 E12=self. calculaDistancia22 (Long12)
1045 E2=self. calculaDistancia22 (Long1)
1046 E22=self. calculaDistancia22 (Long2)
1047 E21=self. calculaDistancia11 (Long12)
1048 E11=self. calculaDistancia11 (Long2)
1049 if (( E1 is not None) and (E12 is not None) and (E2 is not None) and
1050 (E22 is not None) and (E21 is not None) and (E11 is not None)): #si ningun dato de
intensidad de campo electrico esta vacio
1051 ED=E1+E12−E2 #sigue las formulas de Metodo Millington, mirar en ITU−R P.368
1052 ER=E22+E21−E11
1053 ET=(ER+ED)/2
1054 else :
1055 ET=None
1056 if (ET is not None): #si el campo total ET no esta vacio , se rellena en su sitio , redondeando 2
decimales
1057 self . lineEdit_4 . setText ( str (round(ET,2)))
1058 else :
1059 self . lineEdit_4 . setText ("") #si estuviera vacio ET, se marca como vacía su casilla
1060
1061 elif (comboText=="3"): #igual para terreno 3, aadiendo mas variables y usando mas terminos en las
formulas de Millington
1062 #Se comrpueba que los valores del terreno 3 no estan vacios
1063 if ( self . Epsilon3. text () =="" or self . Sigma3.text()=="" or self . Long3.text()==""):
1064 self . lanzarWarning("Es necesario rellenar todos los campos")
1065 #Se comprueba que los valores del terreno 3 son float
1066 elif ( self . is_float ( self . Epsilon3. text () )==False or self . is_float ( self . Sigma3.text() )==False or
1067 self . is_float ( self . Long3.text() )==False):
1068 self . lanzarWarning("Los parámetros deben ser formato float")
1069 else :
1070 SIGMA1=self.Sigma1.text() #Se recogen los textos de las casillas
1071 SIGMA2=self.Sigma2.text()
1072 SIGMA3=self.Sigma3.text()
1073 EPSLON1=self.Epsilon1.text()
1074 EPSLON2=self.Epsilon2.text()
1075 EPSLON3=self.Epsilon3.text()
1076 Label1=’ =’+EPSLON1+’, =’+SIGMA1 #Etiquetas usadas para la leyenda
1077 Label2=’ =’+EPSLON2+’, =’+SIGMA2
1078 Label3=’ =’+EPSLON3+’, =’+SIGMA3
1079 self . generar_2(SIGMA1,EPSLON1,0,’#0093FF’,Label1)
1080 self . generar_2(SIGMA2,EPSLON2,0,’#FF9B00’,Label2)
1081 self . generar_2(SIGMA3,EPSLON3,1,’#60C146’,Label3)
1082 Long1=self.Long1.text()
1083 E1=self. calculaDistancia11 (Long1)
1084 Long2=self.Long2.text()
1085 Long12=str(float(Long1)+float(Long2))
1086 Long3=self.Long3.text()
1087 Long123=str(float(Long1)+float(Long2)+float(Long3))
1088 E33=self. calculaDistancia33 (Long123)
1089 E12=self. calculaDistancia22 (Long12)
1090 E2=self. calculaDistancia22 (Long1)
1091 E32=self. calculaDistancia33 (Long12)
1092 E31=self. calculaDistancia33 (Long3)
1093 Long23=str(float(Long2)+float(Long3))
1094 E23=self. calculaDistancia22 (Long23)
1095 E123=self.calculaDistancia11 (Long123)
1096 E223=self.calculaDistancia22 (Long3)
1097 E11=self. calculaDistancia11 (Long23)
1098 if (( E1 is not None) and (E12 is not None) and (E33 is not None) and (E2 is not None) and
1099 (E32 is not None) and (E31 is not None) and (E23 is not None) and (E123 is not None) and
1100 (E223 is not None) and (E11 is not None)):
78 Appendix A. Código fuente

1101 ED=E1+E12+E33−E2−E32
1102 ER=E31+E23+E123−E223−E11
1103 ET=(ER+ED)/2
1104 else :
1105 ET=None
1106 if (ET is not None):
1107 self . lineEdit_4 . setText ( str (round(ET,2)))
1108 else :
1109 self . lineEdit_4 . setText ("")
1110
1111 elif (comboText=="4"):
1112 #Se comprueba que los valores del terreno 3 y 4 no están vacios
1113 if ( self . Epsilon3. text () =="" or self . Sigma3.text()=="" or self . Long3.text()==""
1114 or self . Epsilon4. text () =="" or self . Sigma4.text()=="" or self . Long4.text()==""):
1115 self . lanzarWarning("Es necesario rellenar todos los campos")
1116 #Se comprueba que los valores del terreno 3 y 4 son float
1117 elif ( self . is_float ( self . Epsilon3. text () )==False or self . is_float ( self . Sigma3.text() )==False or
1118 self . is_float ( self . Long3.text() )==False or self . is_float ( self . Epsilon4. text () )==False
1119 or self . is_float ( self . Sigma4.text() )==False or self . is_float ( self . Long4.text() )==False):
1120 self . lanzarWarning("Los parámetros deben ser formato float")
1121 else :
1122 SIGMA1=self.Sigma1.text()
1123 SIGMA2=self.Sigma2.text()
1124 SIGMA3=self.Sigma3.text()
1125 SIGMA4=self.Sigma4.text()
1126 EPSLON1=self.Epsilon1.text()
1127 EPSLON2=self.Epsilon2.text()
1128 EPSLON3=self.Epsilon3.text()
1129 EPSLON4=self.Epsilon4.text()
1130 Label1=’ =’+EPSLON1+’, =’+SIGMA1
1131 Label2=’ =’+EPSLON2+’, =’+SIGMA2
1132 Label3=’ =’+EPSLON3+’, =’+SIGMA3
1133 Label4=’ =’+EPSLON4+’, =’+SIGMA4
1134 self . generar_2(SIGMA1,EPSLON1,0,’#0093FF’,Label1)
1135 self . generar_2(SIGMA2,EPSLON2,0,’#FF9B00’,Label2)
1136 self . generar_2(SIGMA3,EPSLON3,0,’#60C146’,Label3)
1137 self . generar_2(SIGMA4,EPSLON4,1,’#BE54C7’,Label4)
1138 Long1=self.Long1.text()
1139 E1=self. calculaDistancia11 (Long1)
1140 Long2=self.Long2.text()
1141 Long12=str(float(Long1)+float(Long2))
1142 Long3=self.Long3.text()
1143 Long4=self.Long4.text()
1144 Long123=str(float(Long1)+float(Long2)+float(Long3))
1145 Long1234=str(float(Long1)+float(Long2)+float(Long3)+float(Long4))
1146 E33=self. calculaDistancia33 (Long123)
1147 E12=self. calculaDistancia22 (Long12)
1148 E41234=self.calculaDistancia44(Long1234)
1149 E2=self. calculaDistancia22 (Long1)
1150 E32=self. calculaDistancia33 (Long12)
1151 E4123=self.calculaDistancia44 (Long123)
1152 E4=self. calculaDistancia44 (Long4)
1153 Long34=str(float(Long3)+float(Long4))
1154 E334=self.calculaDistancia33 (Long34)
1155 Long234=str(float(Long2)+float(Long3)+float(Long4))
1156 E2234=self.calculaDistancia22 (Long234)
1157 E11234=self.calculaDistancia11(Long1234)
1158 E34=self. calculaDistancia33 (Long4)
1159 E234=self.calculaDistancia22 (Long34)
1160 E1234=self.calculaDistancia11 (Long234)
1161 if (( E1 is not None) and (E12 is not None) and (E33 is not None) and (E41234 is not None) and
1162 (E2 is not None) and (E32 is not None) and (E4123 is not None) and (E4 is not None) and
1163 (E334 is not None) and (E2234 is not None) and (E11234 is not None) and (E34 is not None)
and
1164 (E234 is not None) and (E1234 is not None)):
1165 ED=E1+E12+E33+E41234−E2−E32−E4123
1166 ER=E4+E334+E2234+E11234−E34−E234−E1234
1167 ET=(ER+ED)/2
1168 else :
1169 ET=None
1170 if (ET is not None):
79

1171 self . lineEdit_4 . setText ( str (round(ET,2)))


1172 else :
1173 self . lineEdit_4 . setText ("")
1174
1175 def generar_2( self , SIGMA,EPSLON,calcular,color,label):
1176 HTT=self.HTT_2.text()#recoge textos de los parametros introducidos
1177 HRR=self.HRR_2.text()
1178 FREQ=self.FREQmixto.text()
1179 if self . Horizontal_2.isChecked():
1180 IPOLRN=’2’ #Para GRWAVE, 2 es horizontal y 1 es vertical en el parametro IPOLRN
1181 elif self . Vertical_2 . isChecked():
1182 IPOLRN=’1’
1183 #Se itera durante 4 veces . Cada curva será consecuencia de 4 llamadas a GRWAVE. Para que la escala
logaritmica del eje X, segmentada en 4 divisiones , tenga en cada
1184 #una de ellas los mismos puntos (100), se ha hecho eso. Cada uno tiene un dstep distinto para
asegurar que hay 100 puntos en cada division .
1185 for i in [0, 1, 2, 3]:
1186 if i==0:
1187 dmin=’1’
1188 dmax=’10’
1189 dstep=’0.1’
1190 inicial =1;
1191 elif i==1:
1192 dmin=’10’
1193 dmax=’100’
1194 dstep=’1’
1195 inicial =0;
1196 elif i==2:
1197 dmin=’100’
1198 dmax=’1000’
1199 dstep=’10’
1200 inicial =0;
1201 elif i==3:
1202 dmin=’1000’
1203 dmax=’10000’
1204 dstep=’100’
1205 inicial =0;
1206 self . rellenarEntrada(HTT,HRR,FREQ,SIGMA,EPSLON,IPOLRN,dmin,dmax,dstep) #se rellena fichero
de entrada para GRWAVE, entrada.dat
1207 seguir= self . empezar(inicial ) # realiza llamada a grwave y recoge datos de salida . Variable SEGUIR
indica si ha habido fallo en la seleccion del entorno Windows o MSDOS.
1208 if ( seguir==True): #no ha habido fallo ejecutando GRWAVE
1209 d_km, intensidad,perdidas=leerGRW2("grwave2.out",2)#el numero 2 viene explicado en dicha funcion, solo
elimina un fallo que se contemplaba para esta funcion
1210 self . update_graph_2(d_km,intensidad,color,label) #se llama a la funcion que genera la grafica
1211 if ( calcular==1): #si el valor de longitud total es valido , se calcula la intensidad recibida
1212 comboText = self . comboBox_3.currentText() #coge valor del desplegable de terrenos
1213 if (comboText=="2"): #indica para cuantos terrenos se calcula , en este caso combinacion de 2
1214 Distancia1= self . Long1.text() #coge variables de longitudes
1215 Distancia2= self . Long2.text()
1216 elif (comboText=="3"):
1217 Distancia1= self . Long1.text()
1218 Distancia2= self . Long2.text()
1219 Distancia3= self . Long3.text()
1220 elif (comboText=="4"):
1221 Distancia1= self . Long1.text()
1222 Distancia2= self . Long2.text()
1223 Distancia3= self . Long3.text()
1224 Distancia4= self . Long4.text()
1225
1226 def update_graph_2(self,distancia , intensidad , colorGrafica , etiqueta ) : #Dibuja gráfica terreno mixto
1227 self . MplWidget_2.canvas.axes. set_title ( ’Curva de propagación’,fontsize=10) #Titulo grafica
1228 self . MplWidget_2.canvas.axes.set_xlabel(’Distancia [km]’, fontsize =8) #Etiqueta eje X
1229 self . MplWidget_2.canvas.axes.set_ylabel(’Intensidad de campo [dB(V /m)]’, fontsize =8) #Etiqueta eje Y
1230 self . MplWidget_2.canvas.axes.set_ylim(−20,120) #Limite superior e inferior de eje Y
1231 self . MplWidget_2.canvas.axes.set_xlim(1,10000) #Limite superior e inferior de eje X
1232 self . MplWidget_2.canvas.axes.grid(True,which="both",linewidth=0.5) #Activar cuadrícula en ambos ejes con
anchura 0.5
1233 #se introduce en la grafica la curva con valores de distancia e intensidad para cada eje . Las etiquetas y
color
1234 #se pasan por parámetros para diferenciar una curva de otra en la misma grafica
80 Appendix A. Código fuente

1235 self . MplWidget_2.canvas.axes.plot(distancia , intensidad , label=etiqueta , color=colorGrafica)


1236 self . MplWidget_2.canvas.axes.legend(loc=’upper right’,prop={’size’ : 8})#leyenda situada arriba a la
derecha, con tamao de fuente 7.
1237 self . MplWidget_2.canvas.axes.semilogx(distancia,intensidad , color=colorGrafica)#aplica escala logaritmica
a eje X. Se vuelve a pasar datos de ejes X e Y, y el color .
1238 self . MplWidget_2.canvas.draw() #Se dibuja en la grá fica , es decir , se aplican y muestran los cambios.
1239
1240 def clearr_2( self ) : #limpia la grá fica mixta, pero vuelve a asignar valores de configuración de grá fica vacía
1241 self . MplWidget_2.canvas.axes.clear()
1242 self . MplWidget_2.canvas.axes. set_title ( ’Curva de propagación’,fontsize=10)
1243 self . MplWidget_2.canvas.axes.set_xlabel(’Distancia [km]’, fontsize =8)
1244 self . MplWidget_2.canvas.axes.set_ylabel(’Intensidad de campo [dB(V /m)]’, fontsize =8)
1245 self . MplWidget_2.canvas.axes.set_ylim(−20,120)
1246 self . MplWidget_2.canvas.axes.set_xlim(1,10000)
1247 self . MplWidget_2.canvas.axes.grid(True,which="both",linewidth=0.5)
1248 self . MplWidget_2.canvas.axes.semilogx()
1249 self . MplWidget_2.canvas.draw()
1250 self . lineEdit_4 . setText ("")
1251
1252 def saveFig2( self ) : #Abre QDialog para guardar archivo seleccionando carpeta, en las extensiones especificadas
.
1253 fileName = QFileDialog.getSaveFileName(self,
1254 self . tr ("Exportar gráfica"),
1255 "", self . tr ("PNG (∗.png);;PDF (∗.pdf);;EPS (∗.eps);;RAW (∗.raw);;PGF (∗.pgf);; PS (∗.ps);;RAW (∗.raw
);;"
1256 "RGBA (∗.rgba);;SVG (∗.svg);;SVGZ (∗.svgz)"))[0]
1257 if fileName: #si se ha seleccionado el boton Guardar, fileName estara relleno y contiene el nombre del
archivo con la extension
1258 self . MplWidget_2.canvas.figure.savefig (fileName, dpi=300, quality=80, optimize=True, progressive=True
)
1259
1260 def calculaDistancia11 ( self , distancia ) : #calcula intensidad de campo electrico segun metodo millington con
datos del terreno 1, a la distancia que se le marque por parametro
1261 if ( float ( distancia )>10000): #La grafica y GRWAVE solo llega a 10000, si es mayor se descarta
1262 self . lanzarWarning(’Suma de distancias fuera de rango’+’\n’+’Imposible calcular E’)
1263 self . lineEdit_4 . setText ("") #Se rellena vacio el resultado del campo, para borrar el resultado
anterior
1264 else : #Se llama a GRWAVE pero solo usando el terreno 1
1265 dmin=distancia
1266 distancia2= float ( distancia )
1267 dmax=distancia2+1
1268 dmax2=str(dmax)
1269 dstep="1"
1270 HTT=self.HTT_2.text()
1271 HRR=self.HRR_2.text()
1272 FREQ=self.FREQmixto.text()
1273 SIGMA=self.Sigma1.text()
1274 EPSLON=self.Epsilon1.text()
1275 if self . Horizontal_2.isChecked():
1276 IPOLRN=’2’
1277 elif self . Vertical_2 . isChecked():
1278 IPOLRN=’1’
1279 self . rellenarEntrada(HTT,HRR,FREQ,SIGMA,EPSLON,IPOLRN,dmin,dmax2,dstep)
1280 seguir= self . empezar(1)
1281 if ( seguir==True): #no ha habido fallo ejecutando GRWAVE
1282 d_km, intensidad,perdidas=leerGRW2("grwave2.out",1)
1283 if (d_km.size==0): #Solo debe devolver un valor el del campo para esa distancia . Si su tamao es
0, error
1284 self . lanzarWarning(’Suma de distancias fuera de rango’+’\n’+’Imposible calcular E’)
1285 self . lineEdit_4 . setText ("")
1286 else :
1287 return intensidad . item(0)
1288
1289 def calculaDistancia22 ( self , distancia ) : #calcula intensidad de campo electrico segun metodo millington con
datos del terreno 2, a la distancia que se le marque por parametro
1290 if ( float ( distancia )>10000):
1291 self . lanzarWarning(’Suma de distancias fuera de rango’+’\n’+’Imposible calcular E’)
1292 self . lineEdit_4 . setText ("")
1293 else : #Se llama a GRWAVE pero solo usando el terreno 2
1294 dmin=distancia
1295 distancia2= float ( distancia )
81

1296 dmax=distancia2+1
1297 dmax2=str(dmax)
1298 dstep="1"
1299 HTT=self.HTT_2.text()
1300 HRR=self.HRR_2.text()
1301 FREQ=self.FREQmixto.text()
1302 SIGMA=self.Sigma2.text()
1303 EPSLON=self.Epsilon2.text()
1304 if self . Horizontal_2.isChecked():
1305 IPOLRN=’2’
1306 elif self . Vertical_2 . isChecked():
1307 IPOLRN=’1’
1308 self . rellenarEntrada(HTT,HRR,FREQ,SIGMA,EPSLON,IPOLRN,dmin,dmax2,dstep)
1309 seguir= self . empezar(1)
1310 if ( seguir==True): #no ha habido fallo ejecutando GRWAVE
1311 d_km, intensidad,perdidas=leerGRW2("grwave2.out",1)
1312 if (d_km.size==0): #Solo debe devolver un valor el del campo para esa distancia . Si su tamao es
0, error
1313 self . lanzarWarning(’Suma de distancias fuera de rango’+’\n’+’Imposible calcular E’)
1314 self . lineEdit_4 . setText ("")
1315 else :
1316 return intensidad . item(0)
1317
1318 def calculaDistancia33 ( self , distancia ) : #para datos del terreno 3
1319 if ( float ( distancia )>10000):
1320 self . lanzarWarning(’Suma de distancias fuera de rango’+’\n’+’Imposible calcular E’)
1321 self . lineEdit_4 . setText ("")
1322 else : #Se llama a GRWAVE pero solo usando el terreno 3
1323 dmin=distancia
1324 distancia2= float ( distancia )
1325 dmax=distancia2+1
1326 dmax2=str(dmax)
1327 dstep="1"
1328 HTT=self.HTT_2.text()
1329 HRR=self.HRR_2.text()
1330 FREQ=self.FREQmixto.text()
1331 SIGMA=self.Sigma3.text()
1332 EPSLON=self.Epsilon3.text()
1333 if self . Horizontal_2.isChecked():
1334 IPOLRN=’2’
1335 elif self . Vertical_2 . isChecked():
1336 IPOLRN=’1’
1337 self . rellenarEntrada(HTT,HRR,FREQ,SIGMA,EPSLON,IPOLRN,dmin,dmax2,dstep)
1338 seguir= self . empezar(1)
1339 if ( seguir==True): #no ha habido fallo ejecutando GRWAVE
1340 d_km, intensidad,perdidas=leerGRW2("grwave2.out",1)
1341 if (d_km.size==0): #Solo debe devolver un valor el del campo para esa distancia . Si su tamao es
0, error
1342 self . lanzarWarning(’Suma de distancias fuera de rango’+’\n’+’Imposible calcular E’)
1343 self . lineEdit_4 . setText ("")
1344 else :
1345 return intensidad . item(0)
1346
1347 def calculaDistancia44 ( self , distancia ) : #para datos del terreno 4
1348 if ( float ( distancia )>10000):
1349 self . lanzarWarning(’Suma de distancias fuera de rango’+’\n’+’Imposible calcular E’)
1350 self . lineEdit_4 . setText ("")
1351 else : #Se llama a GRWAVE pero solo usando el terreno 4
1352 dmin=distancia
1353 distancia2= float ( distancia )
1354 dmax=distancia2+1
1355 dmax2=str(dmax)
1356 dstep="1"
1357 HTT=self.HTT_2.text()
1358 HRR=self.HRR_2.text()
1359 FREQ=self.FREQmixto.text()
1360 SIGMA=self.Sigma4.text()
1361 EPSLON=self.Epsilon4.text()
1362 if self . Horizontal_2.isChecked():
1363 IPOLRN=’2’
1364 elif self . Vertical_2 . isChecked():
82 Apéndice A. Código fuente

1365 IPOLRN=’1’
1366 self . rellenarEntrada(HTT,HRR,FREQ,SIGMA,EPSLON,IPOLRN,dmin,dmax2,dstep)
1367 seguir= self . empezar(1)
1368 if ( seguir==True): #no ha habido fallo ejecutando GRWAVE
1369 d_km, intensidad,perdidas=leerGRW2("grwave2.out",1)
1370 if (d_km.size==0): #Solo debe devolver un valor el del campo para esa distancia . Si su tamao es
0, error
1371 self . lanzarWarning(’Suma de distancias fuera de rango’+’\n’+’Imposible calcular E’)
1372 self . lineEdit_4 . setText ("")
1373 else :
1374 return intensidad . item(0)
1375
1376 if __name__ == "__main__":
1377 app = QtWidgets.QApplication([])
1378 window = MainWindow()
1379 window.show()
1380 app.setWindowIcon(QtGui.QIcon(’iconn.ico’))
1381 window.setWindowIcon(QtGui.QIcon(’iconn.ico’))
1382 app.exec_()

.
Índice de Figuras

2.1 Transmisión de onda superficial y onda de cielo [8] 4


2.2 Vector del frente de ondas en transmisión de onda de superficie 5
2.3 Propagación con reflexión sobre tierra plana 5
2.4 Ejemplo de curva de la Rec. P.368-9 para terreno homogéneo agua de mar (salinidad baja) 7
2.5 Pérdidas de propagación en función de epsilon, con σ =0.001 y distancia=10 m [17] 8
2.6 Pérdidas de propagación en función de sigma, con εr =4.0 y distancia=10 m [17] 8
2.7 Ejemplo de curva de la Rec. P.368-9 para terreno mixto con distintos valores de σ y εr 10
2.8 Curva resultante tras aplicar Método Millington para dos terrenos 11

3.1 Ejemplo de fichero de entrada para GRWAVE 14


3.2 Ejemplo de fichero de salida para GRWAVE 14
3.3 Interfaz inicial DOSBox 15
3.4 Ejecución de GRWAVE en DOSBox 16
3.5 Interfaz de Qt Designer para la construcción de ventanas de la GUI 18

4.1 Ventana de Bienvenida de GroundWave Simulator 19


4.2 Guía sobre uso e instalación del simulador 20
4.3 Botón deslizante de la interfaz principal 20
4.4 QTabWidget Terreno mixto seleccionado 21
4.5 Ventana para los cálculos de un Terreno homogéneo 21
4.6 Desplegable ComboBox para la selección de terreno 22
4.7 Ventana de ayuda para selección de terreno 22
4.8 Desplegable QComboBox para la selección de número de frecuencias 23
4.9 Ejemplo de ejecución de gráfica para terreno homogéneo 23
4.10 Gama de colores para la generación de curvas expresados en hexadecimal 24
4.11 Ventana selector de carpetas para exportar gráfica 25
4.12 Ejemplo de cálculo de intensidad de campo eléctrico para terreno homogéneo 25
4.13 Ventana para los cálculos de un Terreno mixto. 26
4.14 Desplegable ComboBox para la selección del número de terrenos 26
4.15 Ejemplo de producción de gráfica para terreno mixto. 27
4.16 Ventana de error generado por falta de parámetros 28
4.17 Ventana de error generado por frecuencia fuera de rango 28
4.18 Ventana de error producido por incumplir restricciones de altura de antena receptora 28
4.19 Ventana de error producido por distancia fuera de rango 29
4.20 Ventana de error producido por suma de longitudes fuera de rango 30

5.1 Análisis de calidad del código del simulador con SonarQube 31


5.2 Informe de análisis desglosado de SonarQube 32
5.3 Curva de propagación por GroundWave Simulator en tierra húmeda (σ =0.01, εr =30) 32
5.4 Curva de propagación ITU-R de onda de superficie en tierra húmeda (σ =0.01, εr =30) 33

83
84 Índice de Figuras

5.5 Comparación de intensidad de campo eléctrico según la altura de antenas para agua dulce y dis-
tancia de 50 km 33
5.6 Curva de propagación en tierra húmeda (σ =0.01, εr =30) variando polarización y frecuencia 34
5.7 Escenario de transmisión de dos terrenos 35
5.8 Escenarios de transmisión de tres terrenos 35
Índice de Tablas

5.1 Comparación de intensidad según la polarización para Tierra húmeda (σ =0.01 S/m, εr =30) 34
5.2 Comparación de intensidad recibida en Método Millington según el orden de dos terrenos con dis-
tinta longitud y f =1 MHz 35
5.3 Comparación de intensidad recibida en Método Millington según el orden de tres terrenos con
100km de longitud cada uno y f =1 MHz 36

85
Índice de Códigos

3.1 Ejecución de GRWAVE con ficheros de entrada y salida 13


3.2 Ejemplo para montar directorio en DOSBox 15
3.3 Código para llamada automática de GRWAVE en DOSBox 16
3.4 Ejemplo de uso del instalador de paquetes pip 17
3.5 Comando para generar ejecutable del Simulador 17
3.6 Ejemplo traducción de archivo de Qt Designer para Python 18

4.1 Fragmentos de código en referencia a la creación y rellenado de gráfica. No todas las líneas
pertenecen al mismo fichero ni son las únicas para ésto. 23

87
Bibliografía

[1] Curvas de propagación por onda de superficie para frecuencias comprendidas entre 10 kHz y 30 MHz.
Rec ITU-R P.368-9, 02/2007.
[2] A. Sommerfeld, The propagation of waves in wireless telegraphy, 1909, vol. 28.
[3] K. Norton, The propagation of radio waves over the surface of the Earth and in the upper atmosphere.
Part 1, 1936, vol. 24.
[4] K. A. Norton, The propagation of radio waves over the surface of the Earth and in the upper atmosphere.
Part 2, 1937, vol. 25.
[5] B. V. der Pol y H. Bremmer, The diffraction of electromagnetic waves from an electrical point source
round a finitely conducting sphere, 1937, vol. 25.
[6] K. Norton, The calculation of ground-wave field intensity over a finitely conducting spherical Earth,
1941.
[7] G. Millington, Ground-wave propagation over an inhomogeneous smooth earth, 1941, vol. 96, no. 56.
[8] Ground wave physics. Encyclopædia Britannica, Inc. [Online]. Available: https://www.britannica.com/
science/ground-wave
[9] ITU-R, Manual sobre propagación por onda de superficie, 2014.
[10] L. Zhou, LF Ground-Wave Propagation Over Irregular Terrain, 2011, no. 59.
[11] J. A. Richards, An introduction for the non specialist. Springer, 2008.
[12] H. Bremmer, Terrestrial Radio Waves. Elvesier, 1949.
[13] Electrical characteristics of the surface of the Earth, Radiowave propagation. Rec ITU-R P.527,
08/2009.
[14] F. Dagefu and K. Sarabandi, Analysis and modeling of near-ground wave propagation in the presence
of building walls, 2011, vol. 50.
[15] Índice de refracción radioeléctrica: su fórmula y datos sobre la refractividad. Rec ITU-R P.453,
08/2019.
[16] Teoría de imágenes, aplicada a los radiadores electromagnéticos: monopolos y monopolos de λ /4.
Antenaruval. [Online]. Available: https://cutt.ly/Gydysry#eqlatex
[17] N. DeMinco, Free-Field Measurements of the Electrical Properties of Soil Using the Surface Wave
Propagation Between Two Monopole Antennas. NTIA-Report TR-12-484, 2012.
[18] R. Gill, Ground-wave propagation program GRWAVE. Cambridge Press, 2010.
[19] World atlas of ground conductivities. Rec ITU-R P.832, 07/2015.

89

También podría gustarte