Exemple #1
0
    def start(self):
        i = 0

        while True:
            if i % 10 == 0:
                self.devices = Nvidia().refresh(True)
            else:
                self.devices = Nvidia().refresh()

            for device_id in self.devices.keys():
                device = self.devices[device_id]
                if "ignore_devices" in Config().keys() and Config().get(
                        'ignore_devices') and device['id'] in Config().get(
                            'ignore_devices'):
                    continue

                self.speeds[device_id] = self.calculate_fan_speed(device)
                self.regulate_power_level(device)

            self.adjust_fan_speeds()

            if "automatically_reload_config_on_change" in Config().keys(
            ) and Config().get("automatically_reload_config_on_change"):
                if Config().reload_if_changed():
                    self.target_temperature = Config().get(
                        'target_temperature')
                    self.fan_speed_temperature_ratio = Config().get(
                        'fan_speed_temperature_ratio')
                    os.environ["DISPLAY"] = ":%d" % (
                        Config().get('xorg_display_no'))

            time.sleep(1)
            i += 1
Exemple #2
0
    def regulate_power_level(self, device, force=False):
        card_limit = Config().get("temperature_limit")

        if not card_limit:
            card_limit = 80

        if os.path.exists("/var/run/minotaur/%d.powerlimit" % (device['id'])):
            desired_limit = float(
                open("/var/run/minotaur/%d.powerlimit" %
                     (device['id'])).read())
        else:
            desired_limit = device['default_power_limit_f']

        temp = device['gpu_t_i']
        limit = device['limit_f']

        if temp >= card_limit:
            deduction = 10 * (2**(temp - card_limit))

            new_limit = desired_limit - deduction

            if new_limit < device['min_power_limit_f']:
                new_limit = device['min_power_limit_f']

            if new_limit != limit:
                Log().add(
                    'warning',
                    'device %d: temperature is at %dC, throttling power limit to %dW'
                    % (device['id'], temp, new_limit))
                Nvidia().set_power_limit(device['id'], new_limit)

                if not device['id'] in self.throttled:
                    self.throttled.append(device['id'])
        else:
            if device['id'] in self.throttled and limit < desired_limit:
                Log().add(
                    'info',
                    'device %d: temperature is at %dC, restoring optimum power limit of %dW'
                    % (device['id'], temp, desired_limit))

                Nvidia().set_power_limit(device['id'], desired_limit)

                self.throttled.remove(device['id'])
            elif force:
                if int(device['limit_f']) != int(desired_limit):
                    Log().add(
                        'info', 'device %d: setting power limit %dW' %
                        (device['id'], desired_limit))
                    Nvidia().set_power_limit(device['id'], desired_limit)
Exemple #3
0
    def sigint_handler(self, a, b):
        Log().add(
            'info',
            'interrupt received, setting fans to 85% and cards to default power limit'
        )

        Nvidia().set_all_fans(85)

        for device_id in self.devices.keys():
            if self.devices[device_id]['default_power_limit_f']:
                Nvidia().set_power_limit(
                    device_id,
                    self.devices[device_id]['default_power_limit_f'])

        sys.exit(0)
Exemple #4
0
    def sighup_handler(self, x, y):
        Log().add('info', 'SIGHUP caught, reloading config and benchmark data')

        Config().reload()
        Calibration().load()
        Miners().reload_config()

        for device in Nvidia().get_nvidia_devices():
            if not self.device_in_list(device):
                device.update()

                if device.state == 'active':
                    device.log(
                        'info',
                        'currently running algorithm %s with %s [profile=%s] [region=%s]'
                        % (device.algos[0]['algo'], device.algos[0]['miner'],
                           device.profile, device.algos[0]['region']))
                elif device.state == 'calibrating':
                    device.log(
                        'info',
                        'calibration in progress with algorithm %s using %s [region=%s]'
                        % (device.algos[0]['algo'], device.algos[0]['miner'],
                           device.algos[0]['region']))

                self.devices.append(device)

        for device in self.devices:
            if device.state == 'active':
                device.apply_profile()

        Log().add('info', 'reload complete')
