예제 #1
0
    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)))
예제 #2
0
    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
예제 #3
0
    def start_buffered_acquisition(self, channels, down_sampling_factor=1, down_sampling_calculation='max'):
        '''
        This function enables continuous sampling and data throughput upload to upper stream.

        Down sampling is supported. For example, when down_sampling_factor =5, down_sampling_calculation=max,
            select the maximal value from every 5 samples, so the actual data rate is reduced by 5.
        During continuous sampling, the functions, like configure_input_channel() read(), cannot be called.

        Args:
            channels: int, [1, 2], 1 mean the channel for 'voltage', 2 mean the channel for 'current'.
            down_sampling_factor: int, (>0), default 1, down sample rate for decimation.
            down_sampling_calculation: string, ['max', 'min'], default 'max'. This parameter takes effect
                                               as long as down_sampling_factor is higher than 1. Default 'max'.

        Exception:
            InvalidHardwareChannel() if provided channels is not 1 or 2.
            InvalidRange() if provided down_sampling_factor is not an integer or is <1.
            InvalidRange() if provided down_sampling_calculation is not 'max' or 'min'.

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

        Examples:
            odin.configure_input_channel(1, 'battery', 1000)
            odin.start_buffered_acquisition(1)
            result = odin.read_buffer_statistics(1, 10)
            print(result)
            odin.stop_buffered_acquisition(1)
        '''
        if channels not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if not isinstance(down_sampling_factor, int) or down_sampling_factor < 1:
            raise InvalidRange("Invalid range")

        if down_sampling_calculation not in ['max', 'min']:
            raise InvalidRange("Invalid range")

        ch_input_config_dict = {1: self.ch1_input_config, 2: self.ch2_input_config}
        ch_input_config = ch_input_config_dict[channels]
        pin_id, adc_channel = (OdinDef.READ_CURR_BIT, OdinDef.CHANNEL_1) if ch_input_config['channel'] == 'current' \
            else (OdinDef.READ_VOLT_BIT, OdinDef.CHANNEL_0)
        pin_level = OdinDef.HIGH_LEVEL if ch_input_config['type'] == 'battery' else OdinDef.LOW_LEVEL
        sampling_rate = ch_input_config['sample_rate']
        self.tca9538.set_pin(pin_id, pin_level)
        self.ad7175.disable_continuous_sampling(adc_channel)
        time.sleep(0.001)
        self.ad7175.enable_continuous_sampling(adc_channel, sampling_rate,
                                               down_sampling_factor, down_sampling_calculation)

        self.active_channel = channels
        self.continuous_sample_mode = True

        return "done"
예제 #4
0
    def stop_buffered_acquisition(self, session_id):
        '''
        This function disables continuous sampling and data throughput upload to upper stream.

        This function can only be called in continuous mode, a.k.a, after start_buffered_acquisition()
            function is called. Return 0 for the channels that are not enabled.

        Args:
            session_id: int, [1, 2], 1 mean the channel for 'voltage', 2 mean the channel for 'current'.

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

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

        Examples:
            odin.configure_input_channel(1, 'battery', 1000)
            odin.start_buffered_acquisition(1)
            result = odin.read_buffer_statistics(1, 10)
            print(result)
            odin.stop_buffered_acquisition(1)
        '''
        if session_id not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if not self.active_channel:
            return 0
        channel = self.active_channel
        ch_input_config_dict = {
            1: self.ch1_input_config,
            2: self.ch2_input_config
        }
        ch_input_config = ch_input_config_dict[channel]
        if ch_input_config['channel'] == 'current':
            pin_id, adc_channel = (PSU001014Def.READ_CURR_BIT,
                                   PSU001014Def.CHANNEL_1)
        else:
            pin_id, adc_channel = (PSU001014Def.READ_VOLT_BIT,
                                   PSU001014Def.CHANNEL_0)
        # set pin to default level.
        self.tca9538.set_pin(pin_id, PSU001014Def.HIGH_LEVEL)
        self.ad7175.disable_continuous_sampling(adc_channel)
        self.active_channel = None
        self.continuous_sample_mode = False

        return "done"
예제 #5
0
    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
예제 #6
0
    def get_output_channel_configuration(self, channel):
        '''
        Get the configuration of the specified output channel.

        Args:
            channel: int, [1, 2], 1 mean the channel for 'battery', 2 mean the channel for 'charge'.

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

        Returns:
            dict, actual channel configuration. e.g. {"channel": "charge", "voltage": 4000, "current_limit": 1000}.

        Examples:
            config = odin.get_output_channel_configuration(1)
            print(config)
        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        output_config = {1: self.ch1_output_config, 2: self.ch2_output_config}
        return output_config[channel]
예제 #7
0
    def get_input_channel_configuration(self, channel):
        '''
        Get the configuration of the input channel.

        Args:
            channel: int, [1, 2], 1 mean the channel for 'voltage', 2 mean the channel for 'current'.

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

        Returns:
            dict, actual channel configuration.
            e.g. {"channel": "current", "type": "battery", "sample_rate": 1000, "max_range": 1000}.

        Examples:
            config = odin.get_input_channel_configuration(2)
            print(config)
        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        input_config = {1: self.ch1_input_config, 2: self.ch2_input_config}
        return input_config[channel]
