A tutorial for PRBS generation
Published:
This script was used to automatically generate 24- and 48-hour binary signals for our paper Design of a short perturbation method for on-site estimation of a building envelope thermal performance (2022).
The signals are generated by a maximum length sequence generator, conveniently available in the scipy library.
The objective here is to enter physically relatable values to building 24-hour signals.
from scipy.signal import max_len_seq
import matplotlib.pyplot as plt
import numpy as np
def generate_signal(char_time, duration=24, timestep=5, ax=None):
char_time_MLS = [char_time[0], char_time[0] - char_time[1], char_time[1] - char_time[2]]
duration_MLS = duration * 60 / timestep
taps = [int(time * 60 / timestep) for time in char_time_MLS]
nbits = max(taps)
signal, _ = max_len_seq(nbits=nbits,
taps=taps,
length=duration_MLS)
return signal
Signal generation step-by-step
The signal is built with a 5-minute timestep.
timestep = 5 #min
The variable char_time defines 3 step durations (or 5 or 7 or any odd number), supposed to be in hours.
Here, we expect a first 14-hour step, followed by a 7-hour step and a 1.5-hour step.
char_time = [14,7,1.5] #hours
char_time_MLS = [char_time[0], char_time[0] - char_time[1], char_time[1] - char_time[2]]
The signal will be generated here to cover 24 hours.
duration = 24 # hours
duration_MLS = duration * 60 / timestep
Variables taps and nbits are inputs for the maximum length sequance generator.
taps = [int(time * 60 / timestep) for time in char_time_MLS]
nbits = max(taps)
And finally, the signal is generated. Only the first output serves here.
signal, _ = max_len_seq(nbits=nbits,
taps=taps,
length=duration_MLS)
Let us take a look at the generated signal. It has either 1 (On) or 0 (Off) values.
As planned, the first On step has a 14-hour duration, followed by a 7-hour Off step, and again a 1.5h On step.
plt.figure(figsize=(8,3))
plt.plot(signal)
plt.xticks([0, 14 * 12, 21 * 12, 22.5 * 12, 24 * 12], ['start', '14h', '21h', '22.5h', '24h'])
plt.yticks([0,1])
plt.xlim(xmin=0, xmax=duration_MLS);
Sometimes, the char_time duration do not end up as On/Off/On durations, such as this example :
plt.figure(figsize=(8,3))
char_time = [10,8,1]
plt.plot(generate_signal(char_time))
plt.xticks([0,14*6,(14+8)*12,(14+8+1)*18,12*24], ['0', '14', '14+8', '14+8+1', '24'])
plt.yticks([0,1])
plt.xlim(xmin=0, xmax=duration_MLS);
Nevertheless, this code trick generates physically relatable signals, with variability in the hour order of magnitude.
Nota : with this trick, it matters that char_time is given in descending order.
A multitude of signal shapes
fig, axes = plt.subplots(5,5, figsize=(10,6), sharex=True, sharey=True)
for ax in axes.flat:
random_char_times = np.sort(np.random.random(size=3)*22)[::-1]
ax.plot(generate_signal(random_char_times))
ax.set_yticks([0,1])
ax.set_xticks([])
