# Recover high pass filtered square wave

I'm designing UHF RFID receiver. My receiver has high pass filter and I would like to recover original square wave and then use matched filter. My high pass filter is in hardware and it is difficult to remove. Received signal is FM0 encoded (bi-phase space). Bit one is long pulse and bit zero is short pulse. Long pulse is about two times length of short pulse. In addition, there is preamble, which has one longer pulse (1.5x long pulse).

Is there filter, which I can use to recover original square wave (desired output from input signal)? I have tried to add integral length of short pulse, but it would only recover short pulses.

• Do you know parameters of the hardware HP filter (type, order, cut-off frequency, BW/Q)? By swapping a and b coefficients you get reversed filter response. i.postimg.cc/wjX4Ly9L/reversehpf.png . Commented Apr 16 at 7:58
• I have DC block in my hardware and it should be first order filter. Cut-off frequency is adjustable, but we can assume it is fixed value. So I need to figure out, what is a and b of my hardware filter and then swap them? Commented Apr 16 at 8:02
• If it is first order HPF then not much needed to do to get it solved - dsp.stackexchange.com/questions/93446/… Commented Apr 16 at 8:57
• This seems to work as expected. I was able to recover original signal by swapping a and b. Commented Apr 16 at 10:14

From an electronics point of view, your high-pass filter has strong response to the edges, so that you can just use a window comparator to restore the edges and use an edge-triggered flipflop to get back the square wave:

The green lines signify the threshold above and below which your window comparison yields a logical "high" (red line), and then you just toggle on the rising edges of that (pink). (looking at that, I manually did this and forgot to respect the falling edge at ca t=550, oops. You still get the principle, I guess).

That's easy to implement in code, something like (pseudocode/python-style)

previous_out = False
previous_thresh = False
for sample_in, sample_out in (signal, output_buffer):
outside_window = (sample_in < -0.3 or sample_in > 0)
if previous_thresh is False and outside_window: # we just crossed outside the window
previous_out = not previous_out
previous_thresh = outside_window
sample_out = previous_out


i.e., coding-wise, a very simple finite state machine.

