# -*- coding: utf-8 -*-
# <nbformat>3.0</nbformat>
# <codecell>
#!/usr/bin/env python
#
#The MIT CorrelX Correlator
#
#https://github.com/MITHaystack/CorrelX
#Contact: correlX@haystack.mit.edu
#Project leads: Victor Pankratius, Pedro Elosegui Project developer: A.J. Vazquez Alvarez
#
#Copyright 2017 MIT Haystack Observatory
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
#
#------------------------------
#------------------------------
#Project: CorrelX.
#File: lib_quant.py.
#Author: A.J. Vazquez Alvarez (ajvazquez@haystack.mit.edu)
#Description:
"""
Module with basic quantizer and dequantizer.
"""
#History:
#initial version: 2015.12 ajva
#MIT Haystack Observatory
from __future__ import print_function
import numpy as np
import imp
# Quantization levels
import const_quant
imp.reload(const_quant)
from const_quant import *
[docs]def compute_range_bits(bits_per_sample):
"""
Compute list of values for unpacking samples.
[Used by decode_samples_red()]
Notes
-----
|
| **Example:**
|
| >>bits_per_sample=2
| >>list(compute_range_bits(bits_per_sample))
| [6, 4, 2, 0]
"""
return(reversed(range(0,8,bits_per_sample)))
[docs]def decode_samples_red(words,bits_per_sample):
"""
Unpack samples given a binary vector with the packed samples and the number of bits per sample.
[Used by sub_unpack_samples()]
Parameters
----------
words
bits_per_sample : int
number of bits per sample.
Returns
-------
words_nostack : numpy 1D array
unpacked samples.
"""
range_offsets = compute_range_bits(bits_per_sample)
mask_bits = (1<<bits_per_sample)-1
words_extended = [((words)>>i) & (mask_bits) for i in range_offsets]
#words_extended = [np.bitwise_and(np.right_shift(words,i),mask_bits) for i in range_offsets]
words_extended2 = np.concatenate(words_extended)
words_nostack = np.reshape(words_extended2,(-1,len(words))).transpose(1,0).flatten()
return(words_nostack)
[docs]def np_take_samples(v_2bps,unpacked_samples):
"""
Translate vector with sampled values into dequantized values.
[Used by sub_unpack_samples()]
Parameters
----------
v_2bps : list
quantization values.
unpacked_samples
quantized samples.
Returns
-------
out : numpy 1D array
dequantized sample components.
"""
return(np.take(v_2bps,unpacked_samples))
[docs]def sub_unpack_samples(samples_quant,bits_per_sample,current_data_type,v_2bps_complex,v_2bps_real):
"""
Unpack samples packed in a binary vector into dequantized values.
Parameters
----------
See get_samples().
Returns
-------
out : numpy 1D array of complex
dequantized samples.
"""
unpacked_samples = decode_samples_red(samples_quant,bits_per_sample)
if current_data_type=='c':
samples_out = np_take_samples(v_2bps_complex,unpacked_samples)
else:
samples_out = np_take_samples(v_2bps_real,unpacked_samples)
return(samples_out)
[docs]def sub_pack_complex_samples(samples,bits_per_sample):
"""
Group dequantized values into complex values (pair where first one is real part, second one imaginary part).
Parameters
----------
samples : numpy 1D array
samples components (complex): [real_part_0, imag_part_0, real_part_1, ... ].
bits_per_sample : int
number of bits per sample.
Returns
-------
complex_samples : numpy 1D array
complex samples: [complex_0, complex_1, ...]
Notes
-----
|
| **TO DO:**
|
| Currently only 2 bits per sample.
"""
if bits_per_sample==1:
# TO DO: This branch is untested...
samples[samples==0]=-1
complex_samples = samples[::2]+1j*samples[1::2]
else:
samples[1::2] *= 1j
samples_reshape=samples.reshape(-1,2)
complex_samples = np.sum(samples_reshape,axis=1)
return(complex_samples)
[docs]def get_samples(samples_quant,bits_per_sample,current_data_type,num_samples=-1,single_precision=0):
"""
Get dequantized samples from samples in binary format.
Parameters
----------
samples_quant : numpy 1D array
samples (components if complex), packed in binary format (uint8).
bits_per_sample : int
number of bits per sample.
current_data_type : char {'c','r'}
'c' for complex, 'r' for real data.
num_samples : int
number of samples to be returned (first num_samples). This is used to avoid errors if the number
samples is smaller than the number of samples fitting a uint8.
v_2bps_complex : list
quantization levels for 2 bit complex.
v_2bps_real : list
quantization levels for 2 bit real.
Returns
-------
result : numpy 1D array
complex with dequantized samples.
Notes
-----
|
| **TO DO:**
|
| Currently only 2 bits per sample, generalize for different numbers of bits per sample.
"""
#v_2bps_complex=np.array(QUANT_LEVELS_2BIT,dtype=np.complex64) if SINGLE_PRECISION else \
# np.array(QUANT_LEVELS_2BIT,dtype=complex)
v_2bps_complex=np.array(QUANT_LEVELS_2BIT,dtype=np.complex128) if single_precision else \
np.array(QUANT_LEVELS_2BIT,dtype=np.complex128)
#v_2bps_real=np.array(QUANT_LEVELS_2BIT,dtype=np.float32) if SINGLE_PRECISION else \
# np.array(QUANT_LEVELS_2BIT,dtype=float)
v_2bps_real=np.array(QUANT_LEVELS_2BIT,dtype=np.complex128) if single_precision else \
np.array(QUANT_LEVELS_2BIT,dtype=np.complex128)
# Unpack and dequantize
v_all = sub_unpack_samples(samples_quant,bits_per_sample,current_data_type,v_2bps_complex,v_2bps_real)
if current_data_type=='c':
result = sub_pack_complex_samples(v_all,bits_per_sample)
else:
result = v_all
if num_samples>-1:
#if current_data_type=='c':
# result=result[:(num_samples//2)]
#else:
result=result[:num_samples]
return(result)
# -----------------------------------------------------------------------------
# - First implementation, use only for testing. -
# - Used by signal generation libraries...
dictc01={"0":-1,\
"1":1}
dict1={0:-1,\
1:1}
dictc01bit={0:-1,\
1:1}
dict2bps={0:-3.336,\
1:-1.0,\
2:1.0,\
3:3.336}
[docs]def simple_quantizer(samples,bits_quant=1,signal_limits=[-1,+1],force_limits=0):
"""
Basic quantizer: it takes as input a list of samples and returns a list of the same size
with integers from 0 to 2**bits_quant - 1, representing the quantization thressholds,
distributed at equal intervals between teh minimum and the maximum of the signal.
Example:
signal = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
bits_quant = 2
output=simple_quantizer(signal,bits_quant)
output = [0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]
To be done:
-Introduce other quantization strategies
(!) Use only for testing.
(!) TO DO: migrate code using this function to newer implementation.
"""
#bits_quant=1
levels=2**bits_quant
ths=levels-1
if force_limits==0:
max_s = max(samples)
min_s = min(samples)
else:
min_s = signal_limits[0]
max_s = signal_limits[1]
values_v=np.linspace(min_s,max_s,levels)[:]
ths_v_plus=[]
for i in range(levels)[1:]:
ths_v_plus.append(0.5*(values_v[i]+values_v[i-1]))
ths_v_plus.append(values_v[-1])
ths=ths_v_plus
q_samples = []
for i in samples:
for j in range(levels):
#If smaller than threshold or last one (will be last if outside of range)
if (i<=ths_v_plus[j])or(j==levels-1):
#q_samples.append(values_v[j])
q_samples.append(j)
break
return(q_samples)
[docs]def simple_dequantizer(signal=[],bits_quant=1,limits=[-1,1]):
"""
Basic dequantizer: it takes as input a list of quantized samples (output of simple_quantizer())
and returns a list of the same size with float values from limits[0] to limits[1], representing
the quantization thressholds, distributed at equal intervals between these limits
Example:
signal = [0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]
bits_quant = 2
output=simple_dequantizer(signal,bits_quant)
output = [-0.75, -0.75, -0.75, -0.75, -0.25, -0.25, -0.25, 0.25, 0.25, 0.25, 0.75, 0.75, 0.75]
(!) Use only for testing.
(!) TO DO: migrate code using this function to newer implementation.
"""
if bits_quant==1:
return([dict1[x] for x in signal])
levels = 2**bits_quant
max_s=limits[1]
min_s=limits[0]
#values_v=(np.linspace(min_s,max_s,levels+1))[1:]-((max_s-min_s)/(2*levels))
values_v=np.linspace(min_s,max_s,levels)
# Don't convert to list, array is user later, so the line below has better performance
return(values_v[signal])
[docs]def group_pairs_complex(samples):
"""
From a given list of samples, group into pairs of 2 (real and imaginary).
"""
samples_pairs=list(zip(samples[0::2],samples[1::2]))
samples_out=[scp[0]+np.complex("j")*scp[1] for scp in samples_pairs]
return(samples_out)
# <codecell>