Newer
Older
Mattia Mancini
committed
import logging
import os
import matplotlib
matplotlib.use('agg')
Mattia Mancini
committed
import matplotlib.pyplot as plt
import numpy
from celery import shared_task
from django.conf import settings
from .models.rtsm import MODE_TO_COMPONENT, RTSMErrorSummary, RTSMSummaryPlot
Mattia Mancini
committed
logger = logging.getLogger(__name__)
class Metadata:
def __init__(self, **kwargs):
for key, value in kwargs.items():
self.__setattr__(key, value)
MODE_STR = {1: 'LBA Low 10..90 MHz',
2: 'LBA Low 30..90',
3: 'LBA High 10..90',
4: 'LBA High 30..90',
5: 'HBA 110..190',
6: 'HBA 170..230',
7: 'HBA 210..250'}
Mattia Mancini
committed
class ObservationMetadata(Metadata):
station_name = 'CSXXXC'
start_datetime = '1212-12-12 12:12:12'
end_datetime = '1212-12-12 12:12:12'
observation_id = 'XXXXXX'
samples = 120
@staticmethod
def __format_datetime(datetime):
return datetime.strftime("%Y-%m-%d %H:%M:%S")
def set_start_time(self, start_time):
self.start_datetime = ObservationMetadata.__format_datetime(start_time)
Mattia Mancini
committed
def set_end_time(self, end_time):
self.end_datetime = ObservationMetadata.__format_datetime(end_time)
Mattia Mancini
committed
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
def set_style():
plt.minorticks_on()
plt.ylim(55, 160)
plt.grid()
# plt.rcParams['svg.fonttype'] = 'none'
plt.rcParams['lines.linewidth'] = 1
plt.rcParams['figure.autolayout'] = True
plt.rcParams['axes.labelsize'] = 'large'
plt.rcParams['path.simplify'] = True
plt.rcParams['savefig.transparent'] = False
plt.rcParams['savefig.bbox'] = 'tight'
plt.rcParams['legend.loc'] = 'upper right'
plt.rcParams['legend.frameon'] = True
plt.rcParams['legend.framealpha'] = 1
plt.rcParams['legend.edgecolor'] = 'black'
def rcu_mode_to_antenna(rcu, mode):
component_name = MODE_TO_COMPONENT[mode]
antenna = rcu // 2
if component_name in ['LBH', 'HBA']:
polarization = 'X' if rcu % 2 == 0 else 'Y'
elif component_name == 'LBL':
antenna = rcu // 2 + 48
polarization = 'Y' if rcu % 2 == 0 else 'X'
else:
raise ValueError('Unknown component %s' % component_name)
return dict(polarization=polarization, antenna=antenna)
def render_summary_text(error_metadata, observation_metadata):
"""
Render the summary text to include in the picture
:param error_metadata: the metadata information regarding the error
:param observation_metadata: the metadata information regarding the observation
:return:
"""
rendered_text = 'Station Name: {:<25s}'.format(observation_metadata.station_name)
rendered_text += 'Faults : {}\n'.format(error_metadata.error_type)
rendered_text += 'ObsID : {:<25d}'.format(observation_metadata.observation_id)
rendered_text += 'Antenna : {antenna} {polarization}\n' \
.format(**rcu_mode_to_antenna(error_metadata.rcu, error_metadata.mode))
rendered_text += 'Start : {:<25s}'.format(observation_metadata.start_datetime)
rendered_text += 'RCU : {}\n'.format(error_metadata.rcu)
rendered_text += 'Stop : {:<25s}'.format(observation_metadata.end_datetime)
rendered_text += 'Samples : {count:d}/{samples:d}\n'.format(count=error_metadata.count,
samples=observation_metadata.samples)
rendered_text += 'Mode : {:<1d} {:<23s}'.format(error_metadata.mode,
MODE_STR[error_metadata.mode])
Mattia Mancini
committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
rendered_text += 'Badness : {:.2f} %\n'.format(
(100. * error_metadata.count) / observation_metadata.samples)
return rendered_text
def produce_plot(observation_metadata,
error_metadata,
list_bad_spectra, list_good_specta, path):
"""
Produces a plot given the observation metadata the error summary metadata and
the list of the bad spectra for the given (error, rcu) and the average spectrum
of the rest of the array
:param observation_metadata: the metadata information regarding the observation
:param error_metadata: the metadata information regarding the error
:param list_bad_spectra: a list containing the bad spectra for the given error, rcu couple
:param list_good_specta: the average spectrum of the rest of the array
:param path: the path where to store the file
"""
plt.figure(figsize=(12, 9))
set_style()
plt.xlabel('MHz')
plt.ylabel('dB')
frequency_sampling = len(list_bad_spectra[0][0])
n_spectra = len(list_bad_spectra)
frequency = numpy.linspace(error_metadata.start_frequency,
error_metadata.stop_frequency,
frequency_sampling)
bad_spectra_cube = numpy.zeros((n_spectra, frequency_sampling))
average_spectra_cube = numpy.zeros((n_spectra, frequency_sampling))
for i, spectrum in enumerate(list_bad_spectra):
bad_spectra_cube[i, :] = spectrum[0]
for i, spectrum in enumerate(list_good_specta):
average_spectra_cube[i, :] = spectrum[0]
min_bad_spectrum = numpy.min(bad_spectra_cube, axis=0)
max_bad_spectrum = numpy.max(bad_spectra_cube, axis=0)
average_bad_spectrum = numpy.median(bad_spectra_cube, axis=0)
average_good_spectrum = numpy.median(average_spectra_cube, axis=0)
plt.fill_between(frequency, min_bad_spectrum, max_bad_spectrum, color='red',
alpha=.3)
plt.plot(frequency, average_bad_spectrum, color='red', label='median bad spectra')
plt.plot(frequency, average_good_spectrum, color='blue', label='median all spectra')
summary_text = render_summary_text(observation_metadata=observation_metadata,
error_metadata=error_metadata)
plt.text(0.02, .98, summary_text, bbox=dict(facecolor='yellow', alpha=0.2),
family='monospace', verticalalignment='top',
horizontalalignment='left',
transform=plt.axes().transAxes)
plt.legend()
plt.savefig(path)
plt.close()
def generate_summary_plot_for_error(error_summary_id):
"""
Given a error summary id generates the plot in the specific location given by the settings
parameter URL_TO_STORE_RTSM_PLOTS and the file name derived in the following fashion
%(observation_id)d_%(station_name)s_rcu%(rcu)d_%(error_type)s.png
Finally stores in the database the file name to directly access the file
:param error_summary_id: database id of the error summary
:return: the database id of the RTSMSummaryPlot instance
:raise Exception: raise if there is an exception
"""
basePath = settings.URL_TO_STORE_RTSM_PLOTS
error_summary = RTSMErrorSummary.objects.get(pk=error_summary_id)
summary_plot = error_summary.summary_plot.first()
if summary_plot is not None:
if summary_plot.uri == None:
return 'another worker is taken care of the task...'
summary_plot = RTSMSummaryPlot(error_summary=error_summary, uri=None)
summary_plot.save()
try:
observation_metadata = ObservationMetadata()
observation = error_summary.observation
observation_metadata.observation_id = observation.observation_id
observation_metadata.station_name = observation.station.name
observation_metadata.set_start_time(observation.start_datetime)
observation_metadata.set_end_time(observation.end_datetime)
Mattia Mancini
committed
observation_metadata.samples = observation.samples
errors = observation.errors.filter(mode=error_summary.mode,
rcu=error_summary.rcu,
error_type=error_summary.error_type)
list_bad_spectra = errors.values_list('spectra__bad_spectrum')
list_good_spectra = errors.values_list('spectra__average_spectrum')
file_name = '%(observation_id)d_%(station_name)s_rcu%(rcu)d_%(error_type)s.png' % dict(
observation_id=observation_metadata.observation_id,
station_name=observation_metadata.station_name,
rcu=error_summary.rcu,
error_type=error_summary.error_type
)
full_path = os.path.join(basePath, file_name)
produce_plot(observation_metadata,
error_summary,
list_bad_spectra, list_good_spectra,
full_path)
summary_plot.uri = file_name
summary_plot.save()
Mattia Mancini
committed
return summary_plot.pk
Mattia Mancini
committed
except Exception as e:
logger.exception('exception %s occurred skipping...', e)
summary_plot.delete()
raise e
Mattia Mancini
committed
@shared_task()
def check_error_summary_plot(error_summary_id):
Mattia Mancini
committed
"""
Checks if the error summary plot is presents otherwise it generates one
:param self: shared_task
:type self: celery.shared_task()
:param error_summary_id: database id of the RTSMErrorSummary to check
:return:
"""
Mattia Mancini
committed
logger.debug('looking for rtsm error summary %s', error_summary_id)
Mattia Mancini
committed
error_summary = RTSMErrorSummary.objects.get(pk=error_summary_id)
summary_plot = error_summary.summary_plot.first()
Mattia Mancini
committed
logger.debug('summary error found %s', summary_plot)
Mattia Mancini
committed
if summary_plot is None:
return generate_summary_plot_for_error(error_summary_id)
else:
base_path = settings.URL_TO_STORE_RTSM_PLOTS
if summary_plot.uri is None:
return 'another worker is taking care of the task...'
full_path = os.path.join(base_path, summary_plot.uri)
if not os.path.exists(full_path):
return generate_summary_plot_for_error(error_summary_id)
elif os.path.isdir(full_path):
raise Exception('%s is a directory' % full_path)
else:
logger.debug('summary error %s is complete no need to generate additional plot',
summary_plot.pk)
Mattia Mancini
committed
return summary_plot.pk