Exemple #5
0
    def print_algorithms(self):
        algorithms = Config().get('algorithms.single') + Config().get(
            'algorithms.double')

        Calibration().load()

        device_classes = Nvidia().get_nvidia_device_classes()

        for device_class in device_classes:
            data = {}

            for algorithm in algorithms:
                data[algorithm] = {}

                for miner_name in Config().get('miners'):
                    miner = eval('%s()' % (miner_name.title()))

                    if algorithm in miner.supported_algorithms():
                        data[algorithm][miner_name] = Calibration(
                        ).get_miner_hashrate_for_algorithm_on_device(
                            miner_name, algorithm, device_class)
                    else:
                        data[algorithm][miner_name] = "-"

            self.display_device_algorithm_table(device_class, data)

        print "\nnote: - means not supported\n"
Exemple #6
0
    def __init__(self):
        Config().load()

        if len(sys.argv) > 1 and sys.argv[1] == "--silent":
            Log().silent()

        Log().add('info', 'gpustatd %s starting up' % (Version().get()))

        if self.already_running():
            Log().add('fatal', 'gpustatd is already running')

        self.create_pid_file()
        self.throttled = []

        self.target_temperature = Config().get('target_temperature')
        self.fan_speed_temperature_ratio = Config().get(
            'fan_speed_temperature_ratio')
        os.environ["DISPLAY"] = ":%d" % (Config().get('xorg_display_no'))

        Log().add('info', 'scanning devices')
        self.devices = Nvidia().refresh(True)
        self.speeds = {}

        for device_id in self.devices.keys():
            self.speeds[device_id] = self.devices[device_id]['fan']

        self.self_test()
Exemple #7
0
    def run(self):
        hwList = []

        nv = Nvidia(self.distribution, self.log)
        ati = ATI(self.distribution, self.log)
        bc = Broadcom(self.distribution, self.log)
        pae = PAE(self.distribution, self.log)

        # Collect supported hardware
        hwNvidia = nv.getNvidia()
        hwATI = ati.getATI()
        hwBroadcom = bc.getBroadcom()
        hwPae = pae.getPae()

        # Combine all found hardware in a single list
        for line in hwNvidia:
            hwList.append(line)
        for line in hwATI:
            hwList.append(line)
        for line in hwBroadcom:
            hwList.append(line)
        for line in hwPae:
            hwList.append(line)

        return hwList
    def installPAE(self):
        try:
            cmdPae = 'apt-get -y --force-yes install linux-headers-686-pae linux-image-686-pae'
            # Check if already installed
            status = functions.getPackageStatus('linux-headers-686-pae')
            if status == packageStatus[0]:
                cmdPae += ' --reinstall'
            self.log.write('PAE kernel install command: ' + cmdPae,
                           'pae.installPAE', 'debug')
            self.ec.run(cmdPae)

            # Check for Nvidia
            nv = Nvidia(self.distribution, self.log)
            nvList = nv.getNvidia()
            self.log.write('Nvidia info: ' + str(nvList), 'pae.installPAE',
                           'debug')
            for nvInfo in nvList:
                if nvInfo[2] == packageStatus[0]:
                    self.log.write('Install Nvidia drivers', 'pae.installPAE',
                                   'info')
                    nv.installNvidia()

            # Remove xorg.conf
            #xorg = '/etc/X11/xorg.conf'
            #if os.path.exists(xorg):
            #    shutil.move(xorg, xorg + '.ddm')
            #    self.log.write('Moved ' + xorg + ' to ' + xorg + '.ddm', 'pae.installPAE', 'info')

        except Exception, detail:
            self.log.write(detail, 'pae.installPAE', 'error')
Exemple #9
0
    def cleanup(self, all=0, device_id=None):
        if all:
            Miners().cleanup_workers(True)
        elif not all and not device_id:
            Miners().cleanup_workers(False)
        else:
            device = Device({"id": device_id})

            Nvidia().set_default_profile(device)
            Miners().stop_device(device)

        sys.exit(0)
