Exemple #1
0
class StarLord(MIXBoard):
    '''
    StarLord is a high resolution differential input digital audio analyzer module

    Args:
        i2c:    instance(I2C), the instance of I2C bus. which will be used to used
                               to control eeprom, sensor and io expander.
        ipcore: instance(MIXAUT5SGR), the instance of MIXAUT5SGR, which include
                                    AD717x, FFT Analyzer, Signal Source and gpio
                                    function. If device name string is passed
                                    to the parameter, the ipcore can be instanced
                                    in the module.

    Examples:
        i2c = I2C('/dev/i2c-0')
        starlord = StarLord(i2c, '/dev/MIX_AUT5_SG_R_0')
        starlord.module_init()
        # measure left channel input
        result = starlord.measure('left', 20000, 3)
        print("vpp={}, freq={}, thd={}, thdn={}, rms={}, noisefloor={}".format(result['vpp'],
              result['freq'], result['thd'], result['thdn'], result['rms'],
              result['noisefloor']))

        # measure LNA signal
        result = starlord.measure('left', '50mV', 20000, 3, 1000)
        print("vpp={}, freq={}, thd={}, thdn={}, rms={}".format(result['vpp'],
              result['freq'], result['thd'], result['thdn'], result['rms']))
    '''
    compatible = ['GQQ-AUD003002-000']

    rpc_public_api = [
        'module_init', 'enable_upload', 'disable_upload', 'measure',
        'enable_output', 'disable_output', 'measure_lna', 'enable_lna_upload',
        'disable_lna_upload', 'config_lna_scope'
    ] + MIXBoard.rpc_public_api

    def __init__(self, i2c, ipcore=None, range_table=starlord_table):
        if i2c:
            self.eeprom = CAT24C32(StarLordDef.EEPROM_I2C_ADDR, i2c)
            self.nct75 = NCT75(StarLordDef.TEMP_I2C_ADDR, i2c)
            self.pca9536 = PCA9536(StarLordDef.PCA9536_DEV_ADDR, i2c)

        else:
            self.eeprom = EepromEmulator("cat24cxx_emulator")
            self.nct75 = NCT75Emulator("nct75_emulator")
            self.pca9536 = PCA9536Emulator("PCA9536_emulator")

        if ipcore:
            if isinstance(ipcore, basestring):
                ipcore = MIXAUT5SGR(ipcore)
            self.ipcore = ipcore
            self.analyzer = self.ipcore.analyzer
            self.signal_source = self.ipcore.signal_source
            self.ad7175 = self.ipcore.ad717x
            self.ad7175.config = {
                'ch0': {
                    'P': 'AIN0',
                    'N': 'AIN1'
                },
                'ch1': {
                    'P': 'AIN2',
                    'N': 'AIN3'
                }
            }
            self.adc_rst_pin = Pin(self.ipcore.gpio, StarLordDef.ADC_RESET_PIN)
            self.i2s_rx_en_pin = Pin(self.ipcore.gpio,
                                     StarLordDef.I2S_RX_EN_PIN)
            self.dac_rst_pin = Pin(self.ipcore.gpio, StarLordDef.DAC_RESET_PIN)
            self.i2s_tx_en_pin = Pin(self.ipcore.gpio,
                                     StarLordDef.I2S_TX_EN_PIN)
            self.i2s_ch_select = [
                Pin(self.ipcore.gpio, StarLordDef.I2S_CH_SELECT_2),
                Pin(self.ipcore.gpio, StarLordDef.I2S_CH_SELECT_3)
            ]
            self.fft_source_select = Pin(self.ipcore.gpio,
                                         StarLordDef.FFT_SOURCE_SELECT)
            self.ad7175_upload_select = Pin(self.ipcore.gpio,
                                            StarLordDef.AD7175_TO_FFT_OR_NOT)

        else:
            self.analyzer = MIXFftAnalyzerSGEmulator(
                "mix_fftanalyzer_sg_emulator")
            self.signal_source = MIXSignalSourceSGEmulator(
                "mix_signalsource_sg_emulator")
            self.adc_rst_pin = Pin(None, StarLordDef.ADC_RESET_PIN)
            self.i2s_rx_en_pin = Pin(None, StarLordDef.I2S_RX_EN_PIN)
            self.dac_rst_pin = Pin(None, StarLordDef.DAC_RESET_PIN)
            self.i2s_tx_en_pin = Pin(None, StarLordDef.I2S_TX_EN_PIN)
            self.ad7175 = MIXAd7175SGEmulator('mix_ad7175_sg_emulator',
                                              StarLordDef.EMULATOR_REG_SIZE)
            self.i2s_ch_select = [
                Pin(None, StarLordDef.I2S_CH_SELECT_2),
                Pin(None, StarLordDef.I2S_CH_SELECT_3)
            ]
            self.fft_source_select = Pin(None, StarLordDef.FFT_SOURCE_SELECT)
            self.ad7175_upload_select = Pin(None,
                                            StarLordDef.AD7175_TO_FFT_OR_NOT)

        super(StarLord, self).__init__(self.eeprom,
                                       self.nct75,
                                       cal_table={},
                                       range_table=range_table)
        self.is_lna_up = False
        self.is_analyzer_up = False
        self.is_enable_upload = False

    def module_init(self):
        '''
        Init module, which will reset dac/adc, i2s module and load calibration

        Returns:
            string, "done", execution successful.

        Examples:
            starloard.module_init()
        '''
        self.adc_rst_pin.set_dir(StarLordDef.IO_DIR_OUTPUT)
        self.dac_rst_pin.set_dir(StarLordDef.IO_DIR_OUTPUT)
        self.i2s_rx_en_pin.set_dir(StarLordDef.IO_DIR_OUTPUT)
        self.i2s_tx_en_pin.set_dir(StarLordDef.IO_DIR_OUTPUT)
        self.i2s_ch_select[StarLordDef.AUDIO_CHANNEL_SELECT_BIT0].set_dir(
            StarLordDef.IO_DIR_OUTPUT)
        self.i2s_ch_select[StarLordDef.AUDIO_CHANNEL_SELECT_BIT1].set_dir(
            StarLordDef.IO_DIR_OUTPUT)
        self.fft_source_select.set_dir(StarLordDef.IO_DIR_OUTPUT)
        self.ad7175_upload_select.set_dir(StarLordDef.IO_DIR_OUTPUT)

        # reset ADC
        self.adc_rst_pin.set_level(0)
        time.sleep(StarLordDef.RELAY_DELAY_S)
        self.adc_rst_pin.set_level(1)

        # reset DAC
        self.dac_rst_pin.set_level(0)
        time.sleep(StarLordDef.RELAY_DELAY_S)
        self.dac_rst_pin.set_level(1)

        # reset i2s rx
        self.i2s_rx_en_pin.set_level(0)

        # reset i2s tx
        self.i2s_tx_en_pin.set_level(0)

        # io init
        self.pca9536.set_pin_dir(StarLordDef.AD7175_CH_LEFT_CTL_BIT,
                                 StarLordDef.IO_DIR_OUTPUT)
        self.pca9536.set_pin_dir(StarLordDef.AD7175_CH_RIGHT_CTL_BIT,
                                 StarLordDef.IO_DIR_OUTPUT)

        self.pca9536.set_pin(StarLordDef.AD7175_CH_LEFT_CTL_BIT, 0)
        self.pca9536.set_pin(StarLordDef.AD7175_CH_RIGHT_CTL_BIT, 0)

        # ad7175 init
        self.ad7175.channel_init()

        # measure lna config init
        self.config_lna_scope(StarLordDef.CH_LEFT, StarLordDef.LNA_RANGE_5V)

        self.load_calibration()

        return "done"

    def enable_upload(self):
        '''
        Enable module data upload.

        Returns:
            string, "done", execution successful.
        '''
        self.i2s_rx_en_pin.set_level(StarLordDef.I2S_RX_ENABLE)
        self.analyzer.enable_upload()
        self.is_enable_upload = True

        return "done"

    def disable_upload(self):
        '''
        Disable module data upload.

        Returns:
            string, "done", execution successful.
        '''
        self.analyzer.disable_upload()
        self.i2s_rx_en_pin.set_level(StarLordDef.I2S_RX_DISABLE)
        self.is_enable_upload = False

        return "done"

    def measure(self,
                channel,
                bandwidth_hz,
                harmonic_count,
                decimation_type=0xFF,
                sampling_rate=StarLordDef.AUDIO_SAMPLING_RATE):
        '''
        Measure audio input signal, which captures data using CS5361.

        Args:
            channel:         string, ['left', 'right'], select input signal channel.
            bandwidth_hz:    int/string, [42~48000], unit Hz, the signal bandwidth.
            harmonic_count:  int, [2~10], The harmonic count of signal.
            decimation_type: int, [1~255], default 0xFF, sample data decimation.
            sampling_rate:   int, [1~192000], default 192000, unit Hz, ADC sampling rate.

        Returns:
            dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value},
            measurement result.
        '''
        assert channel in StarLordDef.AUDIO_CHANNEL_LIST

        if self.is_enable_upload is False:
            self.i2s_rx_en_pin.set_level(StarLordDef.I2S_RX_ENABLE)

        pin = self.i2s_ch_select[StarLordDef.AUDIO_CHANNEL_SELECT_BIT0]
        pin.set_level(StarLordDef.AUDIO_CHANNEL_LIST[channel][
            StarLordDef.AUDIO_CHANNEL_SELECT_BIT0])
        pin = self.i2s_ch_select[StarLordDef.AUDIO_CHANNEL_SELECT_BIT1]
        pin.set_level(StarLordDef.AUDIO_CHANNEL_LIST[channel][
            StarLordDef.AUDIO_CHANNEL_SELECT_BIT1])

        self.fft_source_select.set_level(StarLordDef.FFT_SOURCE_FROM_CS5361)

        result = self._analyzer(sampling_rate, decimation_type, bandwidth_hz,
                                harmonic_count)

        range_name = "AUDIO_CS5361_RMS_" + channel

        result['rms'] = (self.calibrate(range_name, result['rms'][0]),
                         StarLordDef.VOLT_UNIT_RMS)

        if self.is_enable_upload is False:
            self.i2s_rx_en_pin.set_level(StarLordDef.I2S_RX_DISABLE)

        return result

    def config_lna_scope(self,
                         channel,
                         scope,
                         adc_upload_ch=StarLordDef.AD7175_UPLOAD_TO_FFT):
        '''
        Config LNA measurement scope

        Args:
            channel:        string, ['left', 'right'], the channel to be upload.
            scope:          string, ['5V', '50mV'], AD7175 measurement range.
            adc_upload_ch:  string, ['dma', 'fft'], default 'fft', AD7175 source data is uploaded to DMA or FFT.

        Returns:
            string, "done",  execution successful.
        '''
        assert channel in StarLordDef.AD7175_CH_LIST
        assert scope in StarLordDef.LNA_SCOPE
        assert adc_upload_ch in StarLordDef.AD7175_UPLOAD_CH

        self.channel = channel
        self.scope = scope
        if scope == StarLordDef.LNA_RANGE_5V:
            self.pca9536.set_pin(StarLordDef.AD7175_CH_LIST[channel],
                                 StarLordDef.SEL_GAIN_1)
        else:
            self.pca9536.set_pin(StarLordDef.AD7175_CH_LIST[channel],
                                 StarLordDef.SEL_GAIN_100)

        self.ad7175_upload_select.set_level(
            StarLordDef.AD7175_UPLOAD_CH[adc_upload_ch])

        return "done"

    def enable_lna_upload(self,
                          channel,
                          sampling_rate=StarLordDef.LNA_SAMPLING_RATE):
        '''
        Enable LNA adc data upload

        Args:
            channel:        string, ['left', 'right'], the channel to be upload.
            sampling_rate:  float, default 250000, unit Hz, AD7175 sampling rate,
                                   please refer to datasheet for more information.

        Returns:
            string, "done",  execution successful.
        '''
        assert channel in StarLordDef.AD7175_CH_LIST

        self.is_lna_up = True
        self.ad7175.disable_continuous_sampling(
            StarLordDef.AD7175_CH_LIST[channel])
        time.sleep(StarLordDef.RELAY_DELAY_S)

        self.ad7175.enable_continuous_sampling(
            StarLordDef.AD7175_CH_LIST[channel], sampling_rate)

        return "done"

    def disable_lna_upload(self, channel):
        '''
        Disable LNA adc data upload

        Args:
            channel:        string, ['left', 'right'], the channel to be upload.

        Returns:
            string, "done", execution successful.
        '''
        assert channel in StarLordDef.AD7175_CH_LIST

        self.is_lna_up = False
        if self.is_analyzer_up is True:
            self.analyzer.disable_upload()
            self.is_analyzer_up = False

        self.ad7175.disable_continuous_sampling(
            StarLordDef.AD7175_CH_LIST[channel])

        return "done"

    def measure_lna(self,
                    bandwidth_hz,
                    harmonic_count,
                    decimation_type=0xFF,
                    sampling_rate=StarLordDef.LNA_SAMPLING_RATE):
        '''
        Measure audio LNA input signal, which captures data using AD7175.

        Args:
            bandwidth_hz:    int/string, [42~48000], unit Hz, the signal bandwidth.
            harmonic_count:  int, [2~10], The harmonic count of signal.
            decimation_type: int, [1~255], default 0xFF, sample data decimation.
            sampling_rate:   int, [5~250000], default 250000, unit Hz,
                                  Sample rate of your ADC device.

        Returns:
            dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value},
            measurement result.
        '''
        self.fft_source_select.set_level(StarLordDef.FFT_SOURCE_FROM_AD7175)

        if self.is_lna_up is True and self.is_analyzer_up is False:
            self.analyzer.enable_upload()
            self.is_analyzer_up = True

        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.analyze_config(sampling_rate, decimation_type,
                                     bandwidth_hz, harmonic_count)
        self.analyzer.analyze()

        range_name = "LNA_" + self.scope + "_" + self.channel
        gain = StarLordDef.GAIN_VALUE[self.scope]
        vpp = self.analyzer.get_vpp() * gain
        rms = vpp / StarLordDef.RMS_TO_VPP_RATIO
        rms = self.calibrate(range_name, rms)
        thdn_value = self.analyzer.get_thdn()

        result = dict()
        result["vpp"] = (vpp, StarLordDef.VOLT_UNIT_MV)
        result["freq"] = (self.analyzer.get_frequency(),
                          StarLordDef.FREQ_UNIT_HZ)
        result["thd"] = (self.analyzer.get_thd(), StarLordDef.THD_UNIT_DB)
        result["thdn"] = (thdn_value, StarLordDef.THDN_UNIT_DB)
        result["rms"] = (rms, StarLordDef.VOLT_UNIT_RMS)
        result["noisefloor"] = (10**(thdn_value / 20) * rms,
                                StarLordDef.VOLT_UNIT_RMS)

        return result

    def enable_output(self, freq, vpp):
        '''
        StarLord CS5361 output audio sine waveform.

        Args:
            freq:       int, [5~50000], unit Hz, output signal's frequency.
            vpp:        float, [0~6504], unit mV, output signal's vpp.

        Returns:
            string, "done", execution successful.
        '''
        assert StarLordDef.OUTPUT_FREQ_MIN <= freq
        assert freq <= StarLordDef.OUTPUT_FREQ_MAX
        assert StarLordDef.OUTPUT_VPP_MIN <= vpp
        assert vpp <= StarLordDef.OUTPUT_VPP_MAX

        vpp = self.calibrate(StarLordDef.OUTPUT_CAL_ITEM, vpp)
        vpp = 0 if vpp < 0 else vpp
        # enable I2S tx module
        self.i2s_tx_en_pin.set_level(StarLordDef.AUDIO_OUTPUT_ENABLE)

        self.signal_source.close()
        self.signal_source.open()
        # calculate vpp to vpp scale for FPGA
        vpp_scale = vpp * StarLordDef.VPP_2_SCALE_RATIO
        self.signal_source.set_swg_paramter(StarLordDef.AUDIO_SAMPLING_RATE,
                                            freq, vpp_scale,
                                            StarLordDef.OUTPUT_SIGNAL_DUTY)
        self.signal_source.set_signal_type(StarLordDef.OUTPUT_WAVE)
        self.signal_source.set_signal_time(StarLordDef.SIGNAL_ALWAYS_OUTPUT)
        self.signal_source.output_signal()

        return "done"

    def disable_output(self):
        '''
        Disable Cs5361 output signal.

        Returns:
            string, "done", execution successful.
        '''
        self.signal_source.close()
        self.i2s_tx_en_pin.set_level(StarLordDef.AUDIO_OUTPUT_DISABLE)

        return "done"

    def _analyzer(self, sampling_rate, decimation_type, bandwidth_hz,
                  harmonic_count):
        '''
        Measure audio input signal.

        Returns:
            dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value},
            measurement result.
        '''
        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.analyze_config(sampling_rate, decimation_type,
                                     bandwidth_hz, harmonic_count)
        self.analyzer.analyze()

        # calculate the actual vpp of HW by VREF
        vpp = self.analyzer.get_vpp() * StarLordDef.AUDIO_ANALYZER_VREF
        # vpp = RMS * 2 * sqrt(2)
        rms = vpp / StarLordDef.RMS_TO_VPP_RATIO
        vpp = rms * StarLordDef.RMS_TO_VPP_RATIO
        thdn_value = self.analyzer.get_thdn()

        result = dict()
        result["vpp"] = (vpp, StarLordDef.VOLT_UNIT_MV)
        result["freq"] = (self.analyzer.get_frequency(),
                          StarLordDef.FREQ_UNIT_HZ)
        result["thd"] = (self.analyzer.get_thd(), StarLordDef.THD_UNIT_DB)
        result["thdn"] = (thdn_value, StarLordDef.THDN_UNIT_DB)
        result["rms"] = (rms, StarLordDef.VOLT_UNIT_RMS)
        result["noisefloor"] = (10**(thdn_value / 20) * rms,
                                StarLordDef.VOLT_UNIT_RMS)

        return result
Exemple #2
0
class Dazzler(MIXBoard):
    '''
    Dazzler function class.

    compatible = ['GQQ-AUD001004-000']

    The dazzler has a output sine function and one measure channel.
    It can measure signal's frequency, vpp, THD+N, THD.

    Args:
        i2c:                 instance(I2C),                  which is used to access eeprom and nct75, if not given,
                                                             will create eeprom and nct75 emulator.
        adc_rst_pin:         instance(GPIO),                 used to reset ADC CS5361. Setting low to reset CS5361,
                                                             seting high to enable CS5361.
        i2s_rx_en_pin:       instance(GPIO),                 used to enable fft analyzer module.
        dac_rst_pin:         instance(GPIO),                 used to reset IIS of the CS4398.
        i2s_tx_en_pin:       instance(GPIO),                 used to enable signal source module.
        sample_rate:         int,                            Use to config the CS5361 or CS4398, unit is Hz,
                                                             default 192000.
        ipcore:              instance(MIXAUT1SGR),              aggregated MIXAUT1SGR wrapper.

    Examples:
        i2c = I2C('/dev/i2c-1')
        aut1 = MIXAUT1SGR('/dev/MIX_AUT1_0')
        dazzler = Dazzler(i2c, ipcore=aut1)

        Example for measure, bandwith is 20000Hz, harmonic_num is 5:
            result = dazzler.measure(20000, 5)
            print("vpp={}, freq={}, thd={}, thdn={}, rms={}".format(result["vpp"], result["freq"], result["thd"],
                  result["thdn"], result["rms"]))

        Example for data upload:
            dma = Dma("/dev/MIX_DMA")
            dma.config_channel(0, 1024 * 1024)
            dma.enable_channel(0)
            dazzler.enable_upload()
            time.sleep(1)
            dazzler.disable_upload()
            data = dma.read_channel_all_data(0)
            dma.disable_channel(0)
            print(data)

    '''
    compatible = ['GQQ-AUD001004-000']

    rpc_public_api = [
        'module_init', 'enable_upload', 'disable_upload', 'measure',
        'enable_output', 'disable_output'
    ] + MIXBoard.rpc_public_api

    def __init__(self,
                 i2c,
                 adc_rst_pin=None,
                 i2s_rx_en_pin=None,
                 dac_rst_pin=None,
                 i2s_tx_en_pin=None,
                 sample_rate=192000,
                 ipcore=None):
        assert sample_rate > 0 and sample_rate <= DazzlerDef.MAX_SAMPLING_RATE
        if i2c is None:
            self.eeprom = EepromEmulator("cat24cxx_emulator")
            self.nct75 = NCT75Emulator("nct75_emulator")
        else:
            self.eeprom = CAT24C32(DazzlerDef.EEPROM_I2C_ADDR, i2c)
            self.nct75 = NCT75(DazzlerDef.SENSOR_I2C_ADDR, i2c)

        MIXBoard.__init__(self,
                          self.eeprom,
                          self.nct75,
                          cal_table=dazzler_calibration_info,
                          range_table=dazzler_range_table)

        if ipcore:
            if isinstance(ipcore, basestring):
                axi4 = AXI4LiteBus(ipcore, DazzlerDef.MIX_AUT1_REG_SIZE)
                self.ip = MIXAUT1SGR(axi4)
            else:
                self.ip = ipcore
            self.analyzer = self.ip.analyzer
            self.signal_source = self.ip.signal_source
            self.adc_rst_pin = adc_rst_pin or Pin(self.ip.gpio,
                                                  DazzlerDef.ADC_RESET_PIN)
            self.i2s_rx_en_pin = i2s_rx_en_pin or Pin(self.ip.gpio,
                                                      DazzlerDef.I2S_RX_EN_PIN)
            self.dac_rst_pin = dac_rst_pin or Pin(self.ip.gpio,
                                                  DazzlerDef.DAC_RESET_PIN)
            self.i2s_tx_en_pin = i2s_tx_en_pin or Pin(self.ip.gpio,
                                                      DazzlerDef.I2S_TX_EN_PIN)
        elif (not ipcore and not adc_rst_pin and not i2s_rx_en_pin
              and not dac_rst_pin and not i2s_tx_en_pin):
            self.analyzer = MIXFftAnalyzerSGEmulator(
                "mix_fftanalyzer_sg_emulator")
            self.signal_source = MIXSignalSourceSGEmulator(
                "mix_signalsource_sg_emulator")
            self.adc_rst_pin = Pin(None, DazzlerDef.ADC_RESET_PIN)
            self.i2s_rx_en_pin = Pin(None, DazzlerDef.I2S_RX_EN_PIN)
            self.dac_rst_pin = Pin(None, DazzlerDef.DAC_RESET_PIN)
            self.i2s_tx_en_pin = Pin(None, DazzlerDef.I2S_TX_EN_PIN)
        else:
            if ipcore:
                raise DazzlerException(
                    "parameter 'ipcore' can not be given at same time with 'analyzer', "
                    "'signal_source', 'adc_rst_pin', 'i2s_rx_en_pin', "
                    "'dac_rst_pin', 'i2s_tx_en_pin'")
            else:
                raise DazzlerException(
                    "parameter 'analyzer', 'signal_source', 'adc_rst_pin', "
                    "'i2s_rx_en_pin', 'dac_rst_pin' and 'i2s_tx_en_pin'"
                    " must be given at the same time")

        self.sample_rate = sample_rate

    def __del__(self):
        self.adc_rst_pin.set_level(0)
        self.i2s_rx_en_pin.set_level(0)
        self.dac_rst_pin.set_level(0)
        self.i2s_tx_en_pin.set_level(0)
        self.signal_source.close()

    def module_init(self):
        '''
        Dazzler init module, will reset dac/adc and i2s module.

        Returns:
            string, "done", api execution successful.

        '''
        self.adc_rst_pin.set_dir('output')
        self.dac_rst_pin.set_dir('output')
        self.i2s_rx_en_pin.set_dir('output')
        self.i2s_tx_en_pin.set_dir('output')

        # reset ADC
        self.adc_rst_pin.set_level(0)
        time.sleep(DazzlerDef.DELAY_S)
        self.adc_rst_pin.set_level(1)

        # reset DAC
        self.dac_rst_pin.set_level(0)
        time.sleep(DazzlerDef.DELAY_S)
        self.dac_rst_pin.set_level(1)

        self.i2s_rx_en_pin.set_level(1)

        self.load_calibration()

        return "done"

    def enable_upload(self):
        '''
        Dazzler upoad mode open.

        Control audio upload data of ADC when doing measurement.
        It's not necessary enable upload when doing measurement.
        Note that data transfered into DMA is 32bit each data, but high 24bit data is valid.
        Low 8bit data is invalid. The data format is twos complement.

        Returns:
            string, "done", api execution successful.
        '''
        self.analyzer.enable_upload()
        return "done"

    def disable_upload(self):
        '''
        Dazzler upoad mode close.

        Close data upload doesn't influence to measure.

        Returns:
            string, "done", api execution successful.
        '''
        self.analyzer.disable_upload()
        return "done"

    def measure(self, bandwidth_hz, harmonic_num, decimation_type=0xFF):
        '''
        Dazzler measure signal's Vpp, RMS, THD+N, THD.

        Args:
            bandwidth_hz:    int, [24~95977], Measure signal's limit bandwidth, unit is Hz. The frequency of the
                                              signal should not be greater than half of the bandwidth.
            harmonic_num:    int, [2-10], Use for measuring signal's THD.
            decimation_type: int, [1~255], Decimation for FPGA to get datas. If decimation is 0xFF, FPGA will
                                           choose one suitable number.

        Returns:
            dict, {"vpp":value, "freq":value, "thd":value, "thdn":value, "rms":value},
                  vpp, freq, thd, thdn, rms value.

        Examples:
            dazzler.measure(20000, 5, 0xff)
        '''
        assert bandwidth_hz == 'auto' or isinstance(bandwidth_hz, int)
        assert isinstance(harmonic_num, int) and harmonic_num > 0
        assert isinstance(decimation_type, int) and decimation_type > 0

        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.analyze_config(self.sample_rate, decimation_type,
                                     bandwidth_hz, harmonic_num)
        self.analyzer.analyze()

        # calculate the actual vpp of HW by VREF
        vpp = self.analyzer.get_vpp() * DazzlerDef.AUDIO_ANALYZER_VREF
        # vpp = RMS * 2 * sqrt(2)
        rms = vpp / DazzlerDef.RMS_TO_VPP_RATIO

        rms = self.calibrate(DazzlerDef.MEASURE_CAL_ITEM, rms)
        vpp = rms * DazzlerDef.RMS_TO_VPP_RATIO

        result = dict()
        result["vpp"] = vpp
        result["freq"] = self.analyzer.get_frequency()
        result["thd"] = self.analyzer.get_thd()
        result["thdn"] = self.analyzer.get_thdn()
        result["rms"] = rms
        return result

    def enable_output(self, freq, rms):
        '''
        Dazzler output sine wave, differencial mode.

        Args:
            freq:    int, [10~100000], Ouput signal's frequency, unit is Hz.
            rms:     float, [0~2300], Ouput wave's RMS, unit is mV.

        Returns:
            string, "done", api execution successful.

        Examples:
            dazzler.enable_output(10000, 500)
        '''
        assert freq >= DazzlerDef.OUTPUT_FREQ_MIN
        assert freq <= DazzlerDef.OUTPUT_FREQ_MAX
        assert rms >= DazzlerDef.OUTPUT_RMS_MIN
        assert rms <= DazzlerDef.OUTPUT_RMS_MAX

        # enable I2S tx module
        self.i2s_tx_en_pin.set_level(1)

        self.signal_source.open()

        rms = self.calibrate(DazzlerDef.OUTPUT_CAL_ITEM, rms)
        # vpp = RMS * 2 * sqrt(2)
        vpp = rms * DazzlerDef.RMS_TO_VPP_RATIO
        # calculate vpp to vpp scale for FPGA
        vpp_scale = vpp * DazzlerDef.VPP_2_SCALE_RATIO / DazzlerDef.SIGNAL_SOURCE_VREF
        self.signal_source.set_signal_type(DazzlerDef.OUTPUT_WAVE)
        self.signal_source.set_signal_time(DazzlerDef.SIGNAL_ALWAYS_OUTPUT)
        self.signal_source.set_swg_paramter(self.sample_rate, freq, vpp_scale,
                                            DazzlerDef.OUTPUT_SIGNAL_DUTY)
        self.signal_source.output_signal()
        return "done"

    def disable_output(self):
        '''
        Dazzler close sine wave output.

        Returns:
            string, "done", api execution successful.

        Examples:
            dazzler.disable_output()
        '''
        self.signal_source.close()
        self.i2s_tx_en_pin.set_level(0)
        return "done"