예제 #8
0
    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
예제 #9
0
    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
예제 #10
0
    def read_buffer_statistics(self, session_id, samples_per_channel=1, timeout=10.0):
        '''
        This function takes a number of samples to calculate RMS/average/max/min value of the set of sampled value.

        Values read are in mV or mA, which is determined by DRI.

        This function can only be called in continuous mode, a.k.a, after configure_input_channel(),
            start_buffered_acquisition() function is called. Return 0 for the channels that are not enabled.
        The returned value is calibrated if calibration mode is `cal`. The data read back from the voltage channel
            is calibrated in mV. The data read back in the current channel is calibrated in uA when max_range is 1,
            and it is calibrated in mA when max_range is 1000.
        During continuous sampling, the functions, like configure_input_channel() read(), cannot be called.

        Args:
            session_id: int, [1, 2], 1 mean the channel for 'voltage', 2 mean the channel for 'current'.
            samples_per_channel: int, [1~512], defualt 1, samples count taken for calculation.
            timeout: float, (>=0), default 10.0, the maximum duration the acquisition can take,
                                   in seconds, before returning an exception.

        Exception:
            InvalidHardwareChannel() if provided session_id is not 1 or 2.
            InvalidSampleCount() if provided samples_per_channel is not an integer or is <1 or >512.
            InvalidTimeout() if provided timeout is <0.

        Returns:
            dict, the channel data to be measured. {
                (rms, unit),
                (avg, unit),
                (max, unit),
                (min, unit)
            },
            for voltage channel #1 current channel #2.

        Examples:
            odin.configure_input_channel(1, 'battery', 1000)
            odin.start_buffered_acquisition(1)
            result = odin.read_buffer_statistics(1, 10)
            print(result)
            odin.stop_buffered_acquisition(1)
        '''
        if session_id not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if not isinstance(samples_per_channel, int) or samples_per_channel < 1 or samples_per_channel > 512:
            raise InvalidSampleCount("Invalid sample count")

        if timeout < 0:
            raise InvalidTimeout("Invalid timeout")

        if not self.continuous_sample_mode:
            return 0

        if not self.active_channel:
            return 0
        channel = self.active_channel
        channel_data = list()
        result = dict()
        ch_input_config_dict = {1: self.ch1_input_config, 2: self.ch2_input_config}
        ch_input_config = ch_input_config_dict[channel]
        pin_id, adc_channel = (OdinDef.READ_CURR_BIT, OdinDef.CHANNEL_1) if ch_input_config['channel'] == 'current' \
            else (OdinDef.READ_VOLT_BIT, OdinDef.CHANNEL_0)
        pin_level = OdinDef.HIGH_LEVEL if ch_input_config['type'] == 'battery' else OdinDef.LOW_LEVEL
        self.tca9538.set_pin(pin_id, pin_level)

        if channel == 1:
            cal_type = OdinDef.BATT_VOLT_READ if ch_input_config['type'] == 'battery' else OdinDef.CHARGE_VOLT_READ
        else:
            if ch_input_config['type'] == 'battery':
                if ch_input_config['max_range'] == 1:
                    cal_type = OdinDef.BATT_CURR_READ_1mA
                elif ch_input_config['max_range'] == 1000:
                    cal_type = OdinDef.BATT_CURR_READ_1A
            else:
                cal_type = OdinDef.CHARGE_CURR_READ

        try:
            channel_data = self.ad7175.get_continuous_sampling_voltage(adc_channel, samples_per_channel)
        except Exception as e:
            self.stop_buffered_acquisition(session_id)
            if (OdinDef.ADC_ERROR_BIT & self.ad7175.read_register(OdinDef.STATUS_REG_ADDR)):
                reg_data = self.ad7175.read_register(OdinDef.DATA_REG_ADDR)

                if channel == 1:
                    if ch_input_config['type'] == 'battery':
                        volt_range = OdinDef.BATT_VOLT_RANGE
                    else:
                        volt_range = OdinDef.CHARGE_VOLT_RANGE

                    if reg_data == 0xFFFFFF:
                        raise OdinException('Overrange! the voltage value exceeds the {}V range'.format(volt_range))
                    elif reg_data == 0x000000:
                        raise OdinException('Underrange! the voltage value is lower than \
                                             negative {}V range'.format(volt_range))
                    else:
                        raise OdinException("{}".format(e.message))

                else:
                    if ch_input_config['type'] == 'battery':
                        if ch_input_config['max_range'] == 1:
                            curr_range = OdinDef.BATT_CURR_RANGE_1mA
                        else:
                            curr_range = OdinDef.BATT_CURR_RANGE_1A
                    else:
                        curr_range = OdinDef.CHARGE_CURR_RANGE

                    if reg_data == 0xFFFFFF:
                        raise OdinException('Overrange! the current value exceeds the {}A range'.format(curr_range))
                    elif reg_data == 0x000000:
                        raise OdinException('Underrange! the current value is lower than \
                                             negative {}A range'.format(curr_range))
                    else:
                        raise OdinException("{}".format(e.message))

            else:
                raise OdinException("{}".format(e.message))

        min_data = min(channel_data)
        max_data = max(channel_data)
        avg_data = sum(channel_data) / len(channel_data)
        rms_data = math.sqrt(sum([x**2 for x in channel_data]) / len(channel_data))

        if channel == 1:
            unit = OdinDef.UNIT_mV
            # battery channel: Vout = Vadc * 2.5, charge channel: Vout = Vadc * 4.0
            min_data, max_data, avg_data, rms_data = map(lambda volt: volt * 2.5
                                                         if ch_input_config['type'] == 'battery'
                                                         else volt * 4.0, [min_data, max_data, avg_data, rms_data])
            avg_data = self.calibrate(cal_type, avg_data)
            # set pin to default level.
            if ch_input_config['type'] == 'charge':
                self.tca9538.set_pin(OdinDef.READ_VOLT_BIT, OdinDef.HIGH_LEVEL)
        else:
            unit = OdinDef.UNIT_mA
            if ch_input_config['type'] == 'battery':
                min_data, max_data, avg_data, rms_data = map(lambda volt: volt,
                                                             [min_data, max_data, avg_data, rms_data])
            else:
                # charge channel: I = (Vadc - 24.7mV) / 2
                min_data, max_data, avg_data, rms_data = map(lambda volt: (volt - 24.7) / 2.0,
                                                             [min_data, max_data, avg_data, rms_data])

            avg_data = self.calibrate(cal_type, avg_data)

            if ch_input_config['max_range'] == 1:
                # uA convert to mA
                min_data, max_data, avg_data, rms_data = map(lambda volt: volt / 1000.0,
                                                             [min_data, max_data, avg_data, rms_data])
            # set pin to default level.
            if ch_input_config['type'] == 'charge':
                self.tca9538.set_pin(OdinDef.READ_CURR_BIT, OdinDef.HIGH_LEVEL)

        result['rms'] = (rms_data, unit + 'rms')
        result['avg'] = (avg_data, unit)
        result['max'] = (max_data, unit)
        result['min'] = (min_data, unit)

        return result