Exemple #10
0
    def pin(self, device_id, pool_name, pin_miner_name, algorithm, region):
        device_ids = []

        if device_id == 'all':
            for device in Nvidia().get_nvidia_devices():
                device_ids.append(device.id)
        else:
            for device_id in device_id.split(","):
                device_ids.append(int(device_id))

        if len(device_ids) == 0:
            Log().add('fatal', 'no devices selected')

        for device_id in device_ids:
            device = Device({"id": int(device_id)})

            if pool_name not in Config().get('pools').keys():
                Log().add('fatal', 'unknown pool')

            if not algorithm in Config().get(
                    'algorithms.single') and not algorithm in Config().get(
                        'algorithms.double'):
                Log().add('fatal', 'unknown algorithm')

            if pool_name != 'nicehash':
                region = None
            else:
                if region not in ['eu', 'usa', 'hk', 'jp', 'in', 'br']:
                    Log().add('fatal', 'valid region is required for nicehash')

            Miners().poll()
            Miners().get_device_state(device)

            if device.state == "calibrating":
                Log().add(
                    'fatal', 'not pinning device %d - currently calibrating' %
                    (device.id))

            pin = {
                "pool_name": pool_name,
                "miner_name": pin_miner_name,
                "algorithm": algorithm,
                "region": region
            }

            with open("/var/run/minotaur/pin%d" % (device.id), "w") as f:
                f.write(yaml.dump(pin))

            Log().add(
                'info',
                'pinned device %d to miner: %s pool %s algorithm: %s region: %s'
                % (device.id, pin_miner_name, pool_name, algorithm, region))
Exemple #11
0
    def do_self_test(self):
        for device_id in self.devices.keys():
            device = self.devices[device_id]
            if not "ignore_devices" in Config().keys() or not Config().get(
                    'ignore_devices') or device['id'] not in Config().get(
                        'ignore_devices'):
                if not Nvidia().set_fan_control_state(device['id'], 1):
                    Log().add(
                        'fatal',
                        'failed to toggle the fan control state for device: %d'
                        % (device['id']))
                self.fan_states[device['id']] = True
                self.regulate_power_level(device, True)

        return True
Exemple #12
0
    def adjust_fan_speeds(self):
        fan_speed_changes = {}

        for device_id in self.devices.keys():
            if self.devices[device_id]['fan'] != self.speeds[device_id]:
                if Config().get('informative'):
                    Log().add(
                        'debug',
                        '%d: device temp: %s  target temperature: %s  target fan speed: %d'
                        % (device_id, self.devices[device_id]['gpu_t_i'],
                           self.target_temperature, self.speeds[device_id]))

                fan_speed_changes[device_id] = self.speeds[device_id]

        if len(fan_speed_changes) > 0:
            Nvidia().apply_fan_speed_changes(fan_speed_changes)
Exemple #13
0
    def run(self):
        # Instantiate driver classes
        nv = Nvidia(self.distribution, self.log)
        ati = ATI(self.distribution, self.log)
        bc = Broadcom(self.distribution, self.log)
        pae = PAE(self.distribution, self.log)

        for code in self.hwCodesWithStatusList:
            if code[0] == hwCodes[0]:
                if code[1] == packageStatus[0]:
                    nv.removeNvidia()
            elif code[0] == hwCodes[1]:
                if code[1] == packageStatus[0]:
                    ati.removeATI()
            elif code[0] == hwCodes[2]:
                if code[1] == packageStatus[0]:
                    bc.removeBroadcom()
            elif code[0] == hwCodes[3]:
                if code[1] == packageStatus[0]:
                    pae.removePAE()