Exemple #3
0
class AD9628(object):
    '''
    The AD9628 is a monolithic, dual-channel, 1.8V supply, 12-bit, 125MSPS/105MSPS analog-to-digital converter (ADC).
    It features a high performance sample-and-hold circuit and onchip voltage reference.
    The digital output data is presented in offset binary, Gray code, or twos complement format.
    A data output clock (DCO) is provided for each ADC channel to ensure proper latch timing with receiving logic.
    1.8V CMOS or LVDS output logic levels are supported. Output data can also be multiplexed onto a single output bus.

    ClassType = ADC

    Args:
        spi_bus:     instance(QSPI)/None,  MIXQSPISG class instance, if not using, will create emulator.
        pdwn_ctrl:   instance(GPIO)/None,  Class instance of GPIO, which is used to control ad9628 pdwn_ctrl signal,
                                           default None for not use.
        oeb_ctrl:    instance(GPIO)/None,  Class instance of GPIO, which is used to control ad9628 oeb_ctrl signal,
                                           default None for not use.
        cs_ctrl:     instance(GPIO)/None,  Class instance of GPIO, which is used to control ad9628 cs_ctrl signal,
                                           default None for not use.
        use_cs:      Bool('True'/'False'), determine if CS is used internally.

    Examples:
        pdwn_ctrl = GPIO(cat9555, 6)
        oeb_ctrl = GPIO(cat9555, 7)
        cs_ctrl = GPIO(cat9555, 12)
        axi4_bus = AXI4LiteBus('/dev/MIX_DUT_SPI_0', 256)
        spi_bus = MIXQSPISG(axi4_bus)
        ad9628 = AD9628(spi_bus, pdwn_ctrl, oeb_ctrl, cs_ctrl)

    '''

    def __init__(self, spi_bus=None, pdwn_ctrl=None, oeb_ctrl=None, cs_ctrl=None, use_cs=True):
        if(spi_bus is None and pdwn_ctrl is None and oeb_ctrl is None):
            self.spi_bus = MIXQSPISGEmulator("ad9628_emulator", 256)
            self.pdwn = Pin(None, 6)
            self.oeb = Pin(None, 7)
        elif(spi_bus is not None and pdwn_ctrl is not None and oeb_ctrl is not None):
            self.spi_bus = spi_bus
            self.pdwn = pdwn_ctrl
            self.oeb = oeb_ctrl
        else:
            raise AD9628RException('__init__ error! Please check the parameters')

        self.use_cs = use_cs
        if(self.use_cs is True and cs_ctrl is not None):
            self.cs = cs_ctrl
        elif(self.use_cs is False and cs_ctrl is None):
            self.cs = Pin(None, 12)
        else:
            raise AD9628RException('cs use error! Please check the parameters')

    def read_register(self, reg_addr):
        '''
        AD9628 internal function to read register

        Args:
            reg_addr:    hexmial, [0~0x102], The register range is 0-0x102 in datasheet, register to be read.

        Returns:
            int, value, register read data.

        Examples:
            rd_data = ad9628.read_register(0x01)
            print(rd_data)
            # rd_data will be like 0x05

        '''
        assert isinstance(reg_addr, int)
        assert 0 <= reg_addr <= 0x102

        if self.use_cs is True:
            self.cs.set_level(0)

        reg_address = (1 << 15) + (reg_addr & 0x3FF)
        read_data = 0
        write_date = [(reg_address >> 8) & 0xFF, reg_address & 0xFF]
        reg_value = self.spi_bus.transfer(write_date, 1, False)
        read_data = reg_value[0]

        if self.use_cs is True:
            self.cs.set_level(1)

        return read_data

    def write_register(self, reg_addr, reg_value):
        '''
        AD9628 internal function to write register

        Args:
            reg_addr:     hexmial, [0~0x102], register to be write.
            reg_value:    hexmial, [0~0xff], data to be write.

        Examples:
            ad9628.write_register(0x01, 0x02)

        '''
        assert isinstance(reg_addr, int)
        assert isinstance(reg_value, int)
        assert 0 <= reg_addr <= 0x102
        assert 0 <= reg_value <= 0xFF

        if self.use_cs is True:
            self.cs.set_level(0)

        reg_address = reg_addr & 0x3FF
        write_date = [(reg_address >> 8) & 0xFF, reg_address & 0xFF, reg_value]
        ret = self.spi_bus.write(write_date)

        if self.use_cs is True:
            self.cs.set_level(1)

        if ret is False:
            return False

        return True

    def get_chip_id(self):
        '''
        AD9628 internal function to get AD9628's chip ID number

        Returns:
            int, value, variable which to store the chip ID.

        Examples:
            id = ad9628.get_chip_id()
            print(id)

        '''
        chip_id = self.read_register(AD9628Def.REGISTER_ADDRESS["chip_id"])
        return chip_id

    def get_clock_speed_grade(self):
        '''
        AD9628 internal function to check AD9628's input clock speed supported, 105000000SPS or 125000000SPS.

        Returns:
            int, value, Return 105000000SPS or 125000000SPS.

        Examples:
            grade = ad9628.clock_speed_grade()
            print(grade)

        '''
        grade = 0
        rd_data = self.read_register(
            AD9628Def.REGISTER_ADDRESS["chip_grade_id"])
        if (((rd_data) >> 4) & 0x07) == 0x04:
            grade = 105000000
        elif (((rd_data) >> 4) & 0x07) == 0x05:
            grade = 125000000
        else:
            raise AD9628RException('get clock speed grade fail!')

        return grade

    def select_channel(self, channel):
        '''
        AD9628 internal function to select which channel to receive SPI command.

        Args:
            channel:    string, ["A", "B", "BOTH"]. select which channel to receive SPI command.

        Examples:
            ad9628.select_channel("A")

        '''
        assert channel in AD9628Def.CHANNEL_SELECT

        self.write_register(
            AD9628Def.REGISTER_ADDRESS["device_index"], AD9628Def.CHANNEL_SELECT[channel])

    def get_sampling_rate(self):
        '''
        AD9628 internal function to get sample rate.

        Returns:
            int, value, Return 80000000SPS, 105000000SPS or 125000000SPS.

        Examples:
            rate = ad9628.get_sampling_rate()
            print(rate)

        '''

        sample_rate = 0
        rd_data = self.read_register(AD9628Def.REGISTER_ADDRESS["sample_rate"])

        if (rd_data & 0x07) == 0x03:
            sample_rate = 80000000
        elif (rd_data & 0x07) == 0x04:
            sample_rate = 105000000
        elif (rd_data & 0x07) == 0x05:
            sample_rate = 125000000
        else:
            raise AD9628RException('get sampling rate fail!')

        return sample_rate

    def setup(self, type, format, msps, bits):
        '''
        AD9628 internal function to Initial AD9628.

        Args:
            type:    string, ["CMOS", "LVDS_ANSI", "LVDS_REDUCED"],
                             configure the data output type of the AD9628.
            format:  string, ["OFFSET_BIN", "TWOS_COMPLEMENT", "GRAY_CODE"],
                             configure the data output format of the AD9628.
            msps:    int, [80000000, 105000000, 125000000],
                          configure the sample speed of data output of the AD9628.
            bits:    string, ["WIDTH_10", "WIDTH_12"],
                             configure the output data bits of the AD9628.

        Examples:
            ad9628.setup("LVDS_ANSI", "OFFSET_BIN", "125000000", "WIDTH_12")

        '''
        assert type in AD9628Def.OUTPUT_TYPE
        assert format in AD9628Def.OUTPUT_FORMAT
        assert msps in AD9628Def.SAMPLE_SPEED
        assert bits in ["WIDTH_10", "WIDTH_12"]

        # Digital Output Enable Function
        self.oeb.set_level(0)
        # self.pdwn.set_level(0), external power down.
        self.power_mode("EXTERNAL", "POWER_DOWN")
        # Internal power-down mode ,register digital reset.
        self.power_mode("INTERNAL", "DIGITAL_RESET")
        # Set power mode need to reset it and wait 1 millisecond
        time.sleep(AD9628Def.SLEEP_TIME)
        self.power_mode("INTERNAL", "POWER_ON")

        output_mode = AD9628Def.OUTPUT_TYPE[type] | AD9628Def.OUTPUT_FORMAT[format]
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["output_mode"], output_mode)

        sample_rate_bits = AD9628Def.RESOLUTION_12_BITS
        if bits == "WIDTH_10":
            sample_rate_bits = AD9628Def.RESOLUTION_10_BITS
        elif bits == "WIDTH_12":
            sample_rate_bits = AD9628Def.RESOLUTION_12_BITS
        else:
            raise AD9628RException('width bits set fail!')
        sample = AD9628Def.SAMPLE_SPEED[msps] | sample_rate_bits
        self.write_register(AD9628Def.REGISTER_ADDRESS["sample_rate"], sample)

        self.write_register(
            AD9628Def.REGISTER_ADDRESS["transfer_addr"], AD9628Def.SYNC_TRANSFER)

    def clock_phase(self, dco_invert_ctrl, phase_ratio):
        '''
        AD9628 internal function to input clock divider phase adjust relative to the encode clock.

        Args:
            dco_invert_ctrl:    string, ["NOT_INVERT", "INVERT"],
                                        control DCO(dat clock output) invert or not.
            phase_ratio:        string, ["NO_DELAY", "ONE_INPUT", "TWO_INPUT", "THREE_INPUT",
                                         "FOUR_INPUT", "FIVE_INPUT", "SIX_INPUT", "SEVEN_INPUT"],
                                        selection of clock delays into the input clock divider.

        Examples:
            ad9628.clock_phase("INVERT", "ONE_INPUT")

        '''
        assert dco_invert_ctrl in ["NOT_INVERT", "INVERT"]
        assert phase_ratio in AD9628Def.PHASE_INPUT_CLOCK_CYCLES

        phase_delay = AD9628Def.PHASE_INPUT_CLOCK_CYCLES[phase_ratio]
        if dco_invert_ctrl == "INVERT":
            phase_con = (AD9628Def.INVERT_DCO | phase_delay)
        else:
            phase_con = (AD9628Def.NOT_INVERT_DCO | phase_delay)

        self.write_register(
            AD9628Def.REGISTER_ADDRESS["clock_phase"], phase_con)

    def clock_divide(self, divide_ratio, div_next_sync, div_sync_en):
        '''
        AD9628 internal function to config clock divider's divide ratio.

        Args:
            divide_ratio:     string, ["BY_1", "BY_2", "BY_3", "BY_4", "BY_5", "BY_6", "BY_7", "BY_8"],
                                      config clock divider's divide ratio.
            div_next_sync:    string, ["NEX_SYNC", "NEX_ASYNC"],
                                      config divider next sync only open or not.
            div_sync_en:      string, ["SYNC_OPEN", "SYNC_CLOSE"],
                                      config divider sync open or not.

        Examples:
            ad9628.clock_divide("BY_1", "NEX_SYNC", "SYNC_OPEN")

        '''
        assert divide_ratio in AD9628Def.CLOCK_DIVIDE
        assert div_next_sync in ["NEX_SYNC", "NEX_ASYNC"]
        assert div_sync_en in ["SYNC_OPEN", "SYNC_CLOSE"]

        divider_con = AD9628Def.CLOCK_DIVIDE[divide_ratio]
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["clock_divide"], divider_con)

        if div_next_sync == "NEX_SYNC":
            if div_sync_en == "SYNC_OPEN":
                sync_con = (AD9628Def.DIV_NEX_SYNC | AD9628Def.DIV_SYNC_OPEN)
            else:
                sync_con = (AD9628Def.DIV_NEX_SYNC | AD9628Def.DIV_SYNC_CLOSE)
        else:
            if div_sync_en == "SYNC_OPEN":
                sync_con = (AD9628Def.DIV_NEX_ASYNC | AD9628Def.DIV_SYNC_OPEN)
            else:
                sync_con = (AD9628Def.DIV_NEX_ASYNC | AD9628Def.DIV_SYNC_CLOSE)
        self.write_register(AD9628Def.REGISTER_ADDRESS["sync_ctrl"], sync_con)

    def clock_stabilizer(self, statu):
        '''
        AD9628 internal function to global clock duty cycle stabilizer control.

        Args:
            statu:    string, ["DIS_STABILIZE"/"EN_STABILIZE"],
                              control clock duty cycle stabilizer open or not.

        Examples:
            ad9628.clock_stabilizer("EN_STABILIZE")

        '''
        assert statu in ["DIS_STABILIZE", "EN_STABILIZE"]

        if statu == "EN_STABILIZE":
            stabilizer_con = 0x01
        else:
            stabilizer_con = 0x00
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["global_clock"], stabilizer_con)

    def cmos_output_adjust(self, dco_strength, data_strength):
        '''
        AD9628 internal function to config CMOS output driver strength properties.

        Args:
            dco_strength:     string, ["1X", "2X", "3X", "4X"],
                                      config DCO strength level when chip config in CMOS mode.
            data_strength:    string, ["1X", "2X", "3X", "4X"],
                                      config data strength level when chip config in CMOS mode.

        Examples:
            ad9628.cmos_output_adjust("1X", "1X")

        '''
        assert dco_strength in AD9628Def.DCO_STRENGTH
        assert data_strength in AD9628Def.DATA_STRENGTH

        cmos_adjust = AD9628Def.DCO_STRENGTH[dco_strength] | AD9628Def.DATA_STRENGTH[data_strength]
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["output_adjust"], cmos_adjust)

    def data_output_delay(self, dco_delay_en, data_delay_en, delay):
        '''
        AD9628 internal function to sets the fines output delay of the output clock, not change the internal timeing.

        Args:
            dco_delay_en:     string, ["DCO_DELAY_OPEN", "DCO_DELAY_CLOSE"],
                                      control DCO delay function open or not.
            data_delay_en:    string, ["DATA_DELAY_OPEN", "DATA_DELAY_CLOSE"],
                                      control data delay function open or not.
            delay:            string, ["0_56_NS", "1_12_NS", "1_68_NS", "2_24_NS",
                                       "2_80_NS", "3_36_NS", "3_92_NS", "4_48_NS"], select data delay level.

        Examples:
            ad9628.data_output_delay("DCO_DELAY_OPEN", "DATA_DELAY_OPEN", "1_12_NS")

        '''
        assert delay in AD9628Def.DATA_OUTPUT_DELAY
        assert dco_delay_en in ["DCO_DELAY_OPEN", "DCO_DELAY_CLOSE"]
        assert data_delay_en in ["DATA_DELAY_OPEN", "DATA_DELAY_CLOSE"]

        output_delay = AD9628Def.DATA_OUTPUT_DELAY[delay]
        if dco_delay_en == "DCO_DELAY_OPEN":
            if data_delay_en == "DATA_DELAY_OPEN":
                output_delay = (AD9628Def.DCO_DELAY_OPEN | AD9628Def.DATA_DELAY_OPEN | (output_delay))
            else:
                output_delay = (AD9628Def.DCO_DELAY_OPEN | AD9628Def.DATA_DELAY_CLOSE | (output_delay))
        else:
            if data_delay_en == "DATA_DELAY_OPEN":
                output_delay = (AD9628Def.DCO_DELAY_CLOSE | AD9628Def.DATA_DELAY_OPEN | (output_delay))
            else:
                output_delay = (AD9628Def.DCO_DELAY_CLOSE | AD9628Def.DATA_DELAY_CLOSE | (output_delay))

        self.write_register(
            AD9628Def.REGISTER_ADDRESS["output_delay"], output_delay)

    def vref_set(self, vref, overrange_en):
        '''
        AD9628 internal function to adjuse AD9628 VREF level.

        Args:
            vref:            string, ["1_00", "1_14", "1_33", "1_60", "2_00"], config the inter VREF level.
            overrange_en:    string, ["OVERRANGE", "NOT_OVERRANGE"],  config output overrange enable or not.

        Examples:
            ad9628.vref_set("1_00", "OVERRANGE")

        '''
        assert vref in AD9628Def.VREF_LEVEL
        assert overrange_en in ["OVERRANGE", "NOT_OVERRANGE"]

        overrange_con = 0x01
        if overrange_en == "OVERRANGE":
            overrange_con = 0x01
        else:
            overrange_con = 0x00
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["overrange_reg"], overrange_con)

        vref_con = AD9628Def.VREF_LEVEL[vref]
        self.write_register(AD9628Def.REGISTER_ADDRESS["vref_reg"], vref_con)

    def _internal_power_mode(self, work_mode):
        '''
        AD9628 internal function to config AD9628 internal power mode.

        Args:
            work_mode:    string, ["POWER_ON", "POWER_DOWN", "STANDBY", "DIGITAL_RESET"],
                                  select AD9628's work mode.

        Examples:
            ad9628._internal_power_mode("POWER_ON")

        '''
        assert work_mode in AD9628Def.POWER_MODE

        power_con = 0
        power_con = self.read_register(
            AD9628Def.REGISTER_ADDRESS["power_mode"])
        power_con = ((power_con) & 0xFC) | AD9628Def.POWER_MODE[work_mode]

        self.write_register(
            AD9628Def.REGISTER_ADDRESS["power_mode"], power_con)

    def _external_power_mode(self, work_mode):
        '''
        AD9628 internal function to config AD9628 external power mode.

        Args:
            work_mode:    string, ["POWER_ON", "POWER_DOWN"], select AD9628's work mode.

        Examples:
            ad9628._external_power_mode("POWER_ON")

        '''
        assert work_mode in ["POWER_ON", "POWER_DOWN"]

        if self.pdwn is None:
            raise AD9628RException('power mode set fail!')

        if work_mode == "POWER_ON":
            self.pdwn.set_level(1)
        else:
            self.pdwn.set_level(0)

    def power_mode(self, refer, work_mode):
        '''
        AD9628 internal function to config AD9628 chip power mode.

        Args:
            refer:        string, ["INTERNAL", "EXTERNAL"], select internal or external power mode for controling.
            work_mode:    string, ["POWER_ON", "POWER_DOWN", "STANDBY", "DIGITAL_RESET"],
                                  select AD9628's work mode.

        Examples:
            ad9628.power_mode("EXTERNAL", "POWER_ON")

        '''
        assert refer in ["INTERNAL", "EXTERNAL"]

        if refer == "INTERNAL":
            self._internal_power_mode(work_mode)
        else:
            self._external_power_mode(work_mode)

    def soft_reset(self):
        '''
        AD9628 internal function to AD9628 soft reset.

        Examples:
            ad9628.soft_reset()

        '''
        port_con = self.read_register(
            AD9628Def.REGISTER_ADDRESS["port_config"])
        port_con = ((port_con) | AD9628Def.SOFT_RESET)
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["port_config"], port_con)

    def chop_mode_open(self):
        '''
        AD9628 internal function to AD9628 enhancement control enable.

        Examples:
            ad9628.chop_mode_open()

        '''
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["enhancement_ctrl"], AD9628Def.CHOP_MODE_OPEN)

    def chop_mode_close(self):
        '''
        AD9628 internal function to AD9628 enhancement control disable.

        Examples:
            ad9628.chop_mode_close()

        '''
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["enhancement_ctrl"], AD9628Def.CHOP_MODE_CLOSE)

    def test_mode_config(self, user_mode, rst_pn_l_en, rst_pn_s_en, output_mode):
        '''
        AD9628 internal function to Config test data to AD9628's data output pins.

        Args:
            user_mode:      string, ["SINGLE", "ALTERNATE_REPEAT", "ONCE", "ALTERNATE_ONCE"],
                                    select user test mode.
            rst_pn_l_en:    string, ["EN_RESET_L", "DIS_RESET_L"],
                                    control reset PN long gen enable or not.
            rst_pn_s_en:    string, ["EN_RESET_S", "DIS_RESET_S"],
                                    control reset PN short gen enable or not.
            output_mode:    string, ["OFF", "MIDSCALE_SHORT", "POSITIVE_FS", "NEGATIVE_FS",
                                     "ALTERNATING_CHECKERBOARD", "PN_LONG_SEQUENCE", "PN_SHORT_SEQUENCE",
                                     "ONE_WORD_TOGGLE", "USER_TEST_MODE", "RAMP_OUTPUT"],
                                    select user output mode.

        Examples:
            ad9628.test_mode_config("SINGLE", "EN_RESET_L", "EN_RESET_S", "OFF")

        '''
        assert rst_pn_l_en in ["EN_RESET_L", "DIS_RESET_L"]
        assert rst_pn_s_en in ["EN_RESET_S", "DIS_RESET_S"]
        assert user_mode in AD9628Def.TEST_MODE
        assert output_mode in AD9628Def.OUTPUT_TEST_MODE

        user_test_mode_con = AD9628Def.TEST_MODE[user_mode]
        output_test_mode_con = AD9628Def.OUTPUT_TEST_MODE[output_mode]
        test_mode_con = (user_test_mode_con | AD9628Def.DIS_RESET_PN_LONG_GEN |
                         AD9628Def.DIS_RESET_PN_SHORT_GEN | output_test_mode_con)

        if rst_pn_l_en == "EN_RESET_L":
            if rst_pn_s_en == "EN_RESET_S":
                test_mode_con = (user_test_mode_con | AD9628Def.EN_RESET_PN_LONG_GEN | (
                                 AD9628Def.EN_RESET_PN_SHORT_GEN) | output_test_mode_con)
            else:
                test_mode_con = (user_test_mode_con | AD9628Def.EN_RESET_PN_LONG_GEN | (
                                 AD9628Def.DIS_RESET_PN_SHORT_GEN) | output_test_mode_con)
        else:
            if rst_pn_s_en == "EN_RESET_S":
                test_mode_con = ((user_test_mode_con) | AD9628Def.DIS_RESET_PN_LONG_GEN | (
                                 AD9628Def.EN_RESET_PN_SHORT_GEN) | (output_test_mode_con))
            else:
                test_mode_con = ((user_test_mode_con) | AD9628Def.DIS_RESET_PN_LONG_GEN | (
                                 AD9628Def.DIS_RESET_PN_SHORT_GEN) | (output_test_mode_con))

        self.write_register(
            AD9628Def.REGISTER_ADDRESS["test_mode"], test_mode_con)

    def bist_mode_open(self, init_sequence):
        '''
        AD9628 internal function to BIST(build in self test) mode open.

        Args:
            init_sequence:    string, ["INIT_SEQ", "NOT_INIT_SEQ"],
                                      init sequence or not in BIST(build in self test) mode.

        Examples:
            ad9628.bist_mode_open("INIT_SEQ")

        '''
        assert init_sequence in ["INIT_SEQ", "NOT_INIT_SEQ"]

        bist_mode_con = AD9628Def.INIT_BIST_SEQ
        if init_sequence == "INIT_SEQ":
            bist_mode_con = AD9628Def.INIT_BIST_SEQ
        else:
            bist_mode_con = AD9628Def.NOT_INIT_BIST_SEQ

        self.write_register(
            AD9628Def.REGISTER_ADDRESS["bist_reg"], bist_mode_con)

    def bist_mode_close(self):
        '''
        AD9628 internal function to BIST(build in self test) mode close.

        Examples:
            ad9628.bist_mode_close()

        '''
        self.write_register(
            AD9628Def.REGISTER_ADDRESS["bist_reg"], 0X00)

    def customer_offset_adjust(self, data):
        '''
        AD9628 internal function to Customer offset adjust when AD9628 config in twos complement format output type.

        Args:
            data:    hexmial, [0x00~0xFF], write register value.

        Examples:
            ad9628.customer_offset_adjust(0x00)

        '''
        assert isinstance(data, int)
        assert 0 <= data <= 0xFF

        self.write_register(
            AD9628Def.REGISTER_ADDRESS["customer_offset"], data)

    def user_define_pattern(self, pattern, format_data):
        '''
        AD9628 internal function to AD9628 user pattern definetion config.

        Args:
            pattern:        string, ["PATTERN_1", "PATTERN_2"], select which pattern to control.
            format_data:    hexmial, [0x00~0xFFFF], write register value.

        Examples:
            ad9628.user_define_pattern("PATTERN_1", 0x01)

        '''
        assert isinstance(format_data, int)
        assert pattern in ["PATTERN_1", "PATTERN_2"]
        assert 0x00 <= format_data <= 0xFFFF

        if pattern == "PATTERN_1":
            reg_address = AD9628Def.REGISTER_ADDRESS["user_pattern_1_L"] & 0x3FF
        else:
            reg_address = AD9628Def.REGISTER_ADDRESS["user_pattern_2_L"] & 0x3FF

        write_date = []
        write_date.append((reg_address & 0xFF00) >> 8)
        write_date.append(reg_address & 0x00FF)
        write_date.append(format_data >> 8 & 0xFF)
        write_date.append(format_data & 0xFF)
        self.spi_bus.write(write_date)

    def user_io_ctrl(self, io, state):
        '''
        AD9628 internal function to multiplexing pins control.

        Args:
            io:       string, ["OEB", "PDWN", "VCM", "SDIO"], select which IO to control.
            state:    string, ["EN_PIN", "DIS_PIN"],
                              control pin's function disable(effective when pin is PDWN) or enable.

        Examples:
            ad9628.user_io_ctrl("OEB", "EN_PIN")

        '''
        assert io in ["OEB", "PDWN", "VCM", "SDIO"]
        assert state in ["EN_PIN", "DIS_PIN"]

        if io == "OEB":
            rd_data = self.read_register(
                AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_2"])
            if state == "EN_PIN":
                wr_data = (rd_data & 0x7F | AD9628Def.EN_OEB_PIN)
            else:
                wr_data = (rd_data & 0x7F | AD9628Def.DIS_OEB_PIN)
            return self.write_register(
                AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_2"], wr_data)
        elif io == "PDWN":
            rd_data = self.read_register(
                AD9628Def.REGISTER_ADDRESS["power_mode"])
            if state == "EN_PIN":
                wr_data = (rd_data & 0xDF | AD9628Def.EN_PDWN_PIN)
            else:
                wr_data = (rd_data & 0xDF | AD9628Def.DIS_PDWN_PIN)
            return self.write_register(
                AD9628Def.REGISTER_ADDRESS["power_mode"], wr_data)
        elif io == "VCM":
            if state == "EN_PIN":
                wr_data = AD9628Def.VCM_POWER_ON
            else:
                wr_data = AD9628Def.VCM_POWER_DOWN
            return self.write_register(
                AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_3"], wr_data)
        elif io == "SDIO":
            rd_data = self.read_register(
                AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_2"])
            if state == "EN_PIN":
                wr_data = (rd_data & 0xFE | 1)
            else:
                wr_data = (rd_data & 0xFE | 0)
            return self.write_register(
                AD9628Def.REGISTER_ADDRESS["user_ctrl_reg_2"], wr_data)
class MIK002004(SGModuleDriver):
    '''
    MIK002004 is a high-pressure differential input digital audio module.

    compatible = ["GQQ-Q58K-5-040"]

    Args:
        i2c:                  instance(I2C), the instance of I2C bus. which will be used to used
                              to control eeprom, sensor and io expander.
        ipcore:               instance(MIXMIK002SGR)/string, the instance of Ipcore, which has 2 child IP,
                              MIXFftAnalyzerSG, MIXGPIOSG.
        analyzer:             instance(PLFFTAnalyzer)/string, if not given, will create emulator.
        adc_rst_pin:          instance(GPIO), used to reset IIS of the CS5381.
        adc_ovfl_l_pin:       instance(GPIO), used to get state of the ovfl_l pin for CS5381.
        i2s_left_select_pin:  instance(GPIO), used to enable IIS left channel module.
        i2s_right_select_pin: instance(GPIO), used to enable IIS right channel module.
        i2s_both_select_pin:  instance(GPIO), used to enable IIS both left & right channel module.
        tone_detect_pin:      instance(GPIO), used to get state of the tone detect pin.
        upload_enable_pin:    instance(GPIO), used to enable data upload to DMA directly.
        measure_enable_pin:   instance(GPIO), used to enable FFT measure data.
        sample_rate:          int, unit Hz, default 48000, used to configure the CS5381.

    Examples:
        # use non-aggregated IP
        i2c_bus = I2C('/dev/i2c-1')
        axi4 = AXI4LiteBus(analyzer_dev, 256)
        analyzer = MIXFftAnalyzerSG(axi4)
        adc_rst_pin = GPIO(88, 'output')
        adc_ovfl_l_pin = GPIO(93, 'output')
        i2s_left_select_pin = GPIO(88, 'output')
        i2s_right_select_pin = GPIO(89, 'output')
        i2s_both_select_pin = GPIO(87, 'output')
        tone_detect_pin = GPIO(94, 'output')
        upload_enable_pin = GPIO(97, 'output')
        measure_enable_pin = GPIO(95, 'output')
        warlock = MIK002004(i2c=i2c_bus, ipcore=None,
                          analyzer=analyzer, adc_rst_pin=adc_rst_pin,
                          adc_ovfl_l_pin=adc_ovfl_l_pin, i2s_left_select_pin=i2s_left_select_pin,
                          i2s_right_select_pin=i2s_right_select_pin,i2s_both_select_pin=i2s_both_select_pin,
                          tone_detect_pin=tone_detect_pin,upload_enable_pin=upload_enable_pin,
                          measure_enable_pin=measure_enable_pin, sample_rate=48000)

        # use MIXMIK002SGR aggregated IP
        i2c_bus = I2C('/dev/i2c-1')
        ipcore = MIXMIK002SGR('/dev/MIX_MIK002_SG_R')
        warlock = MIK002004(i2c=i2c_bus, ipcore=ipcore)

        # measure left channel input in china mode.
        warlock.headphone_out('1Vrms', 'CH')
        warlock.measure('left', 20000, 5)

        # measure xtalk signal in china mode.
        warlock.hp2mic_xtalk('CH')
        warlock.xtalk_measure('hp2mic')
    '''

    # launcher will use this to match driver compatible string and load driver if matched.
    compatible = ["GQQ-Q58K-5-040"]

    rpc_public_api = [
        'enable_upload', 'disable_upload', 'measure', 'xtalk_measure',
        'mikey_tone', 'get_tone_detect_state', 'set_sampling_rate',
        'get_sampling_rate', 'adc_hpf_state', 'headset_loopback', 'line_out',
        'headphone_out', 'hp2mic_xtalk', 'io_set', 'io_dir_set', 'adc_reset',
        'get_overflow_state', 'io_dir_read', 'io_read', 'start_record_data',
        'stop_record_data'
    ] + SGModuleDriver.rpc_public_api

    def __init__(self,
                 i2c,
                 ipcore=None,
                 analyzer=None,
                 adc_rst_pin=None,
                 adc_ovfl_l_pin=None,
                 i2s_left_select_pin=None,
                 i2s_right_select_pin=None,
                 i2s_both_select_pin=None,
                 tone_detect_pin=None,
                 upload_enable_pin=None,
                 measure_enable_pin=None,
                 sample_rate=48000):

        assert sample_rate > 0 and sample_rate <= MIK002004Def.MAX_SAMPLING_RATE

        if (i2c and analyzer and adc_rst_pin and adc_ovfl_l_pin
                and i2s_left_select_pin and i2s_right_select_pin
                and i2s_both_select_pin and tone_detect_pin
                and upload_enable_pin and measure_enable_pin):
            self.i2c = i2c

            if isinstance(analyzer, basestring):
                self.analyzer = MIXXtalkMeasureSG(analyzer)
            else:
                self.analyzer = analyzer

            self.adc_rst_pin = adc_rst_pin
            self.adc_ovfl_l_pin = adc_ovfl_l_pin
            self.i2s_left_select_pin = i2s_left_select_pin
            self.i2s_right_select_pin = i2s_right_select_pin
            self.i2s_both_select_pin = i2s_both_select_pin
            self.tone_detect_pin = tone_detect_pin
            self.upload_enable_pin = upload_enable_pin
            self.measure_enable_pin = measure_enable_pin
        elif (ipcore and i2c):
            self.i2c = i2c
            if isinstance(ipcore, basestring):
                axi4_bus = AXI4LiteBus(ipcore,
                                       MIK002004Def.MIX_MIK002_REG_SIZE)
                self.ipcore = MIXMIK002SGR(axi4_bus)
            else:
                self.ipcore = ipcore
            self.analyzer = self.ipcore.analyzer
            self.adc_rst_pin = Pin(self.ipcore.gpio, MIK002004Def.ADC_RST_PIN)
            self.adc_ovfl_l_pin = Pin(self.ipcore.gpio,
                                      MIK002004Def.ADC_OVFL_L_PIN)
            self.i2s_left_select_pin = Pin(self.ipcore.gpio,
                                           MIK002004Def.I2S_LEFT_SELECT_PIN)
            self.i2s_right_select_pin = Pin(self.ipcore.gpio,
                                            MIK002004Def.I2S_RIGHT_SELECT_PIN)
            self.i2s_both_select_pin = Pin(self.ipcore.gpio,
                                           MIK002004Def.I2S_BOTH_SELECT_PIN)
            self.tone_detect_pin = Pin(self.ipcore.gpio,
                                       MIK002004Def.TONE_DETECT_PIN)
            self.upload_enable_pin = Pin(self.ipcore.gpio,
                                         MIK002004Def.UPLOAD_ENABLE_PIN)
            self.measure_enable_pin = Pin(self.ipcore.gpio,
                                          MIK002004Def.MEASURE_ENABLE_PIN)
        else:
            raise MIK002004Exception("Parameter error")

        self.eeprom = CAT24C32(MIK002004Def.EEPROM_DEV_ADDR, i2c)
        self.nct75 = NCT75(MIK002004Def.SENSOR_DEV_ADDR, i2c)
        self.cat9555 = CAT9555(MIK002004Def.CAT9555_DEV_ADDR, i2c)

        super(MIK002004, self).__init__(self.eeprom,
                                        self.nct75,
                                        range_table=warlock_range_table)

        self.sampling_rate = sample_rate
        self.scope = ""
        self.mode = ""

    def post_power_on_init(self, timeout=MIK002004Def.DEFAULT_TIMEOUT):
        '''
        Init MIK002004 module to a know harware state.

        This function will reset adc and i2s module.

        Args:
            timeout:      float, (>=0), default 1, unit Second, execute timeout.

        '''
        self.reset(timeout)

    def reset(self, timeout=MIK002004Def.DEFAULT_TIMEOUT):
        '''
        Reset the instrument module to a know hardware state.

        Args:
            timeout:      float, (>=0), default 1, unit Second, execute timeout.


        Returns:
            string, "done", if execute successfully.

        '''
        start_time = time.time()

        while True:
            try:
                self.adc_rst_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR)
                self.adc_ovfl_l_pin.set_dir(MIK002004Def.IO_INPUT_DIR)
                self.i2s_left_select_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR)
                self.i2s_right_select_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR)
                self.i2s_both_select_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR)
                self.tone_detect_pin.set_dir(MIK002004Def.IO_INPUT_DIR)
                self.upload_enable_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR)
                self.measure_enable_pin.set_dir(MIK002004Def.IO_OUTPUT_DIR)

                # reset ADC
                self.adc_reset()

                self.i2s_left_select_pin.set_level(0)
                self.i2s_right_select_pin.set_level(0)
                self.i2s_both_select_pin.set_level(0)
                self.measure_enable_pin.set_level(0)
                self.upload_enable_pin.set_level(1)

                self.cat9555.set_ports([0x00, 0x1f])
                self.cat9555.set_pins_dir([0x00, 0xf0])

                self._get_default_sampling_rate()
                return "done"
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise MIK002004Exception("Timeout: {}".format(e.message))
        return "done"

    def pre_power_down(self, timeout=MIK002004Def.DEFAULT_TIMEOUT):
        '''
        Put the hardware in a safe state to be powered down.

        Args:
            timeout:      float, (>=0), default 1, unit Second, execute timeout.
        '''
        start_time = time.time()

        while True:
            try:
                self.adc_rst_pin.set_level(0)
                self.i2s_left_select_pin.set_level(0)
                self.i2s_right_select_pin.set_level(0)
                self.i2s_both_select_pin.set_level(0)
                return
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise MIK002004Exception("Timeout: {}".format(e.message))

    def start_record_data(self, channel):
        '''
        MIK002004 start collect the data and upload to DMA directly.

        Args:
            channel:         string, ['left', 'right', 'both'].

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.start_record_data('left')

        '''
        assert channel in MIK002004Def.AUDIO_CHANNEL_LIST + ["both"]
        self._audio_channel_select(channel)
        time.sleep(MIK002004Def.GPIO_DELAY_MS / 1000.0)
        return "done"

    def stop_record_data(self):
        '''
        MIK002004 stop collect the data.

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.stop_record_data()

        '''
        self.i2s_left_select_pin.set_level(0)
        self.i2s_right_select_pin.set_level(0)
        self.i2s_both_select_pin.set_level(0)
        time.sleep(MIK002004Def.GPIO_DELAY_MS / 1000.0)
        return "done"

    def _get_default_sampling_rate(self):
        io_states = self.cat9555.get_ports()
        target_pin = io_states[1] & MIK002004Def.sample_rate_pin_mask
        if target_pin in MIK002004Def.sampling_rate_select:
            self.sampling_rate = MIK002004Def.sampling_rate_select[target_pin]
        else:
            self.set_sampling_rate(self.sampling_rate)

    def _set_function_path(self, config, scope):
        '''
        MIK002004 set function path

        Args:
            config:     string, ['mode_select', 'range_select','mikey_tone',
                                 'short_circuit_detect', 'extra'].
            scope:       string, ['GB','CH','HP_1Vrms','HP_3.5Vrms','line_out','s1_enable',
                                 's2_enable','s3_enable','all_disable','MIC_S0_enable',
                                 'MIC_S0_disable','enable','disable','hp2mic_xtalk'].

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock._set_function_path('mode_select', 'GB')

        '''
        assert config in warlock_function_info
        assert scope in warlock_function_info[config]

        bits = warlock_function_info[config][scope]['bits']
        for bit in bits:
            self.cat9555.set_pin(bit[0], bit[1])

        time.sleep(MIK002004Def.RELAY_DELAY_MS / 1000.0)

    def _volt_to_target_unit(self, scope, volt):
        '''
        MIK002004 get target unit value from measured voltage.

        Args:
            scope:      string, the range of channel measure.
            volt:       float, the measured voltage.

        Returns:
            float, value.

        '''
        assert scope in warlock_function_info['range_select']

        return volt * warlock_function_info['range_select'][scope][
            'coefficient']

    def _audio_channel_select(self, channel):
        '''
        MIK002004 cs5381 chip channel select.

        Args:
            channel:  string, ['left','right','both'], Use for select which channel to measure.

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock._audio_channel_select('left')

        '''
        if channel == 'left':
            self.i2s_right_select_pin.set_level(0)
            self.i2s_both_select_pin.set_level(0)
            self.i2s_left_select_pin.set_level(1)
        elif channel == 'right':
            self.i2s_left_select_pin.set_level(0)
            self.i2s_both_select_pin.set_level(0)
            self.i2s_right_select_pin.set_level(1)
        else:
            self.i2s_both_select_pin.set_level(1)
        return "done"

    def enable_upload(self):
        '''
        MIK002004 upoad mode open.

        Control audio upload data of ADC when doing measurement. It's not necessary
        enable upload when doing measurement. Note that data transfered into DMA is 32bit each data,
        but high 24bit data is valid. Low 8bit data is invalid. The data format is twos complement.

        Returns:
            string, 'done', api execution successful.

        '''
        self.analyzer.enable_upload()
        return "done"

    def disable_upload(self):
        '''
        MIK002004 upoad mode close. Close data upload doesn't influence to measure.

        Returns:
            string, 'done', api execution successful.

        '''
        self.analyzer.disable_upload()
        return "done"

    def set_sampling_rate(self, sampling_rate):
        '''
        MIK002004 set sampling rate.

        Args:
            sampling_rate:     int, [192000, 96000, 48000], unit Hz, adc measure sampling rate.

        Returns:
            string, 'done', api execution successful.

        Examples:
           warlock.set_sampling_rate(96000)

        '''
        assert str(
            sampling_rate) + "Hz" in warlock_function_info["adc_sample_rate"]
        io_dir_state = self.cat9555.get_pins_dir()
        if io_dir_state[1] & MIK002004Def.sample_rate_pin_mask != 0:
            self.cat9555.set_pins_dir([
                io_dir_state[0],
                io_dir_state[1] & ~MIK002004Def.sample_rate_pin_mask
            ])
        self._set_function_path("adc_sample_rate", str(sampling_rate) + "Hz")
        self.sampling_rate = sampling_rate if isinstance(
            sampling_rate, int) else int(sampling_rate)
        return "done"

    def get_sampling_rate(self):
        '''
        MIK002004 get sampling rate.

        Returns:
            int, unit Hz.

        Examples:
           warlock.get_sampling_rate()

        '''
        return self.sampling_rate

    def headset_loopback(self, mode):
        '''
        MIK002004 set headset_loopback function.

        Args:
            mode:      string, ['GB', 'CH'], GB mean global mode, CH mean china mode.

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.headset_loopback('CH')

        '''
        assert mode in warlock_function_info["mode_select"]
        self._set_function_path("extra", "headset_loopback")
        self._set_function_path("mode_select", mode)
        return "done"

    def headphone_out(self, range_name, mode):
        '''
        MIK002004 set headphone_out function.

        Args:
            range_name: string, ['1Vrms', '3.5Vrms'].
            mode:       string, ['GB', 'CH'], GB mean global mode, CH mean china mode.

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.headphone_out('1Vrms', 'CH')

        '''
        assert ("HP_" + range_name) in warlock_function_info["range_select"]
        assert mode in warlock_function_info["mode_select"]
        self._set_function_path("range_select", "HP_" + range_name)
        self._set_function_path("mode_select", mode)
        self.mode = mode
        self.scope = "HP_" + range_name
        return "done"

    def line_out(self):
        '''
        MIK002004 set line_out function.

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.line_out()

        '''
        self._set_function_path("range_select", "line_out")
        self.scope = "line_out"
        return "done"

    def hp2mic_xtalk(self, mode, range_name="32ohm"):
        '''
        MIK002004 set hp2mic_xtalk function.

        Args:
            mode:       string, ['GB', 'CH'], GB mean global mode, CH mean china mode.
            range_name: string, ["32ohm", "400ohm"]

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.hp2mic_xtalk('CH')

        '''
        assert mode in warlock_function_info["mode_select"]

        self._set_function_path("hp2mic_xtalk", range_name)
        self._set_function_path("mode_select", mode)
        self.mode = mode
        self.scope = "HP_1Vrms"
        return "done"

    def io_set(self, io_list):
        '''
        MIK002004 set io state, this is a debug function.

        Args:
            io_list:   list, [[pin,state],[pin,state]], pin [0~15], state [0,1].

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.io_set([[0,1],[1,1]])

        '''
        assert isinstance(io_list, list)
        assert all([isinstance(io, list) and len(io) == 2 for io in io_list])
        assert all(
            [io[0] in xrange(16) and io[1] in xrange(2) for io in io_list])
        for io in io_list:
            self.cat9555.set_pin(io[0], io[1])
        return "done"

    def io_dir_set(self, io_list):
        '''
        MIK002004 set io direction, this is a debug function.

        Args:
            io_list:   list, [[pin,state],[pin,state]], pin [0~15], state ['input','output'].

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.io_dir_set([[0,'output'],[1,'output']])

        '''
        assert isinstance(io_list, list)
        assert all([isinstance(io, list) and len(io) == 2 for io in io_list])
        assert all([
            io[0] in xrange(16) and io[1] in ['output', 'input']
            for io in io_list
        ])
        for io in io_list:
            self.cat9555.set_pin_dir(io[0], io[1])
        return 'done'

    def io_read(self, io_list):
        '''
        MIK002004 set io state, this is a debug function.

        Args:
            io_list:   list, [pinx,pinx,… ,pinx], pin x mean [0~15].

        Returns:
            list, [[pin,state],[pin,state]], pin [0~15], state [0,1].

        Examples:
            warlock.read_pin([0,1,2,7])

        '''
        assert isinstance(io_list, list)
        assert all([isinstance(io, int) for io in io_list])
        assert all([0 <= io <= 15 for io in io_list])
        data_list = []
        for io in io_list:
            data_list.append([io, self.cat9555.get_pin(io)])

        return data_list

    def io_dir_read(self, io_list):
        '''
        MIK002004 set io direction, this is a debug function.

        Args:
            io_list:   list, [pinx,pinx,… ,pinx], pin x mean [0~15].

        Returns:
            list, [[pin,state],[pin,state]], pin [0~15], state ['input','output'].

        Examples:
            warlock.read_pin_dir([0,1])

        '''
        assert isinstance(io_list, list)
        assert all([isinstance(io, int) for io in io_list])
        assert all([0 <= io <= 15 for io in io_list])
        data_list = []
        for io in io_list:
            data_list.append([io, self.cat9555.get_pin_dir(io)])
        return data_list

    def adc_reset(self):
        '''
        MIK002004 reset adc.

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.adc_reset()

        '''
        self.adc_rst_pin.set_level(0)
        time.sleep(MIK002004Def.RST_DELAY_MS / 1000.0)
        self.adc_rst_pin.set_level(1)
        return "done"

    def measure(self,
                channel,
                bandwidth_hz=50000,
                harmonic_num=5,
                decimation_type=0xFF):
        '''
        MIK002004 measure signal's Vpp, RMS, THD+N, THD.

        Args:
            channel:         string, ['left', 'right'].
            bandwidth_hz:    int, [50~95977], default 50000,  unit Hz, Measure signal's limit bandwidth,
                             The frequency of the signal should not be greater than half of the bandwidth.
            harmonic_num:    int, [2~10], default 5, Use for measuring signal's THD.
            decimation_type: int, [1~255], default 255, Decimation for FPGA to get datas,
                             If decimation is 0xFF, FPGA will choose one suitable number.

        Returns:
            dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value),
                  dict with vpp, freq, thd, thdn, rms.

        Examples:
            result = warlock.measure('left', 20000, 5, 255)
            print result['frequency'], result['vpp'], result['thdn'], result['thd'], result['rms']

        '''
        assert channel in MIK002004Def.AUDIO_CHANNEL_LIST

        assert bandwidth_hz == MIK002004Def.BANDWIDTH_AUTO or isinstance(
            bandwidth_hz, int)
        assert MIK002004Def.BANDWIDTH_MIN <= bandwidth_hz <= MIK002004Def.BANDWIDTH_MAX
        assert isinstance(harmonic_num, int) and\
            MIK002004Def.HARMONIC_CNT_MIN <= harmonic_num <= MIK002004Def.HARMONIC_CNT_MAX
        assert isinstance(decimation_type, int) and\
            MIK002004Def.DECIMATION_TYPE_MIN <= decimation_type <= MIK002004Def.DECIMATION_TYPE_MAX

        self.upload_enable_pin.set_level(0)
        self.stop_record_data()
        self._audio_channel_select("both")
        self.measure_enable_pin.set_level(1)

        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.measure_select(channel, MIK002004Def.NORMAL_ANALYZE_MODE)
        self.analyzer.analyze_config(self.sampling_rate, decimation_type,
                                     bandwidth_hz, harmonic_num)
        self.analyzer.analyze()

        # calculate the actual vpp of HW by VREF
        vpp = self.analyzer.get_vpp() * MIK002004Def.AUDIO_ANALYZER_VREF
        vpp = self._volt_to_target_unit(self.scope, vpp)
        self.upload_enable_pin.set_level(1)
        # vpp = RMS * 2 * sqrt(2)
        rms = vpp / MIK002004Def.RMS_TO_VPP_RATIO
        range_name = (channel + "_" +
                      MIK002004Def.RMS_LIST[self.scope]).upper()
        rms = self.calibrate(range_name, rms)

        vpp = rms * MIK002004Def.RMS_TO_VPP_RATIO

        result = dict()
        result["vpp"] = vpp
        result["freq"] = self.analyzer.get_frequency()
        result["thd"] = self.analyzer.get_thd()
        result["thdn"] = self.analyzer.get_thdn()
        result["snr"] = -1 * result["thdn"]
        result["rms"] = rms

        return result

    def _measure(self,
                 channel,
                 bandwidth_hz=50000,
                 harmonic_num=5,
                 decimation_type=1):
        '''
        MIK002004 _measure signal's RMS for Crosstalk measure

        Args:
            channel:         string, ['left', 'right'].
            bandwidth_hz:    int, [50~95977], default 50000,  unit Hz, Measure signal's limit bandwidth,
                             The frequency of the signal should not be greater than half of the bandwidth.
            harmonic_num:    int, [2~10], default 5, Use for measuring signal's THD.
            decimation_type: int, [1~255], default 1, Decimation for FPGA to get datas,
                             If decimation is 0xFF, FPGA will choose one suitable number.

        Returns:
            float.

        Examples:
            result = warlock._measure('left', 20000, 5, 1)
            print result

        '''
        assert channel in MIK002004Def.AUDIO_CHANNEL_LIST

        assert bandwidth_hz == MIK002004Def.BANDWIDTH_AUTO or isinstance(
            bandwidth_hz, int)
        assert MIK002004Def.BANDWIDTH_MIN <= bandwidth_hz <= MIK002004Def.BANDWIDTH_MAX
        assert isinstance(harmonic_num, int) and\
            MIK002004Def.HARMONIC_CNT_MIN <= harmonic_num <= MIK002004Def.HARMONIC_CNT_MAX
        assert isinstance(decimation_type, int) and\
            MIK002004Def.DECIMATION_TYPE_MIN <= decimation_type <= MIK002004Def.DECIMATION_TYPE_MAX

        self.upload_enable_pin.set_level(0)
        self.stop_record_data()
        self._audio_channel_select("both")
        self.measure_enable_pin.set_level(1)
        if channel == "left":
            self.analyzer.disable()
            self.analyzer.enable()
            self.analyzer.analyze_config(self.sampling_rate, decimation_type,
                                         bandwidth_hz, harmonic_num)
        self.analyzer.measure_select(channel, MIK002004Def.XTALK_ANALYZE_MODE)
        self.analyzer.analyze()
        # calculate the actual vpp of HW by VREF
        vpp = self.analyzer.get_vpp() * MIK002004Def.AUDIO_ANALYZER_VREF
        vpp = self._volt_to_target_unit(self.scope, vpp)
        self.upload_enable_pin.set_level(1)
        # vpp = RMS * 2 * sqrt(2)
        rms = vpp / MIK002004Def.RMS_TO_VPP_RATIO
        range_name = (channel + "_" +
                      MIK002004Def.RMS_LIST[self.scope]).upper()
        rms = self.calibrate(range_name, rms)

        result = dict()
        result["vpp"] = vpp
        result["freq"] = self.analyzer.get_frequency()
        result["thd"] = self.analyzer.get_thd()
        result["thdn"] = self.analyzer.get_thdn()
        result["snr"] = -1 * result["thdn"]
        result["rms"] = rms

        return result

    def xtalk_measure(self,
                      channel,
                      bandwidth_hz=50000,
                      harmonic_num=5,
                      decimation_type=1):
        '''
        MIK002004 Crosstalk mode measure.

        Args:
            channel:    string, ['hp2mic', 'hpl2r', 'hpr2l'].
            bandwidth_hz:    int, [50~95977], default 50000,  unit Hz, Measure signal's limit bandwidth,
                             The frequency of the signal should not be greater than half of the bandwidth.
            harmonic_num:    int, [2~10],     default 5, Use for measuring signal's THD.
            decimation_type: int, [1~255], default 1, Decimation for FPGA to get data,
                             If decimation is 0xFF, FPGA will choose one suitable number.

        Returns:
            list, [value, db].

        Examples:
            warlock.xtalk_measure('hp2mic')

        '''
        assert channel in ["hp2mic", "hpl2r", "hpr2l"]
        left_result = self._measure("left", bandwidth_hz, harmonic_num,
                                    decimation_type)
        right_result = self._measure("right", bandwidth_hz, harmonic_num,
                                     decimation_type)
        Va = left_result["rms"]
        Vb = right_result["rms"]

        if channel == "hpr2l":
            xtalk = 20 * math.log(Va / Vb, 10)
        else:
            xtalk = 20 * math.log(Vb / Va, 10)
        return {"xtalk": xtalk, "left": left_result, "right": right_result}

    def mikey_tone(self, freq="default", mode="GB"):
        '''
        MIK002004 set signal output.

        Args:
            freq:      string, ['S1', 'S2', 'S3', 'S0', 'default'], 'default' mean (S0, S1, S2, S3)=1.
            mode:      string, ['GB', 'CH'], default 'GB', GB mean global mode, CH mean china mode.

        Returns:
            string, 'done', api execution successful.

        Examples:
            warlock.mikey_tone('S1', 'CH')

        '''
        assert freq in warlock_function_info["mikey_tone"]
        assert mode in warlock_function_info["mode_select"]

        self._set_function_path("mode_select", mode)
        self._set_function_path("mikey_tone", freq)
        return "done"

    def get_tone_detect_state(self):
        '''
        MIK002004 get tone detect state.

        Returns:
            int, [1,0], 1 mean high level, 0 mean low level.

        Examples:
            warlock.get_tone_detect_state()

        '''
        return self.tone_detect_pin.get_level()

    def get_overflow_state(self):
        '''
        MIK002004 get overflow pin state.

        Returns:
            int, [1,0], 1 mean high level, 0 mean low level.

        Examples:
            warlock.get_overflow_state()

        '''
        return self.adc_ovfl_l_pin.get_level()

    def adc_hpf_state(self, state):
        '''
        MIK002004 set adc hpf pin.

        Args:
            state:     string, ['enable', 'disable'].

        Returns:
            string, 'done', api execution successful.

        Examples:
           warlock.adc_hpf_state('enable')

        '''
        assert state in ["enable", "disable"]
        if self.cat9555.get_pin_dir(
                MIK002004Def.ADC_HPF_PIN) != MIK002004Def.IO_OUTPUT_DIR:
            self.cat9555.set_pin_dir(MIK002004Def.ADC_HPF_PIN,
                                     MIK002004Def.IO_OUTPUT_DIR)

        self._set_function_path("ADC_HPF", state)
        return "done"

    def get_driver_version(self):
        '''
        Get MIK002004 driver version.

        Returns:
            string, current driver version.
        '''
        return __version__