예제 #11
0
    def read_buffer(self, session_id, samples_per_channel=1, timeout=10.0):
        '''
        This function takes a number of samples raw data of the set of sampled value.

        Values read are in mV, which is determined by DRI.

        This function can only be called in continuous mode, a.k.a, after configure_input_channel(),
            start_buffered_acquisition() function is called. Return 0 for the channels that are not enabled.
        During continuous sampling, the functions, like configure_input_channel() read(), cannot be called.

        Args:
            session_id: int, [1, 2], 1 mean the channel for 'voltage', 2 mean the channel for 'current'.
            samples_per_channel: int, [1~512], defualt 1, samples count taken for calculation.
            timeout: float, (>=0), default 10.0, the maximum duration the acquisition can take,
                                   in seconds, before returning an exception.

        Exception:
            InvalidHardwareChannel() if provided session_id is not 1 or 2.
            InvalidSampleCount() if provided samples_per_channel is not an integer or is <1 or >512.
            InvalidTimeout() if provided timeout is <0.

        Returns:
            list, [value,...] the unit of elements in the list is mV.

        Examples:
            odin.configure_input_channel(1, 'battery', 1000)
            odin.start_buffered_acquisition(1)
            result = odin.read_buffer(1, 10)
            print(result)
            odin.stop_buffered_acquisition(1)
        '''
        if session_id not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if not isinstance(samples_per_channel, int) or samples_per_channel < 1 or samples_per_channel > 512:
            raise InvalidSampleCount("Invalid sample count")

        if timeout < 0:
            raise InvalidTimeout("Invalid timeout")

        if not self.continuous_sample_mode:
            return 0

        if not self.active_channel:
            return 0

        raw_data = list()
        channel = self.active_channel
        ch_input_config_dict = {1: self.ch1_input_config, 2: self.ch2_input_config}
        ch_input_config = ch_input_config_dict[channel]
        pin_id, adc_channel = (OdinDef.READ_CURR_BIT, OdinDef.CHANNEL_1) if ch_input_config['channel'] == 'current' \
            else (OdinDef.READ_VOLT_BIT, OdinDef.CHANNEL_0)
        pin_level = OdinDef.HIGH_LEVEL if ch_input_config['type'] == 'battery' else OdinDef.LOW_LEVEL
        self.tca9538.set_pin(pin_id, pin_level)

        try:
            raw_data = self.ad7175.get_continuous_sampling_voltage(adc_channel, samples_per_channel)
        except Exception as e:
            self.stop_buffered_acquisition(session_id)
            if (OdinDef.ADC_ERROR_BIT & self.ad7175.read_register(OdinDef.STATUS_REG_ADDR)):
                reg_data = self.ad7175.read_register(OdinDef.DATA_REG_ADDR)

                if channel == 1:
                    if ch_input_config['type'] == 'battery':
                        volt_range = OdinDef.BATT_VOLT_RANGE
                    else:
                        volt_range = OdinDef.CHARGE_VOLT_RANGE

                    if reg_data == 0xFFFFFF:
                        raise OdinException('Overrange! the voltage value exceeds the {}V range'.format(volt_range))
                    elif reg_data == 0x000000:
                        raise OdinException('Underrange! the voltage value is lower than \
                                             negative {}V range'.format(volt_range))
                    else:
                        raise OdinException("{}".format(e.message))

                else:
                    if ch_input_config['type'] == 'battery':
                        if ch_input_config['max_range'] == 1:
                            curr_range = OdinDef.BATT_CURR_RANGE_1mA
                        else:
                            curr_range = OdinDef.BATT_CURR_RANGE_1A
                    else:
                        curr_range = OdinDef.CHARGE_CURR_RANGE

                    if reg_data == 0xFFFFFF:
                        raise OdinException('Overrange! the current value exceeds the {}A range'.format(curr_range))
                    elif reg_data == 0x000000:
                        raise OdinException('Underrange! the current value is lower than \
                                             negative {}A range'.format(curr_range))
                    else:
                        raise OdinException("{}".format(e.message))

            else:
                raise OdinException("{}".format(e.message))

        return raw_data