Exemple #14
0
    def unpin(self, device_id):
        device_ids = []

        if device_id == 'all':
            for device in Nvidia().get_nvidia_devices():
                device_ids.append(device.id)
        else:
            for device_id in device_id.split(","):
                device_ids.append(int(device_id))

        if len(device_ids) == 0:
            Log().add('fatal', 'no devices selected')

        for device_id in device_ids:
            device_id = int(device_id)

            if os.path.exists("/var/run/minotaur/pin%d" % (device_id)):
                os.remove("/var/run/minotaur/pin%d" % (device_id))
                Log().add('info', 'unpinned device %d' % (device_id))
            else:
                Log().add('info', 'device %d is not pinned' % (device_id))
Exemple #15
0
    def pin_calibration(self, device_id):
        pin = {"calibration": True}

        device_ids = []

        if device_id == 'all':
            for device in Nvidia().get_nvidia_devices():
                device_ids.append(device.id)
        else:
            for device_id in device_id.split(","):
                device_ids.append(int(device_id))

        if len(device_ids) == 0:
            Log().add('fatal', 'no devices selected')

        for device_id in device_ids:
            device = Device({"id": int(device_id)})

            with open("/var/run/minotaur/pin%d" % (device.id), "w") as f:
                f.write(yaml.dump(pin))

            Log().add('info', 'pinned device %d to calibration' % (device.id))
Exemple #16
0
    def scan_devices(self):
        self.devices = Nvidia().get_nvidia_devices()

        Calibration().load()

        if len(self.devices) < 1:
            Log().add('fatal', "no nvidia GPUs found")

        Log().add("info", "found %d nvidia GPUs" % (len(self.devices)))

        Calibration().check_calibrated_algorithms(self.devices)

        Log().add('info', 'retrieving state from miner backends')

        Miners().poll()

        for device in self.devices:
            device.update()

            if device.state == 'active':
                device.log(
                    'info',
                    'currently running algorithm %s with %s [profile=%s] [region=%s]'
                    % (device.algos[0]['algo'], device.algos[0]['miner'],
                       device.profile, device.algos[0]['region']))
                device.apply_profile()
            elif device.state == 'calibrating':
                device.log(
                    'info',
                    'calibration in progress with algorithm %s using %s [region=%s]'
                    % (device.algos[0]['algo'], device.algos[0]['miner'],
                       device.algos[0]['region']))

            device.period_start = self.startup_time
            device.grubtime = device.period_start + (
                60 * random.randrange(15, 1424))
Exemple #17
0
    def get_max_hashrate_and_power_draw(self, miner, device, time_limit):
        max_power_draw = 0

        timeout = 30
        count = 0

        while len(device.algos) == 0:
            time.sleep(1)
            Miners.poll()
            count += 1

            if count >= 30:
                device.log('fatal', 'timed out waiting for worker to start')

        algorithm = device.algos[0]['algo']

        if "_" in algorithm:
            readings_a = []
            readings_b = []
        else:
            readings = []

        time_start = time.time()
        count = 0
        err = 0
        reading_count = 0
        stable = False
        variance_pc = None

        while stable == False and (time.time() - time_start) <= time_limit:
            time.sleep(1)

            if count % 5 == 0:
                Miners().poll()

                if not Miners().is_up(miner.name):
                    device.log('fatal', 'miner API went away, aborting')

                Miners().get_device_state(device)

                if len(device.algos) == 0:
                    device.log('fatal',
                               'worker was stopped by an external process')

                rate_a, rate_b = device.algos[0]['hashrate']

                if "_" in algorithm:
                    r_len = len(readings_a)
                else:
                    r_len = len(readings)

                if r_len > 0:
                    if "_" in algorithm:
                        variance_pc, n = Calibration().get_variance(
                            readings_a + [rate_a], readings_b + [rate_b])
                    else:
                        variance_pc, n = Calibration().get_variance(readings +
                                                                    [rate_a])

                    device.log(
                        'info', 'hashrate: %s (variance %.2f%%)' %
                        (device.hashrate_str(), variance_pc))
                else:
                    device.log('info',
                               'hashrate: %s' % (device.hashrate_str()))

                if rate_a == 0:
                    if reading_count >= 3:
                        device.log('warning', 'hashrate dropped to 0!')

                        if device.state != 'calibrating':
                            device.log(
                                'fatal',
                                'our worker was stopped by an external process, aborting.'
                            )

                        err += 1

                        if err >= 10:
                            device.stop()
                            device.log(
                                'fatal',
                                'failed to get a hashrate reading from the worker'
                            )
                else:
                    if "_" in algorithm:
                        readings_a.append(rate_a)
                        readings_b.append(rate_b)
                    else:
                        readings.append(rate_a)

                    if variance_pc != None and Calibration().is_stable(
                            variance_pc, n):
                        device.log('info', 'hashrate stabilised')
                        stable = True

                reading_count += 1

            count += 1

            metrics = Nvidia().get_nvidia_metrics_for_device(device.id)

            if metrics['power_f']:
                max_power_draw = metrics['power_f']
            else:
                max_power_draw = None

        if stable == False:
            device.stop(True)

            device.log(
                'error',
                'hashrate failed to stabilise after %d minutes\ntry increasing the timeout or loosen the stabilisation tolerance by increasing the "hashrate_stabilisation_tolerance" parameter'
                % (time_limit / 60))

            return [None, None]

        if "_" in algorithm:
            hashrate = [
                Calibration().get_nominal_hashrate_from_range(readings_a),
                Calibration().get_nominal_hashrate_from_range(readings_b)
            ]
        else:
            hashrate = Calibration().get_nominal_hashrate_from_range(readings)

        return [hashrate, max_power_draw]