class Audio005002(Audio005Base):
    '''
    Audio005002 is a high resolution differential input/output digital audio module.

    Args:
        i2c:             instance(I2C), the instance of I2C bus. which will be used to used
                                        to control eeprom, sensor and io expander.
        ipcore:          instance(MIXAudio005SGR), the instance of MIXAudio005SGR, which include
                                                   AD717x, FFT Analyzer, Signal Source, RAM Signal,
                                                   Audio Cache  and gpio function.
                                                   If device name string is passed to the parameter,
                                                   the ipcore can be instanced in the module.
        dma:             instance(MIXDMASG)/None, the instance of MIXDMASG. which will be used to used
        left_ch_id:      int/None, [0~15], Id of the left channel to be configured.
        right_ch_id:     int/None, [0~15], Id of the right channel to be configured.
        dma_mem_size:    int/None, [0~MIXDMASG_MEM_SIZE], default 1M, unit M, MIX_DMA_SG channel size.
                                   1M=1024*1024 Byte and a point is 32 bit, which is 4 Byte,
                                   so 1M=1024*1024/4=262144 points.

    Examples:
        i2c = I2C('/dev/i2c-0')
        audio005002 = Audio005002(i2c, '/dev/MIX_Audio005002_SG_R')
        audio005002.post_power_on_init()
        # measure left channel input
        LEFT_CHANNEL = 1
        audio005002.configure_input_channel(LEFT_CHANNEL, True, False, 48000)
        # start recording
        # wait for 1 s
        # collect samples
        # stop recording
        audio005002.configure_input_channel(LEFT_CHANNEL, False)
        data = audio005002.read(LEFT_CHANNEl, 8192)
        print data
    '''
    rpc_public_api = [
        'configure_input_channel', 'get_input_channel_configuration',
        'configure_output_channel', 'get_output_channel_configuration', 'read',
        'write', 'get_noisefloor'
    ] + Audio005Base.rpc_public_api

    # launcher will use this to match driver compatible string and load driver if matched.
    compatible = ["GQQ-03C6-5-020", "GQQ-03C6-5-02A"]

    def __init__(self,
                 i2c,
                 ipcore,
                 dma=None,
                 left_ch_id=None,
                 right_ch_id=None,
                 dma_mem_size=1):
        super(Audio005002, self).__init__(i2c, ipcore)

        self.ram_signal = self.ipcore.ram_signal
        self.audio_cache = self.ipcore.audio_cache
        self.ram_out_pin = Pin(self.ipcore.gpio, Audio005002Def.RAM_OUT_PIN)

        self.adc_sample_rate = Audio005002Def.DEF_SAMPLING_RATE
        self.dac_sample_rate = Audio005002Def.AUDIO_SAMPLING_RATE
        self.vref = [
            Audio005002Def.ANALYZER_2V_VOLT, Audio005002Def.ANALYZER_2V_VOLT
        ]
        self.input_enable = [False, False]
        self.output_enable = False
        self.model = Audio005002Def.AUDIO005002_CONFIG

        self.dma = dma
        if self.dma is not None:
            self.left_ch_id = left_ch_id
            self.right_ch_id = right_ch_id
            self.dma_mem_size = dma_mem_size
            if self.left_ch_id is None or self.right_ch_id is None or self.dma_mem_size is None:
                raise Audio005002Exception("dma parameter error")

    def read_hardware_config(self):
        '''
        returns a string that represents the config
        '''
        return "".join(
            [chr(c) for c in self.read_nvmem(Audio005002Def.CONFIG_ADDR, 3)])

    def load_calibration(self):
        '''
        Load calibration data. If GQQ is defined in eeprom, this function will load calibration defined.
        '''
        self.model = self.read_hardware_config()
        if self.model == Audio005002Def.AUDIO005002_CONFIG:
            self._range_table = starlordii_range_table
        else:
            # Compatible with 02A model
            self._range_table = audio005002_range_table
        super(Audio005002, self).load_calibration()

    def post_power_on_init(self, timeout=Audio005002Def.TIME_OUT):
        '''
        Init audio005 module to a know harware state.

        This function will reset reset dac/adc and i2s module.

        Args:
            timeout:      float, (>=0), default 1, unit Second, execute timeout.
        '''
        self.reset(timeout)
        if self.dma is not None:
            self.dma.config_channel(self.left_ch_id,
                                    self.dma_mem_size * 0X100000)
            self.dma.config_channel(self.right_ch_id,
                                    self.dma_mem_size * 0X100000)

    def reset(self, timeout=Audio005002Def.TIME_OUT):
        '''
        Reset the instrument module to a know hardware state.

        Args:
            timeout:      float, (>=0), default 1, unit Second, execute timeout.
        '''
        start_time = time.time()
        while True:
            try:
                self.adc_rst_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT)
                self.dac_rst_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT)
                self.i2s_rx_en_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT)
                self.i2s_tx_en_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT)
                self.ram_out_pin.set_dir(Audio005002Def.IO_DIR_OUTPUT)

                # reset ADC
                self.adc_rst_pin.set_level(Audio005002Def.PIN_LEVEL_LOW)
                time.sleep(Audio005002Def.RELAY_DELAY_S)
                self.adc_rst_pin.set_level(Audio005002Def.PIN_LEVEL_HIGH)

                # reset DAC
                self.dac_rst_pin.set_level(Audio005002Def.PIN_LEVEL_LOW)
                time.sleep(Audio005002Def.RELAY_DELAY_S)
                self.dac_rst_pin.set_level(Audio005002Def.PIN_LEVEL_HIGH)

                # reset i2s rx
                self.i2s_rx_en_pin.set_level(Audio005002Def.PIN_LEVEL_LOW)

                # reset i2s tx
                self.i2s_tx_en_pin.set_level(Audio005002Def.PIN_LEVEL_LOW)

                self.ram_out_pin.set_level(Audio005002Def.PIN_LEVEL_LOW)
                # io init
                self.pca9536.set_pin_dir(Audio005002Def.ADC_CH_RIGHT_CTL_BIT,
                                         Audio005002Def.IO_DIR_OUTPUT)
                self.pca9536.set_pin_dir(Audio005002Def.ADC_CH_LEFT_CTL_BIT,
                                         Audio005002Def.IO_DIR_OUTPUT)
                self.pca9536.set_pin_dir(Audio005002Def.ADCM0_CTL_BIT,
                                         Audio005002Def.IO_DIR_OUTPUT)
                self.pca9536.set_pin_dir(Audio005002Def.ADCM1_CTL_BIT,
                                         Audio005002Def.IO_DIR_OUTPUT)

                self.pca9536.set_pin(Audio005002Def.ADC_CH_RIGHT_CTL_BIT,
                                     Audio005002Def.PIN_LEVEL_HIGH)
                self.pca9536.set_pin(Audio005002Def.ADC_CH_LEFT_CTL_BIT,
                                     Audio005002Def.PIN_LEVEL_HIGH)
                self.pca9536.set_pin(Audio005002Def.ADCM0_CTL_BIT,
                                     Audio005002Def.PIN_LEVEL_LOW)
                self.pca9536.set_pin(Audio005002Def.ADCM1_CTL_BIT,
                                     Audio005002Def.PIN_LEVEL_LOW)

                self.model = self.read_hardware_config()
                if self.model == Audio005002Def.AUDIO005002_CONFIG:
                    self._range_table = starlordii_range_table
                else:
                    # Compatible with 02A model
                    self._range_table = audio005002_range_table

                return
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise Audio005002Exception("Timeout: {}".format(e.message))

    def get_driver_version(self):
        '''
        Get audio005 driver version.

        Returns:
            string, current driver version.
        '''
        return __version__

    def measure(self,
                channel,
                scope,
                bandwidth_hz,
                decimation_type=0xFF,
                sampling_rate=Audio005002Def.DEF_SAMPLING_RATE):
        '''
        Measure audio input signal, which captures data using CS5361.

        Args:
            channel:         string, ['left', 'right'], select input signal channel.
            scope:           string, ['2V', '20mV'], AD7175 measurement range.
            bandwidth_hz:    int/string, [42~48000], unit Hz, the signal bandwidth.
                             In theory the bandwidth must smaller than half the sampling rate.
                             eg, if sampling_rate = 192000, so bandwidth_hz  < 96000.
                             The bandwidth must be greater than the frequency of the input signal.
            decimation_type: int, [1~255], default 0xFF, sample data decimation.
                             decimation_type is 1 means not to decimate.
                             The smaller the input frequency, the larger the value should be.
            sampling_rate:   int, [0~192000], default 48000, unit Hz, ADC sampling rate.

        Returns:
            dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value},
            measurement result.
        '''
        assert channel in Audio005002Def.AUDIO_CHANNEL_LIST
        assert scope in Audio005002Def.LNA_SCOPE

        if self.is_enable_upload is False:
            self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_ENABLE)

        pin = self.i2s_ch_select[Audio005002Def.SELECT_BIT0]
        pin.set_level(Audio005002Def.AUDIO_CHANNEL_LIST[channel][
            Audio005002Def.SELECT_BIT0])
        pin = self.i2s_ch_select[Audio005002Def.SELECT_BIT1]
        pin.set_level(Audio005002Def.AUDIO_CHANNEL_LIST[channel][
            Audio005002Def.SELECT_BIT1])

        if scope == Audio005002Def.LNA_RANGE_2V:
            self.pca9536.set_pin(Audio005002Def.ADC_CH_LIST[channel],
                                 Audio005002Def.SEL_GAIN_1)
            gain = Audio005002Def.AUDIO_ANALYZER_2V_VREF
        else:
            self.pca9536.set_pin(Audio005002Def.ADC_CH_LIST[channel],
                                 Audio005002Def.SEL_GAIN_100)
            gain = Audio005002Def.AUDIO_ANALYZER_20mV_VREF

        index = bisect.bisect(Audio005002Def.SAMPLING_RANGE, sampling_rate)

        pin_level = Audio005002Def.ADCM_CTL_LIST[index][
            Audio005002Def.SELECT_BIT0]
        self.pca9536.set_pin(Audio005002Def.ADCM0_CTL_BIT, pin_level)
        pin_level = Audio005002Def.ADCM_CTL_LIST[index][
            Audio005002Def.SELECT_BIT1]
        self.pca9536.set_pin(Audio005002Def.ADCM1_CTL_BIT, pin_level)

        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.analyze_config(sampling_rate, decimation_type,
                                     bandwidth_hz)
        self.analyzer.analyze()

        freq = self.analyzer.get_frequency()

        harmonic_count = int(Audio005002Def.MAX_HARMONIC_WIDTH / freq)
        harmonic_count = 10 if harmonic_count > 10 else harmonic_count
        harmonic_count = 2 if harmonic_count <= 1 else harmonic_count

        self.analyzer.set_harmonic_count(harmonic_count)

        vpp = self.analyzer.get_vpp() * gain
        rms = vpp / Audio005002Def.RMS_TO_VPP_RATIO

        if scope == Audio005002Def.LNA_RANGE_2V:
            # Since the calibration range is within 20000Hz, the actual measurement result may be slightly
            # greater than 20000Hz when the input signal frequency is 20000Hz.
            # Therefore, the calibration frequency is appropriately increased to 20010Hz.
            if self.model == Audio005002Def.AUDIO005002_CONFIG:
                if freq > 20000:
                    index = bisect.bisect(Audio005002Def.CAL_RANGE, freq - 10)
                else:
                    index = bisect.bisect(Audio005002Def.CAL_RANGE, freq)
                range_name = "AUDIO_2V_" + str(
                    Audio005002Def.CAL_RANGE[index]) + "Hz_" + channel
            else:
                # Compatible with 02A model
                range_name = "AUDIO_2V_" + channel
        else:
            range_name = "AUDIO_20mV_" + channel

        rms = self.calibrate(range_name, rms)
        vpp = rms * Audio005002Def.RMS_TO_VPP_RATIO
        thdn_value = self.analyzer.get_thdn()

        result = dict()
        result["vpp"] = (vpp, Audio005002Def.VOLT_UNIT_MV)
        result["freq"] = (freq, Audio005002Def.FREQ_UNIT_HZ)
        result["thd"] = (self.analyzer.get_thd(), Audio005002Def.THD_UNIT_DB)
        result["thdn"] = (thdn_value, Audio005002Def.THDN_UNIT_DB)
        result["rms"] = (rms, Audio005002Def.VOLT_UNIT_RMS)
        result["noisefloor"] = (10**(thdn_value / 20) * rms,
                                Audio005002Def.VOLT_UNIT_RMS)

        if self.is_enable_upload is False:
            self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_DISABLE)

        return result

    def get_noisefloor(self,
                       channel,
                       scope,
                       bandwidth_hz,
                       decimation_type=0xFF,
                       sampling_rate=192000):
        '''
        Measure audio input signal, which captures data using CS5361.

        Args:
            channel:         string, ['left', 'right'], select input signal channel.
            scope:           string, ['2V', '20mV'], AD7175 measurement range.
            bandwidth_hz:    int/string, [42~48000], unit Hz, the signal bandwidth.
                             In theory the bandwidth must smaller than half the sampling rate.
                             eg, if sampling_rate = 192000, so bandwidth_hz  < 96000.
                             The bandwidth must be greater than the frequency of the input signal.
            decimation_type: int, [1~255], default 0xFF, sample data decimation.
                             decimation_type is 1 means not to decimate.
                             The smaller the input frequency, the larger the value should be.
            sampling_rate:   int, [0~192000], default 192000, unit Hz, ADC sampling rate.

        Returns:
            float, value, unit mVrms,  Result of signal's noisefloor.

        '''
        if self.is_enable_upload is False:
            self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_ENABLE)

        pin = self.i2s_ch_select[Audio005002Def.SELECT_BIT0]
        pin.set_level(Audio005002Def.AUDIO_CHANNEL_LIST[channel][
            Audio005002Def.SELECT_BIT0])
        pin = self.i2s_ch_select[Audio005002Def.SELECT_BIT1]
        pin.set_level(Audio005002Def.AUDIO_CHANNEL_LIST[channel][
            Audio005002Def.SELECT_BIT1])

        if scope == Audio005002Def.LNA_RANGE_2V:
            self.pca9536.set_pin(Audio005002Def.ADC_CH_LIST[channel],
                                 Audio005002Def.SEL_GAIN_1)
            gain = Audio005002Def.AUDIO_ANALYZER_2V_VREF
        else:
            self.pca9536.set_pin(Audio005002Def.ADC_CH_LIST[channel],
                                 Audio005002Def.SEL_GAIN_100)
            gain = Audio005002Def.AUDIO_ANALYZER_20mV_VREF

        index = bisect.bisect(Audio005002Def.SAMPLING_RANGE, sampling_rate)

        pin_level = Audio005002Def.ADCM_CTL_LIST[index][
            Audio005002Def.SELECT_BIT0]
        self.pca9536.set_pin(Audio005002Def.ADCM0_CTL_BIT, pin_level)
        pin_level = Audio005002Def.ADCM_CTL_LIST[index][
            Audio005002Def.SELECT_BIT1]
        self.pca9536.set_pin(Audio005002Def.ADCM1_CTL_BIT, pin_level)

        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.analyze_config(sampling_rate, decimation_type,
                                     bandwidth_hz)
        self.analyzer.analyze()

        noisefloor = self.analyzer.get_noisefloor() * gain

        if self.is_enable_upload is False:
            self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_DISABLE)

        return noisefloor

    def enable_output(self, freq, vpp):
        '''
        Audio005 CS5361 output audio sine waveform.

        Args:
            freq:       int, [5~50000], unit Hz, output signal's frequency.
            vpp:        float, [0~6504], unit mV, output signal's vpp.

        Returns:
            string, "done", execution successful.
        '''
        assert Audio005002Def.OUTPUT_FREQ_MIN <= freq
        assert freq <= Audio005002Def.OUTPUT_FREQ_MAX
        assert Audio005002Def.OUTPUT_VPP_MIN <= vpp
        assert vpp <= Audio005002Def.OUTPUT_VPP_MAX

        vpp = self.calibrate(Audio005002Def.OUTPUT_CAL_ITEM, vpp)
        vpp = 0 if vpp < 0 else vpp
        # enable I2S tx module
        self.i2s_tx_en_pin.set_level(Audio005002Def.AUDIO_OUTPUT_ENABLE)
        self.ram_out_pin.set_level(Audio005002Def.USE_SIGNAL_SOURCE)

        self.signal_source.close()
        self.signal_source.open()
        # calculate vpp to vpp scale for FPGA
        vpp_scale = vpp * Audio005002Def.VPP_2_SCALE_RATIO
        self.signal_source.set_swg_paramter(Audio005002Def.AUDIO_SAMPLING_RATE,
                                            freq, vpp_scale,
                                            Audio005002Def.OUTPUT_SIGNAL_DUTY)
        self.signal_source.set_signal_type(Audio005002Def.OUTPUT_WAVE)
        self.signal_source.set_signal_time(Audio005002Def.SIGNAL_ALWAYS_OUTPUT)
        self.signal_source.output_signal()

        return "done"

    def configure_input_channel(self,
                                channel,
                                enable,
                                enable_lna=False,
                                sample_rate=Audio005002Def.DEF_SAMPLING_RATE):
        '''
        Configure input channel based on given parameters.

        Args:
            channel:      int, [1, 2], currently defined are 1(left), 2(right).
            enable:       boolean, indicating if given path should be enabled.
            enable_lna:   boolean, true if LNA should be enabled for given input path.
            sample_rate:  int, [48000, 96000, 192000], Sample rate to use for given channel.

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2.
            InvalidSampleRate() if sample_rate is bad.

        Return:
            dict, { "channel" : 1, "sample_rate" : 48000, "enable" : True, "enable_lna" : True,
                    "supported_sample_rates" : [48000, 96000, 192000] }, Actual channel configuration.
        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("channel is not 1 or 2")

        if sample_rate not in [48000, 96000, 192000]:
            raise InvalidSampleRate("sample_rate is bad")

        ADC_CH_LIST = {1: 1, 2: 0}
        CHANNEL_LIST = {1: self.left_ch_id, 2: self.right_ch_id}
        code = channel - 1

        if enable is True:
            self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_ENABLE)

        if enable_lna is True:
            self.pca9536.set_pin(ADC_CH_LIST[channel],
                                 Audio005002Def.SEL_GAIN_100)
            self.vref[code] = Audio005002Def.ANALYZER_20mV_VOLT
        else:
            self.pca9536.set_pin(ADC_CH_LIST[channel],
                                 Audio005002Def.SEL_GAIN_1)
            self.vref[code] = Audio005002Def.ANALYZER_2V_VOLT

        index = bisect.bisect(Audio005002Def.SAMPLING_RANGE, sample_rate)
        pin_level = Audio005002Def.ADCM_CTL_LIST[index][
            Audio005002Def.SELECT_BIT0]
        self.pca9536.set_pin(Audio005002Def.ADCM0_CTL_BIT, pin_level)
        pin_level = Audio005002Def.ADCM_CTL_LIST[index][
            Audio005002Def.SELECT_BIT1]
        self.pca9536.set_pin(Audio005002Def.ADCM1_CTL_BIT, pin_level)
        self.adc_sample_rate = sample_rate

        if enable is True:
            time.sleep(Audio005002Def.PIN_RELAY_DELAY_S)
            self.dma.enable_channel(CHANNEL_LIST[channel])
            self.dma.reset_channel(CHANNEL_LIST[channel])
            self.input_enable[code] = True
        else:
            self.i2s_rx_en_pin.set_level(Audio005002Def.I2S_RX_DISABLE)
            self.dma.disable_channel(CHANNEL_LIST[channel])
            self.input_enable[code] = False

        result = dict()
        result["channel"] = channel
        result["sample_rate"] = self.adc_sample_rate
        result["enable"] = enable
        result["enable_lna"] = enable_lna
        result["supported_sample_rates"] = [48000, 96000, 192000]

        return result

    def get_input_channel_configuration(self, channel):
        '''
        Returns the configuration of the specified channel.

        Args:
            channel: int, [1, 2], the desired channel.

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2.

         Returns:
            dict, { "channel" : 1, "sample_rate" : 48000, "enable" : True, "enable_lna" : True,
                    "supported_sample_rates" : [48000, 96000, 192000] }.
        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("channel is not 1 or 2")
        code = channel - 1

        if self.input_enable[code] is True:
            enable = True
        else:
            enable = False

        if self.vref[code] == Audio005002Def.ANALYZER_20mV_VOLT:
            enable_lna = True
        else:
            enable_lna = False

        result = dict()
        result["channel"] = channel
        result["sample_rate"] = self.adc_sample_rate
        result["enable"] = enable
        result["enable_lna"] = enable_lna
        result["supported_sample_rates"] = [48000, 96000, 192000]

        return result

    def configure_output_channel(
            self,
            channel,
            enable,
            sample_rate=Audio005002Def.AUDIO_SAMPLING_RATE):
        '''
        Configure output channel based on given parameters.

        Args:
            channel:      int, [1, 2], currently defined are 1(left), 2(right).
            enable:       boolean, indicating if given path should be enabled.
            sample_rate:  int, [192000], Sample rate to use for given channel.

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2.
            InvalidSampleRate() if sample_rate is bad.

        Return:
            dict, { "channel" : 1, "sample_rate" : 192000, "enable" : True,
                    "supported_sample_rates" : [192000] }
        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("channel is not 1 or 2")

        if sample_rate != Audio005002Def.AUDIO_SAMPLING_RATE:
            raise InvalidSampleRate("sample_rate is bad")

        if enable is True:
            self.i2s_tx_en_pin.set_level(Audio005002Def.AUDIO_OUTPUT_ENABLE)
            self.ram_out_pin.set_level(Audio005002Def.USE_RAM)
            self.output_enable = True
        else:
            self.i2s_tx_en_pin.set_level(Audio005002Def.AUDIO_OUTPUT_DISABLE)
            self.output_enable = False

        result = dict()
        result["channel"] = channel
        result["sample_rate"] = sample_rate
        result["enable"] = enable
        result["supported_sample_rates"] = [192000]

        return result

    def get_output_channel_configuration(self, channel):
        '''
        Returns the configuration of the specified channel.

        Args:
            channel: int, [1, 2], the desired channel.

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2

         Returns:
            dict, { "channel" : 1, "sample_rate" : 192000, "enable" : True,
                    "supported_sample_rates" : [192000] }
        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("channel is not 1 or 2")

        if self.output_enable is True:
            enable = True
        else:
            enable = False

        result = dict()
        result["channel"] = channel
        result["sample_rate"] = self.dac_sample_rate
        result["enable"] = enable
        result["supported_sample_rates"] = [192000]

        return result

    def _conversion_voltage_value(self, vref, data):
        '''
        Converts 32-bit data to voltage values.

        Args:
            vref:        float, unit mV.
            data:        list.

        Returns:
            list, voltage, unit mV.
        '''
        data >>= 8
        value = data & 0xffffff
        if (value >= pow(2, 23)):
            volt = value - pow(2, 24)
        else:
            volt = value

        volt = float(volt) / pow(2, 23)
        volt = volt * vref

        return volt

    def _read_voltage_points(self, channel, samples_per_channel, timeout=1000):
        '''
        Args:
            channel:             int, [1, 2].
            samples_per_channel: int. number of points read.
            timeout:             int, (>=0), default 1000, unit ms, execute timeout.

        Returns:
            list, unit mV, voltage.
        '''

        CHANNEL_LIST = {1: self.left_ch_id, 2: self.right_ch_id}
        code = channel - 1

        result, data, num, overflow = self.dma.read_channel_data(
            CHANNEL_LIST[channel], samples_per_channel * 4, timeout)
        if result == 0:
            data_list = data[:num]
        else:
            raise Audio005002Exception("get dma data error:{}".format(result))

        data_info = []
        data_len = len(data_list)
        for one_payload in range(0, data_len, 4):
            data = data_list[one_payload + 0] | data_list[one_payload + 1] << 8 | \
                data_list[one_payload + 2] << 16 | data_list[one_payload + 3] << 24

            if data & 0x05 != 0x00:
                continue
            data = self._conversion_voltage_value(self.vref[code], data)
            data_info.append(data)

        return data_info

    def read(self, channels, samples_per_channel, timeout=10.0):
        '''
        Args:
            channels:            int/list, [1, 2], the desired channel(s).
            samples_per_channel: int. number of points read.
            timeout:             float, (>=0), default 10.0, unit Second, execute timeout.

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2
            InvalidSampleCount() if samples_per_channel is of bad value
            InvalidTimeout() if invalid timeout value

        Returns:
            list, unit mv, ADC measurement data.
        '''
        if isinstance(channels, int) and channels not in [1, 2]:
            raise InvalidHardwareChannel("channel is not 1 or 2")
        if isinstance(channels, list) and (0 == len(channels)
                                           or len(channels) > 2):
            raise InvalidHardwareChannel("channel is not 1 or 2")
        if isinstance(
                channels,
                list) and 1 == len(channels) and channels[0] not in [1, 2]:
            raise InvalidHardwareChannel("channel is not 1 or 2")
        if isinstance(
                channels,
                list) and 2 == len(channels) and channels[0] not in [1, 2]:
            raise InvalidHardwareChannel("channel is not 1 or 2")
        if isinstance(
                channels,
                list) and 2 == len(channels) and channels[1] not in [1, 2]:
            raise InvalidHardwareChannel("channel is not 1 or 2")
        if samples_per_channel < Audio005002Def.MINI_NUM:
            raise InvalidSampleCount("samples_per_channel is of bad value")
        if timeout < 0 or timeout > 10.0:
            raise InvalidTimeout("invalid timeout value")

        data_info = []
        start_time = time.time()
        while True:
            try:
                if isinstance(channels, list):
                    for i in range(len(channels)):
                        data = self._read_voltage_points(
                            channels[i], samples_per_channel,
                            int(timeout * 1000))
                        if len(channels) == 2:
                            data_info.append(data)
                        else:
                            data_info = data
                else:
                    data_info = self._read_voltage_points(
                        channels, samples_per_channel, int(timeout * 1000))
                return data_info
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise InvalidTimeout("Timeout")
                if e.__class__.__name__ == 'Audio005002Exception':
                    raise Audio005002Exception("{}".format(str(e)))

    def write(self, channel, data, repeat):
        '''
        Playback a waveform defined by data array for "repeat" times. Output sample_rate must be setup by means
        configure_output_channel.

        Args:
            channel:  int,  [1, 2], currently defined are 1(playback).
            data:     list, unit mv, list of data samples in volt.
            repeat:   int,  number of repeats.

        Returns:
            string, "done", execution successful.
        '''
        assert len(data) >= Audio005002Def.MINI_NUM
        assert len(data) <= Audio005002Def.OUTPUT_MAX_NUM
        assert repeat >= Audio005002Def.MINI_NUM

        w_data = []
        for volt in data:
            volt = int(volt / Audio005002Def.OUTPUT_VOLT_MAX * pow(2, 23))
            if volt < 0:
                volt = pow(2, 24) + volt

            volt = volt << 8
            w_data.append(volt)

        ram_addr = 0
        self.ram_signal.read_disable()
        self.ram_signal.set_number_of_repeats([repeat])
        start_time = time.time()
        while ram_addr != (len(w_data) - 1):
            self.ram_signal.disable()
            # Due to the existence of capacitance, it is necessary to delay
            # the capacitor discharge for more than 32ms after the loss of energy
            time.sleep(Audio005002Def.OUTPUT_RELAY_DELAY_S)
            self.ram_signal.enable()

            self.ram_signal.set_read_ramend_addr(len(w_data) - 1)
            self.ram_signal.set_tx_data(w_data)
            ram_addr = self.ram_signal.get_write_ramend_addr()

            if time.time() - start_time > Audio005002Def.TIME_OUT:
                raise Audio005002Exception("write timeout")

        self.ram_signal.read_enable()

        return "done"
