From 4c3d8c6b3162ddaca3b57ecd8467d99e85eeaa1b Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 21 Aug 2019 08:24:44 +0200 Subject: analyse_tii_from_file can read u8 --- tii/analyse_tii_from_file.py | 77 ++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/tii/analyse_tii_from_file.py b/tii/analyse_tii_from_file.py index 9d834ea..32a5582 100755 --- a/tii/analyse_tii_from_file.py +++ b/tii/analyse_tii_from_file.py @@ -138,6 +138,18 @@ prs_phases = np.array([0, -1, 1, -1, -1, -1, -1, 0, 0, 1, -1, -1, -1, 1, 1, 0, 0 1, 0, 0, 1, 0, -1, 0, -1, 1, 0, 2, -1, 0, -1, 2, 1, 1, 0, 0, 1, 0, -1, 0, -1, 1, 0]) +def load_iq(filename, u8, count): + if u8: + u8_interleaved = np.fromfile(filename, np.uint8, count=count) + u8_iq = u8_interleaved.reshape(int(len(u8_interleaved)/2), 2) + # This directly converts to fc64 + fc64_unscaled = u8_iq[...,0] + np.complex64(1j) * u8_iq[...,1] + fc64_scaled = (fc64_unscaled - 127.0) / 128.0 + #fc64_dc_comp = fc64_scaled - np.average(fc64_scaled) + return fc64_scaled + else: + return np.fromfile(filename, np.complex64, count=count) + def main(): parser = argparse.ArgumentParser(description="Plot TII") parser.add_argument('--frame', default='0', help='Which transmission frame to analyse', @@ -146,10 +158,14 @@ def main(): required=False) parser.add_argument('--iq-file', default='ofdm.iq', help='File to read', required=False) - parser.add_argument('--test-combs', action='store_const', const=True, help='Test all comb pattern values', - required=False) - parser.add_argument('--old-algo', action='store_const', const=True, help='Do an analysis with the old algorithm', - required=False) + parser.add_argument('--test-combs', action='store_const', const=True, + help='Test all comb pattern values', required=False) + parser.add_argument('--old-algo', action='store_const', const=True, + help='Do an analysis with the old algorithm', required=False) + parser.add_argument('--u8', action='store_const', const=True, + help='File is in u8 and not cf32 format', required=False) + parser.add_argument('--align', action='store_const', const=True, + help='Find NULL and align to it', required=False) cli_args = parser.parse_args() @@ -164,9 +180,9 @@ def main(): prepare_tii_patterns() algo1(cli_args) -tii_ini_template = open("tii.ini.template", "r").read() def prepare_comb_pattern_run(options, comb, pattern): + tii_ini_template = open("tii.ini.template", "r").read() fd = open("tii.ini", "w") fd.write(tii_ini_template.format(comb=comb, pattern=pattern, oldvariant=0)) fd.close() @@ -183,6 +199,17 @@ def prepare_comb_pattern_run(options, comb, pattern): else: print("== FAIL {}".format(ret)) +def advance_to_null(frames): + for i in range(0, len(frames), NullSize//2): + if np.all(np.abs(frames[i:i+NullSize//2]) < 1e-2): + print("NULL at?", i) + # First non-zero sample is first PRS sample + for j in range(NullSize): + if np.abs(frames[i+j]) > 1e-2: + print("NULL at ", i+j - NullSize) + return frames[i+j - NullSize:] + print("NULL not found") + sys.exit(1) def plot_tii_once(options): oversample = int(int(options.samplerate) / 2048000) @@ -192,7 +219,7 @@ def plot_tii_once(options): N = 8 num_samples = num_samples_per_tf * (int(options.frame) + N) - frames = np.fromfile(options.iq_file, np.complex64, count=num_samples) + frames = load_iq(options.iq_file, options.u8, count=num_samples) #skip to frame frames = frames[num_samples_per_tf * int(options.frame):] @@ -306,7 +333,10 @@ def algo1(options): N = 8 num_samples = num_samples_per_tf * (int(options.frame) + N) - frames = np.fromfile(options.iq_file, np.complex64, count=num_samples) + frames = load_iq(options.iq_file, options.u8, count=num_samples) + + if options.align: + frames = advance_to_null(frames) #skip to frame frames = frames[num_samples_per_tf * int(options.frame):] @@ -334,17 +364,22 @@ def algo1(options): # A consequence of the fact that the 0 bin is never used is that the # first carrier of each pair is even for negative k, odd for positive k - blocks = [null_fft[-769:-385], null_fft[-385:-1], null_fft[1:385], null_fft[385:769]] + blocks = [null_fft[-768:-384], null_fft[-384:], null_fft[1:385], null_fft[385:769]] blocks_multiplied = np.zeros(384//2, dtype=np.complex128) + #print("blocks {}".format(i)) for block in blocks: even_odd = block.reshape(-1, 2) b = even_odd[...,0] * np.conj(even_odd[...,1]) + #print("".join('#' if i else '.' for i in np.abs(b) > 1e-7)) blocks_multiplied += b threshold = 0.01 indices_above_threshold = np.nonzero(np.abs(blocks_multiplied) > threshold)[0] carrier_indices = indices_above_threshold * 2 + 1 + if len(frame) < NullSize: + break + if np.max(np.abs(frame[:NullSize])) != 0: print("Frame {} has nonzero power in null symbol and {} TII carriers".format(i, len(carrier_indices))) @@ -361,6 +396,7 @@ def algo1(options): cp_counts[cp] += 1 if len(cp_counts) > 0: + print(carriers) for cp in cp_counts: if cp_counts[cp] >= 4: c, p = cp @@ -376,11 +412,11 @@ def algo1(options): print("Unrecognised TII comb and pattern") def convert_angles(angle): - """Convert 0 to 360 degree angles to 0 to 180, -179 to -1""" - if angle > 180: - return angle - 360 - elif angle < -180: - return angle + 360 + """Convert 0 to 2pi degree angles to -pi to pi""" + if angle * 2.0 > np.pi: + return angle - 2*np.pi + elif angle * 2.0 < -np.pi: + return angle + 2*np.pi else: return angle phase_corrector = np.vectorize(convert_angles) @@ -391,24 +427,23 @@ def analyse_phase(c, p, null_fft, prs_fft): #print("k =" + " ".join("{:>4}".format(k) for k in carriers)) #print("phases =" + " ".join("{:>4}".format(int(round(p))) for p in np.angle(prs_fft[carriers], deg=True))) - phases_tii = np.angle(null_fft[carriers], deg=True) + phases_tii = np.angle(null_fft[carriers]) pair_indices = carriers.reshape(-1,2)[...,0] correct_phase_indices = np.stack([pair_indices, pair_indices]).T.flatten() - phases_prs2 = np.angle(prs_fft[correct_phase_indices], deg=True) + phases_prs2 = np.angle(prs_fft[correct_phase_indices]) fig = pp.figure() fig.suptitle("Phase error C {} P {}".format(c, p)) phase_errors = {} - search_range = (-40, 40) - for err4 in range(*search_range): - err = err4 / 4 + search_range = (-20, 500) + for err in range(*search_range): rotate_vec = np.exp(2j * np.pi * err * carriers / 2048) - rotated = np.angle(null_fft[carriers] * rotate_vec, deg=True) - delta = np.mod(np.asarray(np.around(rotated - phases_prs2), dtype=np.int), 360) + rotated = null_fft[carriers] * rotate_vec + delta = np.asarray(np.around(np.angle(rotated) - phases_prs2), dtype=np.int) delta = phase_corrector(delta) - #print("phases {:>3} =".format(err) + " ".join("{:>4}".format(p) for p in delta)) + #print("phases {:>3} =".format(err) + " ".join("{:>4}".format(int(180*p/np.pi)) for p in delta)) phase_errors[err] = delta #ax1 = fig.add_subplot(7, 1, i+1) -- cgit v1.2.3