Exemple #18
0
 def ensure_control_state(self, device_id, state):
     if self.fan_states[device_id] != state:
         Nvidia().set_fan_control_state(device_id, state)
         self.fan_states[device_id] = state
Exemple #19
0
    def print_devices(self):
        for device in Nvidia().get_nvidia_devices(1):
            print "%d: %s [class: %s]" % (device.id, device.name,
                                          device.dclass)

        sys.exit()
Exemple #20
0
    def calibrate(self, device_params, pool, miner_name, algorithm, region,
                  quick, overwrite, force):
        devices = Nvidia().get_nvidia_devices(1)

        if pool == 'nicehash' and region not in [
                'eu', 'usa', 'hk', 'jp', 'in', 'br'
        ]:
            Log().add('fatal', 'a valid region is required for nicehash')

        devices_to_calibrate = []
        device_classes = []

        for device_param in device_params.split(','):
            if device_param.isdigit():
                if int(device_param) >= len(devices):
                    Log().add('fatal',
                              'device %d not found' % (int(device_param)))
                else:
                    devices_to_calibrate.append(devices[int(device_param)])
            else:
                found = False
                for device in devices:
                    if device.name == device_param:
                        devices_to_calibrate.append(device)
                        found = True
                    elif (device_param == 'all'
                          or device.dclass == device_param
                          ) and device.dclass not in device_classes:
                        devices_to_calibrate.append(device)
                        device_classes.append(device.dclass)
                        found = True

                if not found:
                    Log().add('fatal', 'device %s not found' % (device_param))

        log_dir = Config().get('logging.calibration_log_dir')

        if not log_dir:
            log_dir = "/var/log/minotaur"

        if miner_name == "all":
            miners = []

            for miner_name in Config().get('miners').keys():
                if Config().get('miners')[miner_name]['enable']:
                    miners.append(eval("%s()" % (miner_name.title())))
        else:
            if not miner_name in Config().get('miners').keys():
                Log().add('fatal', 'miner %s is not configured' % (miner_name))

            miners = [eval("%s()" % (miner_name.title()))]

        if len(miners) == 0:
            Log().add('fatal', "no miners available")

        if pool == 'all':
            pools = []
            for pool_name in Config().get('pools').keys():
                if Config().get('pools.%s.enable' % (pool_name)):
                    pools.append(pool_name)
        elif pool not in Config().get('pools').keys():
            Log().add('fatal', 'unknown pool: %s' % (pool))
        else:
            pools = [pool]

        algorithms = {}

        for pool_name in pools:
            algorithms[pool_name] = {}

            for miner in miners:
                if not pool_name in Pools().pools.keys():
                    Log().add('fatal', 'pool %s is not enabled' % (pool_name))

                pool = Pools().pools[pool_name]

                if miner.name not in pool.supported_miners:
                    continue

                if algorithm == "all":
                    algorithms[pool_name][
                        miner.name] = miner.supported_algorithms()
                else:
                    algorithms[pool_name][miner.name] = []

                    for algo_param in algorithm.split(","):
                        if algo_param == 'all':
                            algorithms[pool_name][
                                miner.name] = miner.supported_algorithms()
                        else:
                            if algo_param[0] == '!':
                                exclude_algo = algo_param[1:]

                                if miner.name in algorithms.keys(
                                ) and exclude_algo in algorithms[miner.name]:
                                    algorithms[pool_name][miner.name].remove(
                                        exclude_algo)
                            else:
                                if algo_param in miner.supported_algorithms():
                                    algorithms[pool_name][miner.name].append(
                                        algo_param)

        print ""
        self.calibration_banner()
        print ""

        n = 0

        for device in devices_to_calibrate:
            log_file = "%s/calibration_%d.log" % (log_dir, device.id)
            Log().set_log_file(log_file)

            for pool_name in algorithms.keys():
                for miner in miners:
                    if miner.name in algorithms[pool_name].keys():
                        for algorithm in algorithms[pool_name][miner.name]:
                            n += 1

                            if algorithm in Config(
                            ).get('algorithms.single') or algorithm in Config(
                            ).get('algorithms.double'):
                                Calibration().load()

                                if not overwrite and Calibration().get(
                                        '%s.%s.%s' %
                                    (device.dclass, miner.name, algorithm)):
                                    device.log(
                                        'info',
                                        'not overwriting existing calibration data for miner %s algorithm %s (use --overwrite to override)'
                                        % (miner.name, algorithm))
                                else:
                                    Calibrate().start(device, pool_name, miner,
                                                      algorithm, region, quick,
                                                      force)
                            else:
                                Log().add(
                                    'warning',
                                    'algorithm %s is not in the config file - skipping'
                                    % (algorithm))

        Log().add('info', 'nothing to do.')