Exemple #6
0
class BeastBase(SGModuleDriver):
    '''
    Base class of Beast and BeastCompatible.
    Providing common Beast methods.

    Args:
        i2c:              instance(I2C),              used to control eeprom and nct75 sensor.
        range_ctrl_pin:   instance(GPIO),             used to control Beast range.
        dcac_ctrl_pin:    instance(GPIO),             used to control DC/AC mode.
        output_ctrl_pin:  instance(GPIO),             used to control AD5231 disable/enable state.
        pl_signal_meter:  instance(PLSignalMeter),    used to access PLSignalMeter ipcore.
        ipcore:           instance(MIXDAQT2SGR),      used to access PLGPIO, PLSignalMeter ipcore.
        eeprom_dev_addr:  int,                        eeprom device address.
        sensor_dev_addr:  int,                        NCT75 device address.

    '''

    rpc_public_api = [
        'select_range', 'enable_continuous_measure',
        'disable_continuous_measure', 'set_measure_mask', 'frequency_measure',
        'vpp_measure', 'rms_measure', 'level', 'adc_enable', 'adc_disable'
    ] + SGModuleDriver.rpc_public_api

    def __init__(self,
                 i2c,
                 range_ctrl_pin=None,
                 dcac_ctrl_pin=None,
                 output_ctrl_pin=None,
                 pl_signal_meter=None,
                 ipcore=None,
                 eeprom_dev_addr=BeastDef.EEPROM_DEV_ADDR,
                 sensor_dev_addr=BeastDef.SENSOR_DEV_ADDR):

        if i2c is None:
            super(BeastBase, self).__init__(None,
                                            None,
                                            range_table=beast_range_table)
        else:
            self.eeprom = CAT24C32(eeprom_dev_addr, i2c)
            self.nct75 = NCT75(sensor_dev_addr, i2c)
            super(BeastBase, self).__init__(self.eeprom,
                                            self.nct75,
                                            range_table=beast_range_table)

        if i2c and range_ctrl_pin and dcac_ctrl_pin and output_ctrl_pin and pl_signal_meter and not ipcore:
            self.range_ctrl_pin = range_ctrl_pin
            self.dcac_ctrl_pin = dcac_ctrl_pin
            self.output_ctrl_pin = output_ctrl_pin
            if isinstance(pl_signal_meter, basestring):
                axi4_bus = AXI4LiteBus(pl_signal_meter,
                                       BeastDef.SIGNAL_METER_REG_SIZE)
                self.pl_signal_meter_device = MIXSignalMeterSG(axi4_bus)
            else:
                self.pl_signal_meter_device = pl_signal_meter
        elif i2c and not range_ctrl_pin and not dcac_ctrl_pin and not output_ctrl_pin and not pl_signal_meter \
                and ipcore:
            if isinstance(ipcore, basestring):
                axi4_bus = AXI4LiteBus(ipcore, BeastDef.MIX_DAQT2_REG_SIZE)
                self.mix_daqt2 = MIXDAQT2SGR(axi4_bus,
                                             use_signal_meter1=False,
                                             use_spi=False,
                                             use_gpio=True)
            else:
                self.mix_daqt2 = ipcore
            self.pl_signal_meter_device = self.mix_daqt2.signal_meter0
            self.range_ctrl_pin = Pin(self.mix_daqt2.gpio,
                                      BeastDef.BEAST_RANGE_CTRL_PIN)
            self.dcac_ctrl_pin = Pin(self.mix_daqt2.gpio,
                                     BeastDef.BEAST_DCAC_CTRL_PIN)
            self.output_ctrl_pin = Pin(self.mix_daqt2.gpio,
                                       BeastDef.BEAST_OUTPUT_CTRL_PIN)
        elif i2c and range_ctrl_pin and dcac_ctrl_pin and output_ctrl_pin and not pl_signal_meter and ipcore:
            if isinstance(ipcore, basestring):
                axi4_bus = AXI4LiteBus(ipcore, BeastDef.MIX_DAQT2_REG_SIZE)
                self.mix_daqt2 = MIXDAQT2SGR(axi4_bus,
                                             use_signal_meter1=False,
                                             use_spi=False,
                                             use_gpio=False)
            else:
                self.mix_daqt2 = ipcore
            self.pl_signal_meter_device = self.mix_daqt2.signal_meter0
            self.range_ctrl_pin = range_ctrl_pin
            self.dcac_ctrl_pin = dcac_ctrl_pin
            self.output_ctrl_pin = output_ctrl_pin
        else:
            raise BeastBaseException("Invalid parameter, please check")

        self._mask = 0

    def post_power_on_init(self, timeout=BeastDef.TIME_OUT):
        '''
        Init Beast module to a know harware state.

        This function will set gpio pin io direction to output and set default range to '20Vpp'

        Args:
            timeout:      float, unit Second, execute timeout

        '''
        self.reset(timeout)

    def pre_power_down(self, timeout=BeastDef.TIME_OUT):
        '''
        Put the hardware in a safe state to be powered down.

        This function will set gpio pin io direction to output.

        Args:
            timeout:      float, unit Second, execute timeout

        '''
        self.range_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION)
        self.dcac_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION)
        self.output_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION)

    def reset(self, timeout=BeastDef.TIME_OUT):
        '''
        Reset the instrument module to a know hardware state.

        Args:
            timeout:      float, unit Second, execute timeout.

        '''
        self.range_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION)
        self.dcac_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION)
        self.output_ctrl_pin.set_dir(BeastDef.PIN_OUTPUT_DIRECTION)
        # Use max range is 20 Vpp as initial range
        self.select_range('AC_VOLT', 20)

    def get_driver_version(self):
        '''
        Get beast driver version.

        Returns:
            string, current driver version.

        '''
        return __version__

    def select_range(self, signal_type, value):
        '''
        Select Beast measurement range. All support range shown as below.

        Args:
            signal_type:    string, ['DC_VOLT', 'AC_VOLT'], Range signal_type string.
            value:          int, [2, 20], Range value.

        Returns:
            string, "done", api execution successful.

        '''
        assert signal_type in ["DC_VOLT", "AC_VOLT"]
        assert value in BeastDef.RANGE_VALUE_LIST

        if signal_type == "DC_VOLT":
            self.dcac_ctrl_pin.set_level(BeastDef.IO_LOW_LEVEL)
        else:
            self.dcac_ctrl_pin.set_level(BeastDef.IO_HIGH_LEVEL)

        if value == BeastDef.RANGE_VALUE_LIST[0]:
            self.board_gain = BeastDef.GAIN1
            self.range_ctrl_pin.set_level(BeastDef.IO_LOW_LEVEL)
        else:
            self.board_gain = BeastDef.GAIN10
            self.range_ctrl_pin.set_level(BeastDef.IO_HIGH_LEVEL)
        self.signal_type = signal_type
        return 'done'

    def enable_continuous_measure(self):
        '''
        Beast enable_continuous_measure function. board data will be copyed to dma when this function called.

        Returns:
            string, "done", api execution successful.

        '''
        self.adc_enable()
        self.pl_signal_meter_device.open()
        self.pl_signal_meter_device.enable_upframe(BeastDef.UPFRAME_MODE)

        return 'done'

    def disable_continuous_measure(self):
        '''
        Beast disable_continuous_measure function. Data will disable upload, when this function called.

        Returns:
            string, "done", api execution successful.

        '''
        self.adc_disable()
        self.pl_signal_meter_device.disable_upframe()
        self.pl_signal_meter_device.close()

        return 'done'

    def set_measure_mask(self, mask=0):
        '''
        Beast set signal meter measure mask.

        Args:
            mask:    int, default 0, mask bit value shown as below.

            +---------------+-------------------+
            |   mask        |       function    |
            +===============+===================+
            | bit[0:3]      | Reserved          |
            +---------------+-------------------+
            | bit[4]        | Frequency mask    |
            +---------------+-------------------+
            | bit[5]        | Duty mask         |
            +---------------+-------------------+
            | bit[6]        | Vpp mask          |
            +---------------+-------------------+
            | bit[7]        | rms mask          |
            +---------------+-------------------+

        Returns:
            string, "done", api execution successful.

        '''
        self._mask = mask
        return "done"

    def frequency_measure(self, duration, measure_type="LP"):
        '''
        Measure input signal frequncy and duty in a defined duration.

        Args:
            duration:      int, [1~2000], millisecond time to measure frequncy.
            measure_type:  string, ["HP", "LP"], default "LP", type of measure.

        Returns:
            list, [value, value], result value contain freq and duty, units are Hz, %.

        Examples:
            # adc_enable() and adc_disable() will operate on Pin
            # which might belongs to another i2c Mux port from on-board eeprom so put outside.
            beast.adc_enable()
            ret = beast.frequency_measure(10)
            beast.adc_disable()
            # ret will be list: [freq, duty]

        '''
        assert isinstance(duration, int) and duration > 0 and duration <= 2000
        assert measure_type in ["HP", "LP"]

        self.pl_signal_meter_device.open()
        self.pl_signal_meter_device.set_vpp_interval(
            BeastDef.BEAST_VPP_DURATION)
        self.pl_signal_meter_device.start_measure(duration,
                                                  BeastDef.BEAST_SAMPLE_RATE,
                                                  self._mask)
        freq = self.pl_signal_meter_device.measure_frequency(measure_type)
        duty = self.pl_signal_meter_device.duty()
        self.pl_signal_meter_device.close()

        return [freq, duty]

    def vpp_measure(self, duration):
        '''
        Measure input signal vpp, max and min voltage in a defined duration.

        Args:
            duration:   int, [1~2000], millisecond time to measure vpp.

        Returns:
            list, [value, value, value], result value contain vpp, max and min voltage, unit is mV.

        Examples:
            # adc_enable() and adc_disable() will operate on Pin
            # which might belongs to another i2c Mux port from on-board eeprom so put outside.
            beast.adc_enable()
            ret = beast.vpp_measure(10)
            beast.adc_disable()
            # ret will be list: [vpp, max, min]

        '''
        assert isinstance(duration, int) and duration > 0 and duration <= 2000
        self.pl_signal_meter_device.open()
        self.pl_signal_meter_device.set_vpp_interval(
            BeastDef.BEAST_VPP_DURATION)
        self.pl_signal_meter_device.start_measure(duration,
                                                  BeastDef.BEAST_SAMPLE_RATE,
                                                  self._mask)
        result = self.pl_signal_meter_device.vpp()
        vpp = (result[2] * BeastDef.VPP_SCALE_GAIN +
               BeastDef.VPP_SCALE_OFFSET) * self.board_gain
        max_data = (result[0] * BeastDef.SCALE_GAIN +
                    BeastDef.SCALE_OFFSET) * self.board_gain
        min_data = (result[1] * BeastDef.SCALE_GAIN +
                    BeastDef.SCALE_OFFSET) * self.board_gain

        if self.is_use_cal_data():
            if self.board_gain == BeastDef.GAIN1:
                vpp = self.calibrate('VPP_2V', vpp)
            else:
                if self.frequency_measure(
                        BeastDef.DURATION
                )[0] <= BeastDef.BEAST_HIGH_FREQ_THRESHOLD:
                    vpp = self.calibrate('LOW_FREQ_VPP_20V', vpp)
                else:
                    vpp = self.calibrate('HIGH_FREQ_VPP_20V', vpp)

        self.pl_signal_meter_device.close()

        return [vpp, max_data, min_data]

    def rms_measure(self, duration):
        '''
        Measure input signal rms and average voltage in a defined duration.

        Args:
            duration:   int, [1~2000], millisecond time to measure rms.

        Returns:
            list, [value, value], result value contain rms and average voltage, unit is mV.

        Examples:
            # adc_enable() and adc_disable() will operate on Pin
            # which might belongs to another i2c Mux port from on-board eeprom so put outside.
            beast.adc_enable()
            ret = beast.rms_measure(10)
            beast.adc_disable()
            # ret will be list: [rms, average]

        '''
        assert isinstance(duration, int) and duration > 0 and duration <= 2000

        self.pl_signal_meter_device.open()
        self.pl_signal_meter_device.set_vpp_interval(
            BeastDef.BEAST_VPP_DURATION)
        self.pl_signal_meter_device.start_measure(duration,
                                                  BeastDef.BEAST_SAMPLE_RATE,
                                                  self._mask)
        result = self.pl_signal_meter_device.rms()
        rms = (result[0] * BeastDef.RMS_SCALE_GAIN +
               BeastDef.RMS_SCALE_OFFSET) * self.board_gain
        average = (result[1] * BeastDef.SCALE_GAIN +
                   BeastDef.SCALE_OFFSET) * self.board_gain

        if self.is_use_cal_data() and self.signal_type == "AC_VOLT":
            if self.board_gain == BeastDef.GAIN1:
                rms = self.calibrate('RMS_2V', rms)
            else:
                if self.frequency_measure(
                        BeastDef.DURATION
                )[0] <= BeastDef.BEAST_HIGH_FREQ_THRESHOLD:
                    rms = self.calibrate('LOW_FREQ_RMS_20V', rms)
                else:
                    rms = self.calibrate('HIGH_FREQ_RMS_20V', rms)

        self.pl_signal_meter_device.close()

        return [rms, average]

    def level(self):
        '''
        Get current voltage level.

        Returns:
            int, [0, 1], 0 for low level, 1 for high level.

        '''
        return self.pl_signal_meter_device.level()

    def adc_enable(self):
        '''
        This function is used for enable adc output, it is an internal interface function.

        Returns:
            string, "done", api execution successful.

        '''
        self.output_ctrl_pin.set_level(BeastDef.IO_LOW_LEVEL)
        return "done"

    def adc_disable(self):
        '''
        This function is used for disable adc output, it is an internal interface function.

        Returns:
            string, "done", api execution successful.

        '''
        self.output_ctrl_pin.set_level(BeastDef.IO_HIGH_LEVEL)
        return "done"
