294 lines
11 KiB
Python
294 lines
11 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# @copyright this code is the property of Ubertone.
|
|
# You may use this code for your personal, informational, non-commercial purpose.
|
|
# You may not distribute, transmit, display, reproduce, publish, license, create derivative works from, transfer or sell any information, software, products or services based on this code.
|
|
# @author Marie Burckbuchler, Stéphane Fischer
|
|
|
|
import logging
|
|
from math import ceil
|
|
|
|
from Model.peacock_uvp.apf04_gain import convert_dB_m2code, convert_code2dB_m, convert_code2dB, convert_dB2code, APF04_CODE_MAX_APPLIED
|
|
#from .ap_exception import ap_protocol_error
|
|
from Model.peacock_uvp.apf_type import cast_int16, cast_uint16
|
|
|
|
#ap_protocol_error(3300, "Warning: v_min has to be in [-Nyquist_Range, 0].")
|
|
|
|
class ConfigHw ():
|
|
# @brief To instantiate an object of this class, you can give no parameter to get attributes set to zero, or you can give a settings, the ID of the config and the sound_speed to use.
|
|
# TODO (san 21/04/2020) il serait surement plus approprié, plus propre (et plus compact) de stocker la configHW dans un dict
|
|
# accompagné d'une liste donnant l'ordre des valeurs en mémoire :
|
|
# order = ['div_f0', 'n_tir', 'c_prf', 'n_em', 'n_vol', 'c_vol1', 'c_dvol' ...]
|
|
def __init__(self, _f_sys):
|
|
|
|
logging.debug("f_sys = %.1e"%_f_sys)
|
|
self.f_sys = _f_sys
|
|
|
|
self.div_f0 = 0
|
|
self.n_tir = 0
|
|
self.c_prf = 0
|
|
self.n_em = 0
|
|
self.n_vol = 0
|
|
self.c_vol1 = 0
|
|
self.c_dvol = 0
|
|
self.gain_ca0 = 0
|
|
self.gain_ca1 = 0
|
|
self.tr = 0
|
|
self.phi_min = 0
|
|
self.method = 0
|
|
self.reserved1 = 0
|
|
self.reserved2 = 0
|
|
self.n_avg = 0
|
|
self.blind_ca0 = 0
|
|
self.blind_ca1 = 0
|
|
|
|
|
|
def set(self, _config_data, _sound_speed=1480, _gain_blind_zone=None):
|
|
"""
|
|
docstring
|
|
"""
|
|
if type(_config_data) is dict :
|
|
logging.debug ("call from_dict")
|
|
self.from_dict(_config_data, _sound_speed, _gain_blind_zone)
|
|
elif type(_config_data) is list :
|
|
logging.debug ("call from_dict")
|
|
self.from_list(_config_data)
|
|
else :
|
|
logging.info("wrong data type for _config_data")
|
|
|
|
return self
|
|
|
|
|
|
# @brief Chargement des paramètres d'une configuration acoustique
|
|
# On rétrocalcule à chaque fois la valeur en paramètre utilisateur par rapport au paramètre hardware (pour prendre en compte les modifications dues à des cast etc, lorsqu'il y a interdépendance entre paramètres.)
|
|
# @param _data : ref sur objet de la classe data donnant accès aux configs et au sound speed et à la fréquency système.
|
|
# @param meas_ultrasound_key : clé de la configuration en cours.
|
|
# appelé uniquement par le constructeur
|
|
def from_dict(self, _config_dict, _sound_speed=1480, _gain_blind_zone=None):
|
|
logging.debug("start import dict")
|
|
self.div_f0 = cast_int16(self.f_sys / _config_dict['f0'] -1)
|
|
f0 = self.f_sys / (self.div_f0+1)
|
|
|
|
self.c_prf = cast_int16(f0 / _config_dict['prf'])
|
|
prf = _config_dict['f0']/self.c_prf
|
|
|
|
self.n_tir = cast_int16(_config_dict['n_ech'])
|
|
|
|
# n_em is equal to 0 only if r_em = 0. If not, n_em is at least equal to 1.
|
|
if _config_dict['r_em'] == 0:
|
|
self.n_em = cast_int16(0)
|
|
else:
|
|
self.n_em = cast_int16(round(2./_sound_speed * f0 *_config_dict['r_em']))
|
|
if self.n_em == 0:
|
|
self.n_em = cast_int16(1)
|
|
r_em = _sound_speed/(2.*f0)*self.n_em
|
|
|
|
self.n_vol = cast_int16(_config_dict['n_vol'])
|
|
|
|
self.c_vol1 = cast_uint16(2./_sound_speed * f0 * (_config_dict['r_vol1'] - r_em/2.))
|
|
r_vol1 = _sound_speed/(2.*f0)*self.c_vol1 + r_em/2.
|
|
|
|
self.c_dvol = cast_int16(2./_sound_speed * f0 *_config_dict['r_dvol'])
|
|
if self.c_dvol < 2: # constraint from APF04 hardware
|
|
self.c_dvol = cast_int16(2)
|
|
r_dvol = _sound_speed/(2.*f0)*self.c_dvol
|
|
|
|
self.gain_ca1 = cast_int16(convert_dB_m2code(_config_dict['gain_function']['a1'], r_dvol))
|
|
a1 = convert_code2dB_m(self.gain_ca1, r_dvol)
|
|
|
|
if _gain_blind_zone :
|
|
self.blind_ca1 = cast_int16(convert_dB_m2code(_gain_blind_zone['a1_max'], r_dvol))
|
|
else :
|
|
self.blind_ca1 = 0
|
|
# Pour a1 max, on ne rétrocalcule pas dans le const, puisque c'est un const et que cette valeur n'est utile que quelques lignes plus loin dans le calcul du ca0_max.
|
|
a1_max = convert_code2dB_m(self.blind_ca1, r_dvol)
|
|
|
|
r_ny = _sound_speed*prf/(2*f0)
|
|
self.phi_min = cast_int16(_config_dict['v_min']*65535/(2*r_ny))
|
|
|
|
self.gain_ca0 = cast_int16(convert_dB2code(_config_dict['gain_function']['a0'] + r_vol1*a1))
|
|
if _gain_blind_zone :
|
|
self.blind_ca0 = cast_int16(convert_dB2code(_gain_blind_zone['a0_max'] + r_vol1 * a1_max))
|
|
else :
|
|
self.blind_ca0 = APF04_CODE_MAX_APPLIED
|
|
|
|
# on a vu plus simple comme écriture ...
|
|
self.tr = cast_int16(int(''.join(ele for ele in _config_dict['tr_out'] if ele.isdigit())))-1
|
|
# TODO ça limite à 9, attention
|
|
|
|
if _config_dict['method'] == "ppc_cont":
|
|
self.burst_mode = False
|
|
else: # Donc method == "corr_ampl"
|
|
self.burst_mode = True
|
|
self.phase_coding = _config_dict['phase_coding']
|
|
self.static_echo_filter = _config_dict['static_echo_filter']
|
|
self.gain_auto = _config_dict['gain_function']['auto']
|
|
|
|
# Pour retourner choisir le paramètres methode traitement, remplacer la dernière parenthèse par un 2 ou un 0.
|
|
if(self.gain_auto == True):
|
|
logging.debug("gain auto is set")
|
|
# +2048 pour activer l'I2C (pour firmware >C51)
|
|
self.method = cast_int16(512 + (cast_int16(self.static_echo_filter)<<8) + (cast_int16(self.phase_coding)<<2) + cast_int16(self.burst_mode) + (cast_int16(self.burst_mode)<<1))
|
|
else:
|
|
logging.debug("gain is set to manual")
|
|
self.method = cast_int16(0 + (cast_int16(self.static_echo_filter)<<8) + (cast_int16(self.phase_coding)<<2) + cast_int16(self.burst_mode) + (cast_int16(self.burst_mode)<<1))
|
|
|
|
self.n_avg = cast_int16(_config_dict['n_profile'])
|
|
|
|
self.reserved1 = 0
|
|
self.reserved2 = 0
|
|
|
|
# @brief Chargement des paramètres à partir d'un tableau lu dans le Hardware
|
|
# @param _param_table : tableau des valeurs dans l'ordre indiqué ci-dessous. cf. aussi dt_protocole de l'APF04.
|
|
def from_list(self, _param_table):
|
|
logging.debug("start import list")
|
|
if len(_param_table)==17:
|
|
self.div_f0 = _param_table[0]
|
|
self.n_tir = _param_table[1]
|
|
self.c_prf = _param_table[2]
|
|
self.n_em = _param_table[3]
|
|
self.n_vol = _param_table[4]
|
|
self.c_vol1 = _param_table[5]
|
|
self.c_dvol = _param_table[6]
|
|
self.gain_ca0 = _param_table[7]
|
|
self.gain_ca1 = _param_table[8]
|
|
self.tr = _param_table[9]
|
|
self.phi_min = _param_table[10]
|
|
self.method = _param_table[11]
|
|
self.n_avg = _param_table[14]
|
|
self.blind_ca0 = _param_table[15]
|
|
self.blind_ca1 = _param_table[16]
|
|
|
|
# Other useful parameters (coded in method bits array) :
|
|
if (self.method & 0x0001) == 0:
|
|
self.burst_mode = False
|
|
else:
|
|
self.burst_mode = True
|
|
# en self.method & 0x0002, il y a l'indication de méthode traitement.
|
|
if (self.method & 0x0004) == 0:
|
|
self.phase_coding = False
|
|
else:
|
|
self.phase_coding = True
|
|
if (self.method & 0x0100) == 0:
|
|
self.static_echo_filter = False
|
|
else:
|
|
self.static_echo_filter = True
|
|
if (self.method & 0x0200) == 0:
|
|
self.gain_auto = False
|
|
else:
|
|
self.gain_auto = True
|
|
#else :
|
|
# logging.info("WARNING")
|
|
# TODO raise error
|
|
|
|
# @brief Update the config with the current config_hw.
|
|
# @param _sound_speed: information of the sound speed
|
|
def to_dict(self, _sound_speed):
|
|
# TODO si div_f0 : pas initialisé -> ERROR
|
|
config = {}
|
|
f0_ = (self.f_sys/(self.div_f0+1))
|
|
config['f0'] = f0_
|
|
config['tr_out'] = 'tr'+str(self.tr+1)
|
|
config['prf'] = f0_/self.c_prf
|
|
config['r_vol1'] = _sound_speed*((self.c_vol1+self.n_em/2.)/f0_)/2.
|
|
config['r_dvol'] = _sound_speed*(self.c_dvol/f0_)/2.
|
|
config['n_vol'] = self.n_vol
|
|
config['r_em'] = _sound_speed*(self.n_em/f0_)/2.
|
|
config['n_ech'] = self.n_tir
|
|
if self.burst_mode:
|
|
config['method'] = "corr_ampl"
|
|
else:
|
|
config['method'] = "ppc_cont"
|
|
# en self.method & 0x0002, il y a l'indication de méthode traitement.
|
|
if self.phase_coding:
|
|
config['phase_coding'] = True
|
|
else:
|
|
config['phase_coding'] = False
|
|
if self.static_echo_filter:
|
|
config['static_echo_filter'] = True
|
|
else:
|
|
config['static_echo_filter'] = False
|
|
config['gain_function'] = {}
|
|
if self.gain_auto:
|
|
config['gain_function']['auto'] = True
|
|
else:
|
|
config['gain_function']['auto'] = False
|
|
config['n_profile'] = self.n_avg
|
|
|
|
rdvol = _sound_speed*(self.c_dvol/f0_)/2.
|
|
rvol1 = _sound_speed*((self.c_vol1+self.n_em/2.)/f0_)/2.
|
|
|
|
a1 = convert_code2dB_m(self.gain_ca1, rdvol)
|
|
config['gain_function']['a0'] = convert_code2dB(self.gain_ca0)-a1*rvol1
|
|
config['gain_function']['a1'] = a1
|
|
|
|
config['v_min'] = 2*_sound_speed*config['prf']*self.phi_min/(2*65535*f0_)
|
|
return config
|
|
|
|
|
|
def get_bloc_duration(self):
|
|
#TODO san 27/09/2017 attention ça augmente si n_vol> 100
|
|
return self.n_tir * (self.n_avg) * (self.div_f0 + 1) * self.c_prf / self.f_sys
|
|
|
|
|
|
# @brief Affichage du paramétrage en cours.
|
|
def print_config_hw(self):
|
|
logging.debug("div_F0 = %s", self.div_f0)
|
|
logging.debug("n_tir = %s", self.n_tir)
|
|
logging.debug("c_PRF = %s", self.c_prf)
|
|
logging.debug("n_Em = %s", self.n_em)
|
|
logging.debug("n_vol = %s", self.n_vol)
|
|
logging.debug("c_vol1 = %s", self.c_vol1)
|
|
logging.debug("c_dvol = %s", self.c_dvol)
|
|
logging.debug("CA0_dac = %s", self.gain_ca0)
|
|
logging.debug("CA1_dac = %s", self.gain_ca1)
|
|
logging.debug("CA0_max_dac = %s", self.blind_ca0)
|
|
logging.debug("CA1_max_dac = %s", self.blind_ca1)
|
|
logging.debug("Cs_Tr = %s", self.tr)
|
|
logging.debug("phi_min = %s", self.phi_min)
|
|
logging.debug("Methode = %s", self.method)
|
|
logging.debug("n_avg = %s", self.n_avg)
|
|
# logging.debug("gain auto : %s", self.gain_auto)
|
|
# logging.debug("static_echo_fiter : %s", self.static_echo_filter)
|
|
# logging.debug("burst_mode : %s", self.burst_mode)
|
|
# logging.debug("phase_coding : %s", self.phase_coding)
|
|
|
|
|
|
def to_list(self):
|
|
buf=[]
|
|
buf.append(self.div_f0)
|
|
buf.append(self.n_tir)
|
|
buf.append(self.c_prf)
|
|
buf.append(self.n_em)
|
|
buf.append(self.n_vol)
|
|
buf.append(self.c_vol1)
|
|
buf.append(self.c_dvol)
|
|
buf.append(self.gain_ca0)
|
|
buf.append(self.gain_ca1)
|
|
buf.append(self.tr)
|
|
buf.append(self.phi_min)
|
|
buf.append(self.method)
|
|
buf.append(self.reserved1)
|
|
buf.append(self.reserved2)
|
|
buf.append(self.n_avg)
|
|
buf.append(self.blind_ca0)
|
|
buf.append(self.blind_ca1)
|
|
return buf
|
|
|
|
|
|
def __str__(self):
|
|
return str(self.__dict__)
|
|
|
|
|
|
def __eq__(self, other):
|
|
if not isinstance(other, type(self)):
|
|
logging.info("NOT IMPLEMENTED")
|
|
return NotImplemented
|
|
|
|
return ((self.div_f0, self.tr, self.method, self.c_prf, self.phi_min, self.n_tir, self.c_vol1, self.c_dvol, self.n_em, self.n_vol, self.reserved1, self.reserved2, self.n_avg, self.gain_ca0, self.gain_ca1, self.blind_ca0, self.blind_ca1) == (other.div_f0, other.tr, other.method, other.c_prf, other.phi_min, other.n_tir, other.c_vol1, other.c_dvol, other.n_em, other.n_vol, other.reserved1, other.reserved2, other.n_avg, other.gain_ca0, other.gain_ca1, other.blind_ca0, other.blind_ca1))
|
|
|
|
|
|
def __ne__(self, other):
|
|
return not self == other
|