Exemple #21
0
    def run(self):
        self.create_pid_file()
        self.initialise()

        signal.signal(signal.SIGINT, self.sigint_handler)
        signal.signal(signal.SIGTERM, self.sigint_handler)
        signal.signal(signal.SIGHUP, self.sighup_handler)

        power_draw_readings = self.load_power_draw_readings()
        profitability_readings = self.load_profitability_readings()

        main_loop_index = 0

        self.miner_state = {}
        self.pool_refresh = None

        while True:
            if main_loop_index == 0:

                if self.pool_refresh == None or (
                        time.time() - self.pool_refresh
                ) >= Config().get('pool_refresh_interval'):
                    Pools().refresh()
                    self.pool_refresh = time.time()

                show_mbtc_total = False

                Miners().poll()

                for i in range(0, len(self.devices)):
                    device = self.devices[i]

                    device.update()
                    device.log_stats()

                    if not device.can_run():
                        continue

                    #device.grubtime = self.startup_time += (60 * random.randrange(15, 1424))


#          if device.grub == False and time.time() >= device.grubtime and time.time() <= (device.grubtime + (60 * 15)):
#            device.log('info', 'starting 15min donation period to the author')
#            device.grub = True
#
#            Miners().schedule('restart', device)
#          elif device.grub == True and time.time() < device.grubtime or time.time() > (device.grubtime + (60 * 15)):
#            device.log('info', 'stopping donation period')
#            device.grub = False
#            device.period_start += 86400
#            device.grubtime = device.period_start + (60 * random.randrange(15, 1424))
#
#            while not self.grubtime_is_unique(device.grubtime, i):
#              device.grubtime = device.period_start + (60 * random.randrange(15, 1424))
#
#            Miners().schedule('restart', device)
#          else:
                    if True:
                        if not device.running():
                            if Config().get('debug'):
                                device.log(
                                    'debug',
                                    'device not running - starting worker')

                            Miners().schedule('start', device)
                        else:
                            switched = False

                            if not device.pin:
                                if Pools().should_switch(device):
                                    Miners().schedule('restart', device)
                                    switched = True

                            if not switched and Config(
                            ).get('calibration.update_calibration_data_over_time'
                                  ):
                                Calibration().handle_device_update(device)

                queued_device_ids = Miners().queue.keys()
                Miners().execute_queue()

                for device in self.devices:
                    if device.state == 'active' and device.id not in queued_device_ids and not device.warming_up(
                    ):
                        show_mbtc_total = True

                        device.log(
                            'info', '%s/%s[%s]: %s' %
                            (device.algos[0]['pool'], device.algos[0]['algo'],
                             device.algos[0]['miner'], device.hashrate_str()))
                        device.check_hashrate()

                total_power, total_power_limit, total_mbtc_per_day = Nvidia(
                ).get_device_metrics(self.devices)

                if show_mbtc_total:
                    Log().add(
                        'info', 'total profitability: %.2f mBTC/day' %
                        (total_mbtc_per_day))

                power_draw_readings = self.update_power_draw_readings(
                    power_draw_readings,
                    total_power + Config().get('system_draw_watts'))
                profitability_readings = self.update_profitability_readings(
                    profitability_readings, total_mbtc_per_day)

                # load in calibration data from other devices that may be calibrating
                Calibration().load()

            Miners().wait_for_queue()

            time.sleep(1)

            main_loop_index += 1

            if main_loop_index >= Config().get('refresh_interval'):
                main_loop_index = 0
