Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4a41544

Browse files
committedSep 8, 2022
bsbl-codec-sim
1 parent c640cf1 commit 4a41544

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed
 

‎requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ scipy
55
matplotlib
66
click
77
wfdb
8+
constriction
89
cr-nimble
910
cr-sparse
1011
cr-biosignals

‎setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def _parse_requirements(filename):
128128
'ecg-codec=skecg.apps.codec:main',
129129
'ecg-analyze-excerpt=skecg.apps.analyze_excerpt:analyze',
130130
'ecg-locate-extremes=skecg.apps.locate_extremes:main',
131+
'ecg-sim-codec=skecg.apps.bsbl_codec_sim:main',
131132
],
132133
},
133134
)

‎src/skecg/apps/bsbl_codec_sim.py

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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

Comments
 (0)