|
| 1 | +from .apputils import * |
| 2 | + |
| 3 | +import timeit |
| 4 | +import wfdb |
| 5 | +import constriction |
| 6 | + |
| 7 | +import jax |
| 8 | +import numpy as np |
| 9 | +import jax.numpy as jnp |
| 10 | + |
| 11 | + |
| 12 | +# CR-Suite libraries |
| 13 | +import cr.nimble as crn |
| 14 | +import cr.nimble.dsp as crdsp |
| 15 | +import cr.sparse as crs |
| 16 | +import cr.sparse.dict as crdict |
| 17 | +import cr.sparse.block.bsbl as bsbl |
| 18 | + |
| 19 | +@click.command() |
| 20 | +@click.argument('record_num', type=int) |
| 21 | +@click.option('-n', default=1024, help='Window length') |
| 22 | +@click.option('-m', default=512, help='Measurement length') |
| 23 | +@click.option('-d', default=12, help='Ones in sensing matrix') |
| 24 | +@click.option('-b', '--block-size', default=32, help='BSBL block size') |
| 25 | +def main(record_num, n, m, d, block_size): |
| 26 | + |
| 27 | + # read the signal from the database |
| 28 | + mit_bih_dir = get_db_dir() |
| 29 | + path = f'{mit_bih_dir}/{record_num}' |
| 30 | + header = wfdb.rdheader(path) |
| 31 | + click.echo(f'Processing record: {record_num}: Sampling rate: {header.fs}') |
| 32 | + |
| 33 | + sampfrom=0 |
| 34 | + sampto=30*360 |
| 35 | + record = wfdb.rdrecord(f'{mit_bih_dir}/{record_num}', channels=[0] |
| 36 | + , sampfrom=sampfrom, sampto=sampto, physical=False) |
| 37 | + ecg = np.squeeze(record.d_signal) |
| 38 | + |
| 39 | + X = crn.vec_to_windows(ecg, n) - 1024 |
| 40 | + n_samples = X.size |
| 41 | + n_windows = X.shape[1] |
| 42 | + print(f'n_samples: {n_samples}, n_windows: {n_windows}') |
| 43 | + |
| 44 | + Phi = crdict.sparse_binary_mtx(crn.KEY0, |
| 45 | + m, n, d=d, normalize_atoms=False) |
| 46 | + |
| 47 | + # Measurements |
| 48 | + Y = Phi @ X |
| 49 | + |
| 50 | + |
| 51 | + # Entropy coding |
| 52 | + Y_np = np.array(Y).astype(int) |
| 53 | + Y2 = Y_np.flatten(order='F') |
| 54 | + n_measurements = Y2.size |
| 55 | + max_val = np.max(np.abs(Y2)) |
| 56 | + mean_val = np.round(Y2.mean()) |
| 57 | + std_val = np.round(Y2.std()) |
| 58 | + |
| 59 | + model = constriction.stream.model.QuantizedGaussian(-max_val, max_val) |
| 60 | + encoder = constriction.stream.stack.AnsCoder() |
| 61 | + means = np.full(n_measurements, mean_val) |
| 62 | + stds = np.full(n_measurements, std_val) |
| 63 | + encoder.encode_reverse(Y2, model, means, stds) |
| 64 | + |
| 65 | + # Get and print the compressed representation: |
| 66 | + compressed = encoder.get_compressed() |
| 67 | + uncompressed_bits = n_samples * 11 |
| 68 | + compressed_bits = len(compressed)*32 |
| 69 | + ratio = uncompressed_bits / compressed_bits |
| 70 | + print(f'Uncompressed bits: {uncompressed_bits} Compressed bits: {compressed_bits}, ratio: {ratio:.2f}x') |
| 71 | + print(f'bits per measurement in compressed data: {compressed_bits / n_measurements}') |
| 72 | + print(f'bits per measurement in cs measurements: {np.round(np.log2(2* max_val + 1))}') |
| 73 | + |
| 74 | + |
| 75 | + # Decode the message: |
| 76 | + decoder = constriction.stream.stack.AnsCoder(compressed) |
| 77 | + reconstructed = decoder.decode(model, means, stds) |
| 78 | + reconstructed = reconstructed |
| 79 | + print(f'matched entropy decoding: {np.all(reconstructed == Y2)}') |
| 80 | + |
| 81 | + # Arrange measurements into column vectors |
| 82 | + RY = crn.vec_to_windows(jnp.asarray(reconstructed, dtype=float), m) |
| 83 | + |
| 84 | + DPhi = Phi.todense() |
| 85 | + options = bsbl.bsbl_bo_options(max_iters=20) |
| 86 | + |
| 87 | + for i in range(n_windows): |
| 88 | + x = X[:, i] |
| 89 | + y = RY[:, i] |
| 90 | + start = timeit.default_timer() |
| 91 | + sol = bsbl.bsbl_bo_np_jit(DPhi, y, block_size, options=options) |
| 92 | + stop = timeit.default_timer() |
| 93 | + rtime = stop - start |
| 94 | + x_hat = sol.x |
| 95 | + snr = crn.signal_noise_ratio(x, x_hat) |
| 96 | + prd = crn.percent_rms_diff(x, x_hat) |
| 97 | + nmse = crn.normalized_mse(x, x_hat) |
| 98 | + print(f'[{i}] SNR: {snr:.2f} dB, PRD: {prd:.1f}%, NMSE: {nmse:.5f}, Time: {rtime:.2f} sec, Iters: {sol.iterations}') |
0 commit comments