예제 #12
0
    def read(self, channels, samples_per_channel=1, timeout=10.0):
        '''
        Reads the specified number of samples from each channel.

        Values read are in mV or mA, which is determined by DRI.

        The returned value is calibrated if calibration mode is `cal`. The data read back from the voltage channel
            is calibrated in mV. The data read back in the current channel is calibrated in uA when max_range is 1,
            and it is calibrated in mA when max_range is 1000.

        Args:
            channels: int, [1, 2], 1 mean the channel for 'voltage', 2 mean the channel for 'current'.
            samples_per_channel: int, (>=1), default 1, the number of samples to read from each channel.
            timeout: float, (>=0), default 10.0, the maximum duration the acquisition can take,
                                   in seconds, before returning an exception.

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2.
            InvalidSampleCount() if provided samples_per_channel is not an integer or is <1.
            InvalidTimeout() if provided timeout is <0.

        Returns:
            list, [value1, ..., valueN], measured value defined by configure_input_channel()
                    voltage channel always in mV
                    current channel always in mA

        Examples:
            odin.configure_input_channel(2, 'battery', 1000, 1000)
            result = odin.read(2)
            print(result)
        '''
        if channels not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if not isinstance(samples_per_channel, int) or samples_per_channel < 1:
            raise InvalidSampleCount("Invalid sample count")

        if timeout < 0:
            raise InvalidTimeout("Invalid timeout")

        target_data = list()
        if channels == 1:
            if self.ch1_input_config['type'] == 'battery':
                cal_type = OdinDef.BATT_VOLT_READ
                self.tca9538.set_pin(OdinDef.READ_VOLT_BIT, OdinDef.HIGH_LEVEL)
            elif self.ch1_input_config['type'] == 'charge':
                cal_type = OdinDef.CHARGE_VOLT_READ
                self.tca9538.set_pin(OdinDef.READ_VOLT_BIT, OdinDef.LOW_LEVEL)

            for x in range(samples_per_channel):
                try:
                    volt_raw = self.ad7175.read_volt(OdinDef.CHANNEL_0)
                except Exception as e:
                    if (OdinDef.ADC_ERROR_BIT & self.ad7175.read_register(OdinDef.STATUS_REG_ADDR)):
                        reg_data = self.ad7175.read_register(OdinDef.DATA_REG_ADDR)

                        if self.ch1_input_config['type'] == 'battery':
                            volt_range = OdinDef.BATT_VOLT_RANGE
                        else:
                            volt_range = OdinDef.CHARGE_VOLT_RANGE

                        if reg_data == 0xFFFFFF:
                            raise OdinException('Overrange! the voltage value exceeds the {}V range'.format(volt_range))
                        elif reg_data == 0x000000:
                            raise OdinException('Underrange! the voltage value is lower than \
                                                 negative {}V range'.format(volt_range))
                        else:
                            raise OdinException("{}".format(e.message))
                    else:
                        raise OdinException("{}".format(e.message))
                # if channel is battery: Vout = Vadc * 2.5, if channel is charge: Vout = Vadc * 4
                volt = map(lambda x: (x * 2.5) if
                           self.ch1_input_config['type'] == 'battery' else (x * 4.0), [volt_raw])[0]
                volt = self.calibrate(cal_type, volt)

                target_data.append(volt)

            # set pin to default level.
            if self.ch1_input_config['type'] == 'charge':
                self.tca9538.set_pin(OdinDef.READ_VOLT_BIT, OdinDef.HIGH_LEVEL)

        else:
            if self.ch2_input_config['type'] == 'battery':
                if self.ch2_input_config['max_range'] == 1:
                    cal_type = OdinDef.BATT_CURR_READ_1mA
                elif self.ch2_input_config['max_range'] == 1000:
                    cal_type = OdinDef.BATT_CURR_READ_1A

                self.tca9538.set_pin(OdinDef.READ_CURR_BIT, OdinDef.HIGH_LEVEL)
            elif self.ch2_input_config['type'] == 'charge':
                cal_type = OdinDef.CHARGE_CURR_READ
                self.tca9538.set_pin(OdinDef.READ_CURR_BIT, OdinDef.LOW_LEVEL)

            for x in range(samples_per_channel):
                try:
                    volt = self.ad7175.read_volt(OdinDef.CHANNEL_1)
                except Exception as e:
                    if (OdinDef.ADC_ERROR_BIT & self.ad7175.read_register(OdinDef.STATUS_REG_ADDR)):
                        reg_data = self.ad7175.read_register(OdinDef.DATA_REG_ADDR)

                        if self.ch2_input_config['type'] == 'battery':
                            if self.ch2_input_config['max_range'] == 1:
                                curr_range = OdinDef.BATT_CURR_RANGE_1mA
                            else:
                                curr_range = OdinDef.BATT_CURR_RANGE_1A
                        else:
                            curr_range = OdinDef.CHARGE_CURR_RANGE

                        if reg_data == 0xFFFFFF:
                            raise OdinException('Overrange! the current value exceeds the {}A range'.format(curr_range))
                        elif reg_data == 0x000000:
                            raise OdinException('Underrange! the current value is lower than \
                                                 negative {}A range'.format(curr_range))
                        else:
                            raise OdinException("{}".format(e.message))
                    else:
                        raise OdinException("{}".format(e.message))
                # if channel is battery: I = Vadc, if channel is charge: I = (Vadc - 24.7mV) / 2
                current = (volt - 24.7) / 2.0 if self.ch2_input_config['type'] == 'charge' else volt
                current = self.calibrate(cal_type, current)
                if self.ch2_input_config['max_range'] == 1:
                    # uA convert to mA
                    current = current / 1000.0

                target_data.append(current)

            # set pin to default level.
            if self.ch2_input_config['type'] == 'charge':
                self.tca9538.set_pin(OdinDef.READ_CURR_BIT, OdinDef.HIGH_LEVEL)

        return target_data