Exemple #7
0
class MIXMagneto002SGR(object):
    '''
    MIXMagneto002SGR, support GPIO, FFT.

    Args:
        axi4_bus:            Instance(AXI4LiteBus)/string, axi4lite instance or dev path
        fft_data_cnt:        int,    get fft absolute data count, if not give, with get count from register.

    Example:
             magneto002 = MIXMagneto002SGR('/dev/MIX_Magneto002_SG_R_0')
    '''
    def __init__(self, axi4_bus, fft_data_cnt=None):
        if isinstance(axi4_bus, basestring):
            # device path; create axi4lite instance
            self.axi4_bus = AXI4LiteBus(axi4_bus, MIXMagneto002SGRDef.REG_SIZE)
        else:
            self.axi4_bus = axi4_bus

        self.fft_analyzer_axi4_bus = AXI4LiteSubBus(
            self.axi4_bus, MIXMagneto002SGRDef.MIX_FFT_ANAYLZER_IPCORE_ADDR,
            MIXMagneto002SGRDef.MIX_FFT_REG_SIZE)
        self.gpio_axi4_bus = AXI4LiteSubBus(
            self.axi4_bus, MIXMagneto002SGRDef.MIX_GPIO_IPCORE_ADDR,
            MIXMagneto002SGRDef.MIX_GPIO_REG_SIZE)

        self.gpio = MIXGPIOSG(self.gpio_axi4_bus)

        self.i2s_conf_0 = Pin(self.gpio, MIXMagneto002SGRDef.I2S_CONF0_BIT)
        self.i2s_conf_1 = Pin(self.gpio, MIXMagneto002SGRDef.I2S_CONF1_BIT)

        self.cs5361_ovfl = Pin(self.gpio, MIXMagneto002SGRDef.CS5361_OVFL_BIT)
        self.cs5361_rst = Pin(self.gpio, MIXMagneto002SGRDef.CS5361_RST_BIT)
        self.i2s_en = Pin(self.gpio, MIXMagneto002SGRDef.I2S_EN_BIT)
        self.analyzer = MIXFftAnalyzerSG(self.fft_analyzer_axi4_bus,
                                         fft_data_cnt)

        self.reset()

    def reset(self):
        self.i2s_conf_0.set_dir('output')
        self.i2s_conf_1.set_dir('output')
        self.cs5361_ovfl.set_dir('input')
        self.cs5361_rst.set_dir('output')
        self.i2s_en.set_dir('output')

        # disable ipcore
        self.i2s_en.set_level(0)

        self.config('left')

        # reset ic
        self.cs5361_rst.set_level(0)
        time.sleep(MIXMagneto002SGRDef.DELAY)
        self.cs5361_rst.set_level(1)

    def config(self, mode):
        '''
        Config.

        Args:
            mode:   string, ['left', 'right', 'differential'],  data mode.

        Example:
            magneto002.config('right')

        '''
        assert mode in MIXMagneto002SGRDef.I2S_DATA_MODE

        self.i2s_conf_0.set_level(MIXMagneto002SGRDef.I2S_DATA_MODE[mode][1])
        self.i2s_conf_1.set_level(MIXMagneto002SGRDef.I2S_DATA_MODE[mode][0])
        return 'done'

    def get_driver_version(self):
        return __version__
