def msk_psd(fbin, fmax, bitrate): bins = int(round(2*fmax/fbin)) f = fbin*(np.arange(bins)-int(bins/2)) f_norm = f/bitrate psd = calc_msk(f_norm) psd /= np.sum(psd) return make_signal(fd=np.sqrt(np.fft.fftshift(psd)), fs=bins*fbin)
def gmsk_psd(fbin, fmax, bitrate, bt): bins = int(round(2*fmax/fbin)) f = fbin*(np.arange(bins)-int(bins/2)) f_norm = f/bitrate gauss = np.exp((np.log(2)/-2)*(f_norm/bt)**2) psd = gauss*calc_msk(f_norm) psd /= np.amax(psd) psd *= bins**2 return make_signal(fd=np.sqrt(np.fft.fftshift(psd)), fs=bins*fbin)
def frame_data_bursts(signal, sync_code, payload_len, fs, bitrate, fps, sync_pos="center", autocompute_fd=False, verbose=True, *args, **kwargs): """ Takes data and creates with frames with data payload and sync field Bursts evenly spaced with specified bitrate, but symbol rate increased to fs If sync_pos is "start": |<-sync->|<---------------payload----------------->| If sync_pos is "center": |<------payload----->|<-sync->|<------paylod------>| Will zero pad if not enough data passed to fill all frame """ sync_code = np.array(sync_code) n_frames = int(np.ceil(len(signal.td) / payload_len)) f_len = payload_len + len(sync_code) + int((fs - bitrate) / float(fps)) f_len_error = (fs - bitrate) / float(fps) - int( (fs - bitrate) / float(fps)) s_len = len(sync_code) p_len = payload_len message = np.zeros(int(n_frames * p_len)) message[:len(signal.td)] = signal.td td = np.zeros(int(n_frames * (f_len + f_len_error))) c_offset = int(payload_len / 2.0) for n in range(n_frames): error = int(n * f_len_error) if sync_pos == "center": td[error + n * f_len:error + n * f_len + c_offset] = message[n * p_len:n * p_len + c_offset] td[error + n * f_len + c_offset:error + n * f_len + c_offset + s_len] = sync_code td[error + n * f_len + c_offset + s_len:error + n * f_len + s_len + p_len] = message[n * p_len + c_offset:n * p_len + p_len] elif sync_pos == "start": td[n * f_len:n * f_len + s_len] = sync_code td[n * f_len + s_len:n * f_len + f_len] = message[n * p_len:n * p_len + p_len] return make_signal(td=td, fs=fs, bitrate=bitrate, name=signal.name + "_%db_frames_%db_sync" % (f_len, s_len), autocompute_fd=autocompute_fd, verbose=False)
def frame_data(signal, sync_code, payload_len, fs, bitrate, sync_pos="center", autocompute_fd=False, verbose=True, *args, **kwargs): """ Takes data and creates with frames with data payload and sync field If sync_pos is "start": |<-sync->|<---------------payload----------------->| If sync_pos is "center": |<------payload----->|<-sync->|<------paylod------>| Will zero pad if not enough data passed to fill all frame """ sync_code = np.array(sync_code) n_frames = int(np.ceil(len(signal.td) / payload_len)) f_len = payload_len + len(sync_code) s_len = len(sync_code) p_len = payload_len message = np.zeros(int(n_frames * p_len)) message[:len(signal.td)] = signal.td td = np.zeros(int(n_frames * f_len)) c_offset = int(payload_len / 2.0) for n in range(n_frames): if sync_pos == "center": td[n * f_len:n * f_len + c_offset] = message[n * p_len:n * p_len + c_offset] td[n * f_len + c_offset:n * f_len + c_offset + s_len] = sync_code td[n * f_len + c_offset + s_len:n * f_len + f_len] = message[n * p_len + c_offset:n * p_len + p_len] elif sync_pos == "start": td[n * f_len:n * f_len + s_len] = sync_code td[n * f_len + s_len:n * f_len + f_len] = message[n * p_len:n * p_len + p_len] return make_signal(td=td, fs=fs, bitrate=bitrate, name=signal.name + "_%db_frames_%db_sync" % (f_len, s_len), autocompute_fd=autocompute_fd, verbose=False)
def gmsk_tx_filter(k, m, bt, fs, dt=0.0, norm=False, autocompute_fd=False, verbose=True, *args, **kwargs): """ Design GMSK transmit filter k : samples/symbol m : pulse_span bt : rolloff factor (0 < bt <= 1) dt : fractional sample delay """ if k < 1: raise Exception("error: gmsk_tx_filter(): k must be greater than 0\n") elif m < 1: raise Exception("error: gmsk_tx_filter(): m must be greater than 0\n") elif bt < 0.0 or bt > 1.0: raise Exception("error: gmsk_tx_filter(): beta must be in [0,1]\n") # derived values fir_len = k * m + 1 # compute filter coefficients t = np.arange(fir_len) / float(k) - 0.5 * m + dt tx_fir = q(2 * pi * bt * (t - 0.5) / SQRTLN2) - q(2 * pi * bt * (t + 0.5) / SQRTLN2) # normalize filter coefficients such that the filter's # integral is pi/2 if norm: tx_fir *= 1.0 / float(sum(tx_fir)) else: # pi/2 scale tx_fir *= pi / (2.0 * sum(tx_fir)) return make_signal(td=tx_fir, fs=fs, force_even_samples=False, name="gmsk_tx_fir_bt_%.2f" % bt, autocompute_fd=autocompute_fd, verbose=False)
def make_sync_fir(sync_code, pulse_fir, oversampling, autocompute_fd=False, verbose=True, *args, **kwargs): """ Takes binary sync code word, oversamples it and convolves it with a pulse shape finite impulse response to make a FIR sequence that can be used on Rx signal for synchronization. """ _sync_code = np.array(sync_code, dtype=float) _sync_code[_sync_code <= 0] = -1.0 _sync_code[_sync_code > 0] = 1.0 sync_fir = np.zeros(len(_sync_code) * oversampling) sync_fir[np.arange(len(_sync_code)) * oversampling] = _sync_code sync_fir = np.convolve(sync_fir, pulse_fir.td, mode="full") return make_signal(td=sync_fir, fs=pulse_fir.fs, name="sync_code_" + pulse_fir.name, autocompute_fd=autocompute_fd, verbose=False)
def gmsk_matched_kaiser_rx_filter(k, m, bt_tx, bt_composite, fs, dt=0.0, delta=1e-3, autocompute_fd=False, verbose=True, *args, **kwargs): """ Design GMSK receive filter k : samples/symbol m : fir span bt : rolloff factor (0 < beta <= 1) dt : fractional sample delay """ # validate input if k < 1: raise Exception( "error: gmsk_matched_kaiser_rx_filter(): k must be greater than 0\n" ) elif m < 1: raise Exception( "error: gmsk_matched_kaiser_rx_filter(): m must be greater than 0\n" ) elif bt_tx < 0.0 or bt_tx > 1.0: raise exception( "error: gmsk_matched_kaiser_rx_filter(): beta must be in [0,1]\n") elif bt_composite < 0.0 or bt_composite > 1.0: raise exception( "error: gmsk_matched_kaiser_rx_filter(): beta must be in [0,1]\n") # derived values fir_len = k * m + 1 # filter length # design transmit filter tx_fir = gmsk_tx_filter(k, m, bt_tx, fs).td # start of Rx filter design procedure # create 'prototype' matched filter prototype_fir = kaiser_filter_prototype(k, m, bt_composite, 0.0) prototype_fir *= pi / (2.0 * sum(prototype_fir)) # create 'gain' filter to improve stop-band rejection fc = (0.7 + 0.1 * bt_tx) / float(k) As = 60.0 oob_reject_fir = kaiser_filter_design(fir_len, fc, As, 0.0) # run ffts prototype_fd = np.fft.fft(prototype_fir) oob_reject_fd = np.fft.fft(oob_reject_fir) tx_fd = np.fft.fft(tx_fir) # find minimum of reponses tx_fd_min = np.amin(np.abs(tx_fd)) prototype_fd_min = np.amin(np.abs(prototype_fd)) oob_reject_fd_min = np.amin(np.abs(oob_reject_fd)) # compute approximate matched Rx response, removing minima, and add correction factor rx_fd = (np.abs(prototype_fd) - prototype_fd_min + delta) / (np.abs(tx_fd) - tx_fd_min + delta) # Out of band suppression rx_fd *= (np.abs(oob_reject_fd) - oob_reject_fd_min) / (np.abs( oob_reject_fd[0])) rx_fir = np.fft.fftshift(np.fft.ifft(rx_fd)) rx_fir = np.real(rx_fir) * k return make_signal( td=rx_fir, fs=fs, force_even_samples=False, name="gmsk_kaiser_matched_rx_fir_bt_tx_%.2f_bt_comp_%.2f" % (bt_tx, bt_composite), autocompute_fd=autocompute_fd, verbose=False)
def rcos_composite_tx_rx_filter(k, m, bt_tx, bt_composite, fs, dt=0.0, delta=1e-3, autocompute_fd=False, norm=False, verbose=True, *args, **kwargs): """ Raised cosine response including out of band supression k : samples/symbol m : fir span bt : tx rolloff factor (0 < beta <= 1) beta : dt : fractional sample delay """ # validate input if k < 1: raise Exception( "error: rcos_composite_tx_rx_filter(): k must be greater than 0\n") elif m < 1: raise Exception( "error: rcos_composite_tx_rx_filter(): m must be greater than 0\n") elif bt_tx < 0.0 or bt_tx > 1.0: raise exception( "error: rcos_composite_tx_rx_filter(): beta must be in [0,1]\n") elif bt_composite < 0.0 or bt_composite > 1.0: raise exception( "error: rcos_composite_tx_rx_filter(): beta must be in [0,1]\n") # derived values fir_len = k * m + 1 # filter length # create 'prototype' matched filter t = np.arange(fir_len) / float(k) - 0.5 * m + dt prototype_fir = v_raised_cos(t, 1.0, bt_composite) prototype_fir *= pi / (2.0 * sum(prototype_fir)) # create 'gain' filter to improve stop-band rejection fc = (0.7 + 0.1 * bt_tx) / float(k) As = 60.0 oob_reject_fir = kaiser_filter_design(fir_len, fc, As, 0.0) # run ffts prototype_fd = np.fft.fft(prototype_fir) oob_reject_fd = np.fft.fft(oob_reject_fir) # find minimum of reponses prototype_fd_min = np.amin(np.abs(prototype_fd)) oob_reject_fd_min = np.amin(np.abs(oob_reject_fd)) # compute approximate matched Rx response, removing minima, and add correction factor comp_fd = (np.abs(prototype_fd) - prototype_fd_min + delta) # Out of band suppression comp_fd *= (np.abs(oob_reject_fd) - oob_reject_fd_min) / (np.abs( oob_reject_fd[0])) comp_fir = np.fft.fftshift(np.fft.ifft(comp_fd)) comp_fir = np.real(comp_fir) * k if norm: comp_fir *= 1.0 / float(sum(comp_fir)) return make_signal(td=comp_fir, fs=fs, force_even_samples=False, name="rcos_composite_fir_%.2f_bt_comp_%.2f" % (bt_tx, bt_composite), autocompute_fd=autocompute_fd, verbose=False)