예제 #13
0
    def configure_input_channel(self, channel, type, sample_rate, max_range=None):
        '''
        Configure input channel.

        Args:
            channel: int, [1, 2], 1 mean the channel for 'voltage', 2 mean the channel for 'current'.
            type: string, ['battery', 'charge'], types are supported.
            sample_rate: float, [5~250000], unit is Hz, adc measure sampling rate, which not continuouse,
                                                please refer ad7175 datasheet.
            max_range: int, [1, 1000], unit is mA,  maximum range of current, only supported for 'current' of 'battery'

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2.
            InvalidHardwareChannelType() if provided type is not "battery" or "charge".
            InvalidSampleRate() if sample_rate is bad.
            InvalidRange() if max_range are bad values.

        Returns:
            dict, actual channel configuration.
            e.g. {"channel": "current", "type": "battery", "sample_rate": 1000, "max_range": 1000}.

        Examples:
            config = odin.configure_input_channel(2, 'battery', 1000, 1000)
            print(config)

        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if type not in ['battery', 'charge']:
            raise InvalidHardwareChannelType("Invalid channel type")

        if sample_rate < 5 or sample_rate > 250000:
            raise InvalidSampleRate("Invalid sampling rate range")

        if max_range or max_range == 0:
            if max_range not in [1, 1000]:
                raise InvalidRange("Invalid range")

        pin_level = {'battery': OdinDef.HIGH_LEVEL, 'charge': OdinDef.LOW_LEVEL}
        input_config = {1: self.ch1_input_config, 2: self.ch2_input_config}
        if channel == 1:
            self.tca9538.set_pin(OdinDef.READ_VOLT_BIT, pin_level[type])
            self.ad7175.set_sampling_rate(OdinDef.CHANNEL_0, sample_rate)
            self.ch1_input_config['channel'] = 'voltage'
            self.ch1_input_config['type'] = type
            self.ch1_input_config['sample_rate'] = sample_rate
            self.ch1_input_config['max_range'] = None
        else:
            self.tca9538.set_pin(OdinDef.READ_CURR_BIT, pin_level[type])
            self.ad7175.set_sampling_rate(OdinDef.CHANNEL_1, sample_rate)
            self.ch2_input_config['channel'] = 'current'
            self.ch2_input_config['type'] = type
            self.ch2_input_config['sample_rate'] = sample_rate
            if type == 'battery':
                if max_range == 1:
                    self.ch2_input_config['max_range'] = 1
                    self.tca9538.set_pin(OdinDef.BATT_READ_CURR_RANGE_BIT, OdinDef.HIGH_LEVEL)
                else:
                    self.ch2_input_config['max_range'] = 1000
                    self.tca9538.set_pin(OdinDef.BATT_READ_CURR_RANGE_BIT, OdinDef.LOW_LEVEL)
            else:
                self.ch2_input_config['max_range'] = None

        return input_config[channel]
예제 #14
0
    def configure_output_channel(self, channel, voltage, current_limit=None):
        '''
        Configure output channel.

        The set voltage and current_limit is calibrated if calibration mode is `cal`.
        The voltage is calibrated in mV and the current_limit is calibrated in mA.

        Args:
            channel: int, [1, 2], 1 mean the channel for 'battery', 2 mean the channel for 'charge'.
            voltage: float/int, [0~7500], unit is mV, the output voltage value. 0 effectively disables output.
                                'battery' output range is 0~4800 mV,
                                'charge' output range is 0~7500 mV.
            current_limit: float/int, [0~1000], unit is mA, default None, only supported for charge output.

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2.
            InvalidRange() if the provided arguments voltage or current_limit are out of range.

        Returns:
            dict, actual channel configuration. e.g. {"channel": "charge", "voltage": 4000, "current_limit": 1000}.

        Examples:
            config = odin.configure_output_channel(1, 4000, 1000)
            print(config)

        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if voltage < OdinDef.DAC_RANGE_DICT[channel]["min"] or voltage > OdinDef.DAC_RANGE_DICT[channel]["max"]:
            raise InvalidRange("Invalid voltage range")

        if current_limit or current_limit == 0:
            if current_limit < 0 or current_limit > 1000:
                raise InvalidRange("Invalid current_limit range")

        dac_output = {1: self.ad5667_batt, 2: self.ad5667_charge}
        output_config = {1: self.ch1_output_config, 2: self.ch2_output_config}
        ad5667 = dac_output[channel]

        if channel == 1:
            self.ch1_output_config['channel'] = 'battery'
            self.ch1_output_config['voltage'] = voltage
            self.ch1_output_config['current_limit'] = None
            self.tca9538.set_pin(OdinDef.BATT_ENABLE_BIT, OdinDef.HIGH_LEVEL)
            vdac = self.calibrate(OdinDef.BATT, voltage)
            enable_bit = OdinDef.BATT_ENABLE_BIT
        else:
            self.ch2_output_config['channel'] = 'charge'
            self.ch2_output_config['voltage'] = voltage
            self.tca9538.set_pin(OdinDef.CHARGE_ENABLE_BIT, OdinDef.HIGH_LEVEL)
            volt = self.calibrate(OdinDef.CHARGE, voltage)
            vdac = volt / 2.0
            enable_bit = OdinDef.CHARGE_ENABLE_BIT

        if vdac > OdinDef.MAX_VOLTAGE:
            vdac = OdinDef.MAX_VOLTAGE

        if vdac < OdinDef.VOLTAGE_MIN:
            vdac = OdinDef.VOLTAGE_MIN

        if voltage == 0:
            ad5667.output_volt_dc(OdinDef.CHANNEL_0, OdinDef.VOLTAGE_MIN)
            if channel == 1:
                self.tca9538.set_pin(enable_bit, OdinDef.LOW_LEVEL)
        else:
            ad5667.output_volt_dc(OdinDef.CHANNEL_0, vdac)

        if current_limit and channel == 2:
            self.ch2_output_config['current_limit'] = current_limit
            threshold = self.calibrate(OdinDef.CHARGE_CURR_SET, current_limit)
            # I = (Vdac - 24.7) / 2
            vdac = threshold * 2.0 + 24.7

            if vdac > OdinDef.MAX_VOLTAGE:
                vdac = OdinDef.MAX_VOLTAGE

            if vdac < OdinDef.VOLTAGE_MIN:
                vdac = OdinDef.VOLTAGE_MIN

            self.ad5667_charge.output_volt_dc(OdinDef.CHANNEL_1, vdac)

        return output_config[channel]