Exemple #8
0
class DarkBeastBase(MIXBoard):
    '''
    Base class of DarkBeast and DarkBeastCompatible.

    Providing common DarkBeast methods

    Args:
        firmware_path:       string,           DarkBeast firmware absolute path
        pl_uart_drv_ko_file: string,           DarkBeast pl_uart_drv.ko drive absolute path
                                               if None creating emulator.
        i2c:             instance(I2C)/None,   Class instance of PLI2CBus, which is used to control cat9555,
                                               eeprom and AD5667 sensor.
        gpio:            instance(GPIO)/None,  Class instance of PLGPIO, UART RX connect AID to enable or disable.
                                               if None creating emulator.
        pull_up_pin:     instance(GPIO)/None,  Class instance of GPIO, data line pull-up control, Provider mode.
                                               if None creating emulator.
        pull_down_pin:   instance(GPIO)/None,  Class instance of GPIO, data line pull-down control, Consumer mode.
                                               if None creating emulator.
        tx_en_pin:       instance(GPIO)/None,  Class instance of GPIO, UART_TX buffer enable control.
                                               if None creating emulator.
        connect_det_pin: instance(GPIO)/None,  Class instance of GPIO, input pin control, detects whether
                                               the DUT is connected. if None creating emulator.
        removal_det_pin: instance(GPIO)/None,  Class instance of GPIO, Input pin control, detect disconnection
                                               from DUT. if None creating emulator.
        a0_pin:          instance(GPIO)/None,  Class instance of Pin, if None creating emulator.
        a1_pin:          instance(GPIO)/None,  Class instance of Pin, if None creating emulator.
        a2_pin:          instance(GPIO)/None,  Class instance of Pin, if None creating emulator.
        elaod_en_pin:    instance(GPIO)/None,  Class instance of Pin, if None creating emulator.
        eeprom_devaddr:  int,                  Eeprom device address.

    '''

    rpc_public_api = [
        'module_init', 'close', 'open', 'communicate', 'aid_connect_set',
        'io_set', 'io_get'
    ] + MIXBoard.rpc_public_api

    def __init__(self, firmware_path, pl_uart_drv_ko_file, i2c, pull_up_pin,
                 pull_down_pin, tx_en_pin, removal_det_pin, connect_det_pin,
                 a0_pin, a1_pin, a2_pin, elaod_en_pin, eeprom_devaddr, gpio):
        self.path = firmware_path
        self.process = None
        self.pl_uart_drv_ko_file = pl_uart_drv_ko_file
        if (firmware_path == '' and pl_uart_drv_ko_file == '' and i2c is None
                and pull_up_pin is None and pull_down_pin is None
                and tx_en_pin is None and removal_det_pin is None
                and connect_det_pin is None):
            self.eeprom = EepromEmulator('eeprom_emulator')
            self.pull_up_pin = GPIOEmulator('pull_up_pin')
            self.pull_down_pin = GPIOEmulator('pull_down_pin')
            self.tx_en_pin = GPIOEmulator('tx_en_pin')
            self.removal_det_pin = GPIOEmulator('removal_det_pin')
            self.connect_det_pin = GPIOEmulator('connect_det_pin')
        elif (firmware_path != '' and pl_uart_drv_ko_file != ''
              and i2c is not None and pull_up_pin is not None
              and pull_down_pin is not None and tx_en_pin is not None
              and removal_det_pin is not None and connect_det_pin is not None):
            self.eeprom = CAT24C02(eeprom_devaddr, i2c)
            self.pull_up_pin = pull_up_pin
            self.pull_down_pin = pull_down_pin
            self.tx_en_pin = tx_en_pin
            self.removal_det_pin = removal_det_pin
            self.connect_det_pin = connect_det_pin
        else:
            raise DarkBeastException(
                '__init__ error! Please check the parameters!')
        self.gpio = gpio
        if (a0_pin is None and a1_pin is None and a2_pin is None
                and elaod_en_pin is None):
            self.a0_pin = Pin(None, DarkBeastDef.A0_PIN_ID)
            self.a1_pin = Pin(None, DarkBeastDef.A1_PIN_ID)
            self.a2_pin = Pin(None, DarkBeastDef.A2_PIN_ID)
            self.elaod_en_pin = Pin(None, DarkBeastDef.ELAOD_EN_PIN_ID)
        elif (a0_pin is not None and a1_pin is not None and a2_pin is not None
              and elaod_en_pin is not None):
            self.a0_pin = a0_pin
            self.a1_pin = a1_pin
            self.a2_pin = a2_pin
            self.elaod_en_pin = elaod_en_pin
        else:
            raise DarkBeastException(
                '__init__ error! Please check the parameters!')

        super(DarkBeastBase, self).__init__(self.eeprom, None)
        self.pin_def = {
            'PULL_UP': self.pull_up_pin,
            'PULL_DOWN': self.pull_down_pin,
            'TX_EN': self.tx_en_pin,
            'REMOVAL_DETECTION': self.removal_det_pin,
            'CONNECT_DETECTION': self.connect_det_pin,
            'A0': self.a0_pin,
            'A1': self.a1_pin,
            'A2': self.a2_pin,
            'ELAOD_EN': self.elaod_en_pin
        }

    def __del__(self):
        self.close()

    def _capture_by_flag(self):
        '''
        capture end flag of result.
        '''
        last_time = time.time()
        data_str = ""
        while (time.time() - last_time < DarkBeastDef.DEFAULT_TIMEOUT):
            read_str = self.process.stdout.read(1)
            data_str = data_str + read_str
            if read_str == DarkBeastDef.RESULT_CHECK_FLAG:
                if data_str.endswith(DarkBeastDef.RESULT_END_FLAG):
                    break
        if time.time() - last_time >= DarkBeastDef.DEFAULT_TIMEOUT:
            raise DarkBeastException('process read wait timeout!')

        return data_str.strip(DarkBeastDef.RESULT_END_FLAG)

    def _update_linux_driver(self):
        ''' update pl_uart_drv.ko if exist '''
        if os.path.isfile(self.pl_uart_drv_ko_file):
            os.system('rmmod ' + self.pl_uart_drv_ko_file)
            os.system('insmod ' + self.pl_uart_drv_ko_file)

    def module_init(self):
        '''
        DarkBeast module init, initialize ad5592r channel mode and open DarkBeast process.

        Returns:
            string, str, return open information.

        Examples:
            DarkBeast.module_init()

        '''
        self._update_linux_driver()
        ret_str = 'done'
        if os.path.isfile(self.path):
            ret_str = self.open()

        # set plgpio init
        if self.gpio:
            self.gpio.set_dir('output')
            self.gpio.set_level(DarkBeastDef.LOWE_LEVEL)

        # set gpio init
        self.pull_up_pin.set_dir('output')
        self.pull_up_pin.set_level(DarkBeastDef.LOWE_LEVEL)
        self.pull_down_pin.set_dir('output')
        self.pull_down_pin.set_level(DarkBeastDef.LOWE_LEVEL)
        self.tx_en_pin.set_dir('output')
        self.tx_en_pin.set_level(DarkBeastDef.LOWE_LEVEL)
        self.connect_det_pin.set_dir('input')
        self.removal_det_pin.set_dir('input')

        # set pin init
        self.a0_pin.set_dir('output')
        self.a0_pin.set_level(DarkBeastDef.LOWE_LEVEL)
        self.a1_pin.set_dir('output')
        self.a1_pin.set_level(DarkBeastDef.LOWE_LEVEL)
        self.a2_pin.set_dir('output')
        self.a2_pin.set_level(DarkBeastDef.LOWE_LEVEL)
        self.elaod_en_pin.set_dir('output')
        self.elaod_en_pin.set_level(DarkBeastDef.LOWE_LEVEL)

        return ret_str

    def close(self):
        '''
        close the DarkBeast process.

        Returns:
            string, "done", api execution successful.

        '''
        if self.process is not None:
            string_list = []
            pid_list = []
            pgid = os.getpgid(self.process.pid)

            command = 'ps -C {}'.format(os.path.basename(self.path))
            progress = Popen(command, shell=True, stdin=PIPE, stdout=PIPE)
            string = progress.communicate()[0]
            string_list = string.split("\n")
            for line in string_list:
                pid = re.findall("\d+", line)
                if len(pid) != 0:
                    pid_list.append(pid[0])

            for pid in pid_list:
                if os.getpgid(int(pid)) == pgid:
                    command = "kill " + pid
                    os.system(command)
            self.process = None

        return 'done'

    def open(self):
        '''
        open DarkBeast process by Popen, and return open information or faild raising Exception

        Returns:
            string, str, return open information.

        '''
        assert os.access(self.path, os.F_OK
                         | os.X_OK), 'path <{}> not exist or execute'.format(
                             self.path)
        if self.process is not None:
            return 'done'

        command = 'cd {};./{}'.format(os.path.dirname(self.path),
                                      os.path.basename(self.path))

        # stderr is standard error output, can be file descriptors and STDOUT. None means no output
        self.process = Popen([command, ""],
                             shell=True,
                             stdin=PIPE,
                             stdout=PIPE,
                             stderr=STDOUT)

        return self._capture_by_flag()

    def communicate(self, command):
        '''
        communicate with DarkBeast process. if process not open, raise DarkBeastException

        Args:
            command:  string, command string.

        Returns:
            string, str, return information.

        Examples:
            cmd_string = "SWITCH2HS"
            print DarkBeast.communicate(cmd_string)

        Raise:
            DarkBeastException:  process not open, communicate error!

        '''
        if self.process is not None:
            self.process.stdin.write(command + "\n")
            self.process.stdin.flush()
            if DarkBeastCMDDef.EXIT == command:
                self.process = None
                return DarkBeastDef.EXIT_STRING
            else:
                return self._capture_by_flag()

        raise DarkBeastException('process not open, communicate error!')

    def aid_connect_set(self, status):
        '''
        AID connect to dut enable or disable

        Args:
            status:  string, ['enable', 'disable'].

        Returns:
            string, "done", api execution successful.

        Examples:
            DarkBeast.aid_connect_set(1)

        '''
        assert status in ('enable', 'disable')
        if status == 'enable':
            level = 1
        else:
            level = 0
        if self.gpio:
            self.gpio.set_level(level)

        return 'done'

    def io_set(self, io_name, level):
        '''
        DarkBeast io control.

        Args:
            io_name:  string, ['PULL_UP', 'PULL_DOWN', 'TX_EN', 'CONNECT_DETECTION', 'REMOVAL_DETECTION',
                               'A0', 'A1', 'A2', 'ELAOD_EN'], io_name can be found in the table below.

                +---------------------+----------------------------------+
                |     io  name        |            io  meaning           |
                +=====================+==================================+
                |  PULL_UP            |  Data line pull-up control,      |
                |                     |  Provider mode.                  |
                +---------------------+----------------------------------+
                |  PULL_DOWN          |  Data line pull-down control,    |
                |                     |  Consumer mode.                  |
                +---------------------+----------------------------------+
                |  TX_EN              |  UART_TX buffer enable control   |
                +---------------------+----------------------------------+
                |  CONNECT_DETECTION  |  input pin control, detect       |
                |                     |  whether the DUT is connected.   |
                +---------------------+----------------------------------+
                |  REMOVAL_DETECTION  |  Input pin control, detect       |
                |                     |  disconnection from DUT.         |
                +--------------------------------------------------------+
                |       A0            |  ORION_COMM_CONN connect         |
                |                     |  PPVBUS_ORION_CONN.              |
                +---------------------+----------------------------------+
                |       A1            |  GND connect ORION_COMM_CONN     |
                +---------------------+----------------------------------+
                |       A2            |  GND connect PPVBUS_ORION_CONN   |
                +---------------------+----------------------------------+
                |       ELAOD_EN      |  A0, A1, A2 control1 disabled or |
                |                     |  enabled  (1/disabled  0/enabled)|
                +---------------------+----------------------------------+

            level:  int, [0, 1], 0/1 means lower/high

        Returns:
            string, "done", api execution successful.

        Examples:
            darkbeast.io_set("PULL_UP", 1)

        '''
        assert io_name in [
            "PULL_UP", "PULL_DOWN", "TX_EN", "CONNECT_DETECTION",
            "REMOVAL_DETECTION", "A0", "A1", "A2", "ELAOD_EN"
        ]

        device = self.pin_def[io_name]
        device.set_level(level)

        return 'done'

    def io_get(self, io_name):
        '''
        DarkBeast io level get

        Args:
            io_name:  string, ['PULL_UP', 'PULL_DOWN', 'TX_EN', 'CONNECT_DETECTION', 'REMOVAL_DETECTION',
                               'A0', 'A1', 'A2', 'ELAOD_EN'], io_name can be found in the table below.

                +---------------------+----------------------------------+
                |     io  name        |            io  meaning           |
                +=====================+==================================+
                |  PULL_UP            |  Data line pull-up control,      |
                |                     |  Provider mode.                  |
                +---------------------+----------------------------------+
                |  PULL_DOWN          |  Data line pull-down control,    |
                |                     |  Consumer mode.                  |
                +---------------------+----------------------------------+
                |  TX_EN              |  UART_TX buffer enable control   |
                +---------------------+----------------------------------+
                |  CONNECT_DETECTION  |  input pin control, detect       |
                |                     |  whether the DUT is connected.   |
                +---------------------+----------------------------------+
                |  REMOVAL_DETECTION  |  Input pin control, detect       |
                |                     |  disconnection from DUT.         |
                +--------------------------------------------------------+
                |       A0            |  ORION_COMM_CONN connect         |
                |                     |  PPVBUS_ORION_CONN.              |
                +---------------------+----------------------------------+
                |       A1            |  GND connect ORION_COMM_CONN     |
                +---------------------+----------------------------------+
                |       A2            |  GND connect PPVBUS_ORION_CONN   |
                +---------------------+----------------------------------+
                |       ELAOD_EN      |  A0, A1, A2 control1 disabled or |
                |                     |  enabled  (1/disabled  0/enabled)|
                +---------------------+----------------------------------+

        Returns:
            int, [0, 1], io level.

        Examples:
            level = darkbeast.io_get("PULL_UP")
            print(level)

        '''
        assert io_name in [
            "PULL_UP", "PULL_DOWN", "TX_EN", "CONNECT_DETECTION",
            "REMOVAL_DETECTION", "A0", "A1", "A2", "ELAOD_EN"
        ]

        device = self.pin_def[io_name]

        return device.get_level()
Exemple #9
0
class NegasonicBase(MIXBoard):
    '''
    Base class of Negasonic and NegasonicCompatible,Providing common Negasonic methods.

    Args:
        i2c:              instance(I2C), which is used to access eeprom and nct75, if not given,
                                         will create eeprom and nct75 emulator.
        analyzer:         instance(FFTAnalyzer)/string, if not given, will create emulator.
        signalsource:     instance(MIXSignalSourceSG)/string, if not given, will create emulator.
        adc_rst_pin:      instance(GPIO), used to reset ADC CS5361. Setting low to reset CS5361,
                                          seting high to enable CS5361.
        i2s_rx_en_pin:    instance(GPIO), used to enable fft analyzer module.
        dac_rst_pin:      instance(GPIO), used to reset IIS of the CS4398.
        i2s_tx_en_pin:    instance(GPIO), used to enable signal source module.
        sample_rate:      int, unit Hz, default 192000, used to config the CS5361 or CS4398.
        ipcore:               instance(UART), aggregated MIXAUT1SGR wrapper.
        eeprom_dev_addr:  int,            Eeprom device address.
        sensor_dev_addr:  int,            NCT75 device address.

    '''

    rpc_public_api = [
        'module_init', 'enable_upload', 'disable_upload', 'measure',
        'enable_output', 'disable_output'
    ] + MIXBoard.rpc_public_api

    def __init__(self,
                 i2c,
                 analyzer=None,
                 signal_source=None,
                 adc_rst_pin=None,
                 i2s_rx_en_pin=None,
                 dac_rst_pin=None,
                 i2s_tx_en_pin=None,
                 sample_rate=192000,
                 ipcore=None,
                 eeprom_dev_addr=NegasonicDef.EEPROM_I2C_ADDR,
                 sensor_dev_addr=NegasonicDef.SENSOR_I2C_ADDR,
                 cal_info=None,
                 range_table=None):

        assert sample_rate > 0 and sample_rate <= NegasonicDef.MAX_SAMPLING_RATE
        self.i2c = i2c
        if self.i2c is None:
            self.eeprom = EepromEmulator("cat24cxx_emulator")
            self.nct75 = NCT75Emulator("nct75_emulator")
        else:
            self.eeprom = CAT24C32(eeprom_dev_addr, i2c)
            self.nct75 = NCT75(sensor_dev_addr, i2c)

        super(NegasonicBase, self).__init__(self.eeprom,
                                            self.nct75,
                                            cal_table=cal_info,
                                            range_table=range_table)

        if (ipcore and not analyzer and not signal_source):
            if isinstance(ipcore, basestring):
                axi4 = AXI4LiteBus(ipcore, NegasonicDef.MIXAUT1_REG_SIZE)
                self.ipcore = MIXAUT1SGR(axi4)
            else:
                self.ipcore = ipcore
            self.analyzer = self.ipcore.analyzer
            self.signal_source = self.ipcore.signal_source
            self.adc_rst_pin = adc_rst_pin or Pin(self.ipcore.gpio,
                                                  NegasonicDef.ADC_RESET_PIN)
            self.i2s_rx_en_pin = i2s_rx_en_pin or Pin(
                self.ipcore.gpio, NegasonicDef.I2S_RX_EN_PIN)
            self.dac_rst_pin = dac_rst_pin or Pin(self.ipcore.gpio,
                                                  NegasonicDef.DAC_RESET_PIN)
            self.i2s_tx_en_pin = i2s_tx_en_pin or Pin(
                self.ipcore.gpio, NegasonicDef.I2S_TX_EN_PIN)
        elif (not ipcore and analyzer and signal_source):
            if isinstance(analyzer, basestring):
                axi4 = AXI4LiteBus(analyzer, NegasonicDef.ANALYZER_REG_SIZE)
                analyzer = MIXFftAnalyzerSG(axi4)
            else:
                self.analyzer = analyzer
            if isinstance(signal_source, basestring):
                axi4 = AXI4LiteBus(signal_source,
                                   NegasonicDef.SIGNALSOURCE_REG_SIZE)
                signal_source = MIXSignalSourceSG(axi4)
            else:
                self.signal_source = signal_source
            self.adc_rst_pin = adc_rst_pin
            self.i2s_rx_en_pin = i2s_rx_en_pin
            self.dac_rst_pin = dac_rst_pin
            self.i2s_tx_en_pin = i2s_tx_en_pin
        elif (not ipcore and not analyzer and not signal_source
              and not adc_rst_pin and not i2s_rx_en_pin and not dac_rst_pin
              and not i2s_tx_en_pin):
            self.analyzer = MIXFftAnalyzerSGEmulator(
                "mix_fftanalyzer_sg_emulator")
            self.signal_source = MIXSignalSourceSGEmulator(
                "mix_signalsource_sg_emulator")
            self.adc_rst_pin = Pin(None, NegasonicDef.ADC_RESET_PIN)
            self.i2s_rx_en_pin = Pin(None, NegasonicDef.I2S_RX_EN_PIN)
            self.dac_rst_pin = Pin(None, NegasonicDef.DAC_RESET_PIN)
            self.i2s_tx_en_pin = Pin(None, NegasonicDef.I2S_TX_EN_PIN)
        else:
            if ipcore:
                raise NegasonicException(
                    "parameter 'ipcore' can not be given at same time with 'analyzer', "
                    "'signal_source', 'adc_rst_pin', 'i2s_rx_en_pin', "
                    "'dac_rst_pin', 'i2s_tx_en_pin'")
            else:
                raise NegasonicException(
                    "parameter 'analyzer', 'signal_source', 'adc_rst_pin', "
                    "'i2s_rx_en_pin', 'dac_rst_pin' and 'i2s_tx_en_pin'"
                    " must be given at the same time")

        self.sample_rate = sample_rate

    def __del__(self):
        self.adc_rst_pin.set_level(0)
        self.i2s_rx_en_pin.set_level(0)
        self.dac_rst_pin.set_level(0)
        self.i2s_tx_en_pin.set_level(0)
        self.signal_source.close()

    def module_init(self):
        '''
        Negasonic init module, will reset dac/adc and i2s module.

        Returns:
            string, "done", api execution successful.

        Examples:
            negasonic.module_init()

        '''
        self.adc_rst_pin.set_dir('output')
        self.dac_rst_pin.set_dir('output')
        self.i2s_rx_en_pin.set_dir('output')
        self.i2s_tx_en_pin.set_dir('output')

        # reset ADC
        self.adc_rst_pin.set_level(0)
        time.sleep(NegasonicDef.DELAY_S)
        self.adc_rst_pin.set_level(1)

        # reset DAC
        self.dac_rst_pin.set_level(0)
        time.sleep(NegasonicDef.DELAY_S)
        self.dac_rst_pin.set_level(1)

        self.i2s_rx_en_pin.set_level(1)
        self.load_calibration()

        return "done"

    def enable_upload(self):
        '''
        Negasonic upoad mode open.

        Control audio upload data of ADC when doing measurement. It's not necessary
        enable upload when doing measurement. Note that data transfered into DMA is 32bit each data,
        but high 24bit data is valid. Low 8bit data is invalid. The data format is twos complement.

        Returns:
            string, "done", api execution successful.

        '''
        self.analyzer.enable_upload()
        return "done"

    def disable_upload(self):
        '''
        Negasonic upoad mode close. Close data upload doesn't influence to measure.

        Returns:
            string, "done", api execution successful.

        '''
        self.analyzer.disable_upload()
        return "done"

    def measure(self, bandwidth_hz, harmonic_num, decimation_type=0xFF):
        '''
        Negasonic measure signal's Vpp, RMS, THD+N, THD.

        Args:
            bandwidth_hz:    int, [24~95977], Measure signal's limit bandwidth, unit is Hz. The frequency of the
                                              signal should not be greater than half of the bandwidth.
            harmonic_num:    int, [2~10],     Use for measuring signal's THD.
            decimation_type: int, [1~255],    Decimation for FPGA to get datas. If decimation is 0xFF, FPGA will
                                              choose one suitable number.

        Returns:
            dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value),
                  dict with vpp, freq, thd, thdn, rms.

        Examples:
            result = negasonic.measure(20000, 5, 0xff)
            print result.frequency, result.vpp, result.thdn, result.thd, result.rms

        '''
        assert bandwidth_hz == 'auto' or isinstance(bandwidth_hz, int)
        assert isinstance(harmonic_num, int) and harmonic_num > 0
        assert isinstance(decimation_type, int) and decimation_type > 0

        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.analyze_config(self.sample_rate, decimation_type,
                                     bandwidth_hz, harmonic_num)
        self.analyzer.analyze()

        # calculate the actual vpp of HW by VREF
        vpp = self.analyzer.get_vpp() * NegasonicDef.AUDIO_ANALYZER_VREF
        # vpp = RMS * 2 * sqrt(2)
        rms = vpp / NegasonicDef.RMS_TO_VPP_RATIO

        rms = self.calibrate(NegasonicDef.MEASURE_CAL_ITEM, rms)
        vpp = rms * NegasonicDef.RMS_TO_VPP_RATIO

        result = dict()
        result["vpp"] = vpp
        result["freq"] = self.analyzer.get_frequency()
        result["thd"] = self.analyzer.get_thd()
        result["thdn"] = self.analyzer.get_thdn()
        result["rms"] = rms
        return result

    def enable_output(self, freq, rms):
        '''
        Negasonic output sine wave, differencial mode.

        Args:
            freq:    int, [10~50000], unit Hz, Ouput signal's frequency.
            rms:     float, [0~2300], unit mV, Ouput wave's RMS.

        Returns:
            string, "done", api execution successful.

        Examples:
            negasonic.enable_output(10000, 500)

        '''
        assert freq >= NegasonicDef.OUTPUT_FREQ_MIN
        assert freq <= NegasonicDef.OUTPUT_FREQ_MAX
        assert rms >= NegasonicDef.OUTPUT_RMS_MIN
        assert rms <= NegasonicDef.OUTPUT_RMS_MAX

        # enable I2S tx module
        self.i2s_tx_en_pin.set_level(1)

        self.signal_source.open()
        rms = self.calibrate(NegasonicDef.OUTPUT_CAL_ITEM, rms)
        # vpp = RMS * 2 * sqrt(2)
        vpp = rms * NegasonicDef.RMS_TO_VPP_RATIO
        # calculate vpp to vpp scale for FPGA
        vpp_scale = vpp * NegasonicDef.VPP_2_SCALE_RATIO / NegasonicDef.SIGNAL_SOURCE_VREF
        self.signal_source.set_signal_type(NegasonicDef.OUTPUT_WAVE)
        self.signal_source.set_signal_time(NegasonicDef.SIGNAL_ALWAYS_OUTPUT)
        self.signal_source.set_swg_paramter(self.sample_rate, freq, vpp_scale,
                                            NegasonicDef.OUTPUT_SIGNAL_DUTY)
        self.signal_source.output_signal()
        return "done"

    def disable_output(self):
        '''
        Negasonic close sine wave output.

        Returns:
            string, "done", api execution successful.

        Examples:
            negasonic.disable_output()

        '''
        self.signal_source.close()
        self.i2s_tx_en_pin.set_level(0)
        return "done"

    def legacy_write_calibration_cell(self, unit_index, gain, offset,
                                      threshold):
        '''
        MIXBoard calibration data write

        Args:
            unit_index:   int,    calibration unit index.
            gain:         float,  calibration gain.
            offset:       float,  calibration offset.
            threshold:    float,  if value < threshold, use this calibration unit data.

        Returns:
            string, "done", api execution successful.

        Examples:
            board.write_calibration_cel(0, 1.1, 0.1, 100)

        Raise:
            BoardArgCheckError:  unit_index data type is not int type or unit_index < 0.
            calibration unit format:
                Meaning:    Gain,     Offset,   threshold value, Use flag
                Mem size:   4Bytes,   4Bytes,   4Bytes,            Byte
                Data type:  float,    float,    float,            uint8_t
                Formula:    Y = Gain * X  + Offset

        '''
        if not isinstance(unit_index, int) or unit_index < 0:
            raise BoardArgCheckError(
                "calibration unit memory unit_index data type is not int type or unit_index < 0"
            )

        use_flag = self.calibration_info["use_flag"]
        data = (gain, offset, use_flag)
        s = struct.Struct(NegasonicDef.WRITE_CAL_DATA_PACK_FORMAT)
        pack_data = s.pack(*data)

        s = struct.Struct(NegasonicDef.WRITE_CAL_DATA_UNPACK_FORMAT)
        data = s.unpack(pack_data)
        address = self.calibration_info[
            "unit_start_addr"] + NegasonicDef.CAL_DATA_LEN * unit_index
        self.write_eeprom(address, data)
        return "done"

    def legacy_read_calibration_cell(self, unit_index):
        '''
        MIXBoard read calibration data

        Args:
            unit_index: int, calibration unit index.

        Returns:
            dict, {'gain': value, 'offset': value, 'threshold': value, 'is_use': value}.

        Examples:
            data = board.read_calibration_cel(0)
            print(data)

        Raise:
            BoardArgCheckError:  unit_index data type is not int type or unit_index < 0.
            calibration unit format:
                Meaning:    Gain,     Offset,   threshold value, Use flag
                Mem size:   4Bytes,   4Bytes,   4Bytes,            Byte
                Data type:  float,    float,    float,            uint8_t
                Formula:    Y = Gain * X  + Offset

        '''
        if not isinstance(unit_index, int) or unit_index < 0:
            raise BoardArgCheckError(
                "calibration unit memory unit_index data type is not int type or unit_index < 0"
            )

        address = self.calibration_info[
            "unit_start_addr"] + NegasonicDef.CAL_DATA_LEN * unit_index
        data = self.read_eeprom(address, NegasonicDef.READ_CAL_BYTE)

        s = struct.Struct(NegasonicDef.READ_CAL_DATA_PACK_FORMAT)
        pack_data = s.pack(*data)

        s = struct.Struct(NegasonicDef.READ_CAL_DATA_UNPACK_FORMAT)
        result = s.unpack(pack_data)

        threshold = 0.0
        for cal_item in negasonic_calibration_info:
            for level in negasonic_calibration_info[cal_item]:
                if unit_index == negasonic_calibration_info[cal_item][level][
                        "unit_index"]:
                    threshold = negasonic_calibration_info[cal_item][level][
                        "limit"][0]

        if self.calibration_info["use_flag"] != result[2]:
            return {"gain": 1.0, "offset": 0.0, "threshold": 0, "is_use": True}
        else:
            return {
                "gain": result[0],
                "offset": result[1],
                "threshold": threshold,
                "is_use": True
            }

    def legacy_erase_calibration_cell(self, unit_index):
        '''
        MIXBoard erase calibration unit

        Returns:
            string, "done", api execution successful.

        Examples:
            board.erase_calibration_cell(0)

        '''
        if not isinstance(unit_index, int) or unit_index < 0:
            raise BoardArgCheckError(
                "calibration unit memory unit_index data type is not int type or unit_index < 0"
            )

        data = [0xff for i in range(9)]
        address = self.calibration_info["unit_start_addr"] + 9 * unit_index
        self.write_eeprom(address, data)
        return "done"
