コード例 #1
0
ファイル: plugin.py プロジェクト: benny-ua/martiis-tdt
class PositionerSetup(Screen):
    skin = """
		<screen position="100,100" size="560,400" title="Positioner setup..." >
			<widget name="list" position="100,0" size="350,155" />

			<widget name="red" position="0,155" size="140,80" backgroundColor="red" halign="center" valign="center" font="Regular;21" />
			<widget name="green" position="140,155" size="140,80" backgroundColor="green" halign="center" valign="center" font="Regular;21" />
			<widget name="yellow" position="280,155" size="140,80" backgroundColor="yellow" halign="center" valign="center" font="Regular;21" />
			<widget name="blue" position="420,155" size="140,80" backgroundColor="blue" halign="center" valign="center" font="Regular;21" />

			<widget name="snr_db" position="60,245" size="150,22" halign="center" valign="center" font="Regular;21" />
			<eLabel text="SNR:" position="0,270" size="60,22" font="Regular;21" />
			<eLabel text="BER:" position="0,295" size="60,22" font="Regular;21" />
			<eLabel text="Lock:" position="0,320" size="60,22" font="Regular;21" />
			<widget name="snr_percentage" position="220,270" size="60,22" font="Regular;21" />
			<widget name="ber_value" position="220,295" size="60,22" font="Regular;21" />
			<widget name="lock_state" position="60,320" size="150,22" font="Regular;21" />
			<widget name="snr_bar" position="60,270" size="150,22" />
			<widget name="ber_bar" position="60,295" size="150,22" />

			<eLabel text="Frequency:" position="300,245" size="120,22" font="Regular;21" />
			<eLabel text="Symbolrate:" position="300,270" size="120,22" font="Regular;21" />
			<eLabel text="FEC:" position="300,295" size="120,22" font="Regular;21" />
			<widget name="frequency_value" position="420,245" size="120,22" font="Regular;21" />
			<widget name="symbolrate_value" position="420,270" size="120,22" font="Regular;21" />
			<widget name="fec_value" position="420,295" size="120,22" font="Regular;21" />
		</screen>"""

    def __init__(self, session, feid):
        self.skin = PositionerSetup.skin
        Screen.__init__(self, session)
        self.feid = feid
        self.oldref = None

        cur = {}
        if not self.openFrontend():
            self.oldref = session.nav.getCurrentlyPlayingServiceReference()
            service = session.nav.getCurrentService()
            feInfo = service and service.frontendInfo()
            if feInfo:
                cur = feInfo.getTransponderData(True)
            del feInfo
            del service
            session.nav.stopService()  # try to disable foreground service
            if not self.openFrontend():
                if session.pipshown:  # try to disable pip
                    service = self.session.pip.pipservice
                    feInfo = service and service.frontendInfo()
                    if feInfo:
                        cur = feInfo.getTransponderData()
                    del feInfo
                    del service
                    session.pipshown = False
                    del session.pip
                    if not self.openFrontend():
                        self.frontend = None  # in normal case this should not happen

        self.frontendStatus = {}
        self.diseqc = Diseqc(self.frontend)
        self.tuner = Tuner(self.frontend)

        tp = (cur.get("frequency", 0) / 1000, cur.get("symbol_rate", 0) / 1000,
              cur.get("polarization",
                      eDVBFrontendParametersSatellite.Polarisation_Horizontal),
              cur.get("fec_inner", eDVBFrontendParametersSatellite.FEC_Auto),
              cur.get("inversion",
                      eDVBFrontendParametersSatellite.Inversion_Unknown),
              cur.get("orbital_position", 0),
              cur.get("system", eDVBFrontendParametersSatellite.System_DVB_S),
              cur.get("modulation",
                      eDVBFrontendParametersSatellite.Modulation_QPSK),
              cur.get("rolloff",
                      eDVBFrontendParametersSatellite.RollOff_alpha_0_35),
              cur.get("pilot", eDVBFrontendParametersSatellite.Pilot_Unknown))

        self.tuner.tune(tp)
        self.createConfig()

        self.isMoving = False
        self.stopOnLock = False

        self.red = Label("")
        self["red"] = self.red
        self.green = Label("")
        self["green"] = self.green
        self.yellow = Label("")
        self["yellow"] = self.yellow
        self.blue = Label("")
        self["blue"] = self.blue

        self.list = []
        self["list"] = ConfigList(self.list)
        self.createSetup()

        self["snr_db"] = TunerInfo(TunerInfo.SNR_DB,
                                   statusDict=self.frontendStatus)
        self["snr_percentage"] = TunerInfo(TunerInfo.SNR_PERCENTAGE,
                                           statusDict=self.frontendStatus)
        self["ber_value"] = TunerInfo(TunerInfo.BER_VALUE,
                                      statusDict=self.frontendStatus)
        self["snr_bar"] = TunerInfo(TunerInfo.SNR_BAR,
                                    statusDict=self.frontendStatus)
        self["ber_bar"] = TunerInfo(TunerInfo.BER_BAR,
                                    statusDict=self.frontendStatus)
        self["lock_state"] = TunerInfo(TunerInfo.LOCK_STATE,
                                       statusDict=self.frontendStatus)

        self["frequency_value"] = Label("")
        self["symbolrate_value"] = Label("")
        self["fec_value"] = Label("")

        self["actions"] = ActionMap(
            ["DirectionActions", "OkCancelActions", "ColorActions"], {
                "ok": self.go,
                "cancel": self.keyCancel,
                "up": self.up,
                "down": self.down,
                "left": self.left,
                "right": self.right,
                "red": self.redKey,
                "green": self.greenKey,
                "yellow": self.yellowKey,
                "blue": self.blueKey,
            }, -1)

        self.updateColors("tune")

        self.statusTimer = eTimer()
        self.statusTimer.callback.append(self.updateStatus)
        self.statusTimer.start(50, True)
        self.onClose.append(self.__onClose)

    def __onClose(self):
        self.session.nav.playService(self.oldref)

    def restartPrevService(self, yesno):
        if yesno:
            if self.frontend:
                self.frontend = None
                del self.raw_channel
        else:
            self.oldref = None
        self.close(None)

    def keyCancel(self):
        if self.oldref:
            self.session.openWithCallback(
                self.restartPrevService, MessageBox,
                _("Zap back to service before positioner setup?"),
                MessageBox.TYPE_YESNO)
        else:
            self.restartPrevService(False)

    def openFrontend(self):
        res_mgr = eDVBResourceManager.getInstance()
        if res_mgr:
            self.raw_channel = res_mgr.allocateRawChannel(self.feid)
            if self.raw_channel:
                self.frontend = self.raw_channel.getFrontend()
                if self.frontend:
                    return True
                else:
                    print "getFrontend failed"
            else:
                print "getRawChannel failed"
        else:
            print "getResourceManager instance failed"
        return False

    def createConfig(self):
        self.positioner_tune = ConfigNothing()
        self.positioner_move = ConfigNothing()
        self.positioner_finemove = ConfigNothing()
        self.positioner_limits = ConfigNothing()
        self.positioner_goto0 = ConfigNothing()
        storepos = []
        for x in range(1, 255):
            storepos.append(str(x))
        self.positioner_storage = ConfigSelection(choices=storepos)

    def createSetup(self):
        self.list.append((_("Tune"), self.positioner_tune, "tune"))
        self.list.append(
            (_("Positioner movement"), self.positioner_move, "move"))
        self.list.append((_("Positioner fine movement"),
                          self.positioner_finemove, "finemove"))
        self.list.append((_("Set limits"), self.positioner_limits, "limits"))
        self.list.append(
            (_("Positioner storage"), self.positioner_storage, "storage"))
        self.list.append((_("Goto 0"), self.positioner_goto0, "goto0"))
        self["list"].l.setList(self.list)

    def go(self):
        pass

    def getCurrentConfigPath(self):
        return self["list"].getCurrent()[2]

    def up(self):
        if not self.isMoving:
            self["list"].instance.moveSelection(self["list"].instance.moveUp)
            self.updateColors(self.getCurrentConfigPath())

    def down(self):
        if not self.isMoving:
            self["list"].instance.moveSelection(self["list"].instance.moveDown)
            self.updateColors(self.getCurrentConfigPath())

    def left(self):
        self["list"].handleKey(KEY_LEFT)

    def right(self):
        self["list"].handleKey(KEY_RIGHT)

    def updateColors(self, entry):
        if entry == "tune":
            self.red.setText(_("Tune"))
            self.green.setText("")
            self.yellow.setText("")
            self.blue.setText("")
        elif entry == "move":
            if self.isMoving:
                self.red.setText(_("Stop"))
                self.green.setText(_("Stop"))
                self.yellow.setText(_("Stop"))
                self.blue.setText(_("Stop"))
            else:
                self.red.setText(_("Move west"))
                self.green.setText(_("Search west"))
                self.yellow.setText(_("Search east"))
                self.blue.setText(_("Move east"))
        elif entry == "finemove":
            self.red.setText("")
            self.green.setText(_("Step west"))
            self.yellow.setText(_("Step east"))
            self.blue.setText("")
        elif entry == "limits":
            self.red.setText(_("Limits off"))
            self.green.setText(_("Limit west"))
            self.yellow.setText(_("Limit east"))
            self.blue.setText(_("Limits on"))
        elif entry == "storage":
            self.red.setText("")
            self.green.setText(_("Store position"))
            self.yellow.setText(_("Goto position"))
            self.blue.setText("")
        elif entry == "goto0":
            self.red.setText(_("Goto 0"))
            self.green.setText("")
            self.yellow.setText("")
            self.blue.setText("")
        else:
            self.red.setText("")
            self.green.setText("")
            self.yellow.setText("")
            self.blue.setText("")

    def redKey(self):
        entry = self.getCurrentConfigPath()
        if entry == "move":
            if self.isMoving:
                self.diseqccommand("stop")
                self.isMoving = False
                self.stopOnLock = False
            else:
                self.diseqccommand("moveWest", 0)
                self.isMoving = True
            self.updateColors("move")
        elif entry == "limits":
            self.diseqccommand("limitOff")
        elif entry == "tune":
            fe_data = {}
            self.frontend.getFrontendData(fe_data)
            self.frontend.getTransponderData(fe_data, True)
            feparm = self.tuner.lastparm.getDVBS()
            fe_data["orbital_position"] = feparm.orbital_position
            self.session.openWithCallback(self.tune, TunerScreen, self.feid,
                                          fe_data)
        elif entry == "goto0":
            print "move to position 0"
            self.diseqccommand("moveTo", 0)

    def greenKey(self):
        entry = self.getCurrentConfigPath()
        if entry == "move":
            if self.isMoving:
                self.diseqccommand("stop")
                self.isMoving = False
                self.stopOnLock = False
            else:
                self.isMoving = True
                self.stopOnLock = True
                self.diseqccommand("moveWest", 0)
            self.updateColors("move")
        elif entry == "finemove":
            print "stepping west"
            self.diseqccommand("moveWest", 0xFF)  # one step
        elif entry == "storage":
            print "store at position", int(self.positioner_storage.value)
            self.diseqccommand("store", int(self.positioner_storage.value))

        elif entry == "limits":
            self.diseqccommand("limitWest")

    def yellowKey(self):
        entry = self.getCurrentConfigPath()
        if entry == "move":
            if self.isMoving:
                self.diseqccommand("stop")
                self.isMoving = False
                self.stopOnLock = False
            else:
                self.isMoving = True
                self.stopOnLock = True
                self.diseqccommand("moveEast", 0)
            self.updateColors("move")
        elif entry == "finemove":
            print "stepping east"
            self.diseqccommand("moveEast", 0xFF)  # one step
        elif entry == "storage":
            print "move to position", int(self.positioner_storage.value)
            self.diseqccommand("moveTo", int(self.positioner_storage.value))
        elif entry == "limits":
            self.diseqccommand("limitEast")

    def blueKey(self):
        entry = self.getCurrentConfigPath()
        if entry == "move":
            if self.isMoving:
                self.diseqccommand("stop")
                self.isMoving = False
                self.stopOnLock = False
            else:
                self.diseqccommand("moveEast", 0)
                self.isMoving = True
            self.updateColors("move")
            print "moving east"
        elif entry == "limits":
            self.diseqccommand("limitOn")

    def diseqccommand(self, cmd, param=0):
        self.diseqc.command(cmd, param)
        self.tuner.retune()

    def updateStatus(self):
        if self.frontend:
            self.frontend.getFrontendStatus(self.frontendStatus)
        self["snr_db"].update()
        self["snr_percentage"].update()
        self["ber_value"].update()
        self["snr_bar"].update()
        self["ber_bar"].update()
        self["lock_state"].update()
        transponderdata = ConvertToHumanReadable(
            self.tuner.getTransponderData(), "DVB-S")
        self["frequency_value"].setText(str(transponderdata.get("frequency")))
        self["symbolrate_value"].setText(
            str(transponderdata.get("symbol_rate")))
        self["fec_value"].setText(str(transponderdata.get("fec_inner")))
        if self.frontendStatus.get(
                "tuner_locked", 0) == 1 and self.isMoving and self.stopOnLock:
            self.diseqccommand("stop")
            self.isMoving = False
            self.stopOnLock = False
            self.updateColors(self.getCurrentConfigPath())
        self.statusTimer.start(50, True)

    def tune(self, transponder):
        if transponder is not None:
            self.tuner.tune(transponder)