Adding the logic to convert that to bits is easy, as well, because every time you toggle the output state, you can just start counting a counter; if the counter is below a certain value you get a zero, between that and 1.5 that you get a one, above that, you get preamble. (when you sit down, you'll notice that if you want to make a matched filter for the 0 and one for the zero, end then form the difference between these, to get a variable that's positive if you see a one and negative if you see a zero, it simply becomes a sum that is just proportional to the length of high in our output_buffer above. So, it's identical to just counting how long you're high, and setting a threshold between the ideal zero length and the ideal one length at which you decide that it's a one or a zero. So, the counter approach does the same as your matched filtering.)

Note that it'd be smart (probably!) to first try to low-pass filter the output of your high-pass filtering before you do any conversion to rectangular waves: as you can see in your plots, the input signal is noisy, and thus might "jump" around a bit, just as you cross any threshold. You don't need the edges to be sharp when they cross the threshold, you just need them to reliably cross the threshold. As Juha indicated in the comments, ideally, your prefiltering would largely remove the effect of the high-pass filter (making it an equalizer), but that's probably unattainable without risking adverse effects (noise amplification). Here's an example of the difference low-pass filtering would make to your signal:

Which gives us these signals:

As you can see in the lowest plot, low-pass filtering does reduce the height of the peaks, but makes them "cleaner". You can find the simulation GNU Radio flow graph show above for download here.

I was able to recover signal by swapping a and b of my known hardware HPF. It is a bit tricky to find all parameters for the filter, but results are pretty good.

It seems that recovered signal has still some low frequency error, so some addition filtering might improve the match between desired and recovered signal.

Original data (contains I and Q data) should found from the link https://pastebin.com/ysiaDBsb

Octave code is as following:

close all;
clear;

Fs = 2e6;   % Sampling frequency (Hz)

I_values=data(:,1);
Q_values=data(:,2);

binary_vector=[1,1,0,1,0,0,1,0,0,0,1,1,0,1,0,0,1,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,1,1];
% Define the duration of each bit (in samples)
bit_duration = 25;

% Initialize the vector for the signal
signal = [];

% Iterate through the binary vector
for bit = binary_vector
% Generate samples for each bit
if bit == 1
bit_samples = ones(1, bit_duration);
else
bit_samples = zeros(1, bit_duration);
end

% Append the samples to the signal vector
signal = [signal, bit_samples];
end

I_desired=zeros(1,337);
I_desired=[I_desired signal];
I_desired=[I_desired zeros(1,1+2000-length(I_desired))];
Q_desired=-I_desired;

figure(1);
subplot(3,1,1)
plot(I_values);
title('Input signal');
axis([0 2000]);
grid on;

subplot(3,1,2)
plot(I_desired);
hold on;
plot(Q_desired);
hold off;
title('Desired output');
axis([0 2000 -2 2]);
grid on;

matched_filter=ones(1,25);

I_desired_matched=filter(matched_filter,25,I_desired);
Q_desired_matched=filter(matched_filter,25,Q_desired);

subplot(3,1,3)
plot(I_desired_matched);
hold on;
plot(Q_desired_matched);

hold off;

title('Matched filter output');
axis([0 2000]);
grid on;

%I parameters
Ai=-0.1526;
Bi=0.33;
cutoff_frequency = 26000; % Cutoff frequency (Hz)

% Design a first-order high-pass Butterworth filter
[filter_b, filter_a] = butter(1, cutoff_frequency / (Fs / 2), 'high');

filtered_desired_I = filter(filter_b, filter_a, I_desired);

%Q parameters
Aq=-0.125;
Bq=0.36;

filtered_desired_Q = filter(filter_b, filter_a, Q_desired);

recover_desired_I = filter(filter_a, filter_b, filtered_desired_I);

recover_desired_Q = recover_desired_I;

recover_I_values = filter(filter_a, filter_b, 1/Bi*(-Ai+I_values));

recover_Q_values = filter(filter_a, filter_b, 1/Bq*(-Aq+Q_values));

I_recovered_matched=filter(matched_filter,25,recover_I_values);

Q_recovered_matched=filter(matched_filter,25,recover_Q_values);

figure(2)

subplot(3,1,1)
plot(I_values);
hold on;
plot(Ai+Bi*filtered_desired_I);
hold off;
title('Input signal vs HPF desired signal');
legend("input signal","HPF desired signal");
axis([0 2000]);
grid on;

subplot(3,1,2)

plot(recover_I_values);
hold on;
plot(I_desired);
hold off;
title('Desired signal vs recovered signal');
legend("recovered signal","desired signal");
axis([0 2000]);
grid on;

subplot(3,1,3)
plot(I_recovered_matched);
hold on;
plot(I_desired_matched);
hold off;
title('Matched filter output');
legend("recovered matched filter","desired signal matched filter");
axis([0 2000]);
grid on;

figure(3)

subplot(3,1,1)
plot(Q_values);
hold on;
plot(Aq+Bq*filtered_desired_Q);
hold off;
title('Input signal vs HPF desired signal');
legend("input signal","HPF desired signal");
axis([0 2000]);
grid on;

subplot(3,1,2)

plot(recover_Q_values);
hold on;
plot(Q_desired);

hold off;
title('Desired signal vs recovered signal');
legend("recovered signal","desired signal");
axis([0 2000]);
grid on;

subplot(3,1,3)
plot(Q_recovered_matched);
hold on;
plot(Q_desired_matched);

hold off;
title('Matched filter output');
legend("recovered matched filter","desired signal matched filter");
axis([0 2000]);
grid on;

• If you convert the recovered signal to integer type you'll get either "1" or "0" values then... . Commented Apr 16 at 12:22
• Swapping numerator and denominator is equivalent to inverting the filter, which will indeed give the (in theory) original signal. However, it can be tricky in practice. The inverse of a highpass filter is "lowrise" filter which will give a lot of gain at low frequencies and has technically infinite gain at 0 Hz. This can result in a lot of undesirable noise amplification so you often need to find the trade off between "ideal" inversion and noise/numerical artifacts. However, your result looks fine, so this appears to work well here. Commented Apr 16 at 13:04
• Yes. It seems that this is not perfect solution yet. Even though my "input signal" and "HPF desired signal" seem to match pretty well, there is some low frequency error. Some other input signals are worse than my example. It looks like error is highest around 1000th sample and pretty much zero at beginning and end. Any suggestions, how to improve the situation? Commented Apr 16 at 15:00
• Can you attach input data used in your example (and maybe even the source code you use for recovery and plots (at least sample-rate info, hw filter cut-off frequency)? Commented Apr 16 at 15:54
• I added code and data. Commented Apr 17 at 7:17

I tried another approach to get the square wave restored from linked IQ data. After looking the data I noticed that turning points of square wave are those positive values (first ones after changing from negative to positive) on both I and Q data so that when I(n) is positive it marks to turn the bit(n) ON and when Q(n) is positive it marks turn the bit(n) OFF. So, by finding out these turning points, which comes alternating, one gets the square wave constructed by filling the bits between the turns.

Moved the source code to newer related question by OP.