Exemple #10
0
class MIXAUT1SGR(object):
    '''
    MIXAUT1SGR aggregated IPcore has 3 child IP, MIXFftAnalyzerSG, MIXSignalSourceSG, MIXGPIOSG.

    ClassType = MIXAUT1SGR

    Args:
        axi4_bus:            instance(AXI4LiteBus)/string, axi4lite instance or dev path.
        fft_data_cnt:        int,    get fft absolute data count, if not give, with get count from register.

    Examples:
        mix_aut1 = MIXAUT1SGR('/dev/MIX_AUT1_x')

    '''

    rpc_public_api = [
        'reset_adc', 'reset_dac', 'enable_rx', 'disable_rx', 'enable_tx',
        'disable_tx', 'enable_upload', 'disable_upload', 'measure',
        'enable_output', 'disable_output'
    ]

    def __init__(self, axi4_bus, fft_data_cnt=None):
        if isinstance(axi4_bus, basestring):
            # device path; create axi4lite instance
            self.axi4_bus = AXI4LiteBus(axi4_bus, MIXAUT1SGRDef.REG_SIZE)
        else:
            self.axi4_bus = axi4_bus

        if self.axi4_bus is None:
            self.analyzer = MIXFftAnalyzerSGEmulator(
                'mix_fftanalyzer_sg_emulator')
            self.signal_source = MIXSignalSourceSGEmulator(
                "mix_signalsource_sg_emulator")
            self.gpio = MIXGPIOSGEmulator("mix_gpio_sg_emulator", 256)
        else:
            self.fft_analyzer_axi4_bus = AXI4LiteSubBus(
                self.axi4_bus, MIXAUT1SGRDef.MIX_FFT_ANAYLZER_IPCORE_ADDR,
                MIXAUT1SGRDef.MIX_FFT_REG_SIZE)
            self.analyzer = MIXFftAnalyzerSG(self.fft_analyzer_axi4_bus,
                                             fft_data_cnt)

            self.signal_source_axi4_bus = AXI4LiteSubBus(
                self.axi4_bus, MIXAUT1SGRDef.MIX_SIGNAL_SOURCE_IPCORE_ADDR,
                MIXAUT1SGRDef.MIX_SIGNAL_SOURCE_REG_SIZE)
            self.signal_source = MIXSignalSourceSG(self.signal_source_axi4_bus)

            self.gpio_axi4_bus = AXI4LiteSubBus(
                self.axi4_bus, MIXAUT1SGRDef.MIX_GPIO_IPCORE_ADDR,
                MIXAUT1SGRDef.MIX_GPIO_REG_SIZE)
            self.gpio = MIXGPIOSG(self.gpio_axi4_bus)
        self.adc_rst_pin = Pin(self.gpio, MIXAUT1SGRDef.ADC_RESET_PIN)
        self.i2s_rx_en_pin = Pin(self.gpio, MIXAUT1SGRDef.I2S_RX_EN_PIN)
        self.dac_rst_pin = Pin(self.gpio, MIXAUT1SGRDef.DAC_RESET_PIN)
        self.i2s_tx_en_pin = Pin(self.gpio, MIXAUT1SGRDef.I2S_TX_EN_PIN)

    def reset_adc(self, delay_ms):
        '''
        Reset ADC.

        Args:
            delay_ms:   int, unit ms, reset time in ms

        Returns:
            "done"

        '''
        self.adc_rst_pin.set_level(0)
        time.sleep(delay_ms / 1000.0)
        self.adc_rst_pin.set_level(1)
        return "done"

    def reset_dac(self, delay_ms):
        '''
        Reset DAC.

        Args:
            delay_ms:   int, unit ms, reset time in ms

        Returns:
            "done"

        '''
        self.dac_rst_pin.set_level(0)
        time.sleep(delay_ms / 1000.0)
        self.dac_rst_pin.set_level(1)
        return "done"

    def enable_rx(self):
        '''
        Enable I2S RX
        '''
        self.i2s_rx_en_pin.set_level(1)
        return "done"

    def disable_rx(self):
        '''
        Disable I2S RX
        '''
        self.i2s_rx_en_pin.set_level(0)
        return "done"

    def enable_tx(self):
        '''
        Enable I2S TX
        '''
        self.i2s_tx_en_pin.set_level(1)
        return "done"

    def disable_tx(self):
        '''
        Disable I2S TX
        '''
        self.i2s_tx_en_pin.set_level(0)
        return "done"

    def enable_upload(self):
        '''
        Enable FFT data upload

        Returns:
            string, "done", api execution successful.

        '''
        self.analyzer.enable_upload()
        return "done"

    def disable_upload(self):
        '''
        Disable FFT data upload

        Returns:
            string, "done", api execution successful.

        '''
        self.analyzer.disable_upload()
        return "done"

    def measure(self,
                sampling_rate,
                decimation_type,
                bandwidth_hz='auto',
                harmonic_num=None,
                freq_point=None):
        '''
        Measure signal's freq, vpp, THD+N, THD.

        Args:
            sample_rate:     int, Sample rate of your ADC device, unit is Hz. Eg. 192000.
            decimation_type: int, [1~255],    Decimation for FPGA to get datas. If decimation is 0xFF, FPGA will
                                              choose one suitable number.
            bandwidth:       int/string,       FFT calculation bandwidth limit, must smaller than half of sample_rate,
                                               unit is Hz. Eg. 20000. If 'auto' given, bandwidth will be automatically
                                               adjust based on base frequency.
            harmonic_count:  int/None, [1~10]  The harmonic count of signal, default is None will not do calculate.
            freq_point:      int/None,         Specified frequency for calculating amplitude at this special frequency,
                                               default is None will not do this.

        Returns:
            dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value),
                  dict with vpp, freq, thd, thdn.

        Examples:
            result = aut1.measure(48000, 5, 0xff)
            print(result['frequency'], result['vpp'], ['thdn'], ['thd'])

        '''
        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.analyze_config(sampling_rate, decimation_type,
                                     bandwidth_hz, harmonic_num, freq_point)
        self.analyzer.analyze()
        result = dict()
        result["vpp"] = self.analyzer.get_vpp()
        result["freq"] = self.analyzer.get_frequency()
        result["thd"] = self.analyzer.get_thd()
        result["thdn"] = self.analyzer.get_thdn()

        return result

    def enable_output(self, sampling_rate, freq, vpp):
        '''
        Enable signal source output

        Args:
            sampling_rate:  int, in SPS, DAC sampling rate.
            freq:           int, unit Hz, Ouput signal's frequency.
            vpp:            float, [0.000~0.999], output signal vpp scale.

        Returns:
            string, "done", api execution successful.

        Examples:
            aut1.enable_output(10000, 500)

        '''
        self.signal_source.open()

        self.signal_source.set_signal_type(MIXAUT1SGRDef.OUTPUT_TYPE)
        self.signal_source.set_signal_time(MIXAUT1SGRDef.ALWAYS_OUTPUT)
        self.signal_source.set_swg_paramter(sampling_rate, freq, vpp,
                                            MIXAUT1SGRDef.OUTPUT_DUTY)
        self.signal_source.output_signal()
        return "done"

    def disable_output(self):
        '''
        Disable signal source output
        '''
        self.signal_source.close()
        return "done"
Exemple #11
0
class KarmaBase(SGModuleDriver):
    '''
    Base class of Karma and KarmaCompatible.

    Providing common Karma methods.

    Args:
        i2c:        instance(I2C), instance of I2CBus, which is used to control cat24c32 and nct75.
        ipcore:     instance(MIXBT001SGR)/string, If given, then use MIXBT001SGR's AD7175, MIXQSPI, MIXGPIO.
        eeprom_dev_addr:    int, eeprom device address.
        sensor_dev_addr:    int, NCT75 device address.
        range_table:        dict, which is ICI calibration range table.

    '''

    rpc_public_api = [
        'dds_reset', 'dds_control', 'discharge_control', 'set_line_path',
        'get_line_path', 'get_offset_cal_data', 'sine_output',
        'resistance_measure', 'read_adc_voltage', 'set_sampling_rate',
        'get_sampling_rate'
    ] + SGModuleDriver.rpc_public_api

    def __init__(self,
                 i2c,
                 ipcore,
                 eeprom_dev_addr=KarmaDef.EEPROM_DEV_ADDR,
                 sensor_dev_addr=KarmaDef.SENSOR_DEV_ADDR,
                 range_table=karma_range_table):

        if (i2c and ipcore):
            if isinstance(ipcore, basestring):
                axi4_bus = AXI4LiteBus(ipcore, KarmaDef.REG_SIZE)
                self.ipcore = MIXBT001SGR(axi4_bus=axi4_bus,
                                          ad717x_chip='AD7175',
                                          ad717x_mvref=KarmaDef.ADC_VREF,
                                          use_spi=True,
                                          use_gpio=True)
            else:
                self.ipcore = ipcore

            self.ad7175 = self.ipcore.ad717x
            self.ad7175.config = {
                'ch0': {
                    'P': 'AIN0',
                    'N': 'AIN1'
                },
                'ch1': {
                    'P': 'AIN2',
                    'N': 'AIN3'
                }
            }
            self.vx_ix_select_pin = Pin(self.ipcore.gpio,
                                        KarmaDef.VX_IX_SELECT_PIN)
            self.cur_set1_pin = Pin(self.ipcore.gpio, KarmaDef.CUR_SET1_PIN)
            self.cur_set2_pin = Pin(self.ipcore.gpio, KarmaDef.CUR_SET2_PIN)
            self.discharge_pin = Pin(self.ipcore.gpio, KarmaDef.DISCHARGE_PIN)
            self.lpf_select_pin = Pin(self.ipcore.gpio,
                                      KarmaDef.LPF_SELECT_PIN)
            self.reset_l_pin = Pin(self.ipcore.gpio, KarmaDef.RESET_L_PIN)
            self.trigger_l_pin = Pin(self.ipcore.gpio, KarmaDef.TRIGGER_L_PIN)
            self.ad9106 = AD9106(self.ipcore.spi, KarmaDef.AD9106_MCLK,
                                 KarmaDef.AD9106_DEFAULT_VREF)
            self.eeprom = CAT24C32(eeprom_dev_addr, i2c)
            self.sensor = NCT75(sensor_dev_addr, i2c)
            self.line_path = ""
            self.sine_data_length = 0
            self.cosine_data_length = 0
            self.cycle_count = 2
            super(KarmaBase, self).__init__(self.eeprom,
                                            self.sensor,
                                            range_table=range_table)

        else:
            raise KarmaException('Parameter error')

    def post_power_on_init(self, timeout=KarmaDef.DEFAULT_TIMEOUT):
        '''
        Init Karma module to a know harware state.

        Args:
            timeout:      float, (>=0), default 6, unit Second, execute timeout.

        '''
        self.reset(timeout)

    def pre_power_down(self, timeout=KarmaDef.DEFAULT_TIMEOUT):
        '''
        Put the hardware in a safe state to be powered down.

        Args:
            timeout:      float, (>=0), default 6, unit Second, execute timeout.
        '''
        start_time = time.time()

        while True:
            try:
                self.discharge_control('open', KarmaDef.DISCHARGE_DEFAULT_TIME)
                self.discharge_control('close')
                self.dds_reset()
                self.dds_control('close')
                return
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise KarmaException("Timeout: {}".format(e.message))

    def reset(self, timeout=KarmaDef.DEFAULT_TIMEOUT):
        '''
        Reset the instrument module to a know hardware state.

        Args:
            timeout:      float, (>=0), default 6, unit Second, execute timeout.

        '''
        start_time = time.time()

        while True:
            try:
                self.vx_ix_select_pin.set_dir(KarmaDef.IO_OUTPUT_DIR)
                self.cur_set1_pin.set_dir(KarmaDef.IO_OUTPUT_DIR)
                self.cur_set2_pin.set_dir(KarmaDef.IO_OUTPUT_DIR)
                self.discharge_pin.set_dir(KarmaDef.IO_OUTPUT_DIR)
                self.lpf_select_pin.set_dir(KarmaDef.IO_OUTPUT_DIR)
                self.reset_l_pin.set_dir(KarmaDef.IO_OUTPUT_DIR)
                self.trigger_l_pin.set_dir(KarmaDef.IO_OUTPUT_DIR)
                self.discharge_control('open', KarmaDef.DISCHARGE_DEFAULT_TIME)
                self.discharge_control('close')
                self.dds_reset()
                self.dds_control('close')
                self.ad9106.set_ref_voltage(KarmaDef.AD9106_DEFAULT_VREF)
                self.ad7175.channel_init()
                self.set_sampling_rate(0, KarmaDef.ADC_DEFAULT_SAMPLE_RATE)
                self.set_sampling_rate(1, KarmaDef.ADC_DEFAULT_SAMPLE_RATE)
                self.write_sine_pattern(1000, KarmaDef.IOUT_DEFAULT_VPP,
                                        self.cycle_count)
                self.write_cosine_pattern(1000, KarmaDef.IOUT_DEFAULT_VPP,
                                          self.cycle_count)
                return
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise KarmaException("Timeout: {}".format(e.message))

    def get_driver_version(self):
        '''
        Get driver version.

        Returns:
            string, current driver version.
        '''
        return __version__

    def dds_reset(self):
        '''
        Karma reset ad9106. This is private function.

        Returns:
            string, "done", api execution successful.

        Examples:
            karma.dds_reset()

        '''
        self.reset_l_pin.set_level(0)
        time.sleep(KarmaDef.RST_DELAY_MS / 1000.0)
        self.reset_l_pin.set_level(1)

        return "done"

    def dds_control(self, mode):
        '''
        Pattern trigger input of ad9106. This is private function.

        Args:
            mode:    string, ['open', 'close'], 'open' mean enable pattern trigger input of ad9106,
                                                'close' mean disable pattern trigger input of ad9106.

        Returns:
            string, "done", api execution successful.

        Examples:
            karma.dds_control('open')

        '''
        assert mode in ['open', 'close']

        if mode == 'open':
            self.trigger_l_pin.set_level(0)
        else:
            self.trigger_l_pin.set_level(1)

        return "done"

    def set_sampling_rate(self, channel, sampling_rate):
        '''
        Karma set sampling rate. This is private function.

        Args:
            channel:           int, [0, 1], channel 0 is for the real part of Vx or Ix,
                                            channel 1 is for the imaginary part of Vx or Ix.
            sampling_rate:     float, [5~250000], adc measure sampling rate, which not continuouse,
                                                  please refer ad7175 datasheet.

        Returns:
            string, "done", api execution successful.

        Examples:
           karma.set_sampling_rate(1, 10000)

        '''
        assert channel in KarmaDef.ADC_CHANNEL_LIST
        assert 5 <= sampling_rate <= 250000

        if sampling_rate != self.get_sampling_rate(channel):
            self.ad7175.set_sampling_rate(channel, sampling_rate)

        return "done"

    def get_sampling_rate(self, channel):
        '''
        Karma get sampling rate of adc. This is private function.

        Args:
            channel:     int, [0, 1], channel 0 is for the real part of Vx or Ix,
                                      channel 1 is for the imaginary part of Vx or Ix.

        Returns:
            string, "done", api execution successful.

        Examples:
            sampling_rate = karma.get_sampling_rate(1)
            print(sampling_rate)

        '''
        assert channel in KarmaDef.ADC_CHANNEL_LIST

        return self.ad7175.get_sampling_rate(channel)

    def discharge_control(self,
                          mode,
                          discharge_time=KarmaDef.CLOSE_DEFAULT_TIME):
        '''
        Discharge control. This is private function.

        Args:
            mode:    string, ['open', 'close'], 'open' mean discharge,
                                                     'close' mean charge.
            discharge_time:    float, (>=0), default 0, unit ms, discharge time.

        Returns:
            string, "done", api execution successful.

        Examples:
            karma.discharge_control('open', 20)

        '''
        assert mode in ['open', 'close']

        bits = karma_line_info[mode]['bits']
        for bit in bits:
            if isinstance(bit, list):
                for io_conf in bit:
                    eval(io_conf[0]).set_level(io_conf[1])
                time.sleep(KarmaDef.RST_DELAY_MS / 1000.0)
            else:
                eval(bit[0]).set_level(bit[1])
        time.sleep(discharge_time / 1000.0)
        self.line_path = mode

        return "done"

    def set_line_path(self,
                      channel,
                      scope,
                      freq,
                      delay_time=KarmaDef.RELAY_DELAY_MS):
        '''
        Karma set channel path. This is debug function.

        Args:
            channel:     string, ['Vx', 'Ix'], channel 'Vx' mean the vector voltage of the real part or
                                               the imaginary part of impedance,
                                               channel 'Ix' mean the vector current of the real part or
                                               the imaginary part of impedance.
            scope:       string, ['3000mohm', '300mohm'], impedance measurement range.
            freq:        string, ['1kHz', '1Hz'], impedance measurement frequency.
            delay_time:  int, (>0), default 5, unit ms.

        Returns:
            string, "done", api execution successful.

        Examples:
            karma.set_line_path('Vx', '3000mohm', '1kHz')

        '''
        assert channel in KarmaDef.CHANNEL_LIST
        assert scope in KarmaDef.RANGE_LIST
        assert freq in KarmaDef.FREQ_LIST

        line_path = freq + '_' + scope + '_' + channel
        if line_path != self.line_path:
            bits = karma_line_info[channel]['bits']
            for bit in bits:
                eval(bit[0]).set_level(bit[1])

            bits = karma_line_info[freq]['bits']
            for bit in bits:
                eval(bit[0]).set_level(bit[1])

            bits = karma_line_info[scope]['bits']
            for bit in bits:
                eval(bit[0]).set_level(bit[1])
                time.sleep(delay_time / 1000.0)

            self.line_path = line_path

        return "done"

    def get_line_path(self):
        '''
        Karma get channel path.

        Returns:
            string, value is channel path.

        Examples:
            path = karma.get_line_path()
            print(path)

        '''
        return self.line_path

    def sine_output(self, dac_channel, freq, vpp, offset=0, phase_value=0):
        '''
        AD9106 output sine waveform, this is a private function.

        Args:
            dac_channel:     int, [1, 2, 3], channel for dds output.
            freq:            int, unit Hz, frequency of wavefrom.
            vpp:             float, unit mVpp, vpp of waveform.
            offset:          float, unit mV, default 0, offset of waveform.
            phase_value:     float, (>=0), default 0, phase of waveform.

        Returns:
            string, "done", api execution successful.

        Examples:
            karma.sine_output(0, 1000, 500)

        '''
        assert dac_channel in [1, 2, 3]
        assert 0 <= phase_value

        # IOUTFSx = 32 * VREFIO / xRSET = 32 * VREFIO / 8060, VOUTFS = IOUTFS x Rl = IOUTFS * 249
        vp = 32 * KarmaDef.AD9106_DEFAULT_VREF * 249 / 8060.0
        gain = vpp / (vp * 2)
        self.ad9106.set_phase(dac_channel, phase_value)
        self.ad9106.sine_output(dac_channel, freq, gain, offset)
        return "done"

    def write_sine_pattern(self, freq, vpp, cycle_count=1):
        '''
        Write sine wave data to SRAM.

        Args:
            freq: int/float, unit Hz, frequency value.
            vpp: int/float, unit mVpp, vpp value.
            cycle_count, int/float, (>0), default 1, the number of waveform cycles in SRAM.

        Returns:
            string, "done", api execution successful.

        Examples:
            karma.write_sine_pattern(1000, 1977, 4)

        '''
        sine_data_list = self.calculate_sine_data(freq, vpp)
        sine_data_list = sine_data_list * cycle_count
        if len(sine_data_list) > 4096:
            raise KarmaException("Out of range of per pattern_period")
        self.ad9106.write_pattern(KarmaDef.AD9106_SRAM_SINE_ADDR,
                                  sine_data_list)
        self.sine_data_length = len(sine_data_list)

        return "done"

    def calculate_sine_data(self, freq, vpp):
        '''
        Calculate sine data.

        Args:
            freq: int/float, unit Hz, frequency value.
            vpp: int/float, unit mVpp, vpp value.

        Returns:
            list, sine wave data.

        Examples:
            karma.calculate_sine_data(1000, 1977)

        '''

        data_list = list()
        vp = vpp / 2
        freq = freq * 1.0
        total_points = int(KarmaDef.AD9106_SAMPLE_RATE / freq)
        for i in range(total_points):
            point_vp = math.sin(2 * math.pi * (i / (total_points * 1.0))) * vp
            if (point_vp < 0):
                if point_vp < -KarmaDef.AD9106_DEFAULT_VREF:
                    point_vp = -KarmaDef.AD9106_DEFAULT_VREF
                result = int(
                    KarmaDef.calculation_formula['calculate_negative_data']
                    (point_vp)) & 0xfff
            else:
                if point_vp > KarmaDef.AD9106_DEFAULT_VREF:
                    point_vp = KarmaDef.AD9106_DEFAULT_VREF
                result = int(
                    KarmaDef.calculation_formula['calculate_positive_data']
                    (point_vp)) & 0xfff
            data_list.append(result)

        return data_list

    def write_cosine_pattern(self, freq, vpp, cycle_count=1):
        '''
        Write cosine wave data to SRAM.

        Args:
            freq: int/float, unit Hz, frequency value.
            vpp: int/float, unit mVpp, vpp value.
            cycle_count, int/float, (>0), default 1, the number of waveform cycles in SRAM.

        Returns:
            string, "done", api execution successful.

        Examples:
            karma.write_cosine_pattern(1000, 1977, 4)

        '''
        cosine_data_list = self.calculate_cosine_data(freq, vpp)
        cosine_data_list = cosine_data_list * cycle_count
        if len(cosine_data_list) > 4096:
            raise KarmaException("Out of range of per pattern_period")
        self.ad9106.write_pattern(KarmaDef.AD9106_SRAM_COSINE_ADDR,
                                  cosine_data_list)
        self.cosine_data_length = len(cosine_data_list)

        return "done"

    def calculate_cosine_data(self, freq, vpp):
        '''
        Calculate cosine data.

        Args:
            freq: int/float, unit Hz, frequency value.
            vpp: int/float, unit mVpp, vpp value.

        Returns:
            list, cosine wave data.

        Examples:
            karma.calculate_cosine_data(1000, 1977)

        '''

        data_list = list()
        vp = vpp / 2
        freq = freq * 1.0
        total_points = int(KarmaDef.AD9106_SAMPLE_RATE / freq)
        for i in range(total_points):
            point_vp = math.cos(2 * math.pi * (i / (total_points * 1.0))) * vp
            if (point_vp < 0):
                if point_vp < -KarmaDef.AD9106_DEFAULT_VREF:
                    point_vp = -KarmaDef.AD9106_DEFAULT_VREF
                result = int(
                    KarmaDef.calculation_formula['calculate_negative_data']
                    (point_vp)) & 0xfff
            else:
                if point_vp > KarmaDef.AD9106_DEFAULT_VREF:
                    point_vp = KarmaDef.AD9106_DEFAULT_VREF
                result = int(
                    KarmaDef.calculation_formula['calculate_positive_data']
                    (point_vp)) & 0xfff
            data_list.append(result)

        return data_list

    def output_sine_pattern(self,
                            freq,
                            vpp,
                            cycle_count,
                            pat_period_base=1,
                            dac_repeat_cycle=255):
        '''
        Output sine wave in pattern period.

        Args:
            freq: int/float, unit Hz, frequency value, it is related to the clock of DAC.
            vpp:    float, unit mVpp, vpp of waveform.
            cycle_count: int/float, (>0), the number of waveform cycles in per pattern_period.
            pat_period_base: int, [0-0xf], default 1, the number of dac clock periods per pattern_period lsb.
            dac_repeat_cycle:    int, [0-255], default 1, Number of DAC pattern repeat cycles + 1,
                                                (0 means repeat 1 pattern).

        Returns:
            string, "done", api execution successful.

        Examples:
            karma.write_sine_pattern(1000, 1977, 4)
            karma.output_sine_pattern(1000, 1977, 4)
        '''
        self.dds_control('close')
        # IOUTFSx = 32 * VREFIO / xRSET = 32 * VREFIO / 8060, VOUTFS = IOUTFS x Rl = IOUTFS * 249
        vp = 32 * KarmaDef.AD9106_DEFAULT_VREF * 249 / 8060.0
        gain = vpp / (vp * 2)
        self.ad9106.play_pattern(1,
                                 freq,
                                 KarmaDef.AD9106_SRAM_SINE_START_ADDR,
                                 self.sine_data_length - 1,
                                 pat_period_base=pat_period_base,
                                 dac_repeat_cycle=dac_repeat_cycle,
                                 cycle_value=cycle_count,
                                 gain=gain)
        self.ad9106.play_pattern(2,
                                 freq,
                                 KarmaDef.AD9106_SRAM_COSINE_START_ADDR,
                                 self.cosine_data_length - 1,
                                 pat_period_base=pat_period_base,
                                 dac_repeat_cycle=dac_repeat_cycle,
                                 cycle_value=cycle_count,
                                 gain=gain)
        self.ad9106.play_pattern(3,
                                 freq,
                                 KarmaDef.AD9106_SRAM_SINE_START_ADDR,
                                 self.sine_data_length - 1,
                                 pat_period_base=pat_period_base,
                                 dac_repeat_cycle=dac_repeat_cycle,
                                 cycle_value=cycle_count,
                                 gain=gain)
        self.ad9106.set_ram_update()
        self.dds_control('open')

        return "done"

    def resistance_measure(self,
                           scope,
                           freq,
                           discharge_time=KarmaDef.DISCHARGE_DEFAULT_TIME,
                           delay_time=KarmaDef.RELAY_DELAY_MS):
        '''
        Karma measure impedance.

        Please notes that the hardware does not support 1Hz measurement temporarily.

        Args:
            scope:    string, ['3000mohm', '300mohm'], impedance measurement range.
            freq:     string, ['1kHz', '1Hz'], impedance measurement frequency,
                                            the hardware does not support '1Hz' measurement temporarily.
            discharge_time:    float, (>=20), default 20, unit ms, discharge time.
            delay_time:    int, (>0), default 5, unit ms.

        Returns:
            dict, {'Rs': (Rs, 'ohm'), 'Xs': (Xs, 'ohm')}, for the real part and imaginary part of impedance.

        Examples:
            result = karma.resistance_measure('3000mohm', '1kHz', 20)
            print(result)

        '''
        assert scope in KarmaDef.RANGE_LIST
        assert freq in KarmaDef.FREQ_LIST
        assert 20 <= discharge_time

        sine_freq_dict = {'1kHz': 1000, '1Hz': 1}
        sine_freq = sine_freq_dict[freq]
        r_sense_dict = {'3000mohm': 200, '300mohm': 20}
        r_sense = r_sense_dict[scope]
        self.discharge_control('close')
        if freq == '1kHz':
            self.output_sine_pattern(sine_freq, KarmaDef.IOUT_DEFAULT_VPP,
                                     self.cycle_count)
        else:
            raise KarmaException(
                "Hardware does not support 1Hz measurement temporarily")
        self.set_line_path('Ix', scope, freq, delay_time)
        u_re = self.ad7175.read_volt(0)
        u_im = self.ad7175.read_volt(1)
        u_re = self.calibrate(scope + '_Ix_ch1', u_re, sine_freq)
        u_im = self.calibrate(scope + '_Ix_ch2', u_im, sine_freq)
        self.set_line_path('Vx', scope, freq, delay_time)
        self.output_sine_pattern(sine_freq, KarmaDef.IOUT_DEFAULT_VPP,
                                 self.cycle_count)
        v_re = self.ad7175.read_volt(0)
        v_im = self.ad7175.read_volt(1)
        v_re = self.calibrate(scope + '_Vx_ch1', v_re, sine_freq)
        v_im = self.calibrate(scope + '_Vx_ch2', v_im, sine_freq)
        self.discharge_control('open', discharge_time)
        self.discharge_control('close')

        result = dict()
        unit = 'ohm'
        r_s = -(((v_re * u_re) + (v_im * u_im)) * r_sense /
                ((u_re**2 + u_im**2) * 65.0))
        x_s = -(((v_im * u_re) - (v_re * u_im)) * r_sense /
                ((u_re**2 + u_im**2) * 65.0))
        r_s = self.calibrate(scope, r_s)
        result['Rs'] = (r_s, unit)
        result['Xs'] = (x_s, unit)

        return result

    def read_adc_voltage(self, channel):
        '''
        Read adc voltage, this is a debug function.

        Args:
            channel:    int, [0, 1]. adc channel.

        Returns:
            float, unit is mV.

        Examples:
            result = karma.read_adc_voltage(0)
            print(result)

        '''
        assert channel in KarmaDef.ADC_CHANNEL_LIST
        return self.ad7175.read_volt(channel)

    def get_offset_cal_data(self, frequency):
        '''
        Get offset calibration data, this is a debug function.

        Args:
            frequency:    string, ['1kHz', '1Hz'], frequency.

        Returns:
            dict,
            e.g. {'1kHz_300mohm_Vx_ch1': 37.65133247681454,
                  '1Hz_300mohm_Ix_ch1': 13.997853636613705,
                  '1kHz_300mohm_Ix_ch2': 43.30072065000062,
                  '1kHz_300mohm_Vx_ch2': 43.32542677673262,
                  '1Hz_3000mohm_Ix_ch2': 12.980402289652961,
                  '1kHz_300mohm_Ix_ch1': 37.70277128832169,
                  '1Hz_300mohm_Ix_ch2': 13.53880843751481,
                  '1Hz_3000mohm_Ix_ch1': 13.895274036841037,
                  '1Hz_300mohm_Vx_ch2': 20.317376870952657,
                  '1Hz_3000mohm_Vx_ch2': 28.178902159863846,
                  '1kHz_3000mohm_Vx_ch1': 37.72822247315779,
                  '1kHz_3000mohm_Vx_ch2': 43.30441613819696,
                  '1Hz_3000mohm_Vx_ch1': 27.222545577439405,
                  '1kHz_3000mohm_Ix_ch2': 43.30870767287659,
                  '1Hz_300mohm_Vx_ch1': 27.315230805589607,
                  '1kHz_3000mohm_Ix_ch1': 37.71659956673381}.

        Examples:
            result = karma.get_offset_cal_data()
            print(result)

        '''
        assert frequency in KarmaDef.FREQ_LIST

        cal_offset = OrderedDict()
        adc_channel_dict = {'ch1': 0, 'ch2': 1}
        sine_freq_dict = {'1kHz': 1000, '1Hz': 1}
        if frequency == '1Hz':
            CAL_OFFSET_RANGE = CAL_OFFSET_RANGE_1Hz
        else:
            CAL_OFFSET_RANGE = CAL_OFFSET_RANGE_1kHz

        for offset_range in CAL_OFFSET_RANGE:
            chan = offset_range.split('_')
            freq = chan[0]
            scope = chan[1]
            vi_chan = chan[2]
            adc_chan = chan[3]
            adc_channel = adc_channel_dict[adc_chan]
            sine_freq = sine_freq_dict[freq]
            self.discharge_control('close')
            self.sine_output(1, sine_freq, KarmaDef.IOUT_DEFAULT_VPP)
            self.sine_output(2,
                             sine_freq,
                             KarmaDef.IOUT_DEFAULT_VPP,
                             phase_value=KarmaDef.COSINE_PHASE)
            self.dds_control('open')
            self.set_line_path(vi_chan, scope, freq)

            data_list = []
            for i in range(5):
                data_list.append(self.read_adc_voltage(adc_channel))

            self.dds_control('close')
            self.discharge_control('open', discharge_time=20)
            self.discharge_control('close')

            data_list.sort()
            data_list = data_list[1:-1]
            avg_data = sum(data_list) / len(data_list)

            path = freq + '_' + scope + '_' + vi_chan + '_' + adc_chan
            cal_offset[path] = avg_data

        return cal_offset

    def calibrate(self, range_name, data, frequency=None):
        '''
        This function is used to calibrate data.

        Args:
            range_name:     string, which range used to do calibration
            data:           float, raw data which need to be calibrated.
            frequency:      int/None, default None.

        Returns:
            float:          calibrated data.

        Examples:
            result = karma.calibrate('300mohm_Ix_ch1', 100, 1000)
            print(result)
        '''
        if frequency:

            if self._cal_common_error is not None:
                raise self._cal_common_error

            assert range_name in self._calibration_table

            items = self._calibration_table[range_name]
            if len(items) == 0:
                return data

            if range_name in self._range_err_table:
                raise self._range_err_table[range_name]

            level = 0
            for i in range(len(items)):
                if frequency <= items[i]['threshold']:
                    level = i
                    break
                if not items[i]['is_use']:
                    break
                level = i
            return items[level]['gain'] * data + items[level]['offset']
        else:
            return super(KarmaBase, self).calibrate(range_name, data)