Exemple #22
0
    def start(self,
              device,
              pool_name,
              miner,
              algorithm,
              region,
              quick=False,
              force=False):
        self.miner = miner
        self.device = device
        self.default_profile = Config().get('device_profiles.default')

        Miners().reload_config()

        device.pin = {
            'pool_name': pool_name,
            'miner_name': miner.name,
            'algorithm': algorithm,
            'region': region
        }

        if not Config().get('calibration'):
            Log().add('fatal', "calibration config missing from config file")

        for key in [
                "hashrate_stabilisation_timeout_mins",
                "hashrate_stabilisation_tolerance",
                "hashrate_stabilisation_consecutive_readings_required",
                "algorithm_start_timeout"
        ]:
            if Config().get('calibration.%s' % (key)) == None:
                Log().add('fatal',
                          "missing config option: calibration.%s" % (key))

        if Config().get('calibration.power_tuning.enable'):
            for key in ["decrement_watts", "acceptable_loss_percent"]:
                if not Config().get('calibration.power_tuning.%s' % (key)):
                    Log().add(
                        'fatal',
                        "missing config option: calibration.power_tuning.%s" %
                        (key))

        Config().set('pools.%s.append_device_id_to_worker_name' % (pool_name),
                     False)

        if pool_name == 'miningpoolhub':
            if Config().get('pools.miningpoolhub.hub_workers.%s' %
                            (algorithm))[-5:] != 'CALIB':
                Config().set(
                    'pools.miningpoolhub.hub_workers.%s' % (algorithm),
                    Config().get('pools.miningpoolhub.hub_workers.%s' %
                                 (algorithm)) + 'CALIB')
        else:
            if Config().get('pools.%s.worker_name' %
                            (pool_name))[-5:] != 'CALIB':
                Config().set(
                    'pools.%s.worker_name' % (pool_name),
                    Config().get('pools.%s.worker_name' % (pool_name)) +
                    "CALIB")

        if quick:
            Config().set('calibration.power_tuning.enable', False)

        profile = Profiles().get_for_device_algo(device, algorithm)

        Log().add('info', '     device id: %d' % (device.id))
        Log().add('info', '          pool: %s' % (pool_name))
        Log().add('info', '  device class: %s' % (device.dclass))
        Log().add('info', 'device profile: %s' % (profile.name))
        Log().add('info', '         miner: %s' % (miner.name))
        Log().add('info', '     algorithm: %s' % (algorithm))
        Log().add('info', '        region: %s' % (region))

        Miners().poll()
        Miners().get_device_state(device)

        if device.state != 'inactive':
            if device.state == 'calibrating':
                device.log(
                    'fatal',
                    'this device is already being used for calibration')

            if force:
                device.log('info', 'terminating existing worker')
                if not device.stop(True):
                    device.log('fatal', 'failed to stop existing worker')
            else:
                device.log(
                    'fatal',
                    'unable to start calibration - device is in use (use --force to override)'
                )

        self.set_pin(device.id)

        default_power_limit = device.default_power_limit_f
        min_power_limit = device.min_power_limit_f
        max_power_limit = device.max_power_limit_f

        if max_power_limit == 0:
            device.log('info', 'device does not support power monitoring')
        else:
            device.log('info', 'max power limit: %d W' % (max_power_limit))

        device.log(
            'info', 'stabilisation tolerance: %.2f%%' %
            (Config().get('calibration.hashrate_stabilisation_tolerance')))

        if Config().get('debug'):
            device.log('debug', 'loading default profile')

        Nvidia().set_default_profile(device)

        if Config().get('debug'):
            device.log('debug', 'initialising pools')

        Pools()

        sample_timeout = Config().get(
            'calibration.hashrate_stabilisation_timeout_mins')

        if default_power_limit != None and Config().get(
                'calibration.power_tuning.enable'):
            device.log(
                'info',
                'starting initial run at max power limit [timeout=%dmins]' %
                (sample_timeout))
        else:
            device.log(
                'info',
                'starting single run [timeout=%dmins]' % (sample_timeout))

        Miners().poll()
        Miners().get_device_state(device)

        if device.state != 'calibrating':
            if not device.start(True):
                device.log('fatal', 'worker failed to start')

        if Config().get('calibration.power_tuning.enable'):
            if not device.power_supported():
                device.log('info', 'device does not support power monitoring')
            else:
                device.set_power_limit(max_power_limit)

        initial_hashrate, initial_max_power_draw = self.get_max_hashrate_and_power_draw(
            miner, device, sample_timeout * 60)

        if initial_hashrate == None:
            device.log(
                'info',
                'skipping algorithm as we failed to get a stable reading')
            print ""
            return

        if initial_max_power_draw != None and initial_max_power_draw > max_power_limit:
            initial_max_power_draw = max_power_limit

        device.log(
            'info', 'benchmark hashrate: %s' %
            (Units().hashrate_str(initial_hashrate)))

        if initial_max_power_draw != None:
            device.log('info',
                       'max power draw: %.2f' % (initial_max_power_draw))
            initial_power_limit = int(math.ceil(initial_max_power_draw))

        hashrate = initial_hashrate

        if initial_max_power_draw == None:
            power_limit = None
        elif Config().get('calibration.power_tuning.enable'):
            if max_power_limit == 0 or min_power_limit == 0 or default_power_limit == 0:
                device.log(
                    'error',
                    'device did not give us sane values for its min/max/default power limits'
                )
                device.log(
                    'error',
                    'unable to proceed with power calibration - device may not support changing the power limit'
                )

                power_limit = default_power_limit
            else:
                power_limit = initial_power_limit

                device.log('info', 'tuning power limit for optimum efficiency')
                hashrate, power_limit = self.do_power_calibration(
                    device, power_limit, miner, hashrate, min_power_limit)
        else:
            power_limit = default_power_limit

        Nvidia().set_default_profile(device)

        self.unset_pin(device.id)

        device.log('info', 'storing calibration data')

        Calibration().update_calibration_data(device.dclass, miner.name,
                                              algorithm, hashrate, power_limit)

        if not device.stop(True):
            device.log('fatal', "failed to stop the miner process")

        print ""

        device.log(
            'info',
            '   calibrated hashrate: %s' % (Units().hashrate_str(hashrate)))

        if power_limit != None:
            device.log('info',
                       'calibrated power limit: %.2f W' % (power_limit))

        print ""