Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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
Autor:
Javier Ojeda Prieto
Tutor:
Susana Hornillo Mellado
Profesora Contratada Doctora
El tribunal nombrado para juzgar el trabajo arriba indicado, compuesto por los siguientes profesores:
Presidente:
Vocal/es:
Secretario:
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.
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
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
Í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
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
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.
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
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.
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
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 µσ
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.
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.
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].
donde σ es la conductividad y λ la longitud de onda, en los casos en los que la permitividad relativa
cumpla:
εr 60πλ (2.8)
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.
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
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
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
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:
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.
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 >.
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:
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.
mount c d:\dosprog
Un ejemplo de los pasos para hacer funcionar GRWAVE en DOSBox se muestra en la siguiente figura:
3.3 Anaconda
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:
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.
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.
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:
Qt Designer genera un archivo .ui que se convertirá en .py con código Python utilizando pyuic5 en el
terminal de Windows.
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.
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.
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.
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.
Por último, el QPushButton verde con etiqueta Empezar, avanzaría hacia la segunda ventana del programa
para configurar los cálculos.
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.
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].
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.
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
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.
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
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.
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.12 Ejemplo de cálculo de intensidad de campo eléctrico para terreno homogéneo.
26 Capítulo 4. Funcionalidad de GroundWave Simulator
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.
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.
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.
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
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.
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
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.
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.
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.
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.
A continuación se intenta comprobar los mismos efectos para la combinación de tres terrenos con 100
kilómetros de longitud cada uno.
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.
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
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
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
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
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
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
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
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
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
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
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