also show SNR in dB in PositionerSetup
[enigma2.git] / lib / python / Plugins / SystemPlugins / PositionerSetup / plugin.py
1 from enigma import eTimer, eDVBSatelliteEquipmentControl, eDVBResourceManager, \
2         eDVBDiseqcCommand, eDVBFrontendParametersSatellite, eDVBFrontendParameters,\
3         iDVBFrontend
4
5 from Screens.Screen import Screen
6 from Screens.ScanSetup import ScanSetup
7 from Screens.MessageBox import MessageBox
8 from Plugins.Plugin import PluginDescriptor
9
10 from Components.Label import Label
11 from Components.ConfigList import ConfigList
12 from Components.TunerInfo import TunerInfo
13 from Components.ActionMap import ActionMap
14 from Components.NimManager import nimmanager
15 from Components.MenuList import MenuList
16 from Components.config import ConfigSatlist, ConfigNothing, ConfigSelection, ConfigSubsection, KEY_LEFT, KEY_RIGHT, getConfigListEntry
17
18 from time import sleep
19
20 class PositionerSetup(Screen):
21         skin = """
22                 <screen position="100,100" size="560,400" title="Positioner setup..." >
23                         <widget name="list" position="100,0" size="350,155" />
24
25                         <widget name="red" position="0,155" size="140,80" backgroundColor="red" halign="center" valign="center" font="Regular;21" />
26                         <widget name="green" position="140,155" size="140,80" backgroundColor="green" halign="center" valign="center" font="Regular;21" />
27                         <widget name="yellow" position="280,155" size="140,80" backgroundColor="yellow" halign="center" valign="center" font="Regular;21" />
28                         <widget name="blue" position="420,155" size="140,80" backgroundColor="blue" halign="center" valign="center" font="Regular;21" />
29
30                         <widget name="snr_db" position="60,245" size="150,22" halign="center" valign="center" font="Regular;21" />
31                         <eLabel text="SNR:" position="0,270" size="60,22" font="Regular;21" />
32                         <eLabel text="BER:" position="0,295" size="60,22" font="Regular;21" />
33                         <eLabel text="Lock:" position="0,320" size="60,22" font="Regular;21" />
34                         <widget name="snr_percentage" position="220,270" size="60,22" font="Regular;21" />
35                         <widget name="ber_value" position="220,295" size="60,22" font="Regular;21" />
36                         <widget name="lock_state" position="60,320" size="150,22" font="Regular;21" />
37                         <widget name="snr_bar" position="60,270" size="150,22" />
38                         <widget name="ber_bar" position="60,295" size="150,22" />
39
40                         <eLabel text="Frequency:" position="300,245" size="120,22" font="Regular;21" />
41                         <eLabel text="Symbolrate:" position="300,270" size="120,22" font="Regular;21" />
42                         <eLabel text="FEC:" position="300,295" size="120,22" font="Regular;21" />
43                         <widget name="frequency_value" position="420,245" size="120,22" font="Regular;21" />
44                         <widget name="symbolrate_value" position="420,270" size="120,22" font="Regular;21" />
45                         <widget name="fec_value" position="420,295" size="120,22" font="Regular;21" />
46                 </screen>"""
47         def __init__(self, session, feid):
48                 self.skin = PositionerSetup.skin
49                 Screen.__init__(self, session)
50                 self.feid = feid
51                 self.oldref = None
52                 
53                 if not self.openFrontend():
54                         self.oldref = session.nav.getCurrentlyPlayingServiceReference()
55                         session.nav.stopService() # try to disable foreground service
56                         if not self.openFrontend():
57                                 if session.pipshown: # try to disable pip
58                                         session.pipshown = False
59                                         del session.pip
60                                         if not self.openFrontend():
61                                                 self.frontend = None # in normal case this should not happen
62
63                 self.frontendStatus = { }
64
65                 self.diseqc = Diseqc(self.frontend)
66                 self.tuner = Tuner(self.frontend)
67                 self.tuner.tune((0,0,0,0,0,0))
68                 
69                 self.createConfig()
70                 
71                 self.isMoving = False
72                 self.stopOnLock = False
73                 
74                 self.red = Label("")
75                 self["red"] = self.red
76                 self.green = Label("")
77                 self["green"] = self.green
78                 self.yellow = Label("")
79                 self["yellow"] = self.yellow
80                 self.blue = Label("")
81                 self["blue"] = self.blue
82
83                 self.list = []
84                 self["list"] = ConfigList(self.list)
85                 self.createSetup()
86
87                 self["snr_db"] = TunerInfo(TunerInfo.SNR_DB, statusDict = self.frontendStatus)
88                 self["snr_percentage"] = TunerInfo(TunerInfo.SNR_PERCENTAGE, statusDict = self.frontendStatus)
89                 self["ber_value"] = TunerInfo(TunerInfo.BER_VALUE, statusDict = self.frontendStatus)
90                 self["snr_bar"] = TunerInfo(TunerInfo.SNR_BAR, statusDict = self.frontendStatus)
91                 self["ber_bar"] = TunerInfo(TunerInfo.BER_BAR, statusDict = self.frontendStatus)
92                 self["lock_state"] = TunerInfo(TunerInfo.LOCK_STATE, statusDict = self.frontendStatus)
93
94                 self["frequency_value"] = Label("")
95                 self["symbolrate_value"] = Label("")
96                 self["fec_value"] = Label("")
97                 
98                 self["actions"] = ActionMap(["DirectionActions", "OkCancelActions", "ColorActions"],
99                 {
100                         "ok": self.go,
101                         "cancel": self.keyCancel,
102                         "up": self.up,
103                         "down": self.down,
104                         "left": self.left,
105                         "right": self.right,
106                         "red": self.redKey,
107                         "green": self.greenKey,
108                         "yellow": self.yellowKey,
109                         "blue": self.blueKey,
110                 }, -1)
111                 
112                 self.updateColors("tune")
113                 
114                 self.statusTimer = eTimer()
115                 self.statusTimer.callback.append(self.updateStatus)
116                 self.statusTimer.start(50, True)
117                 self.onClose.append(self.__onClose)
118
119         def __onClose(self):
120                 self.session.nav.playService(self.oldref)
121
122         def restartPrevService(self, yesno):
123                 if yesno:
124                         if self.frontend:
125                                 self.frontend = None
126                                 del self.raw_channel
127                 else:
128                         self.oldref=None
129                 self.close(None)        
130
131         def keyCancel(self):
132                 if self.oldref:
133                         self.session.openWithCallback(self.restartPrevService, MessageBox, _("Zap back to service before positioner setup?"), MessageBox.TYPE_YESNO)
134                 else:
135                         self.restartPrevService(False)
136
137         def openFrontend(self):
138                 res_mgr = eDVBResourceManager.getInstance()
139                 if res_mgr:
140                         self.raw_channel = res_mgr.allocateRawChannel(self.feid)
141                         if self.raw_channel:
142                                 self.frontend = self.raw_channel.getFrontend()
143                                 if self.frontend:
144                                         return True
145                                 else:
146                                         print "getFrontend failed"
147                         else:
148                                 print "getRawChannel failed"
149                 else:
150                         print "getResourceManager instance failed"
151                 return False
152
153         def createConfig(self):
154                 self.positioner_tune = ConfigNothing()
155                 self.positioner_move = ConfigNothing()
156                 self.positioner_finemove = ConfigNothing()
157                 self.positioner_limits = ConfigNothing()
158                 self.positioner_goto0 = ConfigNothing()
159                 storepos = []
160                 for x in range(1,255):
161                         storepos.append(str(x))
162                 self.positioner_storage = ConfigSelection(choices = storepos)
163
164         def createSetup(self):
165                 self.list.append((_("Tune"), self.positioner_tune, "tune"))
166                 self.list.append((_("Positioner movement"), self.positioner_move, "move"))
167                 self.list.append((_("Positioner fine movement"), self.positioner_finemove, "finemove"))
168                 self.list.append((_("Set limits"), self.positioner_limits, "limits"))
169                 self.list.append((_("Positioner storage"), self.positioner_storage, "storage"))
170                 self.list.append((_("Goto 0"), self.positioner_goto0, "goto0"))
171                 self["list"].l.setList(self.list)
172
173         def go(self):
174                 pass
175
176         def getCurrentConfigPath(self):
177                 return self["list"].getCurrent()[2]
178
179         def up(self):
180                 if not self.isMoving:
181                         self["list"].instance.moveSelection(self["list"].instance.moveUp)
182                         self.updateColors(self.getCurrentConfigPath())
183
184         def down(self):
185                 if not self.isMoving:
186                         self["list"].instance.moveSelection(self["list"].instance.moveDown)
187                         self.updateColors(self.getCurrentConfigPath())
188
189         def left(self):
190                 self["list"].handleKey(KEY_LEFT)
191
192         def right(self):
193                 self["list"].handleKey(KEY_RIGHT)
194
195         def updateColors(self, entry):
196                 if entry == "tune":
197                         self.red.setText(_("Tune"))
198                         self.green.setText("")
199                         self.yellow.setText("")
200                         self.blue.setText("")
201                 elif entry == "move":
202                         if self.isMoving:
203                                 self.red.setText(_("Stop"))
204                                 self.green.setText(_("Stop"))
205                                 self.yellow.setText(_("Stop"))
206                                 self.blue.setText(_("Stop"))
207                         else:
208                                 self.red.setText(_("Move west"))
209                                 self.green.setText(_("Search west"))
210                                 self.yellow.setText(_("Search east"))
211                                 self.blue.setText(_("Move east"))
212                 elif entry == "finemove":
213                         self.red.setText("")
214                         self.green.setText(_("Step west"))
215                         self.yellow.setText(_("Step east"))
216                         self.blue.setText("")
217                 elif entry == "limits":
218                         self.red.setText(_("Limits off"))
219                         self.green.setText(_("Limit west"))
220                         self.yellow.setText(_("Limit east"))
221                         self.blue.setText(_("Limits on"))
222                 elif entry == "storage":
223                         self.red.setText("")
224                         self.green.setText(_("Store position"))
225                         self.yellow.setText(_("Goto position"))
226                         self.blue.setText("")
227                 elif entry == "goto0":
228                         self.red.setText(_("Goto 0"))
229                         self.green.setText("")
230                         self.yellow.setText("")
231                         self.blue.setText("")
232                 else:
233                         self.red.setText("")
234                         self.green.setText("")
235                         self.yellow.setText("")
236                         self.blue.setText("")
237
238         def redKey(self):
239                 entry = self.getCurrentConfigPath()
240                 if entry == "move":
241                         if self.isMoving:
242                                 self.diseqccommand("stop")
243                                 self.isMoving = False
244                                 self.stopOnLock = False
245                         else:
246                                 self.diseqccommand("moveWest", 0)
247                                 self.isMoving = True
248                         self.updateColors("move")
249                 elif entry == "limits":
250                         self.diseqccommand("limitOff")
251                 elif entry == "tune":
252                         self.session.openWithCallback(self.tune, TunerScreen, self.feid)
253                 elif entry == "goto0":
254                         print "move to position 0"
255                         self.diseqccommand("moveTo", 0)
256
257         def greenKey(self):
258                 entry = self.getCurrentConfigPath()
259                 if entry == "move":
260                         if self.isMoving:
261                                 self.diseqccommand("stop")
262                                 self.isMoving = False
263                                 self.stopOnLock = False
264                         else:
265                                 self.isMoving = True
266                                 self.stopOnLock = True
267                                 self.diseqccommand("moveWest", 0)
268                         self.updateColors("move")
269                 elif entry == "finemove":
270                         print "stepping west"
271                         self.diseqccommand("moveWest", 0xFF) # one step
272                 elif entry == "storage":
273                         print "store at position", int(self.positioner_storage.value)
274                         self.diseqccommand("store", int(self.positioner_storage.value))
275                 elif entry == "limits":
276                         self.diseqccommand("limitWest")
277
278         def yellowKey(self):
279                 entry = self.getCurrentConfigPath()
280                 if entry == "move":
281                         if self.isMoving:
282                                 self.diseqccommand("stop")
283                                 self.isMoving = False
284                                 self.stopOnLock = False
285                         else:
286                                 self.isMoving = True
287                                 self.stopOnLock = True
288                                 self.diseqccommand("moveEast", 0)
289                         self.updateColors("move")
290                 elif entry == "finemove":
291                         print "stepping east"
292                         self.diseqccommand("moveEast", 0xFF) # one step
293                 elif entry == "storage":
294                         print "move to position", int(self.positioner_storage.value)
295                         self.diseqccommand("moveTo", int(self.positioner_storage.value))
296                 elif entry == "limits":
297                         self.diseqccommand("limitEast")
298
299         def blueKey(self):
300                 entry = self.getCurrentConfigPath()
301                 if entry == "move":
302                         if self.isMoving:
303                                 self.diseqccommand("stop")
304                                 self.isMoving = False
305                                 self.stopOnLock = False
306                         else:
307                                 self.diseqccommand("moveEast", 0)
308                                 self.isMoving = True
309                         self.updateColors("move")
310                         print "moving east"
311                 elif entry == "limits":
312                         self.diseqccommand("limitOn")
313
314         def diseqccommand(self, cmd, param = 0):
315                 self.diseqc.command(cmd, param)
316                 self.tuner.retune()
317
318         def updateStatus(self):
319                 if self.frontend:
320                         self.frontend.getFrontendStatus(self.frontendStatus)
321                 self["snr_db"].update()
322                 self["snr_percentage"].update()
323                 self["ber_value"].update()
324                 self["snr_bar"].update()
325                 self["ber_bar"].update()
326                 self["lock_state"].update()
327                 transponderdata = self.tuner.getTransponderData()
328                 self["frequency_value"].setText(str(transponderdata.get("frequency")))
329                 self["symbolrate_value"].setText(str(transponderdata.get("symbol_rate")))
330                 self["fec_value"].setText(str(transponderdata.get("fec_inner")))
331                 if self.frontendStatus.get("tuner_locked", 0) == 1 and self.isMoving and self.stopOnLock:
332                         self.diseqccommand("stop")
333                         self.isMoving = False
334                         self.stopOnLock = False
335                         self.updateColors(self.getCurrentConfigPath())
336                 self.statusTimer.start(50, True)
337
338         def tune(self, transponder):
339                 if transponder is not None:
340                         self.tuner.tune(transponder)
341
342 class Diseqc:
343         def __init__(self, frontend):
344                 self.frontend = frontend
345
346         def command(self, what, param = 0):
347                 if self.frontend:
348                         cmd = eDVBDiseqcCommand()
349                         if what == "moveWest":
350                                 string = 'e03169' + ("%02x" % param)
351                         elif what == "moveEast":
352                                 string = 'e03168' + ("%02x" % param)
353                         elif what == "moveTo":
354                                 string = 'e0316b' + ("%02x" % param)
355                         elif what == "store":
356                                 string = 'e0316a' + ("%02x" % param)
357                         elif what == "limitOn":
358                                 string = 'e0316a00'
359                         elif what == "limitOff":
360                                 string = 'e03163'
361                         elif what == "limitEast":
362                                 string = 'e03166'
363                         elif what == "limitWest":
364                                 string = 'e03167'
365                         else:
366                                 string = 'e03160' #positioner stop
367                         
368                         print "diseqc command:",
369                         print string
370                         cmd.setCommandString(string)
371                         self.frontend.setTone(iDVBFrontend.toneOff)
372                         sleep(0.015) # wait 15msec after disable tone
373                         self.frontend.sendDiseqc(cmd)
374                         if string == 'e03160': #positioner stop
375                                 sleep(0.05)
376                                 self.frontend.sendDiseqc(cmd) # send 2nd time
377
378 class Tuner:
379         def __init__(self, frontend):
380                 self.frontend = frontend
381
382         def tune(self, transponder):
383                 print "tuning to transponder with data", transponder
384                 parm = eDVBFrontendParametersSatellite()
385                 parm.frequency = transponder[0] * 1000
386                 parm.symbol_rate = transponder[1] * 1000
387                 parm.polarisation = transponder[2]
388                 parm.fec = transponder[3]
389                 parm.inversion = transponder[4]
390                 parm.orbital_position = transponder[5]
391                 parm.system = 0  # FIXMEE !! HARDCODED DVB-S (add support for DVB-S2)
392                 parm.modulation = 1 # FIXMEE !! HARDCODED QPSK
393                 feparm = eDVBFrontendParameters()
394                 feparm.setDVBS(parm, True)
395                 self.lastparm = feparm
396                 if self.frontend:
397                         self.frontend.tune(feparm)
398
399         def retune(self):
400                 if self.frontend:
401                         self.frontend.tune(self.lastparm)
402
403         def getTransponderData(self):
404                 ret = { }
405                 if self.frontend:
406                         self.frontend.getTransponderData(ret, True)
407                 return ret
408
409 tuning = None
410
411 class TunerScreen(ScanSetup):
412         skin = """
413                 <screen position="90,100" size="520,400" title="Tune">
414                         <widget name="config" position="20,10" size="460,350" scrollbarMode="showOnDemand" />
415                         <widget name="introduction" position="20,360" size="350,30" font="Regular;23" />
416                 </screen>"""
417
418         def __init__(self, session, feid):
419                 self.feid = feid
420                 ScanSetup.__init__(self, session)
421                 self["introduction"].setText("")
422
423         def createSetup(self):
424                 self.typeOfTuningEntry = None
425                 self.satEntry = None
426                 self.list = []
427                 self.typeOfTuningEntry = getConfigListEntry(_('Tune'), tuning.type)
428                 self.list.append(self.typeOfTuningEntry)
429                 self.satEntry = getConfigListEntry(_('Satellite'), tuning.sat)
430                 self.list.append(self.satEntry)
431                 if tuning.type.value == "manual_transponder":
432                         self.list.append(getConfigListEntry(_('Frequency'), self.scan_sat.frequency))
433                         self.list.append(getConfigListEntry(_('Inversion'), self.scan_sat.inversion))
434                         self.list.append(getConfigListEntry(_('Symbol Rate'), self.scan_sat.symbolrate))
435                         self.list.append(getConfigListEntry(_("Polarity"), self.scan_sat.polarization))
436                         self.list.append(getConfigListEntry(_("FEC"), self.scan_sat.fec))
437                 elif tuning.type.value == "predefined_transponder":
438                         self.list.append(getConfigListEntry(_("Transponder"), tuning.transponder))
439                 self["config"].list = self.list
440                 self["config"].l.setList(self.list)
441
442         def newConfig(self):
443                 if self["config"].getCurrent() == self.typeOfTuningEntry:
444                         self.createSetup()
445                 elif self["config"].getCurrent() == self.satEntry:
446                         self.createSetup()
447
448         def createConfig(self, foo):
449                 global tuning
450                 if not tuning:
451                         tuning = ConfigSubsection()
452                         tuning.type = ConfigSelection(
453                                 default = "manual_transponder",
454                                 choices = { "manual_transponder" : _("Manual transponder"),
455                                                         "predefined_transponder" : _("Predefined transponder") } )
456                         tuning.sat = ConfigSatlist(list=nimmanager.getRotorSatListForNim(self.feid))
457                         tuning.sat.addNotifier(self.tuningSatChanged)
458                         self.updateTransponders()
459                         TunerScreenConfigCreated = True
460                 ScanSetup.createConfig(self, None)
461
462         def tuningSatChanged(self, *parm):
463                 self.updateTransponders()
464
465         def updateTransponders(self):
466                 if len(tuning.sat.choices):
467                         transponderlist = nimmanager.getTransponders(int(tuning.sat.value))
468                         tps = []
469                         cnt=0
470                         for x in transponderlist:
471                                 if x[3] == 0:
472                                         pol = "H"
473                                 elif x[3] == 1:
474                                         pol = "V"
475                                 elif x[3] == 2:
476                                         pol = "CL"
477                                 elif x[3] == 3:
478                                         pol = "CR"
479                                 else:
480                                         pol = "??"
481                                 if x[4] == 0:
482                                         fec = "FEC_AUTO"
483                                 elif x[4] == 1:
484                                         fec = "FEC_1_2"
485                                 elif x[4] == 2:
486                                         fec = "FEC_2_3"
487                                 elif x[4] == 3:
488                                         fec = "FEC_3_4"
489                                 elif x[4] == 4:
490                                         fec = "FEC_5_6"
491                                 elif x[4] == 5:
492                                         fec = "FEC_7_8"
493                                 elif x[4] == 6:
494                                         fec = "FEC_8_9"
495                                 elif x[4] == 7:
496                                         fec = "FEC_3_5"
497                                 elif x[4] == 8:
498                                         fec = "FEC_4_5"
499                                 elif x[4] == 9:
500                                         fec = "FEC_9_10"
501                                 elif x[4] == 15:
502                                         fec = "FEC_None"
503                                 else:
504                                         fec = "FEC_Unknown"
505                                 tps.append(str(x[1]) + "," + str(x[2]) + "," + pol + "," + fec)
506                         tuning.transponder = ConfigSelection(choices=tps)
507
508         def keyGo(self):
509                 returnvalue = (0, 0, 0, 0, 0, 0)
510                 satpos = int(tuning.sat.value)
511                 if tuning.type.value == "manual_transponder":
512                         returnvalue = (
513                                 self.scan_sat.frequency.value,
514                                 self.scan_sat.symbolrate.value,
515                                 self.scan_sat.polarization.index,
516                                 self.scan_sat.fec.index,
517                                 self.scan_sat.inversion.index,
518                                 satpos)
519                 elif tuning.type.value == "predefined_transponder":
520                         transponder = nimmanager.getTransponders(satpos)[tuning.transponder.index]
521                         returnvalue = (int(transponder[1] / 1000), int(transponder[2] / 1000), transponder[3], transponder[4], 2, satpos)
522                 self.close(returnvalue)
523
524         def keyCancel(self):
525                 self.close(None)
526
527 class RotorNimSelection(Screen):
528         skin = """
529                 <screen position="140,165" size="400,100" title="select Slot">
530                         <widget name="nimlist" position="20,10" size="360,75" />
531                 </screen>"""
532
533         def __init__(self, session):
534                 Screen.__init__(self, session)
535
536                 nimlist = nimmanager.getNimListOfType("DVB-S")
537                 nimMenuList = []
538                 for x in nimlist:
539                         nimMenuList.append((nimmanager.nim_slots[x].friendly_full_description, x))
540                 
541                 self["nimlist"] = MenuList(nimMenuList)
542
543                 self["actions"] = ActionMap(["OkCancelActions"],
544                 {
545                         "ok": self.okbuttonClick ,
546                         "cancel": self.close
547                 }, -1)
548
549         def okbuttonClick(self):
550                 selection = self["nimlist"].getCurrent()
551                 self.session.open(PositionerSetup, selection[1])
552
553 def PositionerMain(session, **kwargs):
554         nimList = nimmanager.getNimListOfType("DVB-S")
555         if len(nimList) == 0:
556                 session.open(MessageBox, _("No positioner capable frontend found."), MessageBox.TYPE_ERROR)
557         else:
558                 if session.nav.RecordTimer.isRecording():
559                         session.open(MessageBox, _("A recording is currently running. Please stop the recording before trying to configure the positioner."), MessageBox.TYPE_ERROR)
560                 else:
561                         usableNims = []
562                         for x in nimList:
563                                 configured_rotor_sats = nimmanager.getRotorSatListForNim(x)
564                                 if len(configured_rotor_sats) != 0:
565                                         usableNims.append(x)
566                         if len(usableNims) == 1:
567                                 session.open(PositionerSetup, usableNims[0])
568                         elif len(usableNims) > 1:
569                                 session.open(RotorNimSelection)
570                         else:
571                                 session.open(MessageBox, _("No tuner is configured for use with a diseqc positioner!"), MessageBox.TYPE_ERROR)
572
573 def PositionerSetupStart(menuid, **kwargs):
574         if menuid == "scan":
575                 return [(_("Positioner setup"), PositionerMain, "positioner_setup", None)]
576         else:
577                 return []
578
579 def Plugins(**kwargs):
580         if (nimmanager.hasNimType("DVB-S")):
581                 return PluginDescriptor(name=_("Positioner setup"), description="Setup your positioner", where = PluginDescriptor.WHERE_MENU, fnc=PositionerSetupStart)
582         else:
583                 return []