class BladeBase(SGModuleDriver):
    '''
    Base class of Blade and BladeCompatible.
    Providing common iceman methods.

    Args:
        i2c:              instance(I2C), If not given, PLI2CBus emulator will be created.
        adc:              instance(ADC)/string, If not given, AD760X emulator will be created.
        xadc:             instance(XADC)/string, if not given, MIXXADCSG emulator will be created.
        adc_signal_meter: instance(MIXSignalMeterSG)/string, if not given, MIXSignalMeterSG emulator will be created.
        ac_signal_meter:  instance(MIXSignalMeterSG)/string, if not given, MIXSignalMeterSG emulator will be created.
        fsk_signal_meter: instance(MIXSignalMeterSG)/string, if not given, MIXSignalMeterSG emulator will be created.
        ask_code:         instance(MIXQIASKLinkEncodeSG)/string,
                          if not given, MIXQIASKLinkEncodeSG emulator will be created.
        fsk_decode:       instance(MIXQIFSKLinkDecodeSG)/string,
                          if not given,MIXQIFSKLinkDecodeSG emulator will be created.
        adc_ctrl_pin_0:   instance(GPIO), determine AD7608's data stream whether into AXI4_Signal_Meter_1 or DMA.
        adc_ctrl_pin_1:   instance(GPIO), determine AD7608's data stream whether into AXI4_Signal_Meter_1 or DMA.
        fsk_ctrl_pin:     instance(GPIO), fsk enable/disable control pin.
        fsk_cic_ctrl_pin: instance(GPIO), fsk enable/disable cic_filter control pin.
        adc_filter_pin_0: instance(GPIO), determine which AD7608's channel to select.
        adc_filter_pin_1: instance(GPIO), determine which AD7608's channel to select.
        adc_filter_pin_2: instance(GPIO), determine which AD7608's channel to select.
        eeprom_devaddr:   int,            Eeprom device address.

    '''

    rpc_public_api = [
        'adc_voltage_measure', 'dac_output', 'ac_signal_measure',
        'ask_write_encode_data', 'fsk_frequency_measure', 'fsk_decode_state',
        'fsk_read_decode_data', 'adc_measure_upload_enable', 'adc_fft_measure',
        'adc_measure_upload_disable'
    ] + SGModuleDriver.rpc_public_api

    def __init__(self, i2c, adc, xadc, adc_signal_meter, ac_signal_meter,
                 fsk_signal_meter, ask_code, fsk_decode, adc_ctrl_pin_0,
                 adc_ctrl_pin_1, fsk_ctrl_pin, fsk_cic_ctrl_pin,
                 adc_filter_pin_0, adc_filter_pin_1, adc_filter_pin_2, ipcore,
                 eeprom_devaddr):

        self.dac1 = AD5667(BladeDef.DAC1_DEV_ADDR, i2c,
                           BladeDef.DAC1_VOLTAGE_REF)
        self.dac2 = AD5667(BladeDef.DAC2_DEV_ADDR, i2c,
                           BladeDef.DAC2_VOLTAGE_REF)
        self.dac3 = AD5667(BladeDef.DAC3_DEV_ADDR, i2c,
                           BladeDef.DAC3_VOLTAGE_REF)
        self.eeprom = CAT24C32(eeprom_devaddr, i2c)
        super(BladeBase, self).__init__(self.eeprom,
                                        None,
                                        range_table=blade_range_table)

        self.ipcore = ipcore
        if ipcore:
            if isinstance(ipcore, basestring):
                self.ipcore = MIXScope007SGR(ipcore)
            ac_signal_meter = self.ipcore.ac_signal_meter
            adc_signal_meter = self.ipcore.adc_signal_meter
            adc_filter_pin_0 = self.ipcore.adc_filter_pin_0
            adc_filter_pin_1 = self.ipcore.adc_filter_pin_1
            adc_filter_pin_2 = self.ipcore.adc_filter_pin_2
            adc_ctrl_pin_0 = self.ipcore.adc_ctrl_pin_0
            adc_ctrl_pin_1 = self.ipcore.adc_ctrl_pin_1
            fsk_ctrl_pin = self.ipcore.fsk_ctrl_pin
            fsk_cic_ctrl_pin = self.ipcore.fsk_cic_ctrl_pin
            self.adc_fft_pin_0 = self.ipcore.adc_fft_pin_0
            self.adc_fft_pin_1 = self.ipcore.adc_fft_pin_1
            self.adc_fft_pin_2 = self.ipcore.adc_fft_pin_2
            self.freq_gear_pin_0 = self.ipcore.freq_gear_pin_0
            self.freq_gear_pin_1 = self.ipcore.freq_gear_pin_1
            adc = self.ipcore.adc
            fsk_decode = self.ipcore.fsk_decode
            ask_code = self.ipcore.ask_code
            self.fftanalyzer = self.ipcore.fftanalyzer

        if isinstance(adc, basestring):
            axi4_bus = AXI4LiteBus(adc, BladeDef.PL_AD7608_REG_SIZE)
            self.adc = MIXAd7608SG(axi4_bus)
        elif adc:
            self.adc = adc
        else:
            raise BladeException(
                "parameter 'adc' and 'ipcore' Can't be empty at the same time")

        if isinstance(xadc, basestring):
            axi4_bus = AXI4LiteBus(xadc, BladeDef.PL_XADC_REG_SIZE)
            self.xadc = MIXXADCSG(axi4_bus)
        elif xadc:
            self.xadc = xadc
        else:
            raise BladeException("parameter 'xadc' can not be None")

        if isinstance(adc_signal_meter, basestring):
            axi4_bus = AXI4LiteBus(adc_signal_meter,
                                   BladeDef.SIGNAL_METER_REG_SIZE)
            self.adc_signal_meter = MIXSignalMeterSG(axi4_bus)
        elif adc_signal_meter:
            self.adc_signal_meter = adc_signal_meter
        else:
            raise BladeException(
                "parameter 'adc_signal_meter' and 'ipcore' Can't be empty at the same time"
            )

        if isinstance(ac_signal_meter, basestring):
            axi4_bus = AXI4LiteBus(ac_signal_meter,
                                   BladeDef.SIGNAL_METER_REG_SIZE)
            self.ac_signal_meter = MIXSignalMeterSG(axi4_bus)
        elif ac_signal_meter:
            self.ac_signal_meter = ac_signal_meter
        else:
            raise BladeException(
                "parameter 'ac_signal_meter' and 'ipcore' Can't be empty at the same time"
            )

        if isinstance(fsk_signal_meter, basestring):
            axi4_bus = AXI4LiteBus(fsk_signal_meter,
                                   BladeDef.SIGNAL_METER_REG_SIZE)
            self.fsk_signal_meter = MIXSignalMeterSG(axi4_bus)
        else:
            self.fsk_signal_meter = fsk_signal_meter

        if isinstance(ask_code, basestring):
            axi4_bus = AXI4LiteBus(ask_code,
                                   BladeDef.MIX_QI_ASKLINK_CODE_REG_SIZE)
            self.ask_code = MIXQIASKLinkEncodeSG(axi4_bus)
        elif ask_code:
            self.ask_code = ask_code
        else:
            raise BladeException(
                "parameter 'ask_code' and 'ipcore' Can't be empty at the same time"
            )

        if isinstance(fsk_decode, basestring):
            axi4_bus = AXI4LiteBus(fsk_decode,
                                   BladeDef.MIX_QI_FSKLINK_DECODE_REG_SIZE)
            self.fsk_decode = MIXQIFSKLinkDecodeSG(axi4_bus)
        elif fsk_decode:
            self.fsk_decode = fsk_decode
        else:
            raise BladeException(
                "parameter 'fsk_decode' and 'ipcore' Can't be empty at the same time"
            )

        # These Pins supported by FPGA
        self.adc_ctrl_pin_0 = adc_ctrl_pin_0
        self.adc_ctrl_pin_1 = adc_ctrl_pin_1
        self.fsk_ctrl_pin = fsk_ctrl_pin
        self.fsk_cic_ctrl_pin = fsk_cic_ctrl_pin
        self.adc_filter_pin_0 = adc_filter_pin_0
        self.adc_filter_pin_1 = adc_filter_pin_1
        self.adc_filter_pin_2 = adc_filter_pin_2

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

        Configure GPIO pin default direction.
        Configure XADC sampling rate and polar.

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

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

        Args:
            timeout:      float, default 1, unit Second, execute timeout.
        '''
        start_time = time.time()
        while True:
            try:
                self.adc_ctrl_pin_0.set_dir('output')
                self.adc_ctrl_pin_1.set_dir('output')
                self.fsk_ctrl_pin.set_dir('output')
                self.fsk_cic_ctrl_pin.set_dir('output')
                self.adc_filter_pin_0.set_dir('output')
                self.adc_filter_pin_1.set_dir('output')
                self.adc_filter_pin_2.set_dir('output')
                if (self.ipcore is not None):
                    self.adc_fft_pin_0.set_dir('output')
                    self.adc_fft_pin_1.set_dir('output')
                    self.adc_fft_pin_2.set_dir('output')

                if isinstance(self.xadc, MIXXADCSG):
                    self.xadc.config(BladeDef.XADC_SAMPLING_RATE,
                                     BladeDef.XADC_BIPOLAR)
                return
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise BladeException("Timeout: {}".format(e.message))

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

        This function will set pin level to 0 and close signal source.

        Args:
            timeout:      float, default 1, unit Second, execute timeout.
        '''
        start_time = time.time()
        while True:
            try:
                self.dac_output('ch0', 0)
                self.dac_output('ch1', 0)
                self.dac_output('ch2', 0)
                self.dac_output('ch3', 0)
                self.dac_output('ch4', 0)
                self.dac_output('ch5', 0)
                return
            except Exception as e:
                if time.time() - start_time > timeout:
                    raise BladeException("Timeout: {}".format(e.message))

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

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

    def _check_adc_channel(self, adc_channel):
        '''
        Check the channel if it is valid.

        Args:
            adc_channel:    string, ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7'], the channel to check.

        Returns:
            string, ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7'], the channel in specific format.

        Raise:
            BladeException:  If adc_channel is invalid, exception will be raised.

        '''
        for ch in BladeDef.ADC_FILTER_CONFIG:
            if adc_channel.lower() == ch.lower():
                return ch
        raise BladeException("adc_channel {} is invalid".format(adc_channel))

    def _check_dac_channel(self, dac_channel):
        '''
        Check the channel if it is valid.

        Args:
            dac_channel:    string, ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5'], the channel to check.

        Returns:
            string,    ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5'], the channel in specific format.

        Raise:
            BladeException:  If dac_channel is invalid, exception will be raised.

        '''
        for ch in blade_dac_channel_info:
            if dac_channel.lower() == ch.lower():
                return ch
        raise BladeException("dac_channel {} is invalid".format(dac_channel))

    def _check_adc_range(self, adc_range):
        '''
        Check the range if it is valid.

        Args:
            adc_range:    string, ['5V', '10V'], the range to check.

        Returns:
            string, ['5V', '10V'], the range in specific format.

        Raise:
            BladeException:  If adc_range is invalid, exception will be raised.

        '''
        for ch in BladeDef.ADC_RANGES:
            if adc_range.upper() == ch.upper():
                return ch
        raise BladeException("adc_range {} is invalid".format(adc_range))

    def _check_adc_option(self, option):
        '''
        Check the option if it is valid.

        Args:
            option:    string, ['AVG', 'RMS'], the option to check.

        Returns:
            string, ['AVG', 'RMS'], the option in specific format.

        Raise:
            BladeException:  If option is invalid, exception will be raised.

        '''
        for ch in BladeDef.ADC_OPTIONS:
            if option.upper() == ch.upper():
                return ch
        raise BladeException("ADC option {} is invalid".format(option))

    def _select_adc_channel(self, adc_channel):
        '''
        Blade select specific analog input channel.

        Note AD7608 simultaneous sampling on all analog input channels,
        filter specific channel data with PLGPIOs, supported by FPGA.

        Args:
            adc_channel:    string, ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7'], the analog input channel.

        '''
        adc_channel = self._check_adc_channel(adc_channel)

        bit = BladeDef.ADC_FILTER_CONFIG[adc_channel]
        self.adc_filter_pin_0.set_level(bit[0])
        self.adc_filter_pin_1.set_level(bit[1])
        self.adc_filter_pin_2.set_level(bit[2])

    def _select_adc_fft_channel(self, adc_channel):
        '''
        Blade select specific analog input channel.

        Note AD7608 simultaneous sampling on all analog input channels,
        filter specific channel data with PLGPIOs, supported by FPGA.

        Args:
            adc_channel:    string, ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7'], the analog input channel.

        '''
        adc_channel = self._check_adc_channel(adc_channel)

        bit = BladeDef.ADC_FILTER_CONFIG[adc_channel]
        if (self.ipcore is not None):
            self.adc_fft_pin_0.set_level(bit[0])
            self.adc_fft_pin_1.set_level(bit[1])
            self.adc_fft_pin_2.set_level(bit[2])

    def adc_voltage_measure(self, option, adc_channel, adc_range,
                            sampling_rate, count, time_ms):
        '''
        Blade Analog input measurement, measure DC voltage with option `AVG` or `RMS`.

        Args:
            option:          string, ['AVG', 'RMS'], 'AVG' means average, 'RMS' means Root Mean Square.
            adc_channel:     string, ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7'], ADC channel.
            adc_range:       string, ['10V', '5V'], measure range.
            sampling_rate:   int, [2000~200000], sampling rate.
            count:           int, [1~512], sample count.
            time_ms:         int, [1~2000], unit ms.

        Returns:
            list, [value, "mV"], voltage data list.

        '''
        option = self._check_adc_option(option)
        adc_channel = self._check_adc_channel(adc_channel)
        adc_range = self._check_adc_range(adc_range)
        assert isinstance(sampling_rate,
                          int) and (2000 <= sampling_rate <= 200000)
        assert isinstance(count, int) and (count > 0)
        assert isinstance(time_ms, int) and (1 <= time_ms <= 2000)

        # Switch to ADC conversion, supported by FPGA.
        self.adc_ctrl_pin_0.set_level(0)
        self.adc_ctrl_pin_1.set_level(0)

        # Use Ip to control signal meter.
        if (self.ipcore is not None):
            measure_mask = 0x30
        else:
            measure_mask = 0x00

        # Select specific AD7608 channel, supported by FPGA.
        self._select_adc_channel(adc_channel)

        # Enable AD7608 continuous sampling, without over sampling.
        self.adc.enable_continuous_sampling(BladeDef.ADC_OVER_SAMPLING,
                                            adc_range, sampling_rate)

        # Use Signal Meter to measure
        self.adc_signal_meter.close()
        self.adc_signal_meter.open()

        voltage = list()
        for i in range(count):
            adc_value = 0
            self.adc_signal_meter.start_measure(time_ms, sampling_rate,
                                                measure_mask)
            rms_value = self.adc_signal_meter.rms()

            if adc_range == '5V':
                adc_value = rms_value[1] * BladeDef.ADC_5V_CALC_RATIO
            elif adc_range == '10V':
                adc_value = rms_value[1] * BladeDef.ADC_10V_CALC_RATIO
            adc_value = self.calibrate(
                'ADC_' + adc_channel.upper() + "_" + adc_range, adc_value)

            voltage.append(adc_value)

        # Close Signal Meter, Disable AD7608 continuous sampling.
        self.adc_signal_meter.close()
        self.adc.disable_continuous_sampling()

        result = list()
        if option == 'RMS':
            # The sign of rms value
            sign = -1 if voltage[0] < 0 else 1
            rms = math.sqrt(sum(x**2 for x in voltage) / count)
            result = [sign * rms, 'mV']
        elif option == 'AVG':
            avg = sum(voltage) / count
            result = [avg, 'mV']

        return result

    def dac_output(self, dac_channel, voltage):
        '''
        Blade adc output voltage, 0-5500mV.

        Note 'ch0'~'ch3' for analog output circuit driver,
        'ch4'~'ch5' for fsk and frequency measurement circuit driver.

        Args:
            dac_channel:    string, ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5'].
            voltage:        int, [0~5500], unit mV.

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

        Raise:
            BladeException:  If dac_channel_id is invalid, exception will be raised.

        '''

        dac_channel = self._check_dac_channel(dac_channel)
        assert 0 <= voltage <= BladeDef.DAC1_VOLTAGE_REF

        cal_item = 'DAC_' + dac_channel.upper()
        if cal_item in blade_range_table:
            voltage = self.calibrate(cal_item, voltage)

        channel_id = blade_dac_channel_info[dac_channel]['id']
        if channel_id // 2 == 0:
            self.dac1.output_volt_dc(channel_id % 2, voltage)
        elif channel_id // 2 == 1:
            self.dac2.output_volt_dc(channel_id % 2, voltage)
        elif channel_id // 2 == 2:
            self.dac3.output_volt_dc(channel_id % 2, voltage)
        else:
            raise BladeException(
                "dac_channel_id {} is invalid".format(channel_id))

        return 'done'

    def _read_vpp_value(self):
        '''
        Blade XADC read vpp voltage value, anolog input connect to pin VP_0 and VN_0.

        XADC read 1000 voltage samples and sort them;
        Average the maximum 150 voltage samples as volt_max;
        Average the minimum 150 voltage samples as volt_min;
        Vpp = volt_max – volt_min

        Returns:
            int, value, unit mV.

        '''

        voltage = list()

        if isinstance(self.xadc, MIXXADCSG):
            # XADC read 1000 voltage samples.
            for i in range(BladeDef.XADC_SAMPLE_COUNT):
                rd_data = self.xadc.read_volt()
                voltage.append(rd_data)

        if isinstance(self.xadc, _IIOXADC):
            for i in range(BladeDef.XADC_SAMPLE_COUNT):
                rd_data = self.xadc.read_voltage('vpvn')
                voltage.append(rd_data)

        # Sort them, pick up the maximum or minimum samples.
        voltage.sort()

        # Remove abnormal voltage samples
        for i in range(BladeDef.XADC_SAMPLE_COUNT):
            if max(voltage) > BladeDef.XADC_MAX_VOLTAGE:
                voltage.remove(max(voltage))
            else:
                break

        # Calculate volt_max and volt_min
        volt_max = sum(voltage[-BladeDef.XADC_AVERAGE_COUNT:]
                       ) / BladeDef.XADC_AVERAGE_COUNT
        volt_min = sum(voltage[:BladeDef.XADC_AVERAGE_COUNT]
                       ) / BladeDef.XADC_AVERAGE_COUNT
        # Calculate vpp
        vpp = volt_max - volt_min

        return vpp

    def ac_signal_measure(self, option, ref_volt, time_ms, gear=None):
        '''
        Blade AC signal measurement, Amplitude,Frequency and Duty Cycle Measurement.

        Args:
            option:        string, ['f', 'd', 'v', 'w']. 'f', freq, unit is Hz, 'd', duty, unit is '%%'.
                                    'v', vpp, unit is 'mV'. 'w', width, unit is 'us',
                                    You can combine them any way you want.
            ref_volt:      int, [0~5000], unit mV, control voltage comparator
                                                   ref_volt should be 1/4 Amplitude input.
            time_ms:       int, [1~2000], unit ms.
            gear:          string, ['high', 'mid', 'low'] 'high':<1MHz;'mid":1MHz~7.5MHz;'low':>7.5MHz.

        Returns:
            dict, {"freq": (value, 'Hz'), "duty": (value, '%'), "width": (value, 'us'), "vpp": (value, 'mV')}.

        '''
        assert isinstance(time_ms, int) and (1 <= time_ms <= 2000)
        assert isinstance(ref_volt, int)
        assert (0 <= ref_volt <= BladeDef.DAC1_VOLTAGE_REF)

        # Use Ip to control signal measure.
        if (self.ipcore is not None):
            measure_mask = 0xC0
        else:
            measure_mask = 0x00

        if (gear is not None):
            assert isinstance(gear, basestring)
            if gear == 'high':
                self.freq_gear_pin_0.set_level(1)
                self.freq_gear_pin_1.set_level(0)
            elif gear == 'mid':
                self.freq_gear_pin_0.set_level(0)
                self.freq_gear_pin_1.set_level(1)
            elif gear == 'low':
                self.freq_gear_pin_0.set_level(0)
                self.freq_gear_pin_1.set_level(0)
            else:
                raise BladeException('Please check the parameters!')

        # dac_ch5 output voltage, hardware devider 1/2.
        # ref_volt should be 1/4 Amplitude input.
        self.dac_output('ch5', ref_volt)

        self.ac_signal_meter.open()
        self.ac_signal_meter.start_measure(time_ms,
                                           BladeDef.SIGNAL_METER_SAMPLE_RATE,
                                           measure_mask)
        freq = self.ac_signal_meter.measure_frequency('LP')
        duty = self.ac_signal_meter.duty()
        # Calculate Width Unit is 'us'
        width = 1000000.0 / freq * duty / 100
        self.ac_signal_meter.close()

        result = dict()
        if 'f' in option:
            result['freq'] = (freq, 'Hz')
        if 'd' in option:
            result['duty'] = (duty, '%')
        if 'w' in option:
            result['width'] = (width, 'us')
        if 'v' in option:
            vpp = self._read_vpp_value() * BladeDef.CALC_VPP_RATIO
            result['vpp'] = (vpp, 'mV')

        return result

    def adc_fft_measure(self,
                        adc_channel,
                        adc_range,
                        decimation_type,
                        sampling_rate,
                        bandwidth,
                        harmonic_count=5):
        '''
         adc fft measure, measure frequency ,thd, thdn, vpp

         Args:
              adc_channel:       string,     ['ch0', 'ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7']
              adc_range:         string,     ('10V', '5V')
              decimation_type:   int,        [1~255]
              sampling_rate:     int,        [2000- 200000]
              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.
         Returns:
              dict, {"freq": (value, 'Hz'),  "thd":(vslue,'dB'),  "vpp": (value, 'V'),  "thdn":(value,'dB')}.
        '''
        assert isinstance(sampling_rate,
                          int) and (2000 <= sampling_rate <= 200000)
        adc_channel = self._check_adc_channel(adc_channel)
        self.adc_ctrl_pin_0.set_level(0)
        self.adc_ctrl_pin_1.set_level(1)
        self._select_adc_fft_channel(adc_channel)
        self.adc.enable_continuous_sampling(BladeDef.ADC_OVER_SAMPLING,
                                            adc_range, sampling_rate)
        self.fftanalyzer.disable()
        self.fftanalyzer.enable()
        self.fftanalyzer.disable_upload()
        self.fftanalyzer.analyze_config(sampling_rate, decimation_type,
                                        bandwidth, harmonic_count)
        self.fftanalyzer.analyze()
        result = dict()
        result['vpp'] = (self.fftanalyzer.get_vpp() *
                         (20 if (adc_range == '10V') else 10), 'V')
        result['freq'] = (self.fftanalyzer.get_frequency(), 'Hz')
        result['thdn'] = (self.fftanalyzer.get_thdn(), 'dB')
        result['thd'] = (self.fftanalyzer.get_thd(), 'dB')

        return result

    def ask_write_encode_data(self, data, freq=BladeDef.DEFAULT_ASK_FREQ):
        '''
        Blade Write Ask encoded signal data.

        Args:
            data:  list, Data to write, the list item is byte.
            freq:  int, [100~100000], unit Hz,  Ask square wave frequency.

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

        '''
        assert isinstance(
            freq,
            int) and BladeDef.MIN_ASK_FREQ <= freq <= BladeDef.MAX_ASK_FREQ
        assert isinstance(data, list) and len(data) > 0

        self.ask_code.set_frequency(freq)
        self.ask_code.write_encode_data(data)
        return 'done'

    def fsk_frequency_measure(self, time_ms):
        '''
        Blade measure FSK signal frequency.

        Args:
            time_ms:    int, [1~2000], unit ms, signal meter measure time.

        Returns:
            int, value, unit Hz.

        '''
        assert isinstance(time_ms, int) and (1 <= time_ms <= 2000)

        if not self.fsk_signal_meter:
            raise BladeException("parameter 'fsk_signal_meter' is None")
        self.fsk_signal_meter.open()
        self.fsk_signal_meter.start_measure(time_ms,
                                            BladeDef.SIGNAL_METER_SAMPLE_RATE)
        freq = self.fsk_signal_meter.measure_frequency('LP')
        self.fsk_signal_meter.close()

        return freq

    def fsk_decode_state(self):
        '''
        Blade Get Fsk Decode state.

        Returns:
            boolean, [True, False], True: Fsk decode Parity bit is ok,
                                    False: Fsk decode Parity bit is error

        '''
        # FSK Decode enable
        self.fsk_ctrl_pin.set_level(0)

        return self.fsk_decode.state()

    def fsk_read_decode_data(self, cic_filter=True):
        '''
        Blade Read FSK signal data from fifo.

        Args:
            cic_filter:  boolean, [True, False], True: enable CIC_Filter
                                                 False:disable CIC_Filter

        Returns:
            list, [value], the returned data list item is byte.

        '''
        # FSK Decode enable
        self.fsk_ctrl_pin.set_level(0)

        # CIC filter enable/disable
        if cic_filter:
            self.fsk_cic_ctrl_pin.set_level(0)
        else:
            self.fsk_cic_ctrl_pin.set_level(1)

        return self.fsk_decode.read_decode_data()

    def adc_measure_upload_enable(self, adc_range, sampling_rate):
        '''
        Blade adc measure data upoad mode open. Control ad7608 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,
        formate: bit[0-17] adc_value; bit[29-31] channel number; bit[18-28] unused.

        Args:
            adc_range:       string, ['10V', '5V'], measure range.
            sampling_rate:   int, [2000~200000], sampling rate.

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

        '''
        adc_range = self._check_adc_range(adc_range)
        assert isinstance(sampling_rate,
                          int) and (2000 <= sampling_rate <= 200000)

        # Switch to data upoad mode, supported by FPGA.
        self.adc_ctrl_pin_0.set_level(1)
        self.adc_ctrl_pin_1.set_level(0)

        self.adc.enable_continuous_sampling(BladeDef.ADC_OVER_SAMPLING,
                                            adc_range, sampling_rate)
        return 'done'

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

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

        '''
        self.adc.disable_continuous_sampling()
        return 'done'
Beispiel #2
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"