예제 #15
0
    def read_buffer(self, session_id, samples_per_channel=1, timeout=10.0):
        '''
        This function takes a number of samples raw data of the set of sampled value.

        Values read are in mV, which is determined by DRI.

        This function can only be called in continuous mode, a.k.a, after configure_input_channel(),
            start_buffered_acquisition() function is called. Return 0 for the channels that are not enabled.
        During continuous sampling, the functions, like configure_input_channel() read(), cannot be called.

        Args:
            session_id: int, [1, 2], 1 mean the channel for 'voltage', 2 mean the channel for 'current'.
            samples_per_channel: int, [1~512], defualt 1, samples count taken for calculation.
            timeout: float, (>=0), default 10.0, the maximum duration the acquisition can take,
                                   in seconds, before returning an exception.

        Exception:
            InvalidHardwareChannel() if provided session_id is not 1 or 2.
            InvalidSampleCount() if provided samples_per_channel is not an integer or is <1 or >512.
            InvalidTimeout() if provided timeout is <0.

        Returns:
            list, [value,...] the unit of elements in the list is mV.

        Examples:
            odin.configure_input_channel(1, 'battery', 1000)
            odin.start_buffered_acquisition(1)
            result = odin.read_buffer(1, 10)
            print(result)
            odin.stop_buffered_acquisition(1)
        '''
        if session_id not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if not isinstance(
                samples_per_channel,
                int) or samples_per_channel < 1 or samples_per_channel > 512:
            raise InvalidSampleCount("Invalid sample count")

        if timeout < 0:
            raise InvalidTimeout("Invalid timeout")

        if not self.continuous_sample_mode:
            return 0

        if not self.active_channel:
            return 0

        raw_data = list()
        channel = self.active_channel
        ch_input_config_dict = {
            1: self.ch1_input_config,
            2: self.ch2_input_config
        }
        ch_input_config = ch_input_config_dict[channel]
        if ch_input_config['channel'] == 'current':
            pin_id, adc_channel = (PSU001014Def.READ_CURR_BIT,
                                   PSU001014Def.CHANNEL_1)
        else:
            pin_id, adc_channel = (PSU001014Def.READ_VOLT_BIT,
                                   PSU001014Def.CHANNEL_0)
        pin_level = PSU001014Def.HIGH_LEVEL if ch_input_config[
            'type'] == 'battery' else PSU001014Def.LOW_LEVEL
        self.tca9538.set_pin(pin_id, pin_level)

        try:
            raw_data = self.ad7175.get_continuous_sampling_voltage(
                adc_channel, samples_per_channel)
        except Exception:
            raise

        return raw_data