Exemple #12
0
class Audio005Base(SGModuleDriver):
    '''
    Audio005Base is a high resolution differential input/output digital audio module.

    Args:
        i2c:    instance(I2C), the instance of I2C bus. which will be used to used
                               to control eeprom, sensor and io expander.
        ipcore: instance(MIXAudio005SGR), the instance of MIXAudio005SGR, which include
                                    AD717x, FFT Analyzer, Signal Source and gpio
                                    function. If device name string is passed
                                    to the parameter, the ipcore can be instanced
                                    in the module.

    '''
    rpc_public_api = [
        'enable_upload', 'disable_upload', 'measure', 'enable_output',
        'disable_output'
    ] + SGModuleDriver.rpc_public_api

    def __init__(self, i2c, ipcore, range_table=starlordii_range_table):

        self.eeprom = CAT24C32(Audio005Def.EEPROM_I2C_ADDR, i2c)
        self.nct75 = NCT75(Audio005Def.TEMP_I2C_ADDR, i2c)
        self.pca9536 = PCA9536(Audio005Def.PCA9536_DEV_ADDR, i2c)

        if isinstance(ipcore, basestring):
            ipcore = MIXAudio005SGR(ipcore)

        self.ipcore = ipcore
        self.analyzer = self.ipcore.analyzer
        self.signal_source = self.ipcore.signal_source
        self.adc_rst_pin = Pin(self.ipcore.gpio, Audio005Def.ADC_RESET_PIN)
        self.i2s_rx_en_pin = Pin(self.ipcore.gpio, Audio005Def.I2S_RX_EN_PIN)
        self.dac_rst_pin = Pin(self.ipcore.gpio, Audio005Def.DAC_RESET_PIN)
        self.i2s_tx_en_pin = Pin(self.ipcore.gpio, Audio005Def.I2S_TX_EN_PIN)
        self.i2s_ch_select = [
            Pin(self.ipcore.gpio, Audio005Def.I2S_CH_SELECT_2),
            Pin(self.ipcore.gpio, Audio005Def.I2S_CH_SELECT_3)
        ]

        super(Audio005Base, self).__init__(self.eeprom,
                                           self.nct75,
                                           range_table=range_table)
        self.is_enable_upload = False

    def post_power_on_init(self, timeout=Audio005Def.TIME_OUT):
        '''
        Init audio005 module to a know harware state.

        This function will reset reset dac/adc and i2s module.

        Args:
            timeout:      float, (>=0), default 1, unit Second, execute timeout.
        '''
        self.reset(timeout)

    def reset(self, timeout=Audio005Def.TIME_OUT):
        '''
        Reset the instrument module to a know hardware state.

        Args:
            timeout:      float, (>=0), default 1, unit Second, execute timeout.
        '''
        start_time = time.time()
        while True:
            try:
                self.adc_rst_pin.set_dir(Audio005Def.IO_DIR_OUTPUT)
                self.dac_rst_pin.set_dir(Audio005Def.IO_DIR_OUTPUT)
                self.i2s_rx_en_pin.set_dir(Audio005Def.IO_DIR_OUTPUT)
                self.i2s_tx_en_pin.set_dir(Audio005Def.IO_DIR_OUTPUT)

                # reset ADC
                self.adc_rst_pin.set_level(Audio005Def.PIN_LEVEL_LOW)
                time.sleep(Audio005Def.RELAY_DELAY_S)
                self.adc_rst_pin.set_level(Audio005Def.PIN_LEVEL_HIGH)

                # reset DAC
                self.dac_rst_pin.set_level(Audio005Def.PIN_LEVEL_LOW)
                time.sleep(Audio005Def.RELAY_DELAY_S)
                self.dac_rst_pin.set_level(Audio005Def.PIN_LEVEL_HIGH)

                # reset i2s rx
                self.i2s_rx_en_pin.set_level(Audio005Def.PIN_LEVEL_LOW)

                # reset i2s tx
                self.i2s_tx_en_pin.set_level(Audio005Def.PIN_LEVEL_LOW)

                # io init
                self.pca9536.set_pin_dir(Audio005Def.ADC_CH_RIGHT_CTL_BIT,
                                         Audio005Def.IO_DIR_OUTPUT)
                self.pca9536.set_pin_dir(Audio005Def.ADC_CH_LEFT_CTL_BIT,
                                         Audio005Def.IO_DIR_OUTPUT)
                self.pca9536.set_pin_dir(Audio005Def.ADCM0_CTL_BIT,
                                         Audio005Def.IO_DIR_OUTPUT)
                self.pca9536.set_pin_dir(Audio005Def.ADCM1_CTL_BIT,
                                         Audio005Def.IO_DIR_OUTPUT)

                self.pca9536.set_pin(Audio005Def.ADC_CH_RIGHT_CTL_BIT,
                                     Audio005Def.PIN_LEVEL_LOW)
                self.pca9536.set_pin(Audio005Def.ADC_CH_LEFT_CTL_BIT,
                                     Audio005Def.PIN_LEVEL_LOW)
                self.pca9536.set_pin(Audio005Def.ADCM0_CTL_BIT,
                                     Audio005Def.PIN_LEVEL_LOW)
                self.pca9536.set_pin(Audio005Def.ADCM1_CTL_BIT,
                                     Audio005Def.PIN_LEVEL_LOW)
                return
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise Audio005Exception("Timeout: {}".format(e.message))

    def pre_power_down(self, timeout=Audio005Def.TIME_OUT):
        '''
        Put the hardware in a safe state to be powered down.

        This function will set pca9536 io direction to output and set pin level to 0.

        Args:
            timeout:      float, (>=0), default 1, unit Second, execute timeout.
        '''
        start_time = time.time()
        while True:
            try:
                self.adc_rst_pin.set_level(0)
                self.i2s_rx_en_pin.set_level(0)
                self.dac_rst_pin.set_level(0)
                self.i2s_tx_en_pin.set_level(0)
                self.signal_source.close()
                return
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise Audio005Exception("Timeout: {}".format(e.message))

    def get_driver_version(self):
        '''
        Get audio005 driver version.

        Returns:
            string, current driver version.
        '''
        return __version__

    def enable_upload(self):
        '''
        Enable module data upload.

        Returns:
            string, "done", execution successful.
        '''
        self.i2s_rx_en_pin.set_level(Audio005Def.I2S_RX_ENABLE)
        self.analyzer.enable_upload()
        self.is_enable_upload = True

        return "done"

    def disable_upload(self):
        '''
        Disable module data upload.

        Returns:
            string, "done", execution successful.
        '''
        self.analyzer.disable_upload()
        self.i2s_rx_en_pin.set_level(Audio005Def.I2S_RX_DISABLE)
        self.is_enable_upload = False

        return "done"

    def measure(self,
                channel,
                scope,
                bandwidth_hz,
                harmonic_count,
                decimation_type=0xFF,
                sampling_rate=Audio005Def.DEF_SAMPLING_RATE):
        '''
        Measure audio input signal, which captures data using CS5361.

        Args:
            channel:         string, ['left', 'right'], select input signal channel.
            scope:           string, ['2V', '20mV'], AD7175 measurement range.
            bandwidth_hz:    int/string, [42~48000], unit Hz, the signal bandwidth.
                             In theory the bandwidth must smaller than half the sampling rate.
                             eg, if sampling_rate = 192000, so bandwidth_hz  < 96000.
                             The bandwidth must be greater than the frequency of the input signal.
            harmonic_count:  int, [2~10], The harmonic count of signal.
                             The harmonic frequency is the frequency of the input signal times the counts of harmonics.
                             eg, The input is 20K, the first harmonic is 20K, the second harmonic is 40K,
                                 the third harmonic is 60K, the fourth harmonic is 80K, the fourth harmonic is 100K.
                             The harmonic frequency must be smaller than half the sampling rate.
                             eg, if sampling_rate = 192000, input_signal_frequency = 20000, harmonic_count = 4
                                 so harmonic_frequency = 80000 < 96000.
                             The signal bandwidth  must be greater than harmonic frequency.
            decimation_type: int, [1~255], default 0xFF, sample data decimation.
                             decimation_type is 1 means not to decimate.
                             The smaller the input frequency, the larger the value should be.
            sampling_rate:   int, [0~192000], default 48000, unit Hz, ADC sampling rate.

        Returns:
            dict, {'vpp': value, 'freq': value, 'thd': value, 'thdn': value, 'rms': value, 'noisefloor': value},
            measurement result.
        '''
        assert channel in Audio005Def.AUDIO_CHANNEL_LIST
        assert scope in Audio005Def.LNA_SCOPE

        if self.is_enable_upload is False:
            self.i2s_rx_en_pin.set_level(Audio005Def.I2S_RX_ENABLE)

        pin = self.i2s_ch_select[Audio005Def.SELECT_BIT0]
        pin.set_level(
            Audio005Def.AUDIO_CHANNEL_LIST[channel][Audio005Def.SELECT_BIT0])
        pin = self.i2s_ch_select[Audio005Def.SELECT_BIT1]
        pin.set_level(
            Audio005Def.AUDIO_CHANNEL_LIST[channel][Audio005Def.SELECT_BIT1])

        if scope == Audio005Def.LNA_RANGE_2V:
            self.pca9536.set_pin(Audio005Def.ADC_CH_LIST[channel],
                                 Audio005Def.SEL_GAIN_1)
            gain = Audio005Def.AUDIO_ANALYZER_2V_VREF / Audio005Def.AUDIO_HARDWARE_ATTENUATION
        else:
            self.pca9536.set_pin(Audio005Def.ADC_CH_LIST[channel],
                                 Audio005Def.SEL_GAIN_100)
            gain = Audio005Def.AUDIO_ANALYZER_20mV_VREF / Audio005Def.AUDIO_HARDWARE_ATTENUATION

        index = bisect.bisect(Audio005Def.SAMPLING_RANGE, sampling_rate)

        self.pca9536.set_pin(
            Audio005Def.ADCM0_CTL_BIT,
            Audio005Def.ADCM_CTL_LIST[index][Audio005Def.SELECT_BIT0])
        self.pca9536.set_pin(
            Audio005Def.ADCM1_CTL_BIT,
            Audio005Def.ADCM_CTL_LIST[index][Audio005Def.SELECT_BIT1])

        self.analyzer.disable()
        self.analyzer.enable()
        self.analyzer.analyze_config(sampling_rate, decimation_type,
                                     bandwidth_hz, harmonic_count)
        self.analyzer.analyze()

        freq = self.analyzer.get_frequency()
        vpp = self.analyzer.get_vpp() * gain
        rms = vpp / Audio005Def.RMS_TO_VPP_RATIO

        if scope == Audio005Def.LNA_RANGE_2V:
            index = bisect.bisect(Audio005Def.CAL_RANGE, freq)
            range_name = "AUDIO_2V_" + str(
                Audio005Def.CAL_RANGE[index]) + "Hz_" + channel
        else:
            range_name = "AUDIO_20mV_" + channel

        rms = self.calibrate(range_name, rms)
        vpp = rms * Audio005Def.RMS_TO_VPP_RATIO
        thdn_value = self.analyzer.get_thdn()

        result = dict()
        result["vpp"] = (vpp, Audio005Def.VOLT_UNIT_MV)
        result["freq"] = (freq, Audio005Def.FREQ_UNIT_HZ)
        result["thd"] = (self.analyzer.get_thd(), Audio005Def.THD_UNIT_DB)
        result["thdn"] = (thdn_value, Audio005Def.THDN_UNIT_DB)
        result["rms"] = (rms, Audio005Def.VOLT_UNIT_RMS)
        result["noisefloor"] = (10**(thdn_value / 20) * rms,
                                Audio005Def.VOLT_UNIT_RMS)

        if self.is_enable_upload is False:
            self.i2s_rx_en_pin.set_level(Audio005Def.I2S_RX_DISABLE)

        return result

    def enable_output(self, freq, vpp):
        '''
        Audio005 CS5361 output audio sine waveform.

        Args:
            freq:       int, [5~50000], unit Hz, output signal's frequency.
            vpp:        float, [0~6504], unit mV, output signal's vpp.

        Returns:
            string, "done", execution successful.
        '''
        assert Audio005Def.OUTPUT_FREQ_MIN <= freq
        assert freq <= Audio005Def.OUTPUT_FREQ_MAX
        assert Audio005Def.OUTPUT_VPP_MIN <= vpp
        assert vpp <= Audio005Def.OUTPUT_VPP_MAX

        vpp = self.calibrate(Audio005Def.OUTPUT_CAL_ITEM, vpp)
        vpp = 0 if vpp < 0 else vpp
        # enable I2S tx module
        self.i2s_tx_en_pin.set_level(Audio005Def.AUDIO_OUTPUT_ENABLE)

        self.signal_source.close()
        self.signal_source.open()
        # calculate vpp to vpp scale for FPGA
        vpp_scale = vpp * Audio005Def.VPP_2_SCALE_RATIO
        self.signal_source.set_swg_paramter(Audio005Def.AUDIO_SAMPLING_RATE,
                                            freq, vpp_scale,
                                            Audio005Def.OUTPUT_SIGNAL_DUTY)
        self.signal_source.set_signal_type(Audio005Def.OUTPUT_WAVE)
        self.signal_source.set_signal_time(Audio005Def.SIGNAL_ALWAYS_OUTPUT)
        self.signal_source.output_signal()

        return "done"

    def disable_output(self):
        '''
        Disable Cs5361 output signal.

        Returns:
            string, "done", execution successful.
        '''
        self.signal_source.close()
        self.i2s_tx_en_pin.set_level(Audio005Def.AUDIO_OUTPUT_DISABLE)

        return "done"