I am trying to demodulate an analog video signal of PAL format and encountered a problem in demodulating the color part. So my input data is as follows: I have 2500 spectra, spectrum width 200 MHz, my sample rate 245.76 MHz. The time of each spectrum is 33 μs. The signal is at a frequency of 5820 MHz and has the following form:

I successfully manage to demodulate the luma component and get a picture in black and white background

But when I try to get a picture in color, nothing happens. The signal I get after demodulating the chroma looks terrible and I don't know what to do.

The photo should be like this, I modulate the signal through the hacktv program using hackrf:

The entire code that I use for chroma demodulation is given below. Please, if anyone knows how to properly perform chroma demodulation, tell me. I've been trying to do this for over 3 months and nothing works.
class MyApp(QMainWindow):
def __init__(self, full_signal):
super().__init__()
self.UV = None
self.real_env = None
self.imag_env = None
self.signal_freq = None
self.full_signal = full_signal
uic.loadUi("main.ui", self)
self.figure_time = Figure()
self.canvas_time = FigureCanvas(self.figure_time)
self.plot_signal_time_real = self.figure_time.add_subplot(111)
self.figure_time.tight_layout()
self.line_time_real = None
self.line_time_real_env = None
self.line_time_imag = None
self.line_time_imag_env = None
toolbar_time = NavigationToolbar(self.canvas_time, self)
layout_time = QVBoxLayout(self.findChild(QWidget, "widget_plot_time"))
layout_time.setContentsMargins(0, 0, 0, 0)
layout_time.addWidget(toolbar_time)
layout_time.addWidget(self.canvas_time)
self.figure_freq = Figure()
self.canvas_freq = FigureCanvas(self.figure_freq)
self.plot_signal_freq = self.figure_freq.add_subplot(111)
self.figure_freq.tight_layout()
self.line_freq = None
toolbar_freq = NavigationToolbar(self.canvas_freq, self)
layout_freq = QVBoxLayout(self.findChild(QWidget, "plot_freq"))
layout_freq.setContentsMargins(0, 0, 0, 0)
layout_freq.addWidget(toolbar_freq)
layout_freq.addWidget(self.canvas_freq)
self.figure_imag = Figure()
self.canvas_imag = FigureCanvas(self.figure_imag)
self.plot_signal_imag = self.figure_imag.add_subplot(111)
self.figure_imag.tight_layout()
self.line_imag = None
toolbar_imag = NavigationToolbar(self.canvas_imag, self)
layout_imag = QVBoxLayout(self.findChild(QWidget, "plot_imag"))
layout_imag.setContentsMargins(0, 0, 0, 0)
layout_imag.addWidget(toolbar_imag)
layout_imag.addWidget(self.canvas_imag)
#############################
self.stepSlider = 1
self.sliderShift.valueChanged.connect(self.on_slider_value_changed)
self.sliderShift.setMaximum(LENGTH_SPECTRUM_CUT)
self.sliderShift.setMinimum(0)
self.buttonUpdate.clicked.connect(self.plot_example)
self.tabWidget.currentChanged.connect(self.on_tab_changed)
###############################################################################
def on_slider_value_changed(self, value):
rounded_value = (value // self.stepSlider) * self.stepSlider
if rounded_value != value:
self.sliderShift.blockSignals(True)
self.sliderShift.setValue(rounded_value)
self.sliderShift.blockSignals(False)
self.textSlider.setText(str(rounded_value))
self.plot_example(rounded_value)
##########################################################################################################################
def plot_example(self, shiftSignal=0):
self.UV = []
self.real_env = []
self.imag_env = []
fs = 4.43e6
band_with = 1e6
low = (fs - band_with) / (sample_rate / 2)
high = (fs + band_with) / (sample_rate / 2)
for i in range(0, NSpec):
spectr = self.full_signal[i]
spectr = np.roll(spectr, signalShift)
right_spectr = spectr[int(8192 / 2 - (LENGTH_SPECTRUM_CUT // 2)):int(8192 / 2 + (LENGTH_SPECTRUM_CUT // 2))]
spectr_512 = np.roll(right_spectr, shiftSignal)
if i == 0:
self.signal_freq = np.abs(spectr_512)
signal_time = np.fft.ifft(np.fft.ifftshift(spectr_512))
b, a = signal.butter(10, [low, high], btype='bandpass')
chroma_signal = signal.filtfilt(b, a, signal_time)
chroma_signal = np.roll(np.fft.fftshift(np.fft.fft(chroma_signal)), -int(fs + (band_with // 2) // 30))
chroma_signal = np.fft.ifft(np.fft.fftshift(chroma_signal))
b_lp, a_lp = signal.butter(5, 1e6 / (sample_rate / 2))
signal_time = signal.filtfilt(b_lp, a_lp, chroma_signal)
self.UV.append(signal_time)
self.UV = np.concatenate(self.UV)
self.plot_time()
self.plot_frequency()
self.plot_image()
##########################################################################################################################
def plot_time(self):
if not self.UV is None:
if self.line_time_real is None:
self.plot_signal_time_real.clear()
self.line_time_real, = self.plot_signal_time_real.plot(np.real(self.UV), label="Real")
self.plot_signal_time_real.legend("real")
self.plot_signal_time_real.grid(True)
else:
self.line_time_real.set_ydata(np.real(self.UV))
self.plot_signal_time_real.relim()
self.plot_signal_time_real.autoscale_view()
self.canvas_time.draw()
##########################################################################################################################
def plot_frequency(self):
if not self.signal_freq is None:
if self.line_freq is None:
self.plot_signal_freq.clear()
self.line_freq, = self.plot_signal_freq.plot(self.signal_freq)
self.plot_signal_freq.set_title("Синус")
self.plot_signal_freq.grid(True)
else:
self.line_freq.set_ydata(self.signal_freq)
self.line_freq.set_xdata(np.arange(len(self.signal_freq)))
self.plot_signal_freq.relim()
self.plot_signal_freq.autoscale_view()
self.canvas_freq.draw()
##########################################################################################################################
def plot_image(self):
U_lines = np.real(self.UV)
V_lines = np.imag(self.UV)
U_lines = U_lines / np.max(np.abs(U_lines))
V_lines = V_lines / np.max(np.abs(V_lines))
image = []
picture = []
for i in range(0, testFrame.indexLine):
Y = IQ[testFrame.index_start_line[i]:testFrame.index_start_line[i] + SAMPLE_IN_FULL_NOT_SYNC]
U = U_lines[testFrame.index_start_line[i]:testFrame.index_start_line[i] + SAMPLE_IN_FULL_NOT_SYNC]
V = V_lines[testFrame.index_start_line[i]:testFrame.index_start_line[i] + SAMPLE_IN_FULL_NOT_SYNC]
if i % 2 == 0:
V = V * -1
R = Y + 1.13983 * V
G = Y - 0.39465 * U - 0.58060 * V
B = Y + 2.03211 * U
R = np.clip(R * 255, 0, 255).astype(np.uint8)
G = np.clip(G * 255, 0, 255).astype(np.uint8)
B = np.clip(B * 255, 0, 255).astype(np.uint8)
image.append(np.stack([R, G, B], axis=-1))
picture.append(image)
for im in picture:
if self.line_imag is None:
self.line_imag = self.plot_signal_imag.imshow(im, aspect='auto')
self.plot_signal_imag.axis("off")
else:
self.line_imag.set_data(im)
self.canvas_imag.draw()
def on_tab_changed(self, index):
if index == 0:
self.plot_frequency()
elif index == 1:
self.plot_image()