コード例 #2
0
ファイル: plugin.py プロジェクト: OpenESI/Wetek-ESI
class PositionerSetup(Screen):

	@staticmethod
	def satposition2metric(position):
		if position > 1800:
			position = 3600 - position
			orientation = "west"
		else:
			orientation = "east"
		return position, orientation

	@staticmethod
	def orbital2metric(position, orientation):
		if orientation == "west":
			position = 360 - position
		if orientation == "south":
			position = - position
		return position

	@staticmethod
	def longitude2orbital(position):
		if position >= 180:
			return 360 - position, "west"
		else:
			return position, "east"

	@staticmethod
	def latitude2orbital(position):
		if position >= 0:
			return position, "north"
		else:
			return -position, "south"

	UPDATE_INTERVAL = 50					# milliseconds
	STATUS_MSG_TIMEOUT = 2					# seconds
	LOG_SIZE = 16 * 1024					# log buffer size

	def __init__(self, session, feid):
		self.session = session
		Screen.__init__(self, session)
		self.feid = feid
		self.oldref = None
		log.open(self.LOG_SIZE)
		if config.Nims[self.feid].configMode.value == 'advanced':
			self.advanced = True
			self.advancedconfig = config.Nims[self.feid].advanced
			self.advancedsats = self.advancedconfig.sat
			self.availablesats = map(lambda x: x[0], nimmanager.getRotorSatListForNim(self.feid))
		else:
			self.advanced = False

		cur = { }
		if not self.openFrontend():
			self.oldref = session.nav.getCurrentlyPlayingServiceReference()
			service = session.nav.getCurrentService()
			feInfo = service and service.frontendInfo()
			if feInfo:
				cur = feInfo.getTransponderData(True)
			del feInfo
			del service
			session.nav.stopService() # try to disable foreground service
			if not self.openFrontend():
				if session.pipshown: # try to disable pip
					service = self.session.pip.pipservice
					feInfo = service and service.frontendInfo()
					if feInfo:
						cur = feInfo.getTransponderData(True)
					del feInfo
					del service
					from Screens.InfoBar import InfoBar
					InfoBar.instance and hasattr(InfoBar.instance, "showPiP") and InfoBar.instance.showPiP()
				if not self.openFrontend():
					self.frontend = None # in normal case this should not happen
					if hasattr(self, 'raw_channel'):
						del self.raw_channel

		self.frontendStatus = { }
		self.diseqc = Diseqc(self.frontend)
		# True means we dont like that the normal sec stuff sends commands to the rotor!
		self.tuner = Tuner(self.frontend, ignore_rotor = True)

		tp = ( cur.get("frequency", 0) / 1000,
			cur.get("symbol_rate", 0) / 1000,
			cur.get("polarization", eDVBFrontendParametersSatellite.Polarisation_Horizontal),
			cur.get("fec_inner", eDVBFrontendParametersSatellite.FEC_Auto),
			cur.get("inversion", eDVBFrontendParametersSatellite.Inversion_Unknown),
			cur.get("orbital_position", 0),
			cur.get("system", eDVBFrontendParametersSatellite.System_DVB_S),
			cur.get("modulation", eDVBFrontendParametersSatellite.Modulation_QPSK),
			cur.get("rolloff", eDVBFrontendParametersSatellite.RollOff_alpha_0_35),
			cur.get("pilot", eDVBFrontendParametersSatellite.Pilot_Unknown))

		self.tuner.tune(tp)
		self.isMoving = False
		self.stopOnLock = False

		self.red = Button("")
		self["key_red"] = self.red
		self.green = Button("")
		self["key_green"] = self.green
		self.yellow = Button("")
		self["key_yellow"] = self.yellow
		self.blue = Button("")
		self["key_blue"] = self.blue

		self.list = []
		self["list"] = ConfigList(self.list)

		self["snr_db"] = TunerInfo(TunerInfo.SNR_DB, statusDict = self.frontendStatus)
		self["snr_percentage"] = TunerInfo(TunerInfo.SNR_PERCENTAGE, statusDict = self.frontendStatus)
		self["ber_value"] = TunerInfo(TunerInfo.BER_VALUE, statusDict = self.frontendStatus)
		self["snr_bar"] = TunerInfo(TunerInfo.SNR_BAR, statusDict = self.frontendStatus)
		self["ber_bar"] = TunerInfo(TunerInfo.BER_BAR, statusDict = self.frontendStatus)
		self["lock_state"] = TunerInfo(TunerInfo.LOCK_STATE, statusDict = self.frontendStatus)

		self["frequency_value"] = Label("")
		self["symbolrate_value"] = Label("")
		self["fec_value"] = Label("")
		self["polarisation"] = Label("")
		self["status_bar"] = Label("")
		self.statusMsgTimeoutTicks = 0
		self.statusMsgBlinking = False
		self.statusMsgBlinkCount = 0
		self.statusMsgBlinkRate = 500 / self.UPDATE_INTERVAL	# milliseconds
		self.tuningChangedTo(tp)

		self["actions"] = NumberActionMap(["DirectionActions", "OkCancelActions", "ColorActions", "TimerEditActions", "InputActions"],
		{
			"ok": self.keyOK,
			"cancel": self.keyCancel,
			"up": self.keyUp,
			"down": self.keyDown,
			"left": self.keyLeft,
			"right": self.keyRight,
			"red": self.redKey,
			"green": self.greenKey,
			"yellow": self.yellowKey,
			"blue": self.blueKey,
			"log": self.showLog,
			"1": self.keyNumberGlobal,
			"2": self.keyNumberGlobal,
			"3": self.keyNumberGlobal,
			"4": self.keyNumberGlobal,
			"5": self.keyNumberGlobal,
			"6": self.keyNumberGlobal,
			"7": self.keyNumberGlobal,
			"8": self.keyNumberGlobal,
			"9": self.keyNumberGlobal,
			"0": self.keyNumberGlobal
		}, -1)

		self.updateColors("tune")

		self.statusTimer = eTimer()
		self.statusTimer.callback.append(self.updateStatus)
		self.collectingStatistics = False
		self.statusTimer.start(self.UPDATE_INTERVAL, True)
		self.dataAvailable = Event()
		self.onClose.append(self.__onClose)

		self.createConfig()
		self.createSetup()

	def __onClose(self):
		self.statusTimer.stop()
		log.close()
		self.session.nav.playService(self.oldref)

	def restartPrevService(self, yesno):
		if yesno:
			if self.frontend:
				self.frontend = None
				del self.raw_channel
		else:
			self.oldref=None
		self.close(None)

	def keyCancel(self):
		if self.oldref:
			self.session.openWithCallback(self.restartPrevService, MessageBox, _("Zap back to service before positioner setup?"), MessageBox.TYPE_YESNO)
		else:
			self.restartPrevService(False)

	def openFrontend(self):
		res_mgr = eDVBResourceManager.getInstance()
		if res_mgr:
			self.raw_channel = res_mgr.allocateRawChannel(self.feid)
			if self.raw_channel:
				self.frontend = self.raw_channel.getFrontend()
				if self.frontend:
					return True
				else:
					print "getFrontend failed"
			else:
				print "getRawChannel failed"
		else:
			print "getResourceManager instance failed"
		return False

	def setLNB(self, lnb):
		try:
			self.sitelon = lnb.longitude.float
			self.longitudeOrientation = lnb.longitudeOrientation.value
			self.sitelat = lnb.latitude.float
			self.latitudeOrientation = lnb.latitudeOrientation.value
			self.tuningstepsize = lnb.tuningstepsize.float
			self.rotorPositions = lnb.rotorPositions.value
			self.turningspeedH = lnb.turningspeedH.float
			self.turningspeedV = lnb.turningspeedV.float
		except: # some reasonable defaults from NimManager
			self.sitelon = 5.1
			self.longitudeOrientation = 'east'
			self.sitelat = 50.767
			self.latitudeOrientation = 'north'
			self.tuningstepsize = 0.36
			self.rotorPositions = 99
			self.turningspeedH = 2.3
			self.turningspeedV = 1.7
		self.sitelat = PositionerSetup.orbital2metric(self.sitelat, self.latitudeOrientation)
		self.sitelon = PositionerSetup.orbital2metric(self.sitelon, self.longitudeOrientation)

	def getLNBfromConfig(self, orb_pos):
		lnb = None
		if orb_pos in self.availablesats:
			lnbnum = int(self.advancedsats[orb_pos].lnb.value)
			if not lnbnum:
				for allsats in range(3601, 3607):
					lnbnum = int(self.advancedsats[allsats].lnb.value)
					if lnbnum:
						break
			if lnbnum:
				self.printMsg(_("Using LNB %d") % lnbnum)
				lnb = self.advancedconfig.lnb[lnbnum]
		if not lnb:
			self.logMsg(_("Warning: no LNB; using factory defaults."), timeout = 4)
		return lnb

	def createConfig(self):
		rotorposition = 1
		orb_pos = 0
		self.printMsg(_("Using tuner %s") % chr(0x41 + self.feid))
		if not self.advanced:
			self.printMsg(_("Configuration mode: %s") % _("simple"))
			nim = config.Nims[self.feid]
			self.sitelon = nim.longitude.float
			self.longitudeOrientation = nim.longitudeOrientation.value
			self.sitelat = nim.latitude.float
			self.latitudeOrientation = nim.latitudeOrientation.value
			self.sitelat = PositionerSetup.orbital2metric(self.sitelat, self.latitudeOrientation)
			self.sitelon = PositionerSetup.orbital2metric(self.sitelon, self.longitudeOrientation)
			self.tuningstepsize = nim.tuningstepsize.float
			self.rotorPositions = nim.rotorPositions.value
			self.turningspeedH = nim.turningspeedH.float
			self.turningspeedV = nim.turningspeedV.float
		else:	# it is advanced
			self.printMsg(_("Configuration mode: %s") % _("advanced"))
			fe_data = { }
			self.frontend.getFrontendData(fe_data)
			self.frontend.getTransponderData(fe_data, True)
			orb_pos = fe_data.get("orbital_position", None)
			if orb_pos in self.availablesats:
				rotorposition = int(self.advancedsats[orb_pos].rotorposition.value)
			self.setLNB(self.getLNBfromConfig(orb_pos))
		self.positioner_tune = ConfigNothing()
		self.positioner_move = ConfigNothing()
		self.positioner_finemove = ConfigNothing()
		self.positioner_limits = ConfigNothing()
		self.positioner_storage = ConfigInteger(default = rotorposition, limits = (1, self.rotorPositions))
		self.allocatedIndices = []
		m = PositionerSetup.satposition2metric(orb_pos)
		self.orbitalposition = ConfigFloat(default = [int(m[0] / 10), m[0] % 10], limits = [(0,180),(0,9)])
		self.orientation = ConfigSelection([("east", _("East")), ("west", _("West"))], m[1])

	def createSetup(self):
		self.list.append((_("Tune and focus"), self.positioner_tune, "tune"))
		self.list.append((_("Movement"), self.positioner_move, "move"))
		self.list.append((_("Fine movement"), self.positioner_finemove, "finemove"))
		self.list.append((_("Set limits"), self.positioner_limits, "limits"))
		self.list.append((_("Memory index"), self.positioner_storage, "storage"))
		self.list.append((_("Goto"), self.orbitalposition, "goto"))
		self.list.append((" ", self.orientation, "goto"))
		self["list"].l.setList(self.list)

	def keyOK(self):
		pass

	def getCurrentConfigPath(self):
		return self["list"].getCurrent()[2]

	def keyUp(self):
		if not self.isMoving:
			self["list"].instance.moveSelection(self["list"].instance.moveUp)
			self.updateColors(self.getCurrentConfigPath())

	def keyDown(self):
		if not self.isMoving:
			self["list"].instance.moveSelection(self["list"].instance.moveDown)
			self.updateColors(self.getCurrentConfigPath())

	def keyNumberGlobal(self, number):
		self["list"].handleKey(KEY_0 + number)

	def keyLeft(self):
		self["list"].handleKey(KEY_LEFT)

	def keyRight(self):
		self["list"].handleKey(KEY_RIGHT)

	def updateColors(self, entry):
		if entry == "tune":
			self.red.setText(_("Tune"))
			self.green.setText(_("Auto focus"))
			self.yellow.setText(_("Calibrate"))
			self.blue.setText(_("Calculate"))
		elif entry == "move":
			if self.isMoving:
				self.red.setText(_("Stop"))
				self.green.setText(_("Stop"))
				self.yellow.setText(_("Stop"))
				self.blue.setText(_("Stop"))
			else:
				self.red.setText(_("Move west"))
				self.green.setText(_("Search west"))
				self.yellow.setText(_("Search east"))
				self.blue.setText(_("Move east"))
		elif entry == "finemove":
			self.red.setText("")
			self.green.setText(_("Step west"))
			self.yellow.setText(_("Step east"))
			self.blue.setText("")
		elif entry == "limits":
			self.red.setText(_("Limits off"))
			self.green.setText(_("Limit west"))
			self.yellow.setText(_("Limit east"))
			self.blue.setText(_("Limits on"))
		elif entry == "storage":
			self.red.setText("")
			self.green.setText(_("Store position"))
			self.yellow.setText(_("Goto position"))
			if self.advanced:
				self.blue.setText(_("Allocate"))
			else:
				self.blue.setText("")
		elif entry == "goto":
			self.red.setText("")
			self.green.setText(_("Goto 0"))
			self.yellow.setText(_("Goto X"))
			self.blue.setText("")
		else:
			self.red.setText("")
			self.green.setText("")
			self.yellow.setText("")
			self.blue.setText("")

	def printMsg(self, msg):
		print msg
		print>>log, msg

	def stopMoving(self):
		self.printMsg(_("Stop"))
		self.diseqccommand("stop")
		self.isMoving = False
		self.stopOnLock = False
		self.statusMsg(_("Stopped"), timeout = self.STATUS_MSG_TIMEOUT)

	def redKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.stopMoving()
			else:
				self.printMsg(_("Move west"))
				self.diseqccommand("moveWest", 0)
				self.isMoving = True
				self.statusMsg(_("Moving west ..."), blinking = True)
			self.updateColors("move")
		elif entry == "limits":
			self.printMsg(_("Limits off"))
			self.diseqccommand("limitOff")
			self.statusMsg(_("Limits cancelled"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "tune":
			fe_data = { }
			self.frontend.getFrontendData(fe_data)
			self.frontend.getTransponderData(fe_data, True)
			feparm = self.tuner.lastparm.getDVBS()
			fe_data["orbital_position"] = feparm.orbital_position
			self.statusTimer.stop()
			self.session.openWithCallback(self.tune, TunerScreen, self.feid, fe_data)

	def greenKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "tune":
			# Auto focus
			self.printMsg(_("Auto focus"))
			print>>log, (_("Site latitude") + "      : %5.1f %s") % PositionerSetup.latitude2orbital(self.sitelat)
			print>>log, (_("Site longitude") + "     : %5.1f %s") % PositionerSetup.longitude2orbital(self.sitelon)
			Thread(target = self.autofocus).start()
		elif entry == "move":
			if self.isMoving:
				self.stopMoving()
			else:
				self.printMsg(_("Search west"))
				self.isMoving = True
				self.stopOnLock = True
				self.diseqccommand("moveWest", 0)
				self.statusMsg(_("Searching west ..."), blinking = True)
			self.updateColors("move")
		elif entry == "finemove":
			self.printMsg(_("Step west"))
			self.diseqccommand("moveWest", 0xFF) # one step
			self.statusMsg(_("Stepped west"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "storage":
			self.printMsg(_("Store at index"))
			index = int(self.positioner_storage.value)
			self.diseqccommand("store", index)
			self.statusMsg((_("Position stored at index") + " %2d") % index, timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "limits":
			self.printMsg(_("Limit west"))
			self.diseqccommand("limitWest")
			self.statusMsg(_("West limit set"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "goto":
			self.printMsg(_("Goto 0"))
			self.diseqccommand("moveTo", 0)
			self.statusMsg(_("Moved to position 0"), timeout = self.STATUS_MSG_TIMEOUT)

	def yellowKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.stopMoving()
			else:
				self.printMsg(_("Move east"))
				self.isMoving = True
				self.stopOnLock = True
				self.diseqccommand("moveEast", 0)
				self.statusMsg(_("Searching east ..."), blinking = True)
			self.updateColors("move")
		elif entry == "finemove":
			self.printMsg(_("Step east"))
			self.diseqccommand("moveEast", 0xFF) # one step
			self.statusMsg(_("Stepped east"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "storage":
			self.printMsg(_("Goto index position"))
			index = int(self.positioner_storage.value)
			self.diseqccommand("moveTo", index)
			self.statusMsg((_("Moved to position at index") + " %2d") % index, timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "limits":
			self.printMsg(_("Limit east"))
			self.diseqccommand("limitEast")
			self.statusMsg(_("East limit set"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "goto":
			self.printMsg(_("Move to position X"))
			satlon = self.orbitalposition.float
			position = "%5.1f %s" % (satlon, self.orientation.value)
			print>>log, (_("Satellite longitude:") + " %s") % position
			satlon = PositionerSetup.orbital2metric(satlon, self.orientation.value)
			self.statusMsg((_("Moving to position") + " %s") % position, timeout = self.STATUS_MSG_TIMEOUT)
			self.gotoX(satlon)
		elif entry == "tune":
			# Start USALS calibration
			self.printMsg(_("USALS calibration"))
			print>>log, (_("Site latitude") + "      : %5.1f %s") % PositionerSetup.latitude2orbital(self.sitelat)
			print>>log, (_("Site longitude") + "     : %5.1f %s") % PositionerSetup.longitude2orbital(self.sitelon)
			Thread(target = self.gotoXcalibration).start()

	def blueKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.stopMoving()
			else:
				self.printMsg(_("Move east"))
				self.diseqccommand("moveEast", 0)
				self.isMoving = True
				self.statusMsg(_("Moving east ..."), blinking = True)
			self.updateColors("move")
		elif entry == "limits":
			self.printMsg(_("Limits on"))
			self.diseqccommand("limitOn")
			self.statusMsg(_("Limits enabled"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "tune":
			# Start (re-)calculate
			self.session.openWithCallback(self.recalcConfirmed, MessageBox, _("This will (re-)calculate all positions of your rotor and may remove previously memorised positions and fine-tuning!\nAre you sure?"), MessageBox.TYPE_YESNO, default = False, timeout = 10)
		elif entry == "storage":
			if self.advanced:
				self.printMsg(_("Allocate unused memory index"))
				while True:
					if not len(self.allocatedIndices):
						for sat in self.availablesats:
							current_index = int(self.advancedsats[sat].rotorposition.value)
							if current_index not in self.allocatedIndices:
								self.allocatedIndices.append(current_index)
						if len(self.allocatedIndices) == self.rotorPositions:
							self.statusMsg(_("No free index available"), timeout = self.STATUS_MSG_TIMEOUT)
							break
					index = 1
					if len(self.allocatedIndices):
						for i in sorted(self.allocatedIndices):
							if i != index:
								break
							index += 1
					if index <= self.rotorPositions:
						self.positioner_storage.value = index
						self["list"].invalidateCurrent()
						self.allocatedIndices.append(index)
						self.statusMsg((_("Index allocated:") + " %2d") % index, timeout = self.STATUS_MSG_TIMEOUT)
						break
					else:
						self.allocatedIndices = []

	def recalcConfirmed(self, yesno):
		if yesno:
			self.printMsg(_("Calculate all positions"))
			print>>log, (_("Site latitude") + "      : %5.1f %s") % PositionerSetup.latitude2orbital(self.sitelat)
			print>>log, (_("Site longitude") + "     : %5.1f %s") % PositionerSetup.longitude2orbital(self.sitelon)
			lon = self.sitelon
			if lon >= 180:
				lon -= 360
			if lon < -30:	# americas, make unsigned binary west positive polarity
				lon = -lon
			lon = int(round(lon)) & 0xFF
			lat = int(round(self.sitelat)) & 0xFF
			index = int(self.positioner_storage.value) & 0xFF
			self.diseqccommand("calc", (((index << 8) | lon) << 8) | lat)
			self.statusMsg(_("Calculation complete"), timeout = self.STATUS_MSG_TIMEOUT)

	def showLog(self):
		self.session.open(PositionerSetupLog)

	def diseqccommand(self, cmd, param = 0):
		print>>log, "Diseqc(%s, %X)" % (cmd, param)
		self.diseqc.command(cmd, param)
		self.tuner.retune()

	def tune(self, transponder):
		# re-start the update timer
		self.statusTimer.start(self.UPDATE_INTERVAL, True)
		if transponder is not None:
			self.tuner.tune(transponder)
			self.tuningChangedTo(transponder)
		feparm = self.tuner.lastparm.getDVBS()
		orb_pos = feparm.orbital_position
		m = PositionerSetup.satposition2metric(orb_pos)
		self.orbitalposition.value = [int(m[0] / 10), m[0] % 10]
		self.orientation.value = m[1]
		if self.advanced:
			if orb_pos in self.availablesats:
				rotorposition = int(self.advancedsats[orb_pos].rotorposition.value)
				self.positioner_storage.value = rotorposition
				self.allocatedIndices = []
			self.setLNB(self.getLNBfromConfig(orb_pos))

	def isLocked(self):
		return self.frontendStatus.get("tuner_locked", 0) == 1

	def statusMsg(self, msg, blinking = False, timeout = 0):			# timeout in seconds
		self.statusMsgBlinking = blinking
		if not blinking:
			self["status_bar"].visible = True
		self["status_bar"].setText(msg)
		self.statusMsgTimeoutTicks = (timeout * 1000 + self.UPDATE_INTERVAL / 2) / self.UPDATE_INTERVAL

	def updateStatus(self):
		self.statusTimer.start(self.UPDATE_INTERVAL, True)
		if self.frontend:
			self.frontend.getFrontendStatus(self.frontendStatus)
		self["snr_db"].update()
		self["snr_percentage"].update()
		self["ber_value"].update()
		self["snr_bar"].update()
		self["ber_bar"].update()
		self["lock_state"].update()
		if self.statusMsgBlinking:
			self.statusMsgBlinkCount += 1
			if self.statusMsgBlinkCount == self.statusMsgBlinkRate:
				self.statusMsgBlinkCount = 0
				self["status_bar"].visible = not self["status_bar"].visible
		if self.statusMsgTimeoutTicks > 0:
			self.statusMsgTimeoutTicks -= 1
			if self.statusMsgTimeoutTicks == 0:
				self["status_bar"].setText("")
				self.statusMsgBlinking = False
				self["status_bar"].visible = True
		if self.isLocked() and self.isMoving and self.stopOnLock:
			self.stopMoving()
			self.updateColors(self.getCurrentConfigPath())
		if self.collectingStatistics:
			self.low_rate_adapter_count += 1
			if self.low_rate_adapter_count == self.MAX_LOW_RATE_ADAPTER_COUNT:
				self.low_rate_adapter_count = 0
				self.snr_percentage += self["snr_percentage"].getValue(TunerInfo.SNR)
				self.lock_count += self["lock_state"].getValue(TunerInfo.LOCK)
				self.stat_count += 1
				if self.stat_count == self.max_count:
					self.collectingStatistics = False
					count = float(self.stat_count)
					self.lock_count /= count
					self.snr_percentage *= 100.0 / 0x10000 / count
					self.dataAvailable.set()

	def tuningChangedTo(self, tp):

		def setLowRateAdapterCount(symbolrate):
			# change the measurement time and update interval in case of low symbol rate,
			# since more time is needed for the front end in that case.
			# It is an heuristic determination without any pretence. For symbol rates
			# of 5000 the interval is multiplied by 3 until 15000 which is seen
			# as a high symbol rate. Linear interpolation elsewhere.
			return max(int(round((3 - 1) * (symbolrate - 15000) / (5000 - 15000) + 1)), 1)

		self.symbolrate = tp[1]
		self.polarisation = tp[2]
		self.MAX_LOW_RATE_ADAPTER_COUNT = setLowRateAdapterCount(self.symbolrate)
		transponderdata = ConvertToHumanReadable(self.tuner.getTransponderData(), "DVB-S")
		self["frequency_value"].setText(str(transponderdata.get("frequency")))
		self["symbolrate_value"].setText(str(transponderdata.get("symbol_rate")))
		self["fec_value"].setText(str(transponderdata.get("fec_inner")))
		self["polarisation"].setText(str(transponderdata.get("polarization")))
	
	@staticmethod
	def rotorCmd2Step(rotorCmd, stepsize):
		return round(float(rotorCmd & 0xFFF) / 0x10 / stepsize) * (1 - ((rotorCmd & 0x1000) >> 11))

	@staticmethod
	def gotoXcalc(satlon, sitelat, sitelon):
		def azimuth2Rotorcode(angle):
			gotoXtable = (0x00, 0x02, 0x03, 0x05, 0x06, 0x08, 0x0A, 0x0B, 0x0D, 0x0E)
			a = int(round(abs(angle) * 10.0))
			return ((a / 10) << 4) + gotoXtable[a % 10]

		satHourAngle = rotor_calc.calcSatHourangle(satlon, sitelat, sitelon)
		if sitelat >= 0: # Northern Hemisphere
			rotorCmd = azimuth2Rotorcode(180 - satHourAngle)
			if satHourAngle <= 180: # the east
				rotorCmd |= 0xE000
			else:					# west
				rotorCmd |= 0xD000
		else: # Southern Hemisphere
			if satHourAngle <= 180: # the east
				rotorCmd = azimuth2Rotorcode(satHourAngle) | 0xD000
			else: # west
				rotorCmd = azimuth2Rotorcode(360 - satHourAngle) | 0xE000
		return rotorCmd

	def gotoX(self, satlon):
		rotorCmd = PositionerSetup.gotoXcalc(satlon, self.sitelat, self.sitelon)
		self.diseqccommand("gotoX", rotorCmd)
		x = PositionerSetup.rotorCmd2Step(rotorCmd, self.tuningstepsize)
		print>>log, (_("Rotor step position:") + " %4d") % x
		return x

	def getTurningspeed(self):
		if self.polarisation == eDVBFrontendParametersSatellite.Polarisation_Horizontal:
			turningspeed = self.turningspeedH
		else:
			turningspeed = self.turningspeedV
		return max(turningspeed, 0.1)

	TURNING_START_STOP_DELAY = 1.600	# seconds
	MAX_SEARCH_ANGLE = 12.0				# degrees
	MAX_FOCUS_ANGLE = 6.0				# degrees
	LOCK_LIMIT = 0.1					# ratio
	MEASURING_TIME = 2.500				# seconds

	def measure(self, time = MEASURING_TIME):	# time in seconds
		self.snr_percentage = 0.0
		self.lock_count = 0.0
		self.stat_count = 0
		self.low_rate_adapter_count = 0
		self.max_count = max(int((time * 1000 + self.UPDATE_INTERVAL / 2)/ self.UPDATE_INTERVAL), 1)
		self.collectingStatistics = True
		self.dataAvailable.clear()
		self.dataAvailable.wait()

	def logMsg(self, msg, timeout = 0):
		self.statusMsg(msg, timeout = timeout)
		self.printMsg(msg)

	def sync(self):
		self.lock_count = 0.0
		n = 0
		while self.lock_count < (1 - self.LOCK_LIMIT) and n < 5:
			self.measure(time = 0.500)
			n += 1
		if self.lock_count < (1 - self.LOCK_LIMIT):
			return False
		return True

	randomGenerator = None
	def randomBool(self):
		if self.randomGenerator is None:
			self.randomGenerator = SystemRandom()
		return self.randomGenerator.random() >= 0.5

	def gotoXcalibration(self):

		def move(x):
			z = self.gotoX(x + satlon)
			time = int(abs(x - prev_pos) / turningspeed + 2 * self.TURNING_START_STOP_DELAY)
			sleep(time * self.MAX_LOW_RATE_ADAPTER_COUNT)
			return z

		def reportlevels(pos, level, lock):
			print>>log, (_("Signal quality") + " %5.1f" + chr(176) + "   : %6.2f") % (pos, level)
			print>>log, (_("Lock ratio") + "     %5.1f" + chr(176) + "   : %6.2f") % (pos, lock)

		def optimise(readings):
			xi = readings.keys()
			yi = map(lambda (x, y) : x, readings.values())
			x0 = sum(map(mul, xi, yi)) / sum(yi)
			xm = xi[yi.index(max(yi))]
			return x0, xm

		def toGeopos(x):
			if x < 0:
				return _("W")
			else:
				return _("E")

		def toGeoposEx(x):
			if x < 0:
				return _("west")
			else:
				return _("east")

		self.logMsg(_("GotoX calibration"))
		satlon = self.orbitalposition.float
		print>>log, (_("Satellite longitude:") + " %5.1f" + chr(176) + " %s") % (satlon, self.orientation.value)
		satlon = PositionerSetup.orbital2metric(satlon, self.orientation.value)
		prev_pos = 0.0						# previous relative position w.r.t. satlon
		turningspeed = self.getTurningspeed()

		x = 0.0								# relative position w.r.t. satlon
		dir = 1
		if self.randomBool():
			dir = -dir
		while abs(x) < self.MAX_SEARCH_ANGLE:
			if self.sync():
				break
			x += (1.0 * dir)						# one degree east/west
			self.statusMsg((_("Searching") + " " + toGeoposEx(dir) + " %2d" + chr(176)) % abs(x), blinking = True)
			move(x)
			prev_pos = x
		else:
			x = 0.0
			dir = -dir
			while abs(x) < self.MAX_SEARCH_ANGLE:
				x += (1.0 * dir)					# one degree east/west
				self.statusMsg((_("Searching") + " " + toGeoposEx(dir) + " %2d" + chr(176)) % abs(x), blinking = True)
				move(x)
				prev_pos = x
				if self.sync():
					break
			else:
				msg = _("Cannot find any signal ..., aborting !")
				self.printMsg(msg)
				self.statusMsg("")
				self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
				return
		x = round(x / self.tuningstepsize) * self.tuningstepsize
		move(x)
		prev_pos = x
		measurements = {}
		self.measure()
		print>>log, (_("Initial signal quality") + " %5.1f" + chr(176) + ": %6.2f") % (x, self.snr_percentage)
		print>>log, (_("Initial lock ratio") + "     %5.1f" + chr(176) + ": %6.2f") % (x, self.lock_count)
		measurements[x] = (self.snr_percentage, self.lock_count)

		start_pos = x
		x = 0.0
		dir = 1
		if self.randomBool():
			dir = -dir
		while x < self.MAX_FOCUS_ANGLE:
			x += self.tuningstepsize * dir					# one step east/west
			self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %5.1f" + chr(176)) % abs(x + start_pos), blinking = True)
			move(x + start_pos)
			prev_pos = x + start_pos
			self.measure()
			measurements[x + start_pos] = (self.snr_percentage, self.lock_count)
			reportlevels(x + start_pos, self.snr_percentage, self.lock_count)
			if self.lock_count < self.LOCK_LIMIT:
				break
		else:
			msg = _("Cannot determine") + " " + toGeoposEx(dir) + " " + _("limit ..., aborting !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		x = 0.0
		dir = -dir
		self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %5.1f" + chr(176)) % abs(start_pos), blinking = True)
		move(start_pos)
		prev_pos = start_pos
		if not self.sync():
			msg = _("Sync failure moving back to origin !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		while abs(x) < self.MAX_FOCUS_ANGLE:
			x += self.tuningstepsize * dir					# one step west/east
			self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %5.1f" + chr(176)) % abs(x + start_pos), blinking = True)
			move(x + start_pos)
			prev_pos = x + start_pos
			self.measure()
			measurements[x + start_pos] = (self.snr_percentage, self.lock_count)
			reportlevels(x + start_pos, self.snr_percentage, self.lock_count)
			if self.lock_count < self.LOCK_LIMIT:
				break
		else:
			msg = _("Cannot determine") + " " + toGeoposEx(dir) + " " + _("limit ..., aborting !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		(x0, xm) = optimise(measurements)
		x = move(x0)
		if satlon > 180:
			satlon -= 360
		x0 += satlon
		xm += satlon
		print>>log, (_("Weighted position") + "     : %5.1f" + chr(176) + " %s") % (abs(x0), toGeopos(x0))
		print>>log, (_("Strongest position") + "    : %5.1f" + chr(176) + " %s") % (abs(xm), toGeopos(xm))
		self.logMsg((_("Final position at") + " %5.1f" + chr(176) + " %s / %d; " + _("offset is") + " %4.1f" + chr(176)) % (abs(x0), toGeopos(x0), x, x0 - satlon), timeout = 10)

	def autofocus(self):

		def move(x):
			if x > 0:
				self.diseqccommand("moveEast", (-x) & 0xFF)
			elif x < 0:
				self.diseqccommand("moveWest", x & 0xFF)
			if x != 0:
				time = int(abs(x) * self.tuningstepsize / turningspeed + 2 * self.TURNING_START_STOP_DELAY)
				sleep(time * self.MAX_LOW_RATE_ADAPTER_COUNT)

		def reportlevels(pos, level, lock):
			print>>log, (_("Signal quality") + " [%2d]   : %6.2f") % (pos, level)
			print>>log, (_("Lock ratio") + " [%2d]       : %6.2f") % (pos, lock)

		def optimise(readings):
			xi = readings.keys()
			yi = map(lambda (x, y) : x, readings.values())
			x0 = int(round(sum(map(mul, xi, yi)) / sum(yi)))
			xm = xi[yi.index(max(yi))]
			return x0, xm

		def toGeoposEx(x):
			if x < 0:
				return _("west")
			else:
				return _("east")

		self.logMsg(_("Auto focus commencing ..."))
		turningspeed = self.getTurningspeed()
		measurements = {}
		maxsteps = max(min(round(self.MAX_FOCUS_ANGLE / self.tuningstepsize), 0x1F), 3)
		self.measure()
		print>>log, (_("Initial signal quality:") + " %6.2f") % self.snr_percentage
		print>>log, (_("Initial lock ratio") + "    : %6.2f") % self.lock_count
		if self.lock_count < 1 - self.LOCK_LIMIT:
			msg = _("There is no signal to lock on !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		print>>log, _("Signal OK, proceeding")
		x = 0
		dir = 1
		if self.randomBool():
			dir = -dir
		measurements[x] = (self.snr_percentage, self.lock_count)
		nsteps = 0
		while nsteps < maxsteps:
			x += dir
			self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %2d") % abs(x), blinking = True)
			move(dir) 		# one step
			self.measure()
			measurements[x] = (self.snr_percentage, self.lock_count)
			reportlevels(x, self.snr_percentage, self.lock_count)
			if self.lock_count < self.LOCK_LIMIT:
				break
			nsteps += 1
		else:
			msg = _("Cannot determine") + " " + toGeoposEx(dir) + " " + _("limit ..., aborting !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		dir = -dir
		self.statusMsg(_("Moving") + " " + toGeoposEx(dir) + "  0", blinking = True)
		move(-x)
		if not self.sync():
			msg = _("Sync failure moving back to origin !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		x = 0
		nsteps = 0
		while nsteps < maxsteps:
			x += dir
			self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %2d") % abs(x), blinking = True)
			move(dir) 		# one step
			self.measure()
			measurements[x] = (self.snr_percentage, self.lock_count)
			reportlevels(x, self.snr_percentage, self.lock_count)
			if self.lock_count < self.LOCK_LIMIT:
				break
			nsteps += 1
		else:
			msg = _("Cannot determine") + " " + toGeoposEx(dir) + " " + _("limit ..., aborting !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		(x0, xm) = optimise(measurements)
		print>>log, (_("Weighted position") + "     : %2d") % x0
		print>>log, (_("Strongest position") + "    : %2d") % xm
		self.logMsg((_("Final position at index") + " %2d (%5.1f" + chr(176) + ")") % (x0, x0 * self.tuningstepsize), timeout = 6)
		move(x0 - x)
コード例 #3
0
ファイル: plugin.py プロジェクト: popazerty/12
class PositionerSetup(Screen):
	skin = """
		<screen position="100,100" size="560,400" title="Positioner setup..." >
			<widget name="list" position="100,0" size="350,155" />

			<widget name="red" position="0,155" size="140,80" backgroundColor="red" halign="center" valign="center" font="Regular;21" />
			<widget name="green" position="140,155" size="140,80" backgroundColor="green" halign="center" valign="center" font="Regular;21" />
			<widget name="yellow" position="280,155" size="140,80" backgroundColor="yellow" halign="center" valign="center" font="Regular;21" />
			<widget name="blue" position="420,155" size="140,80" backgroundColor="blue" halign="center" valign="center" font="Regular;21" />

			<widget name="snr_db" position="60,245" size="150,22" halign="center" valign="center" font="Regular;21" />
			<eLabel text="SNR:" position="0,270" size="60,22" font="Regular;21" />
			<eLabel text="BER:" position="0,295" size="60,22" font="Regular;21" />
			<eLabel text="Lock:" position="0,320" size="60,22" font="Regular;21" />
			<widget name="snr_percentage" position="220,270" size="60,22" font="Regular;21" />
			<widget name="ber_value" position="220,295" size="60,22" font="Regular;21" />
			<widget name="lock_state" position="60,320" size="150,22" font="Regular;21" />
			<widget name="snr_bar" position="60,270" size="150,22" />
			<widget name="ber_bar" position="60,295" size="150,22" />

			<eLabel text="Frequency:" position="300,245" size="120,22" font="Regular;21" />
			<eLabel text="Symbolrate:" position="300,270" size="120,22" font="Regular;21" />
			<eLabel text="FEC:" position="300,295" size="120,22" font="Regular;21" />
			<widget name="frequency_value" position="420,245" size="120,22" font="Regular;21" />
			<widget name="symbolrate_value" position="420,270" size="120,22" font="Regular;21" />
			<widget name="fec_value" position="420,295" size="120,22" font="Regular;21" />
		</screen>"""
	def __init__(self, session, feid):
		self.skin = PositionerSetup.skin
		Screen.__init__(self, session)
		self.feid = feid
		self.oldref = None

		cur = { }
		if not self.openFrontend():
			self.oldref = session.nav.getCurrentlyPlayingServiceReference()
			service = session.nav.getCurrentService()
			feInfo = service and service.frontendInfo()
			if feInfo:
				cur = feInfo.getTransponderData(True)
			del feInfo
			del service
			session.nav.stopService() # try to disable foreground service
			if not self.openFrontend():
				if session.pipshown: # try to disable pip
					service = self.session.pip.pipservice
					feInfo = service and service.frontendInfo()
					if feInfo:
						cur = feInfo.getTransponderData()
					del feInfo
					del service
					session.pipshown = False
					session.deleteDialog(session.pip)
					del session.pip
					if not self.openFrontend():
						self.frontend = None # in normal case this should not happen

		self.frontendStatus = { }
		self.diseqc = Diseqc(self.frontend)
		self.tuner = Tuner(self.frontend, True) #True means we dont like that the normal sec stuff sends commands to the rotor!

		tp = ( cur.get("frequency", 0) / 1000,
			cur.get("symbol_rate", 0) / 1000,
			cur.get("polarization", eDVBFrontendParametersSatellite.Polarisation_Horizontal),
			cur.get("fec_inner", eDVBFrontendParametersSatellite.FEC_Auto),
			cur.get("inversion", eDVBFrontendParametersSatellite.Inversion_Unknown),
			cur.get("orbital_position", 0),
			cur.get("system", eDVBFrontendParametersSatellite.System_DVB_S),
			cur.get("modulation", eDVBFrontendParametersSatellite.Modulation_QPSK),
			cur.get("rolloff", eDVBFrontendParametersSatellite.RollOff_alpha_0_35),
			cur.get("pilot", eDVBFrontendParametersSatellite.Pilot_Unknown))

		self.tuner.tune(tp)
		self.createConfig()

		self.isMoving = False
		self.stopOnLock = False

		self.red = Label("")
		self["red"] = self.red
		self.green = Label("")
		self["green"] = self.green
		self.yellow = Label("")
		self["yellow"] = self.yellow
		self.blue = Label("")
		self["blue"] = self.blue

		self.list = []
		self["list"] = ConfigList(self.list)
		self.createSetup()

		self["snr_db"] = TunerInfo(TunerInfo.SNR_DB, statusDict = self.frontendStatus)
		self["snr_percentage"] = TunerInfo(TunerInfo.SNR_PERCENTAGE, statusDict = self.frontendStatus)
		self["ber_value"] = TunerInfo(TunerInfo.BER_VALUE, statusDict = self.frontendStatus)
		self["snr_bar"] = TunerInfo(TunerInfo.SNR_BAR, statusDict = self.frontendStatus)
		self["ber_bar"] = TunerInfo(TunerInfo.BER_BAR, statusDict = self.frontendStatus)
		self["lock_state"] = TunerInfo(TunerInfo.LOCK_STATE, statusDict = self.frontendStatus)

		self["frequency_value"] = Label("")
		self["symbolrate_value"] = Label("")
		self["fec_value"] = Label("")

		self["actions"] = ActionMap(["DirectionActions", "OkCancelActions", "ColorActions"],
		{
			"ok": self.go,
			"cancel": self.keyCancel,
			"up": self.up,
			"down": self.down,
			"left": self.left,
			"right": self.right,
			"red": self.redKey,
			"green": self.greenKey,
			"yellow": self.yellowKey,
			"blue": self.blueKey,
		}, -1)

		self.updateColors("tune")

		self.statusTimer = eTimer()
		self.statusTimer_conn = self.statusTimer.timeout.connect(self.updateStatus)
		self.statusTimer.start(50, True)
		self.onClose.append(self.__onClose)

	def __onClose(self):
		self.session.nav.playService(self.oldref)

	def restartPrevService(self, yesno):
		if yesno:
			if self.frontend:
				self.frontend = None
				del self.raw_channel
		else:
			self.oldref=None
		self.close(None)

	def keyCancel(self):
		if self.oldref:
			self.session.openWithCallback(self.restartPrevService, MessageBox, _("Zap back to service before positioner setup?"), MessageBox.TYPE_YESNO)
		else:
			self.restartPrevService(False)

	def openFrontend(self):
		res_mgr = eDVBResourceManager.getInstance()
		if res_mgr:
			self.raw_channel = res_mgr.allocateRawChannel(self.feid)
			if self.raw_channel:
				self.frontend = self.raw_channel.getFrontend()
				if self.frontend:
					return True
				else:
					print "getFrontend failed"
			else:
				print "getRawChannel failed"
		else:
			print "getResourceManager instance failed"
		return False

	def createConfig(self):
		self.positioner_tune = ConfigNothing()
		self.positioner_move = ConfigNothing()
		self.positioner_finemove = ConfigNothing()
		self.positioner_limits = ConfigNothing()
		self.positioner_goto0 = ConfigNothing()
		storepos = []
		for x in range(1,255):
			storepos.append(str(x))
		self.positioner_storage = ConfigSelection(choices = storepos)

	def createSetup(self):
		self.list.append((_("Tune"), self.positioner_tune, "tune"))
		self.list.append((_("Positioner movement"), self.positioner_move, "move"))
		self.list.append((_("Positioner fine movement"), self.positioner_finemove, "finemove"))
		self.list.append((_("Set limits"), self.positioner_limits, "limits"))
		self.list.append((_("Positioner storage"), self.positioner_storage, "storage"))
		self.list.append((_("Goto 0"), self.positioner_goto0, "goto0"))
		self["list"].l.setList(self.list)

	def go(self):
		pass

	def getCurrentConfigPath(self):
		return self["list"].getCurrent()[2]

	def up(self):
		if not self.isMoving:
			self["list"].instance.moveSelection(self["list"].instance.moveUp)
			self.updateColors(self.getCurrentConfigPath())

	def down(self):
		if not self.isMoving:
			self["list"].instance.moveSelection(self["list"].instance.moveDown)
			self.updateColors(self.getCurrentConfigPath())

	def left(self):
		self["list"].handleKey(KEY_LEFT)

	def right(self):
		self["list"].handleKey(KEY_RIGHT)

	def updateColors(self, entry):
		if entry == "tune":
			self.red.setText(_("Tune"))
			self.green.setText("")
			self.yellow.setText("")
			self.blue.setText("")
		elif entry == "move":
			if self.isMoving:
				self.red.setText(_("Stop"))
				self.green.setText(_("Stop"))
				self.yellow.setText(_("Stop"))
				self.blue.setText(_("Stop"))
			else:
				self.red.setText(_("Move west"))
				self.green.setText(_("Search west"))
				self.yellow.setText(_("Search east"))
				self.blue.setText(_("Move east"))
		elif entry == "finemove":
			self.red.setText("")
			self.green.setText(_("Step west"))
			self.yellow.setText(_("Step east"))
			self.blue.setText("")
		elif entry == "limits":
			self.red.setText(_("Limits off"))
			self.green.setText(_("Limit west"))
			self.yellow.setText(_("Limit east"))
			self.blue.setText(_("Limits on"))
		elif entry == "storage":
			self.red.setText("")
			self.green.setText(_("Store position"))
			self.yellow.setText(_("Goto position"))
			self.blue.setText("")
		elif entry == "goto0":
			self.red.setText(_("Goto 0"))
			self.green.setText("")
			self.yellow.setText("")
			self.blue.setText("")
		else:
			self.red.setText("")
			self.green.setText("")
			self.yellow.setText("")
			self.blue.setText("")

	def redKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.diseqccommand("stop")
				self.isMoving = False
				self.stopOnLock = False
			else:
				self.diseqccommand("moveWest", 0)
				self.isMoving = True
			self.updateColors("move")
		elif entry == "limits":
			self.diseqccommand("limitOff")
		elif entry == "tune":
			fe_data = { }
			self.frontend.getFrontendData(fe_data)
			self.frontend.getTransponderData(fe_data, True)
			feparm = self.tuner.lastparm.getDVBS()
			fe_data["orbital_position"] = feparm.orbital_position
			self.session.openWithCallback(self.tune, TunerScreen, self.feid, fe_data)
		elif entry == "goto0":
			print "move to position 0"
			self.diseqccommand("moveTo", 0)

	def greenKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.diseqccommand("stop")
				self.isMoving = False
				self.stopOnLock = False
			else:
				self.isMoving = True
				self.stopOnLock = True
				self.diseqccommand("moveWest", 0)
			self.updateColors("move")
		elif entry == "finemove":
			print "stepping west"
			self.diseqccommand("moveWest", 0xFF) # one step
		elif entry == "storage":
			print "store at position", int(self.positioner_storage.value)
			self.diseqccommand("store", int(self.positioner_storage.value))

		elif entry == "limits":
			self.diseqccommand("limitWest")

	def yellowKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.diseqccommand("stop")
				self.isMoving = False
				self.stopOnLock = False
			else:
				self.isMoving = True
				self.stopOnLock = True
				self.diseqccommand("moveEast", 0)
			self.updateColors("move")
		elif entry == "finemove":
			print "stepping east"
			self.diseqccommand("moveEast", 0xFF) # one step
		elif entry == "storage":
			print "move to position", int(self.positioner_storage.value)
			self.diseqccommand("moveTo", int(self.positioner_storage.value))
		elif entry == "limits":
			self.diseqccommand("limitEast")

	def blueKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.diseqccommand("stop")
				self.isMoving = False
				self.stopOnLock = False
			else:
				self.diseqccommand("moveEast", 0)
				self.isMoving = True
			self.updateColors("move")
			print "moving east"
		elif entry == "limits":
			self.diseqccommand("limitOn")

	def diseqccommand(self, cmd, param = 0):
		self.diseqc.command(cmd, param)
		self.tuner.retune()

	def updateStatus(self):
		if self.frontend:
			self.frontendStatus.clear()
			self.frontend.getFrontendStatus(self.frontendStatus)
		self["snr_db"].update()
		self["snr_percentage"].update()
		self["ber_value"].update()
		self["snr_bar"].update()
		self["ber_bar"].update()
		self["lock_state"].update()
		transponderdata = ConvertToHumanReadable(self.tuner.getTransponderData(), "DVB-S")
		self["frequency_value"].setText(str(transponderdata.get("frequency")))
		self["symbolrate_value"].setText(str(transponderdata.get("symbol_rate")))
		self["fec_value"].setText(str(transponderdata.get("fec_inner")))
		if self.frontendStatus.get("tuner_locked", 0) == 1 and self.isMoving and self.stopOnLock:
			self.diseqccommand("stop")
			self.isMoving = False
			self.stopOnLock = False
			self.updateColors(self.getCurrentConfigPath())
		self.statusTimer.start(50, True)

	def tune(self, transponder):
		if transponder is not None:
			self.tuner.tune(transponder)
コード例 #4
0
class PositionerSetup(Screen):

	@staticmethod
	def satposition2metric(position):
		if position > 1800:
			position = 3600 - position
			orientation = "west"
		else:
			orientation = "east"
		return position, orientation

	@staticmethod
	def orbital2metric(position, orientation):
		if orientation == "west":
			position = 360 - position
		if orientation == "south":
			position = - position
		return position

	@staticmethod
	def longitude2orbital(position):
		if position >= 180:
			return 360 - position, "west"
		else:
			return position, "east"

	@staticmethod
	def latitude2orbital(position):
		if position >= 0:
			return position, "north"
		else:
			return -position, "south"

	UPDATE_INTERVAL = 50					# milliseconds
	STATUS_MSG_TIMEOUT = 2					# seconds
	LOG_SIZE = 16 * 1024					# log buffer size

	def __init__(self, session, feid):
		self.session = session
		Screen.__init__(self, session)
		self.feid = feid
		self.oldref = None
		log.open(self.LOG_SIZE)
		if config.Nims[self.feid].configMode.value == 'advanced':
			self.advanced = True
			self.advancedconfig = config.Nims[self.feid].advanced
			self.advancedsats = self.advancedconfig.sat
			self.availablesats = map(lambda x: x[0], nimmanager.getRotorSatListForNim(self.feid))
		else:
			self.advanced = False

		cur = { }
		if not self.openFrontend():
			self.oldref = session.nav.getCurrentlyPlayingServiceReference()
			service = session.nav.getCurrentService()
			feInfo = service and service.frontendInfo()
			if feInfo:
				cur = feInfo.getTransponderData(True)
			del feInfo
			del service
			session.nav.stopService() # try to disable foreground service
			if not self.openFrontend():
				if session.pipshown: # try to disable pip
					service = self.session.pip.pipservice
					feInfo = service and service.frontendInfo()
					if feInfo:
						cur = feInfo.getTransponderData(True)
					del feInfo
					del service
					from Screens.InfoBar import InfoBar
					InfoBar.instance and hasattr(InfoBar.instance, "showPiP") and InfoBar.instance.showPiP()
				if not self.openFrontend():
					self.frontend = None # in normal case this should not happen
					if hasattr(self, 'raw_channel'):
						del self.raw_channel

		self.frontendStatus = { }
		self.diseqc = Diseqc(self.frontend)
		# True means we dont like that the normal sec stuff sends commands to the rotor!
		self.tuner = Tuner(self.frontend, ignore_rotor = True)

		tp = ( cur.get("frequency", 0) / 1000,
			cur.get("symbol_rate", 0) / 1000,
			cur.get("polarization", eDVBFrontendParametersSatellite.Polarisation_Horizontal),
			cur.get("fec_inner", eDVBFrontendParametersSatellite.FEC_Auto),
			cur.get("inversion", eDVBFrontendParametersSatellite.Inversion_Unknown),
			cur.get("orbital_position", 0),
			cur.get("system", eDVBFrontendParametersSatellite.System_DVB_S),
			cur.get("modulation", eDVBFrontendParametersSatellite.Modulation_QPSK),
			cur.get("rolloff", eDVBFrontendParametersSatellite.RollOff_alpha_0_35),
			cur.get("pilot", eDVBFrontendParametersSatellite.Pilot_Unknown))

		self.tuner.tune(tp)
		self.isMoving = False
		self.stopOnLock = False

		self.red = Button("")
		self["key_red"] = self.red
		self.green = Button("")
		self["key_green"] = self.green
		self.yellow = Button("")
		self["key_yellow"] = self.yellow
		self.blue = Button("")
		self["key_blue"] = self.blue

		self.list = []
		self["list"] = ConfigList(self.list)

		self["snr_db"] = TunerInfo(TunerInfo.SNR_DB, statusDict = self.frontendStatus)
		self["snr_percentage"] = TunerInfo(TunerInfo.SNR_PERCENTAGE, statusDict = self.frontendStatus)
		self["ber_value"] = TunerInfo(TunerInfo.BER_VALUE, statusDict = self.frontendStatus)
		self["snr_bar"] = TunerInfo(TunerInfo.SNR_BAR, statusDict = self.frontendStatus)
		self["ber_bar"] = TunerInfo(TunerInfo.BER_BAR, statusDict = self.frontendStatus)
		self["lock_state"] = TunerInfo(TunerInfo.LOCK_STATE, statusDict = self.frontendStatus)

		self["frequency_value"] = Label("")
		self["symbolrate_value"] = Label("")
		self["fec_value"] = Label("")
		self["polarisation"] = Label("")
		self["status_bar"] = Label("")
		self.statusMsgTimeoutTicks = 0
		self.statusMsgBlinking = False
		self.statusMsgBlinkCount = 0
		self.statusMsgBlinkRate = 500 / self.UPDATE_INTERVAL	# milliseconds
		self.tuningChangedTo(tp)

		self["actions"] = NumberActionMap(["DirectionActions", "OkCancelActions", "ColorActions", "TimerEditActions", "InputActions"],
		{
			"ok": self.keyOK,
			"cancel": self.keyCancel,
			"up": self.keyUp,
			"down": self.keyDown,
			"left": self.keyLeft,
			"right": self.keyRight,
			"red": self.redKey,
			"green": self.greenKey,
			"yellow": self.yellowKey,
			"blue": self.blueKey,
			"log": self.showLog,
			"1": self.keyNumberGlobal,
			"2": self.keyNumberGlobal,
			"3": self.keyNumberGlobal,
			"4": self.keyNumberGlobal,
			"5": self.keyNumberGlobal,
			"6": self.keyNumberGlobal,
			"7": self.keyNumberGlobal,
			"8": self.keyNumberGlobal,
			"9": self.keyNumberGlobal,
			"0": self.keyNumberGlobal
		}, -1)

		self.updateColors("tune")

		self.statusTimer = eTimer()
		self.statusTimer.callback.append(self.updateStatus)
		self.collectingStatistics = False
		self.statusTimer.start(self.UPDATE_INTERVAL, True)
		self.dataAvailable = Event()
		self.onClose.append(self.__onClose)

		self.createConfig()
		self.createSetup()

	def __onClose(self):
		self.statusTimer.stop()
		log.close()
		self.session.nav.playService(self.oldref)

	def restartPrevService(self, yesno):
		if yesno:
			if self.frontend:
				self.frontend = None
				del self.raw_channel
		else:
			self.oldref=None
		self.close(None)

	def keyCancel(self):
		if self.oldref:
			self.session.openWithCallback(self.restartPrevService, MessageBox, _("Zap back to service before positioner setup?"), MessageBox.TYPE_YESNO)
		else:
			self.restartPrevService(False)

	def openFrontend(self):
		res_mgr = eDVBResourceManager.getInstance()
		if res_mgr:
			self.raw_channel = res_mgr.allocateRawChannel(self.feid)
			if self.raw_channel:
				self.frontend = self.raw_channel.getFrontend()
				if self.frontend:
					return True
				else:
					print "getFrontend failed"
			else:
				print "getRawChannel failed"
		else:
			print "getResourceManager instance failed"
		return False

	def setLNB(self, lnb):
		try:
			self.sitelon = lnb.longitude.float
			self.longitudeOrientation = lnb.longitudeOrientation.value
			self.sitelat = lnb.latitude.float
			self.latitudeOrientation = lnb.latitudeOrientation.value
			self.tuningstepsize = lnb.tuningstepsize.float
			self.rotorPositions = lnb.rotorPositions.value
			self.turningspeedH = lnb.turningspeedH.float
			self.turningspeedV = lnb.turningspeedV.float
		except: # some reasonable defaults from NimManager
			self.sitelon = 5.1
			self.longitudeOrientation = 'east'
			self.sitelat = 50.767
			self.latitudeOrientation = 'north'
			self.tuningstepsize = 0.36
			self.rotorPositions = 99
			self.turningspeedH = 2.3
			self.turningspeedV = 1.7
		self.sitelat = PositionerSetup.orbital2metric(self.sitelat, self.latitudeOrientation)
		self.sitelon = PositionerSetup.orbital2metric(self.sitelon, self.longitudeOrientation)

	def getLNBfromConfig(self, orb_pos):
		lnb = None
		if orb_pos in self.availablesats:
			lnbnum = int(self.advancedsats[orb_pos].lnb.value)
			if not lnbnum:
				for allsats in range(3601, 3607):
					lnbnum = int(self.advancedsats[allsats].lnb.value)
					if lnbnum:
						break
			if lnbnum:
				self.printMsg(_("Using LNB %d") % lnbnum)
				lnb = self.advancedconfig.lnb[lnbnum]
		if not lnb:
			self.logMsg(_("Warning: no LNB; using factory defaults."), timeout = 4)
		return lnb

	def createConfig(self):
		rotorposition = 1
		orb_pos = 0
		self.printMsg(_("Using tuner %s") % chr(0x41 + self.feid))
		if not self.advanced:
			self.printMsg(_("Configuration mode: %s") % _("simple"))
			nim = config.Nims[self.feid]
			self.sitelon = nim.longitude.float
			self.longitudeOrientation = nim.longitudeOrientation.value
			self.sitelat = nim.latitude.float
			self.latitudeOrientation = nim.latitudeOrientation.value
			self.sitelat = PositionerSetup.orbital2metric(self.sitelat, self.latitudeOrientation)
			self.sitelon = PositionerSetup.orbital2metric(self.sitelon, self.longitudeOrientation)
			self.tuningstepsize = nim.tuningstepsize.float
			self.rotorPositions = nim.rotorPositions.value
			self.turningspeedH = nim.turningspeedH.float
			self.turningspeedV = nim.turningspeedV.float
		else:	# it is advanced
			self.printMsg(_("Configuration mode: %s") % _("advanced"))
			fe_data = { }
			self.frontend.getFrontendData(fe_data)
			self.frontend.getTransponderData(fe_data, True)
			orb_pos = fe_data.get("orbital_position", None)
			if orb_pos in self.availablesats:
				rotorposition = int(self.advancedsats[orb_pos].rotorposition.value)
			self.setLNB(self.getLNBfromConfig(orb_pos))
		self.positioner_tune = ConfigNothing()
		self.positioner_move = ConfigNothing()
		self.positioner_finemove = ConfigNothing()
		self.positioner_limits = ConfigNothing()
		self.positioner_storage = ConfigInteger(default = rotorposition, limits = (1, self.rotorPositions))
		self.allocatedIndices = []
		m = PositionerSetup.satposition2metric(orb_pos)
		self.orbitalposition = ConfigFloat(default = [int(m[0] / 10), m[0] % 10], limits = [(0,180),(0,9)])
		self.orientation = ConfigSelection([("east", _("East")), ("west", _("West"))], m[1])

	def createSetup(self):
		self.list.append((_("Tune and focus"), self.positioner_tune, "tune"))
		self.list.append((_("Movement"), self.positioner_move, "move"))
		self.list.append((_("Fine movement"), self.positioner_finemove, "finemove"))
		self.list.append((_("Set limits"), self.positioner_limits, "limits"))
		self.list.append((_("Memory index"), self.positioner_storage, "storage"))
		self.list.append((_("Goto"), self.orbitalposition, "goto"))
		self.list.append((" ", self.orientation, "goto"))
		self["list"].l.setList(self.list)

	def keyOK(self):
		pass

	def getCurrentConfigPath(self):
		return self["list"].getCurrent()[2]

	def keyUp(self):
		if not self.isMoving:
			self["list"].instance.moveSelection(self["list"].instance.moveUp)
			self.updateColors(self.getCurrentConfigPath())

	def keyDown(self):
		if not self.isMoving:
			self["list"].instance.moveSelection(self["list"].instance.moveDown)
			self.updateColors(self.getCurrentConfigPath())

	def keyNumberGlobal(self, number):
		self["list"].handleKey(KEY_0 + number)

	def keyLeft(self):
		self["list"].handleKey(KEY_LEFT)

	def keyRight(self):
		self["list"].handleKey(KEY_RIGHT)

	def updateColors(self, entry):
		if entry == "tune":
			self.red.setText(_("Tune"))
			self.green.setText(_("Auto focus"))
			self.yellow.setText(_("Calibrate"))
			self.blue.setText(_("Calculate"))
		elif entry == "move":
			if self.isMoving:
				self.red.setText(_("Stop"))
				self.green.setText(_("Stop"))
				self.yellow.setText(_("Stop"))
				self.blue.setText(_("Stop"))
			else:
				self.red.setText(_("Move west"))
				self.green.setText(_("Search west"))
				self.yellow.setText(_("Search east"))
				self.blue.setText(_("Move east"))
		elif entry == "finemove":
			self.red.setText("")
			self.green.setText(_("Step west"))
			self.yellow.setText(_("Step east"))
			self.blue.setText("")
		elif entry == "limits":
			self.red.setText(_("Limits off"))
			self.green.setText(_("Limit west"))
			self.yellow.setText(_("Limit east"))
			self.blue.setText(_("Limits on"))
		elif entry == "storage":
			self.red.setText("")
			self.green.setText(_("Store position"))
			self.yellow.setText(_("Goto position"))
			if self.advanced:
				self.blue.setText(_("Allocate"))
			else:
				self.blue.setText("")
		elif entry == "goto":
			self.red.setText("")
			self.green.setText(_("Goto 0"))
			self.yellow.setText(_("Goto X"))
			self.blue.setText("")
		else:
			self.red.setText("")
			self.green.setText("")
			self.yellow.setText("")
			self.blue.setText("")

	def printMsg(self, msg):
		print msg
		print>>log, msg

	def stopMoving(self):
		self.printMsg(_("Stop"))
		self.diseqccommand("stop")
		self.isMoving = False
		self.stopOnLock = False
		self.statusMsg(_("Stopped"), timeout = self.STATUS_MSG_TIMEOUT)

	def redKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.stopMoving()
			else:
				self.printMsg(_("Move west"))
				self.diseqccommand("moveWest", 0)
				self.isMoving = True
				self.statusMsg(_("Moving west ..."), blinking = True)
			self.updateColors("move")
		elif entry == "limits":
			self.printMsg(_("Limits off"))
			self.diseqccommand("limitOff")
			self.statusMsg(_("Limits cancelled"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "tune":
			fe_data = { }
			self.frontend.getFrontendData(fe_data)
			self.frontend.getTransponderData(fe_data, True)
			feparm = self.tuner.lastparm.getDVBS()
			fe_data["orbital_position"] = feparm.orbital_position
			self.statusTimer.stop()
			self.session.openWithCallback(self.tune, TunerScreen, self.feid, fe_data)

	def greenKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "tune":
			# Auto focus
			self.printMsg(_("Auto focus"))
			print>>log, (_("Site latitude") + "      : %5.1f %s") % PositionerSetup.latitude2orbital(self.sitelat)
			print>>log, (_("Site longitude") + "     : %5.1f %s") % PositionerSetup.longitude2orbital(self.sitelon)
			Thread(target = self.autofocus).start()
		elif entry == "move":
			if self.isMoving:
				self.stopMoving()
			else:
				self.printMsg(_("Search west"))
				self.isMoving = True
				self.stopOnLock = True
				self.diseqccommand("moveWest", 0)
				self.statusMsg(_("Searching west ..."), blinking = True)
			self.updateColors("move")
		elif entry == "finemove":
			self.printMsg(_("Step west"))
			self.diseqccommand("moveWest", 0xFF) # one step
			self.statusMsg(_("Stepped west"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "storage":
			self.printMsg(_("Store at index"))
			index = int(self.positioner_storage.value)
			self.diseqccommand("store", index)
			self.statusMsg((_("Position stored at index") + " %2d") % index, timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "limits":
			self.printMsg(_("Limit west"))
			self.diseqccommand("limitWest")
			self.statusMsg(_("West limit set"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "goto":
			self.printMsg(_("Goto 0"))
			self.diseqccommand("moveTo", 0)
			self.statusMsg(_("Moved to position 0"), timeout = self.STATUS_MSG_TIMEOUT)

	def yellowKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.stopMoving()
			else:
				self.printMsg(_("Move east"))
				self.isMoving = True
				self.stopOnLock = True
				self.diseqccommand("moveEast", 0)
				self.statusMsg(_("Searching east ..."), blinking = True)
			self.updateColors("move")
		elif entry == "finemove":
			self.printMsg(_("Step east"))
			self.diseqccommand("moveEast", 0xFF) # one step
			self.statusMsg(_("Stepped east"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "storage":
			self.printMsg(_("Goto index position"))
			index = int(self.positioner_storage.value)
			self.diseqccommand("moveTo", index)
			self.statusMsg((_("Moved to position at index") + " %2d") % index, timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "limits":
			self.printMsg(_("Limit east"))
			self.diseqccommand("limitEast")
			self.statusMsg(_("East limit set"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "goto":
			self.printMsg(_("Move to position X"))
			satlon = self.orbitalposition.float
			position = "%5.1f %s" % (satlon, self.orientation.value)
			print>>log, (_("Satellite longitude:") + " %s") % position
			satlon = PositionerSetup.orbital2metric(satlon, self.orientation.value)
			self.statusMsg((_("Moving to position") + " %s") % position, timeout = self.STATUS_MSG_TIMEOUT)
			self.gotoX(satlon)
		elif entry == "tune":
			# Start USALS calibration
			self.printMsg(_("USALS calibration"))
			print>>log, (_("Site latitude") + "      : %5.1f %s") % PositionerSetup.latitude2orbital(self.sitelat)
			print>>log, (_("Site longitude") + "     : %5.1f %s") % PositionerSetup.longitude2orbital(self.sitelon)
			Thread(target = self.gotoXcalibration).start()

	def blueKey(self):
		entry = self.getCurrentConfigPath()
		if entry == "move":
			if self.isMoving:
				self.stopMoving()
			else:
				self.printMsg(_("Move east"))
				self.diseqccommand("moveEast", 0)
				self.isMoving = True
				self.statusMsg(_("Moving east ..."), blinking = True)
			self.updateColors("move")
		elif entry == "limits":
			self.printMsg(_("Limits on"))
			self.diseqccommand("limitOn")
			self.statusMsg(_("Limits enabled"), timeout = self.STATUS_MSG_TIMEOUT)
		elif entry == "tune":
			# Start (re-)calculate
			self.session.openWithCallback(self.recalcConfirmed, MessageBox, _("This will (re-)calculate all positions of your rotor and may remove previously memorised positions and fine-tuning!\nAre you sure?"), MessageBox.TYPE_YESNO, default = False, timeout = 10)
		elif entry == "storage":
			if self.advanced:
				self.printMsg(_("Allocate unused memory index"))
				while True:
					if not len(self.allocatedIndices):
						for sat in self.availablesats:
							current_index = int(self.advancedsats[sat].rotorposition.value)
							if current_index not in self.allocatedIndices:
								self.allocatedIndices.append(current_index)
						if len(self.allocatedIndices) == self.rotorPositions:
							self.statusMsg(_("No free index available"), timeout = self.STATUS_MSG_TIMEOUT)
							break
					index = 1
					if len(self.allocatedIndices):
						for i in sorted(self.allocatedIndices):
							if i != index:
								break
							index += 1
					if index <= self.rotorPositions:
						self.positioner_storage.value = index
						self["list"].invalidateCurrent()
						self.allocatedIndices.append(index)
						self.statusMsg((_("Index allocated:") + " %2d") % index, timeout = self.STATUS_MSG_TIMEOUT)
						break
					else:
						self.allocatedIndices = []

	def recalcConfirmed(self, yesno):
		if yesno:
			self.printMsg(_("Calculate all positions"))
			print>>log, (_("Site latitude") + "      : %5.1f %s") % PositionerSetup.latitude2orbital(self.sitelat)
			print>>log, (_("Site longitude") + "     : %5.1f %s") % PositionerSetup.longitude2orbital(self.sitelon)
			lon = self.sitelon
			if lon >= 180:
				lon -= 360
			if lon < -30:	# americas, make unsigned binary west positive polarity
				lon = -lon
			lon = int(round(lon)) & 0xFF
			lat = int(round(self.sitelat)) & 0xFF
			index = int(self.positioner_storage.value) & 0xFF
			self.diseqccommand("calc", (((index << 8) | lon) << 8) | lat)
			self.statusMsg(_("Calculation complete"), timeout = self.STATUS_MSG_TIMEOUT)

	def showLog(self):
		self.session.open(PositionerSetupLog)

	def diseqccommand(self, cmd, param = 0):
		print>>log, "Diseqc(%s, %X)" % (cmd, param)
		self.diseqc.command(cmd, param)
		self.tuner.retune()

	def tune(self, transponder):
		# re-start the update timer
		self.statusTimer.start(self.UPDATE_INTERVAL, True)
		if transponder is not None:
			self.tuner.tune(transponder)
			self.tuningChangedTo(transponder)
		feparm = self.tuner.lastparm.getDVBS()
		orb_pos = feparm.orbital_position
		m = PositionerSetup.satposition2metric(orb_pos)
		self.orbitalposition.value = [int(m[0] / 10), m[0] % 10]
		self.orientation.value = m[1]
		if self.advanced:
			if orb_pos in self.availablesats:
				rotorposition = int(self.advancedsats[orb_pos].rotorposition.value)
				self.positioner_storage.value = rotorposition
				self.allocatedIndices = []
			self.setLNB(self.getLNBfromConfig(orb_pos))

	def isLocked(self):
		return self.frontendStatus.get("tuner_locked", 0) == 1

	def statusMsg(self, msg, blinking = False, timeout = 0):			# timeout in seconds
		self.statusMsgBlinking = blinking
		if not blinking:
			self["status_bar"].visible = True
		self["status_bar"].setText(msg)
		self.statusMsgTimeoutTicks = (timeout * 1000 + self.UPDATE_INTERVAL / 2) / self.UPDATE_INTERVAL

	def updateStatus(self):
		self.statusTimer.start(self.UPDATE_INTERVAL, True)
		if self.frontend:
			self.frontend.getFrontendStatus(self.frontendStatus)
		self["snr_db"].update()
		self["snr_percentage"].update()
		self["ber_value"].update()
		self["snr_bar"].update()
		self["ber_bar"].update()
		self["lock_state"].update()
		if self.statusMsgBlinking:
			self.statusMsgBlinkCount += 1
			if self.statusMsgBlinkCount == self.statusMsgBlinkRate:
				self.statusMsgBlinkCount = 0
				self["status_bar"].visible = not self["status_bar"].visible
		if self.statusMsgTimeoutTicks > 0:
			self.statusMsgTimeoutTicks -= 1
			if self.statusMsgTimeoutTicks == 0:
				self["status_bar"].setText("")
				self.statusMsgBlinking = False
				self["status_bar"].visible = True
		if self.isLocked() and self.isMoving and self.stopOnLock:
			self.stopMoving()
			self.updateColors(self.getCurrentConfigPath())
		if self.collectingStatistics:
			self.low_rate_adapter_count += 1
			if self.low_rate_adapter_count == self.MAX_LOW_RATE_ADAPTER_COUNT:
				self.low_rate_adapter_count = 0
				self.snr_percentage += self["snr_percentage"].getValue(TunerInfo.SNR)
				self.lock_count += self["lock_state"].getValue(TunerInfo.LOCK)
				self.stat_count += 1
				if self.stat_count == self.max_count:
					self.collectingStatistics = False
					count = float(self.stat_count)
					self.lock_count /= count
					self.snr_percentage *= 100.0 / 0x10000 / count
					self.dataAvailable.set()

	def tuningChangedTo(self, tp):

		def setLowRateAdapterCount(symbolrate):
			# change the measurement time and update interval in case of low symbol rate,
			# since more time is needed for the front end in that case.
			# It is an heuristic determination without any pretence. For symbol rates
			# of 5000 the interval is multiplied by 3 until 15000 which is seen
			# as a high symbol rate. Linear interpolation elsewhere.
			return max(int(round((3 - 1) * (symbolrate - 15000) / (5000 - 15000) + 1)), 1)

		self.symbolrate = tp[1]
		self.polarisation = tp[2]
		self.MAX_LOW_RATE_ADAPTER_COUNT = setLowRateAdapterCount(self.symbolrate)
		transponderdata = ConvertToHumanReadable(self.tuner.getTransponderData(), "DVB-S")
		self["frequency_value"].setText(str(transponderdata.get("frequency")))
		self["symbolrate_value"].setText(str(transponderdata.get("symbol_rate")))
		self["fec_value"].setText(str(transponderdata.get("fec_inner")))
		self["polarisation"].setText(str(transponderdata.get("polarization")))

	@staticmethod
	def rotorCmd2Step(rotorCmd, stepsize):
		return round(float(rotorCmd & 0xFFF) / 0x10 / stepsize) * (1 - ((rotorCmd & 0x1000) >> 11))

	@staticmethod
	def gotoXcalc(satlon, sitelat, sitelon):
		def azimuth2Rotorcode(angle):
			gotoXtable = (0x00, 0x02, 0x03, 0x05, 0x06, 0x08, 0x0A, 0x0B, 0x0D, 0x0E)
			a = int(round(abs(angle) * 10.0))
			return ((a / 10) << 4) + gotoXtable[a % 10]

		satHourAngle = rotor_calc.calcSatHourangle(satlon, sitelat, sitelon)
		if sitelat >= 0: # Northern Hemisphere
			rotorCmd = azimuth2Rotorcode(180 - satHourAngle)
			if satHourAngle <= 180: # the east
				rotorCmd |= 0xE000
			else:					# west
				rotorCmd |= 0xD000
		else: # Southern Hemisphere
			if satHourAngle <= 180: # the east
				rotorCmd = azimuth2Rotorcode(satHourAngle) | 0xD000
			else: # west
				rotorCmd = azimuth2Rotorcode(360 - satHourAngle) | 0xE000
		return rotorCmd

	def gotoX(self, satlon):
		rotorCmd = PositionerSetup.gotoXcalc(satlon, self.sitelat, self.sitelon)
		self.diseqccommand("gotoX", rotorCmd)
		x = PositionerSetup.rotorCmd2Step(rotorCmd, self.tuningstepsize)
		print>>log, (_("Rotor step position:") + " %4d") % x
		return x

	def getTurningspeed(self):
		if self.polarisation == eDVBFrontendParametersSatellite.Polarisation_Horizontal:
			turningspeed = self.turningspeedH
		else:
			turningspeed = self.turningspeedV
		return max(turningspeed, 0.1)

	TURNING_START_STOP_DELAY = 1.600	# seconds
	MAX_SEARCH_ANGLE = 12.0				# degrees
	MAX_FOCUS_ANGLE = 6.0				# degrees
	LOCK_LIMIT = 0.1					# ratio
	MEASURING_TIME = 2.500				# seconds

	def measure(self, time = MEASURING_TIME):	# time in seconds
		self.snr_percentage = 0.0
		self.lock_count = 0.0
		self.stat_count = 0
		self.low_rate_adapter_count = 0
		self.max_count = max(int((time * 1000 + self.UPDATE_INTERVAL / 2)/ self.UPDATE_INTERVAL), 1)
		self.collectingStatistics = True
		self.dataAvailable.clear()
		self.dataAvailable.wait()

	def logMsg(self, msg, timeout = 0):
		self.statusMsg(msg, timeout = timeout)
		self.printMsg(msg)

	def sync(self):
		self.lock_count = 0.0
		n = 0
		while self.lock_count < (1 - self.LOCK_LIMIT) and n < 5:
			self.measure(time = 0.500)
			n += 1
		if self.lock_count < (1 - self.LOCK_LIMIT):
			return False
		return True

	randomGenerator = None
	def randomBool(self):
		if self.randomGenerator is None:
			self.randomGenerator = SystemRandom()
		return self.randomGenerator.random() >= 0.5

	def gotoXcalibration(self):

		def move(x):
			z = self.gotoX(x + satlon)
			time = int(abs(x - prev_pos) / turningspeed + 2 * self.TURNING_START_STOP_DELAY)
			sleep(time * self.MAX_LOW_RATE_ADAPTER_COUNT)
			return z

		def reportlevels(pos, level, lock):
			print>>log, (_("Signal quality") + " %5.1f" + chr(176) + "   : %6.2f") % (pos, level)
			print>>log, (_("Lock ratio") + "     %5.1f" + chr(176) + "   : %6.2f") % (pos, lock)

		def optimise(readings):
			xi = readings.keys()
			yi = map(lambda (x, y) : x, readings.values())
			x0 = sum(map(mul, xi, yi)) / sum(yi)
			xm = xi[yi.index(max(yi))]
			return x0, xm

		def toGeopos(x):
			if x < 0:
				return _("W")
			else:
				return _("E")

		def toGeoposEx(x):
			if x < 0:
				return _("west")
			else:
				return _("east")

		self.logMsg(_("GotoX calibration"))
		satlon = self.orbitalposition.float
		print>>log, (_("Satellite longitude:") + " %5.1f" + chr(176) + " %s") % (satlon, self.orientation.value)
		satlon = PositionerSetup.orbital2metric(satlon, self.orientation.value)
		prev_pos = 0.0						# previous relative position w.r.t. satlon
		turningspeed = self.getTurningspeed()

		x = 0.0								# relative position w.r.t. satlon
		dir = 1
		if self.randomBool():
			dir = -dir
		while abs(x) < self.MAX_SEARCH_ANGLE:
			if self.sync():
				break
			x += (1.0 * dir)						# one degree east/west
			self.statusMsg((_("Searching") + " " + toGeoposEx(dir) + " %2d" + chr(176)) % abs(x), blinking = True)
			move(x)
			prev_pos = x
		else:
			x = 0.0
			dir = -dir
			while abs(x) < self.MAX_SEARCH_ANGLE:
				x += (1.0 * dir)					# one degree east/west
				self.statusMsg((_("Searching") + " " + toGeoposEx(dir) + " %2d" + chr(176)) % abs(x), blinking = True)
				move(x)
				prev_pos = x
				if self.sync():
					break
			else:
				msg = _("Cannot find any signal ..., aborting !")
				self.printMsg(msg)
				self.statusMsg("")
				self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
				return
		x = round(x / self.tuningstepsize) * self.tuningstepsize
		move(x)
		prev_pos = x
		measurements = {}
		self.measure()
		print>>log, (_("Initial signal quality") + " %5.1f" + chr(176) + ": %6.2f") % (x, self.snr_percentage)
		print>>log, (_("Initial lock ratio") + "     %5.1f" + chr(176) + ": %6.2f") % (x, self.lock_count)
		measurements[x] = (self.snr_percentage, self.lock_count)

		start_pos = x
		x = 0.0
		dir = 1
		if self.randomBool():
			dir = -dir
		while x < self.MAX_FOCUS_ANGLE:
			x += self.tuningstepsize * dir					# one step east/west
			self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %5.1f" + chr(176)) % abs(x + start_pos), blinking = True)
			move(x + start_pos)
			prev_pos = x + start_pos
			self.measure()
			measurements[x + start_pos] = (self.snr_percentage, self.lock_count)
			reportlevels(x + start_pos, self.snr_percentage, self.lock_count)
			if self.lock_count < self.LOCK_LIMIT:
				break
		else:
			msg = _("Cannot determine") + " " + toGeoposEx(dir) + " " + _("limit ..., aborting !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		x = 0.0
		dir = -dir
		self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %5.1f" + chr(176)) % abs(start_pos), blinking = True)
		move(start_pos)
		prev_pos = start_pos
		if not self.sync():
			msg = _("Sync failure moving back to origin !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		while abs(x) < self.MAX_FOCUS_ANGLE:
			x += self.tuningstepsize * dir					# one step west/east
			self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %5.1f" + chr(176)) % abs(x + start_pos), blinking = True)
			move(x + start_pos)
			prev_pos = x + start_pos
			self.measure()
			measurements[x + start_pos] = (self.snr_percentage, self.lock_count)
			reportlevels(x + start_pos, self.snr_percentage, self.lock_count)
			if self.lock_count < self.LOCK_LIMIT:
				break
		else:
			msg = _("Cannot determine") + " " + toGeoposEx(dir) + " " + _("limit ..., aborting !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		(x0, xm) = optimise(measurements)
		x = move(x0)
		if satlon > 180:
			satlon -= 360
		x0 += satlon
		xm += satlon
		print>>log, (_("Weighted position") + "     : %5.1f" + chr(176) + " %s") % (abs(x0), toGeopos(x0))
		print>>log, (_("Strongest position") + "    : %5.1f" + chr(176) + " %s") % (abs(xm), toGeopos(xm))
		self.logMsg((_("Final position at") + " %5.1f" + chr(176) + " %s / %d; " + _("offset is") + " %4.1f" + chr(176)) % (abs(x0), toGeopos(x0), x, x0 - satlon), timeout = 10)

	def autofocus(self):

		def move(x):
			if x > 0:
				self.diseqccommand("moveEast", (-x) & 0xFF)
			elif x < 0:
				self.diseqccommand("moveWest", x & 0xFF)
			if x != 0:
				time = int(abs(x) * self.tuningstepsize / turningspeed + 2 * self.TURNING_START_STOP_DELAY)
				sleep(time * self.MAX_LOW_RATE_ADAPTER_COUNT)

		def reportlevels(pos, level, lock):
			print>>log, (_("Signal quality") + " [%2d]   : %6.2f") % (pos, level)
			print>>log, (_("Lock ratio") + " [%2d]       : %6.2f") % (pos, lock)

		def optimise(readings):
			xi = readings.keys()
			yi = map(lambda (x, y) : x, readings.values())
			x0 = int(round(sum(map(mul, xi, yi)) / sum(yi)))
			xm = xi[yi.index(max(yi))]
			return x0, xm

		def toGeoposEx(x):
			if x < 0:
				return _("west")
			else:
				return _("east")

		self.logMsg(_("Auto focus commencing ..."))
		turningspeed = self.getTurningspeed()
		measurements = {}
		maxsteps = max(min(round(self.MAX_FOCUS_ANGLE / self.tuningstepsize), 0x1F), 3)
		self.measure()
		print>>log, (_("Initial signal quality:") + " %6.2f") % self.snr_percentage
		print>>log, (_("Initial lock ratio") + "    : %6.2f") % self.lock_count
		if self.lock_count < 1 - self.LOCK_LIMIT:
			msg = _("There is no signal to lock on !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		print>>log, _("Signal OK, proceeding")
		x = 0
		dir = 1
		if self.randomBool():
			dir = -dir
		measurements[x] = (self.snr_percentage, self.lock_count)
		nsteps = 0
		while nsteps < maxsteps:
			x += dir
			self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %2d") % abs(x), blinking = True)
			move(dir) 		# one step
			self.measure()
			measurements[x] = (self.snr_percentage, self.lock_count)
			reportlevels(x, self.snr_percentage, self.lock_count)
			if self.lock_count < self.LOCK_LIMIT:
				break
			nsteps += 1
		else:
			msg = _("Cannot determine") + " " + toGeoposEx(dir) + " " + _("limit ..., aborting !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		dir = -dir
		self.statusMsg(_("Moving") + " " + toGeoposEx(dir) + "  0", blinking = True)
		move(-x)
		if not self.sync():
			msg = _("Sync failure moving back to origin !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		x = 0
		nsteps = 0
		while nsteps < maxsteps:
			x += dir
			self.statusMsg((_("Moving") + " " + toGeoposEx(dir) + " %2d") % abs(x), blinking = True)
			move(dir) 		# one step
			self.measure()
			measurements[x] = (self.snr_percentage, self.lock_count)
			reportlevels(x, self.snr_percentage, self.lock_count)
			if self.lock_count < self.LOCK_LIMIT:
				break
			nsteps += 1
		else:
			msg = _("Cannot determine") + " " + toGeoposEx(dir) + " " + _("limit ..., aborting !")
			self.printMsg(msg)
			self.statusMsg("")
			self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR, timeout = 5)
			return
		(x0, xm) = optimise(measurements)
		print>>log, (_("Weighted position") + "     : %2d") % x0
		print>>log, (_("Strongest position") + "    : %2d") % xm
		self.logMsg((_("Final position at index") + " %2d (%5.1f" + chr(176) + ")") % (x0, x0 * self.tuningstepsize), timeout = 6)
		move(x0 - x)