예제 #16
0
    def configure_output_channel(self, channel, voltage, current_limit=None):
        '''
        Configure output channel.

        The set voltage and current_limit is calibrated if calibration mode is `cal`.
        The voltage is calibrated in mV and the current_limit is calibrated in mA.

        Args:
            channel: int, [1, 2], 1 mean the channel for 'battery', 2 mean the channel for 'charge'.
            voltage: float/int, [0~7500], unit is mV, the output voltage value. 0 effectively disables output.
                                'battery' output range is 0~4800 mV,
                                'charge' output range is 0~7500 mV.
            current_limit: float/int, [0~1100], unit is mA, default None, only supported for charge output,
                                      recommended range is [0~1000], [0~1100] is just for debug.

        Exception:
            InvalidHardwareChannel() if provided channel is not 1 or 2.
            InvalidRange() if the provided arguments voltage or current_limit are out of range.

        Returns:
            dict, actual channel configuration. e.g. {"channel": "charge", "voltage": 4000, "current_limit": 1000}.

        Examples:
            config = odin.configure_output_channel(1, 4000, 1000)
            print(config)

        '''
        if channel not in [1, 2]:
            raise InvalidHardwareChannel("Invalid channel")

        if voltage < PSU001014Def.DAC_RANGE_DICT[channel]["min"]:
            raise InvalidRange("Invalid voltage range")
        elif voltage > PSU001014Def.DAC_RANGE_DICT[channel]["max"]:
            raise InvalidRange("Invalid voltage range")

        if current_limit or current_limit == 0:
            if current_limit < 0 or current_limit > 1100:
                raise InvalidRange("Invalid current_limit range")

        dac_output = {1: self.ad5667_batt, 2: self.ad5667_charge}
        output_config = {1: self.ch1_output_config, 2: self.ch2_output_config}
        ad5667 = dac_output[channel]

        if channel == 1:
            self.ch1_output_config['channel'] = 'battery'
            self.ch1_output_config['voltage'] = voltage
            self.ch1_output_config['current_limit'] = None
            self.tca9538.set_pin(PSU001014Def.BATT_ENABLE_BIT,
                                 PSU001014Def.HIGH_LEVEL)
            vdac = self.calibrate(PSU001014Def.BATT, voltage)
            enable_bit = PSU001014Def.BATT_ENABLE_BIT
        else:
            if voltage > PSU001014Def.CHARGE_FLAG_VOLT:
                self.tca9538.set_pin(PSU001014Def.CHARGE_FLAG_BIT,
                                     PSU001014Def.HIGH_LEVEL)
            else:
                self.tca9538.set_pin(PSU001014Def.CHARGE_FLAG_BIT,
                                     PSU001014Def.LOW_LEVEL)

            self.ch2_output_config['channel'] = 'charge'
            self.ch2_output_config['voltage'] = voltage
            if voltage == PSU001014Def.DAC_RANGE_DICT[channel]["min"]:
                self.tca9538.set_pin(PSU001014Def.CHARGE_ENABLE_BIT,
                                     PSU001014Def.LOW_LEVEL)
            else:
                self.tca9538.set_pin(PSU001014Def.CHARGE_ENABLE_BIT,
                                     PSU001014Def.LOW_LEVEL)

            volt = self.calibrate(PSU001014Def.CHARGE, voltage)
            vdac = volt / 2.0
            enable_bit = PSU001014Def.CHARGE_ENABLE_BIT

        if vdac > PSU001014Def.MAX_VOLTAGE:
            vdac = PSU001014Def.MAX_VOLTAGE

        if vdac < PSU001014Def.VOLTAGE_MIN:
            vdac = PSU001014Def.VOLTAGE_MIN

        if channel == 2:
            # V_dcdc = R1 * (0.6 / R2 + 0.6 / R3 - VDC_SET / R3) + 0.6, R1=165000, R2=10000, R3=100000
            # VDC_SET = R3 * (0.6 / R2 + 0.6 / R3 -(V_dcdc - 0.6) / R1)
            #         = 0.6 * (R3 / R2 + 1) - R3 * (V_dcdc -0.6) / R1
            v_dcdc = (voltage + self.volt_diff) / 1000.0
            r1, r2, r3 = 165000, 10000, 100000
            vdc_set = (0.6 * (r3 / r2 + 1) - r3 * (v_dcdc - 0.6) / r1) * 1000.0

            if voltage < PSU001014Def.CHARGE_VOLT_DEF:
                vdc_set -= PSU001014Def.CHARGE_DIFF
            if vdc_set < PSU001014Def.MCP4725_MIN_VOLTAGE:
                vdc_set = PSU001014Def.MCP4725_MIN_VOLTAGE
            elif vdc_set > PSU001014Def.MCP4725_MAX_VOLTAGE:
                vdc_set = PSU001014Def.MCP4725_MAX_VOLTAGE
            if self.charge_current_vdac <= vdac:
                self.mcp4725.fast_output_volt_dc(vdc_set)
        elif channel == 1:
            # V_dcdc= R1 * (0.6 / R2 + 0.6 / R3 - VDC_SET / R3) + 0.6, R1=75000, R2=10000, R3=100000
            # VDC_SET = R3 * (0.6 / R2 + 0.6 / R3 -(V_dcdc - 0.6) / R1)
            #         = 0.6 * (R3 / R2 + 1) - R3 * (V_dcdc -0.6) / R1
            if voltage <= PSU001014Def.BATT_VOLT_REF:
                v_dcdc = 2.5
            else:
                v_dcdc = (voltage + PSU001014Def.BATT_DIFF) / 1000.0
            r1, r2, r3 = 75000, 10000, 100000
            vdc_set = (0.6 * (r3 / r2 + 1) - r3 * (v_dcdc - 0.6) / r1) * 1000.0
            if vdc_set > PSU001014Def.MAX_VOLTAGE:
                vdc_set = PSU001014Def.MAX_VOLTAGE
            elif vdc_set < PSU001014Def.VOLTAGE_MIN:
                vdc_set = PSU001014Def.VOLTAGE_MIN
            if self.battery_current_vdac <= vdac:
                self.ad5667_batt.output_volt_dc(PSU001014Def.CHANNEL_1,
                                                vdc_set)

        if voltage == 0 and channel == 1:
            ad5667.output_volt_dc(PSU001014Def.CHANNEL_0,
                                  PSU001014Def.VOLTAGE_MIN)
            self.tca9538.set_pin(enable_bit, PSU001014Def.LOW_LEVEL)
        else:
            ad5667.output_volt_dc(PSU001014Def.CHANNEL_0, vdac)

        if channel == 2:
            if self.charge_current_vdac > vdac:
                self.mcp4725.fast_output_volt_dc(vdc_set)
            self.charge_current_vdac = vdac

        if current_limit and channel == 2:
            self.ch2_output_config['current_limit'] = current_limit
            threshold = self.calibrate(PSU001014Def.CHARGE_CURR_SET,
                                       current_limit)
            # I = (Vdac - 24.7) / 2
            vdac = threshold * 2.0 + 24.7

            if vdac > PSU001014Def.MAX_VOLTAGE:
                vdac = PSU001014Def.MAX_VOLTAGE

            if vdac < PSU001014Def.VOLTAGE_MIN:
                vdac = PSU001014Def.VOLTAGE_MIN

            self.ad5667_charge.output_volt_dc(PSU001014Def.CHANNEL_1, vdac)

        return output_config[channel]