basepath = !pwd
fits_files = !ls data/set1/*.fits  # Quedan con el path data/*.fits 
np.shape(fits_files)

"""
Explanation: Data structure
Data is acquired using another jupyter notebook that is in the same repository as this one (check https://github.com/ijpulidos/solar-physics-ex/) and it's called Acquiring_Data.ipynb
The structure of the data sets downloaded with that notebook is as follows (relative to the directory of this notebook):
data
├── set1
│   ├── hmi_ic_45s_2012_01_29_00_01_30_tai_continuum.fits.fits
│   ├── hmi_ic_45s_2012_01_29_00_16_30_tai_continuum.fits.fits
│   ├── hmi_ic_45s_2012_01_29_00_31_30_tai_continuum.fits.fits
|   ...
│   └── hmi_ic_45s_2012_01_30_14_46_30_tai_continuum.fits.fits
├── set2
│   ├── hmi_ic_45s_2013_03_04_00_01_30_tai_continuum.fits.fits
│   ├── hmi_ic_45s_2013_03_04_00_44_15_tai_continuum.fits.fits
│   ├── hmi_ic_45s_2013_03_04_00_45_00_tai_continuum.fits.fits
|   ...
│   └── hmi_ic_45s_2013_03_09_17_12_45_tai_continuum.fits.fits
├── set3
│   ├── hmi_ic_45s_2014_09_23_00_02_15_tai_continuum.fits.fits
│   ├── hmi_ic_45s_2014_09_23_00_03_00_tai_continuum.fits.fits
│   ├── hmi_ic_45s_2014_09_23_00_03_45_tai_continuum.fits.fits
|   ...
│   └── hmi_ic_45s_2014_09_28_23_06_00_tai_continuum.fits.fits
├── set4
│   ├── hmi_ic_45s_2015_09_03_00_01_30_tai_continuum.fits.fits
│   ├── hmi_ic_45s_2015_09_03_00_02_15_tai_continuum.fits.fits
│   ├── hmi_ic_45s_2015_09_03_05_46_30_tai_continuum.fits.fits
|   ...
│   └── hmi_ic_45s_2015_09_08_22_56_15_tai_continuum.fits.fits
...

12 directories, 1290 files (in my case)
End of explanation
"""


#from IPython.display import clear_output
# Read header and data from all fits files programatically
#i = 0  # index for files
for file in fits_files:
    fitsfile = fits.open(basepath[0]+'/'+file)
    fitsfile.verify('fix')
    header = fitsfile[1].header
    del fitsfile
    #time.sleep(1)
    #header.values

# Fixing headers programatically
for file in tqdm_notebook(fits_files):
    print(file)
    fitsfile = fits.open(basepath[0]+'/data/'+file)
    fitsfile.verify('fix')
    header = (fitsfile[0].header)
    if header['CUNIT2'] == 'sin(latitude)' and header['CTYPE1'] == 'CRLN-CEA' and header['CTYPE2'] == 'CRLT-CEA':
        tqdm.write("Wrong header information found in %s. Changing..." % file)
        #print("Wrong header information found in %s. Changing..." % file)
        header['CUNIT2'] = 'deg'
        header['CTYPE1'] == 'CSLN-CEA'
        header['CTYPE2'] == 'CSLT-CEA'
    try:
        if header['HGLN_OBS'] == 'nan':
            del header['HGLN_OBS']
    except KeyError:
        #tdqm.write("Warning: Key not found, letting it pass.")
        print("Warning: Key not found, letting it pass.")
        pass
    if header.get('CD1_2') == None:
        header['CD1_2'] = 0
    if header.get('CD2_1') == None:
        header['CD2_1'] = 0

# NOT NEEDED - KEPT FOR REMEMBERING IN THE OCCASION THIS IS NEEDED
#astropy.wcs.utils.WCS_FRAME_MAPPINGS.append([sinehpc_wcs_frame_mapping]) # Adding the map to the WCS astropy utils module

"""
Explanation: Arreglando headers de archivos FITS descargados
Desafortunadamente para ver los resultados hay muchas imágenes que no tienen los headers correctos, entonces es necesario hacer algo como lo que se muestra en este enlace para corregir estos problemas: http://docs.sunpy.org/en/latest/generated/gallery/gallery/hmi_synoptic_maps.html and specially the updated example in: https://gist.github.com/Cadair/cbc73dc7888b9bae5d06708270aedd68
End of explanation
"""


# Esto es para ver alguna imagen en específico, con coordenadas y todo
%matplotlib inline
plt.figure(figsize=(16,9))
# Making the map 
fitsfile = fits.open(basepath[0]+'/'+fits_files[10])
fitsfile.verify('fix')
data = fitsfile[1].data
header = fitsfile[1].header
m = Map((data, header))
m.peek()
# Set the colorbar properties.
#m.plot_settings['cmap'] = 'hmimag'
#m.plot_settings['norm'] = plt.Normalize(-1500, 1500)

# Para dibujar perfiles
plt.plot(data[2048,:])
plt.xlabel('Pixeles')
plt.ylabel('Intensidad')
plt.grid(True)
plt.show()

"""
Explanation: Graficar visualizar resultados de la búsqueda
End of explanation
"""


%matplotlib inline
from IPython.html.widgets import *
number_of_images = len(fits_files)

def image_sequence(image):
    #fitsfile = fits.open(basepath[0]+'/data/'+fits_files[0])
    #print(fitsfile)
    fitsfile = fits.open(basepath[0]+'/'+fits_files[image])
    fitsfile.verify('fix')
    plt.figure(figsize=(16,9))
    plt.imshow(fitsfile[1].data)
    plt.title(fitsfile.filename())
    plt.show()
    
interact(image_sequence, image=(0,number_of_images-1,1), continuous_update=False)

"""
Explanation: Widget to see time evolution
Since we are really dealing with a "big" sequence of images in FITS format it is helpful to have a widget to actually see the sequence and check wether the time range chosen is correct and see what to expect beforehand.
End of explanation
"""


# Módulos básicos para detectar contornos/manchas
from imutils import contours
from skimage import measure
import skimage
#import numpy as np
import imutils
import cv2

"""
Explanation: Procesamiento de datos
Aplicaré algo de lo que dicen acá http://www.pyimagesearch.com/2016/10/31/detecting-multiple-bright-spots-in-an-image-with-python-and-opencv/ para detectar manchas simultáneamente y hacer una estadística automática y "grande".
Tengo que aplicar lo de esa página pero con oscuros, no con claros. Por ahora se aplica a una imágen en específico, luego hay que aplicarla a todas las imágenes y que lance los datos de velocidad/trayectoria.
Algoritmo:
En términos generales el algoritmo es como sigue:
1) Remover background/borde oscuro
2) Suavizar la imagen aplicando un filtro Gaussiano
3) "Binarizando" con umbral
4) Labeling the spots
5) Obteniendo centros de masa (en unidades arcsec)
End of explanation
"""


# Borrar background/contorno negro
## se hace una máscara circular con centro = (2048, 2048)
centerx, centery = 2048, 2048
r = 1920  # radius set1
# r = 1850 # radius set2
# r = 1810 # radius set3
# r = 1810 # radius set4
#r = 1860 # radius set5
#r = 1860 # radius set6
#r = 1850 # radius set7
y,x = np.ogrid[-centerx:4096-centerx, -centery:4096-centery]
mask = x*x + y*y > r*r  # circular mask
#array = np.ones((n, n))
#array[mask] = 255

# FUNCIONES BÁSICAS PARA HACER LO QUE NECESITO (deberían ir en un .py aparte - WIP)
# 
def extract_fits_data(index):
    '''
    Function to extract data from fitsfile given just an index
    '''
    fitsfile = fits.open(basepath[0]+'/'+fits_files[index])
    fitsfile.verify('fix')
    return fitsfile[1].header, fitsfile[1].data

# Apply mask
header, data = extract_fits_data(2)
sun_map = Map((data, header))
data[mask] = 100000
# plot data to check background removal
%matplotlib inline
plt.figure(figsize=(16,9))
plt.imshow(data)
#plt.figure()
#plt.imshow(extract_fits_data(14))
#plt.show()

"""
Explanation: 1) Remover background/borde oscuro, de medidas en casos anteriores sabemos que
3851 px = Diametro horizontal sol
3967 - 117 = 3850 px = Diametro vert. sol
Recordar que X -> vertical y Y -> horizontal, para tratamiento de imágenes.
La imagen es de 4096 x 4096 y si asumimos que la imagen está bien centrada en 2048, entonces podemos hacer un círculo de radio $r\le 3850/2 \approx 1925$ pixeles, tal que lo que esté por fuera de este círculo se vuelva "nan" y lo de adentro se quede igual.
Más información en: http://stackoverflow.com/questions/8647024/how-to-apply-a-disc-shaped-mask-to-a-numpy-array
End of explanation
"""


# Just blur the image with a gaussian filter
blurred = cv2.GaussianBlur(data, (31, 31), 0)
#blurred = cv2.GaussianBlur(data, (11, 11), 0) # set 4

# Plot blurred image
%matplotlib inline
plt.figure(figsize=(16,9))
plt.imshow(blurred)
plt.colorbar()
plt.show()

"""
Explanation: 2) Suavizar la imagen aplicando un filtro Gaussiano
End of explanation
"""


thresh = cv2.threshold(blurred, 29250, 74078, cv2.THRESH_BINARY)[1]

# Plot binary image
%matplotlib inline
plt.figure(figsize=(16,9))
plt.imshow(thresh)
plt.colorbar()
plt.show()

# Removing small "blobs" with erode and dilate
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)

"""
Explanation: Al parecer usar 31 en el filtro Gaussiano da buenos resultados con esta imagen que tiene algunas manchas MUY pequeñas, de tal forma que puedo descartar todas las cosas que sean más brillantes a 4.5e4 (45000), detectando así solo las dos manchas grandes. Porque no puedo garantizar que las manchas pequeñas siempre sean visibles.
3) "Binarizando" con umbral
End of explanation
"""


labels = measure.label(thresh, neighbors=8, background=74078)
mask = np.zeros(thresh.shape, dtype="uint8")
# loop over the unique components
for label in np.unique(labels):
    # if this is the background label, ignore it
    if label == 0:
        continue
 
    # otherwise, construct the label mask and count the
    # number of pixels 
    labelMask = np.zeros(thresh.shape, dtype="uint8")
    labelMask[labels == label] = 255
    numPixels = cv2.countNonZero(labelMask)
 
    # if the number of pixels in the component is sufficiently
    # large, then add it to our mask of "large blobs"
    if numPixels > 30: # set1
    #if numPixels > 20: # set4
    #if numPixels > 200: # set5
    #if numPixels > 200: # set6
    #if numPixels > 50: # set7
        mask = cv2.add(mask, labelMask)

# Plot the resulting mask
# Plot binary image
%matplotlib inline
plt.figure(figsize=(16,9))
plt.imshow(mask)
plt.colorbar()
plt.show()

image = data.astype("uint8")
# find the contours in the mask, then sort them from left to right
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
cnts = contours.sort_contours(cnts)[0]
#cv2.namedWindow("Image", cv2.WINDOW_NORMAL or cv2.WINDOW_AUTOSIZE)  # To show window that's larger than screen 
# loop over the contours
centros_de_masa = []
for (i, c) in enumerate(cnts):
    # draw the bright spot on the image
    (x, y, w, h) = cv2.boundingRect(c)
    ((cX, cY), radius) = cv2.minEnclosingCircle(c)
    print(cX,cY)
    centros_de_masa.append([cX, cY])
    cv2.circle(image, (int(cX), int(cY)), int(radius), (0, 0, 255), 3)
    cv2.putText(image, "#{}".format(i + 1), (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)

# show the output image
#cv2.imshow("Image",image)
#cv2.waitKey(0)

"""
Explanation: 4) Labeling the spots
Using https://en.wikipedia.org/wiki/Connected-component_labeling
End of explanation
"""


# hago mapa de SunPy
sun_map = Map((data, header))

for i in centros_de_masa:
    print(sun_map.pixel_to_data(i[0]*u.pix, i[1]*u.pix))

"""
Explanation: 5) Obteniendo centros de masa
De lo anterior se obtienen centros en pixeles, ahora tengo que pasar estos centros a centros de masa en coordenadas de sunpy (arcsec).
End of explanation
"""


sun_map.coordinate_frame

from astropy.coordinates import SkyCoord

import sunpy.map
import sunpy.coordinates
import sunpy.coordinates.wcs_utils

CoM_pix = centros_de_masa*u.pix # Transforming CoM to astropy pix unit
CoM_arcsec = [sun_map.pixel_to_data(*i) for i in CoM_pix] # Then transforming them to arcsec
#print(CoM_arcsec)
hpc_coords = SkyCoord([*CoM_arcsec], frame=sun_map.coordinate_frame)  # Built skycoord object with sunspots/CoM coords
#print(hpc_coords)
hgs_coords = hpc_coords.transform_to('heliographic_stonyhurst')  # Transform from HPC to HGS coordinate system
print(hgs_coords)
type(hgs_coords.data)

hgs_coords.data[1].lon.deg # acceder a dato numérico "puro" de lat o lon en grados

for i in hgs_coords.data:
    print(i.lon.deg)
    print(i.lat.deg)

"""
Explanation: Transforming coordinates
Coordinates from the data of HMI are in Helioprojective-cartesian, to account for changes in the perspective of the Sun since the observer is sometimes above Sun's equator, sometimes below and the north pole is not always the highest point in the image (check http://jgiesen.de/sunrot/index.html for visualization and explanations) we have to perform a coordinate transformation to use Stonyhurst-Heliocentric coordinates. Fortunately astropy does this for us.
Based on code from http://docs.sunpy.org/en/latest/generated/gallery/tutorials/skip_SDO_to_STEREO_Coordinate_Conversion.html#sphx-glr-generated-gallery-tutorials-skip-sdo-to-stereo-coordinate-conversion-py
End of explanation
"""


# Configparser to parse options for each set of data
from configparser import ConfigParser

# Read data set parameters from config file
config = ConfigParser()
# TODO

def plot_data(data, title):
    %matplotlib inline
    plt.figure(figsize=(16,9))
    plt.title(title)
    plt.imshow(data)
    plt.colorbar()
    plt.show()

number_of_spot = 1  # Initiliazing variable set1
#number_of_spot = 0 # Initiliazing variable set4
#number_of_spot = 0 # Initiliazing variable set5
#number_of_spot = 0 # Initiliazing variable set6
#number_of_spot = 0 # Initiliazing variable set7
#number_of_spot = 0 # Initiliazing variable set8
#number_of_spot = 0 # Initiliazing variable set9
#number_of_spot = 1 # Initiliazing variable set10
centros_de_masa = 0 # Initializing variable

longitudes = []
latitudes = []
datetimes = []

#for imag_idx in range(1,140):  # For testing purposes
#for imag_idx in [1,140]:
#for imag_idx in range(15,len(fits_files)): # set 4
for imag_idx in range(len(fits_files)):
    # 0) Extract header and data and make map
    header, data = extract_fits_data(imag_idx)
    sun_map = Map((data, header))
    
    # Prior to applying mask
    # Borrar background/contorno negro
    ## se hace una máscara circular con centro = (2048, 2048)
    centerx, centery = 2048, 2048
    r = 1920  # radius set1
    #r = 1850  # radius set4
    # r = 1860  # radius set5
    #r = 1860  # radius set6
    #r = 1850  # radius set7
    #r = 1850  # radius set8
    #r = 1850  # radius set9
    #r = 1850  # radius set10
    y,x = np.ogrid[-centerx:4096-centerx, -centery:4096-centery]
    mask = x*x + y*y > r*r  # circular mask
    #array = np.ones((n, n))
    #array[mask] = 255
        
    # 1) Apply mask
    data[mask] = 100000
    #plot_data(data, "data masked") # plot data masked
    
    # 2) Apply Gaussian filter
    blurred = cv2.GaussianBlur(data, (31, 31), 0) # set1
    #blurred = cv2.GaussianBlur(data, (11, 11), 0) # set4
    #blurred = cv2.GaussianBlur(data, (11, 11), 0) # set5
    #blurred = cv2.GaussianBlur(data, (11, 11), 0) # set6
    #blurred = cv2.GaussianBlur(data, (11, 11), 0) # set7
    #blurred = cv2.GaussianBlur(data, (11, 11), 0) # set8
    #blurred = cv2.GaussianBlur(data, (11, 11), 0) # set9
    #blurred = cv2.GaussianBlur(data, (11, 11), 0) # set10
    #plot_data(blurred, "data blurred") # plot data masked
    
    # 3) Binarizing
    thresh = cv2.threshold(blurred, 29250, 74078, cv2.THRESH_BINARY)[1]  # set1
    #thresh = cv2.threshold(blurred, 28000, 74078, cv2.THRESH_BINARY)[1]  # set2
    #thresh = cv2.threshold(blurred, 28000, 74078, cv2.THRESH_BINARY)[1]  # set3
    #thresh = cv2.threshold(blurred, 27000, 74078, cv2.THRESH_BINARY)[1]  # set4
    #thresh = cv2.threshold(blurred, 29000, 74078, cv2.THRESH_BINARY)[1]  # set5
    #thresh = cv2.threshold(blurred, 27000, 74078, cv2.THRESH_BINARY)[1]  # set6
    #thresh = cv2.threshold(blurred, 26000, 74078, cv2.THRESH_BINARY)[1]  # set7
    #thresh = cv2.threshold(blurred, 26000, 74078, cv2.THRESH_BINARY)[1]  # set8
    #thresh = cv2.threshold(blurred, 26000, 74078, cv2.THRESH_BINARY)[1]  # set9
    #thresh = cv2.threshold(blurred, 22000, 74078, cv2.THRESH_BINARY)[1]  # set10
    ## Removing small "blobs" with erode and dilate
    thresh = cv2.erode(thresh, None, iterations=2)
    thresh = cv2.dilate(thresh, None, iterations=2)
    #plot_data(thresh, "data binarized") # plot data masked
    
    # 4) Labeling and computing center in pixels
    labels = measure.label(thresh, neighbors=8, background=74078)
    mask = np.zeros(thresh.shape, dtype="uint8")
    # loop over the unique components
    for label in np.unique(labels):
        # if this is the background label, ignore it
        if label == 0:
            continue

        # otherwise, construct the label mask and count the
        # number of pixels 
        labelMask = np.zeros(thresh.shape, dtype="uint8")
        labelMask[labels == label] = 255
        numPixels = cv2.countNonZero(labelMask)

        # if the number of pixels in the component is sufficiently
        # large, then add it to our mask of "large blobs"
        if numPixels > 30: # set1
        #if numPixels > 50: # set4
        #if numPixels > 200: # set5
        #if numPixels > 200: # set6
        #if numPixels > 250: # set7
        #if numPixels > 250: # set8
        #if numPixels > 250: # set9
        #if numPixels > 50: # set10
            mask = cv2.add(mask, labelMask)
    image = data.astype("uint8")
    # 5) Obteniendo centros de masa (en pixeles)
    # find the contours in the mask, then sort them from left to right
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if imutils.is_cv2() else cnts[1]
    cnts = contours.sort_contours(cnts)[0]
    #cv2.namedWindow("Image", cv2.WINDOW_NORMAL or cv2.WINDOW_AUTOSIZE)  # To show window that's larger than screen 
    # loop over the contours
    centros_de_masa_old = centros_de_masa
    centros_de_masa = []
    number_of_spot_old = number_of_spot
    for (number_of_spot, c) in enumerate(cnts):
        # draw the bright spot on the image
        (x, y, w, h) = cv2.boundingRect(c)
        ((cX, cY), radius) = cv2.minEnclosingCircle(c)
        #print(cX,cY)
        # cX and cY I have to transform to HGS coords
       
        # 6) Transforming coordinates
        centros_de_masa.append([cX, cY])        
        CoM_pix = centros_de_masa*u.pix # Transforming CoM to astropy pix unit
        CoM_arcsec = [sun_map.pixel_to_data(*i) for i in CoM_pix] # Then transforming them to arcsec
        #print(CoM_arcsec)
        hpc_coords = SkyCoord([*CoM_arcsec], frame=sun_map.coordinate_frame)  # Built skycoord object with sunspots/CoM coords
        #print(hpc_coords)
        hgs_coords = hpc_coords.transform_to('heliographic_stonyhurst')  # Transform from HPC to HGS coordinate system
        print(hgs_coords)
        #type(hgs_coords.data)
        
        cv2.circle(image, (int(cX), int(cY)), int(radius), (0, 0, 255), 3)
        cv2.putText(image, "#{}".format(number_of_spot + 1), (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
    print("Image index: ", imag_idx)
    print("Detected ", number_of_spot+1, " spots.")
    if number_of_spot_old != number_of_spot:
        print("Last index image calculated: ", imag_idx)
        raise ValueError("Number of spots detected different from before. Stoping calculation.")
        
    # 7) Getting centers of mass in lat,long (deg)
    # hago mapa de SunPy
    #sun_map = Map((data, header))
    longitude_temp = []
    latitude_temp = []
    for i in hgs_coords.data:
        print(i.lon.deg)
        print(i.lat.deg)
        longitude, latitude = i.lon.deg, i.lat.deg  # pair longitude, latitude for a single spot
        longitude_temp.append(longitude)
        latitude_temp.append(latitude)
        #print(longitude, latitude)
    #print(longitude_temp)
    longitudes.append(longitude_temp)
    latitudes.append(latitude_temp)
    datetimes.append(sun_map.date)
    
        
    # Borrar todas las variables para evitar errores
    del(sun_map, header, mask, data, blurred, thresh, image, longitude_temp, hpc_coords, hgs_coords)

# Volver arreglos de tiempos, latitudes, longitudes un arreglo numérico de numpy
times = []
deltatimes = []
longitudes_0 = []
longitudes_1 = []
latitudes_0 = []
latitudes_1 = []
delta_longitudes_0 = []
delta_longitudes_1 = []

# Times in seconds since first observation/image
for i in range(len(datetimes)):
    temp = datetimes[i]-datetimes[0]
    times.append(temp.total_seconds())
# Datetimes deltas in seconds
for i in range(len(datetimes)-1):
    delta_t = datetimes[i+1] - datetimes[i]
    #print(datetimes[i+1], " - ", datetimes[i]," = ", delta_t)
    deltatimes.append(delta_t.total_seconds())
# Longitudes and latitudes in arccsec (assuming len(longitudes) == len(latitudes))
for i in range(len(longitudes)):
    longitudes_0.append(longitudes[i][0])
    latitudes_0.append(latitudes[i][0])
    longitudes_1.append(longitudes[i][1]) # set1
    latitudes_1.append(latitudes[i][1]) # set1

times = np.array(times)
deltatimes = np.array(deltatimes)
longitudes_0 = np.array(longitudes_0)
longitudes_1 = np.array(longitudes_1)
latitudes_0 = np.array(latitudes_0)
latitudes_1 = np.array(latitudes_1)



# Saving data arrays in text file in the filesystem for storage
setstr = "set1"
np.savetxt(setstr+"_times.txt", times)
np.savetxt(setstr+"_deltatimes.txt", deltatimes)
np.savetxt(setstr+"_longitudes_0.txt", longitudes_0)
np.savetxt(setstr+"_longitudes_1.txt", longitudes_1)
np.savetxt(setstr+"_latitudes_0.txt", latitudes_0)
np.savetxt(setstr+"_latitudes_1.txt", latitudes_1)

"""
Explanation: Aplicando a todo el set
Lo anterior consiste en el laboratorio para ver los diferentes pasos por separado y entender los resultados de cada uno de los pasos del algoritmo. A continuación se unen todos estos pasos y se aplican a TODO el conjunto de imágenes.
Algoritmo:
En términos generales el algoritmo es como sigue:
1) Remover background/borde oscuro (aplicar máscara)
2) Suavizar la imagen aplicando un filtro Gaussiano
3) "Binarizando" con umbral
4) Labeling the spots
5) Obteniendo centros de masa (en unidades de pixeles)
6) Transformar coordenadas a HGS (Heliographic-Stonyhurst)
7) Obtener datos latitud/longitud en degrees
8) Guardar datos en archivos de texto
Nota
Para cada set de datos es necesario aplicar diferentes valores para los parámetros del procesamiento de imágenes, entonces se usará un archivo de configuración (del tipo ConfigParser) para leer y guardar estos parámetros para cada set.
End of explanation
"""


