GOTOX patch for vdr-1.7.16

Message ID 4D42D55D.90904@quick.cz
State New
Headers

Commit Message

Ales Jurik Jan. 28, 2011, 2:40 p.m. UTC
  Hi,

after some time I've improved the gotox patch for vdr - it is now
correctly resolving condition for sending diseqc command in
cDvbTuner::SetFrontend(void) even if diseqc command doesn't change also
with cascaded diseqc switches. So the diseqc is now sent immediately
after channel switch and not after frontend tuning is timeouted.

It was tested with diseqc.conf entries like this (cascaded: motor <->
uncommitted switch <-> committed switch):

S1.0W   11700 V  9750  t V G [E0 10 39 F1] v W30 [E0 10 38 FC] W30 v t
S1.0W   99999 V 10600  t V G [E0 10 39 F1] v W30 [E0 10 38 FD] W30 v T
S1.0W   11700 H  9750  t V G [E0 10 39 F1] V W30 [E0 10 38 FE] W30 V t
S1.0W   99999 H 10600  t V G [E0 10 39 F1] V W30 [E0 10 38 FF] W30 V T

S9.0E   11700 V  9750  t V G [E0 10 39 F1] v W30 [E0 10 38 FC] W30 v t
S9.0E   99999 V 10600  t V G [E0 10 39 F1] v W30 [E0 10 38 FD] W30 v T
S9.0E   11700 H  9750  t V G [E0 10 39 F1] V W30 [E0 10 38 FE] W30 V t
S9.0E   99999 H 10600  t V G [E0 10 39 F1] V W30 [E0 10 38 FF] W30 V T


BR,

Ales
  

Comments

Timothy D. Lenz Jan. 28, 2011, 7:44 p.m. UTC | #1
I can't try it atm because a relay went bad in my rotor. Only goes one 
direction. But looking at the patch, you put the user location in the 
patch, not the conf?

On 1/28/2011 7:40 AM, Ales Jurik wrote:
> Hi,
>
> after some time I've improved the gotox patch for vdr - it is now
> correctly resolving condition for sending diseqc command in
> cDvbTuner::SetFrontend(void) even if diseqc command doesn't change also
> with cascaded diseqc switches. So the diseqc is now sent immediately
> after channel switch and not after frontend tuning is timeouted.
>
> It was tested with diseqc.conf entries like this (cascaded: motor<->
> uncommitted switch<->  committed switch):
>
> S1.0W   11700 V  9750  t V G [E0 10 39 F1] v W30 [E0 10 38 FC] W30 v t
> S1.0W   99999 V 10600  t V G [E0 10 39 F1] v W30 [E0 10 38 FD] W30 v T
> S1.0W   11700 H  9750  t V G [E0 10 39 F1] V W30 [E0 10 38 FE] W30 V t
> S1.0W   99999 H 10600  t V G [E0 10 39 F1] V W30 [E0 10 38 FF] W30 V T
>
> S9.0E   11700 V  9750  t V G [E0 10 39 F1] v W30 [E0 10 38 FC] W30 v t
> S9.0E   99999 V 10600  t V G [E0 10 39 F1] v W30 [E0 10 38 FD] W30 v T
> S9.0E   11700 H  9750  t V G [E0 10 39 F1] V W30 [E0 10 38 FE] W30 V t
> S9.0E   99999 H 10600  t V G [E0 10 39 F1] V W30 [E0 10 38 FF] W30 V T
>
>
> BR,
>
> Ales
>
>
>
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
  
Ales Jurik Jan. 28, 2011, 8:29 p.m. UTC | #2
On 01/28/11 20:44, Timothy D. Lenz wrote:
> I can't try it atm because a relay went bad in my rotor. Only goes one
> direction. But looking at the patch, you put the user location in the
> patch, not the conf?
> 
> On 1/28/2011 7:40 AM, Ales Jurik wrote:
>> Hi,
>>
>> after some time I've improved the gotox patch for vdr - it is now
>> correctly resolving condition for sending diseqc command in
>> cDvbTuner::SetFrontend(void) even if diseqc command doesn't change also
>> with cascaded diseqc switches. So the diseqc is now sent immediately
>> after channel switch and not after frontend tuning is timeouted.
>>
>> It was tested with diseqc.conf entries like this (cascaded: motor<->
>> uncommitted switch<->  committed switch):
>>
>> S1.0W   11700 V  9750  t V G [E0 10 39 F1] v W30 [E0 10 38 FC] W30 v t
>> S1.0W   99999 V 10600  t V G [E0 10 39 F1] v W30 [E0 10 38 FD] W30 v T
>> S1.0W   11700 H  9750  t V G [E0 10 39 F1] V W30 [E0 10 38 FE] W30 V t
>> S1.0W   99999 H 10600  t V G [E0 10 39 F1] V W30 [E0 10 38 FF] W30 V T
>>
>> S9.0E   11700 V  9750  t V G [E0 10 39 F1] v W30 [E0 10 38 FC] W30 v t
>> S9.0E   99999 V 10600  t V G [E0 10 39 F1] v W30 [E0 10 38 FD] W30 v T
>> S9.0E   11700 H  9750  t V G [E0 10 39 F1] V W30 [E0 10 38 FE] W30 V t
>> S9.0E   99999 H 10600  t V G [E0 10 39 F1] V W30 [E0 10 38 FF] W30 V T
>>
>>
>> BR,
>>
>> Ales
>>
>>
>>
>> _______________________________________________
>> vdr mailing list
>> vdr@linuxtv.org
>> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
> 
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
> 

No, all parameters are in setup.conf. If you mean initial values, they
are in this patch from the first version
(http://www.linuxtv.org/pipermail/vdr/2008-March/016164.html). If this
is a problem in next version I'll zero it :) .

You also could see that all parameters are in config file if you take a
look more down in the patch in place where file menu.c is used.

BR,

Ales
  
Seppo Ingalsuo Jan. 28, 2011, 8:32 p.m. UTC | #3
pe, 2011-01-28 kello 12:44 -0700, Timothy D. Lenz kirjoitti:
> I can't try it atm because a relay went bad in my rotor. Only goes one 
> direction. But looking at the patch, you put the user location in the 
> patch, not the conf?

There is an OSD menu setting for it (Settings -> LNB). The value in the
patch is just some initial default.

Seppo
  

Patch

diff -rup a/config.c b/config.c
--- a/config.c	2010-06-06 12:06:43.000000000 +0200
+++ b/config.c	2011-01-28 14:44:30.888676207 +0100
@@ -326,6 +326,12 @@  cSetup::cSetup(void)
   LnbFrequLo =  9750;
   LnbFrequHi = 10600;
   DiSEqC = 0;
+  UseGotox = 0;
+  GotoxSpeed = 100;
+  GotoxRepeat = 0;
+  GotoxSN = 0; GotoxLat = 613; GotoxEW = 1; GotoxLong = 236; // Somewhere at Tampere, Finland :^)
+  GotoxMaxSwing = 60; 
+  GotoxPrevSource = 0;
   SetSystemTime = 0;
   TimeSource = 0;
   TimeTransponder = 0;
@@ -517,6 +523,15 @@  bool cSetup::Parse(const char *Name, con
   else if (!strcasecmp(Name, "LnbFrequLo"))          LnbFrequLo         = atoi(Value);
   else if (!strcasecmp(Name, "LnbFrequHi"))          LnbFrequHi         = atoi(Value);
   else if (!strcasecmp(Name, "DiSEqC"))              DiSEqC             = atoi(Value);
+  else if (!strcasecmp(Name, "UseGotox"))            UseGotox           = atoi(Value);
+  else if (!strcasecmp(Name, "GotoxSpeed"))          GotoxSpeed         = atoi(Value);
+  else if (!strcasecmp(Name, "GotoxRepeat"))         GotoxRepeat        = atoi(Value);
+  else if (!strcasecmp(Name, "GotoxSN"))             GotoxSN            = atoi(Value);
+  else if (!strcasecmp(Name, "GotoxLat"))            GotoxLat           = atoi(Value);
+  else if (!strcasecmp(Name, "GotoxEW"))             GotoxEW            = atoi(Value);
+  else if (!strcasecmp(Name, "GotoxLong"))           GotoxLong          = atoi(Value);
+  else if (!strcasecmp(Name, "GotoxMaxSwing"))       GotoxMaxSwing      = atoi(Value);
+  else if (!strcasecmp(Name, "GotoxPrevSource"))     GotoxPrevSource    = atoi(Value);
   else if (!strcasecmp(Name, "SetSystemTime"))       SetSystemTime      = atoi(Value);
   else if (!strcasecmp(Name, "TimeSource"))          TimeSource         = cSource::FromString(Value);
   else if (!strcasecmp(Name, "TimeTransponder"))     TimeTransponder    = atoi(Value);
@@ -613,6 +628,15 @@  bool cSetup::Save(void)
   Store("LnbFrequLo",         LnbFrequLo);
   Store("LnbFrequHi",         LnbFrequHi);
   Store("DiSEqC",             DiSEqC);
+  Store("UseGotox",           UseGotox);
+  Store("GotoxSpeed",         GotoxSpeed);
+  Store("GotoxRepeat",        GotoxRepeat);
+  Store("GotoxSN",            GotoxSN);
+  Store("GotoxLat",           GotoxLat);
+  Store("GotoxEW",            GotoxEW);
+  Store("GotoxLong",          GotoxLong);
+  Store("GotoxMaxSwing",	  GotoxMaxSwing);
+  Store("GotoxPrevSource",    GotoxPrevSource);
   Store("SetSystemTime",      SetSystemTime);
   Store("TimeSource",         cSource::ToString(TimeSource));
   Store("TimeTransponder",    TimeTransponder);
diff -rup a/config.h b/config.h
--- a/config.h	2010-09-12 13:31:21.000000000 +0200
+++ b/config.h	2011-01-28 14:45:10.882778898 +0100
@@ -229,6 +229,15 @@  public:
   int LnbFrequLo;
   int LnbFrequHi;
   int DiSEqC;
+  int GotoxRepeat;
+  int GotoxSN;
+  int GotoxEW;
+  int GotoxSpeed;
+  int GotoxLat;
+  int GotoxLong;
+  int GotoxMaxSwing;
+  int UseGotox;
+  int GotoxPrevSource;
   int SetSystemTime;
   int TimeSource;
   int TimeTransponder;
diff -rup a/diseqc.c b/diseqc.c
--- a/diseqc.c	2010-02-06 16:43:31.000000000 +0100
+++ b/diseqc.c	2011-01-28 14:46:03.418659831 +0100
@@ -134,6 +134,7 @@  cDiseqc::eDiseqcActions cDiseqc::Execute
           case 'V': return daVoltage18;
           case 'A': return daMiniA;
           case 'B': return daMiniB;
+	      case 'G': return daGotoX;
           case 'W': *CurrentAction = Wait(*CurrentAction); break;
           case '[': *CurrentAction = Codes(*CurrentAction); return *CurrentAction ? daCodes : daNone;
           default: return daNone;
diff -rup a/diseqc.h b/diseqc.h
--- a/diseqc.h	2010-02-06 16:14:42.000000000 +0100
+++ b/diseqc.h	2011-01-28 14:46:10.495659376 +0100
@@ -22,6 +22,7 @@  public:
     daVoltage18,
     daMiniA,
     daMiniB,
+    daGotoX,
     daCodes,
     };
   enum { MaxDiseqcCodes = 6 };
diff -rup a/dvbdevice.c b/dvbdevice.c
--- a/dvbdevice.c	2010-05-01 11:47:13.000000000 +0200
+++ b/dvbdevice.c	2011-01-28 14:47:30.655659479 +0100
@@ -15,9 +15,11 @@ 
 #include <linux/dvb/frontend.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
+#include <math.h>
 #include "channels.h"
 #include "diseqc.h"
 #include "dvbci.h"
+#include "skins.h"
 #include "menuitems.h"
 #include "sourceparams.h"
 
@@ -265,6 +267,7 @@  private:
   fe_delivery_system frontendType;
   cChannel channel;
   const char *diseqcCommands;
+  int lastSource;
   eTunerStatus tunerStatus;
   cMutex mutex;
   cCondVar locked;
@@ -292,6 +295,7 @@  cDvbTuner::cDvbTuner(int Device, int Fd_
   lockTimeout = 0;
   lastTimeoutReport = 0;
   diseqcCommands = NULL;
+  lastSource = 0;
   tunerStatus = tsIdle;
   if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
      CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
@@ -365,6 +369,99 @@  static unsigned int FrequencyToHz(unsign
   return f;
 }
 
+void HandleGotox(int fd_frontend, int new_source)
+{
+  
+  int gotoXTable[10] = { 0x00, 0x02, 0x03, 0x05, 0x06, 0x08, 0x0A, 0x0B, 0x0D, 0x0E };
+  int satlong;
+  int satprev;
+  float waitseconds = 0;
+  float waitmaximum = 60;
+
+  if (Setup.UseGotox == 0)
+    return;
+
+  // Check if zapped into new source position?
+  if (new_source != Setup.GotoxPrevSource) {
+    if ((new_source & 0xFF000000) != 0x53000000) 
+      return; // Fail, not S type source
+    satlong = (new_source & 0x00000FFFF);
+    satprev = (Setup.GotoxPrevSource & 0x0000FFFF);
+    if (new_source & 0x00008000) {
+      satlong ^= 0x0000FFFF; satlong++; satlong *= (-1);
+    }
+    if (Setup.GotoxPrevSource & 0x00008000) {
+      satprev ^= 0x0000FFFF; satprev++; satprev *= (-1);
+    }
+    if (Setup.GotoxSpeed > 0) {
+      waitseconds = fabs(satlong-satprev)/(float)(Setup.GotoxSpeed);
+      waitmaximum = 10*(float)(Setup.GotoxMaxSwing)/(float)(Setup.GotoxSpeed);
+      if (waitseconds < 0.0) waitseconds = 0.0; // Should not happen but ...
+      if (waitseconds > waitmaximum) waitseconds = 2 + waitmaximum; // Limit wait time to +2s over dish max. move
+    }
+    int Long=Setup.GotoxEW ? -Setup.GotoxLong : Setup.GotoxLong;
+    int Lat=Setup.GotoxSN ? -Setup.GotoxLat : Setup.GotoxLat;
+    double azimuth=M_PI+atan(tan((satlong-Long)*M_PI/1800)/sin(Lat*M_PI/1800));
+    double x=acos(cos((satlong-Long)*M_PI/1800)*cos(Lat*M_PI/1800));
+    double elevation=atan((cos(x)-0.1513)/sin(x));
+    double SatHourangle=180+atan((-cos(elevation)*sin(azimuth))/(sin(elevation)*cos(Lat*M_PI/1800)
+			   -cos(elevation)*sin(Lat*M_PI/1800)*cos(azimuth)))*180/M_PI;
+    int tmp=(int)(fabs(180-SatHourangle)*10);
+    tmp=(tmp/10)*0x10 + gotoXTable[ tmp % 10 ];
+    int p2=(tmp%0x0100);
+    int p1=(tmp/0x0100);
+    if (SatHourangle < 180)
+      p1 |= 0xe0;
+    else
+      p1 |= 0xd0;
+
+    dsyslog("DiSEqC GotoX %d (%d) -> %d (%d), wait time %4.1fs",
+	    satprev, Setup.GotoxPrevSource, satlong, new_source, waitseconds);
+
+#if 1
+    // Set high LNB voltage and tone off, then wait > 15ms
+    CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18));
+    CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
+    usleep(20000);
+    
+    // Send 1st GotoX command, then wait > 15ms
+    uchar gotox_bytes[5] = { 0xe0, 0x31, 0x6e, p1, p2};
+    struct dvb_diseqc_master_cmd gotox_cmd;
+    memcpy(gotox_cmd.msg, gotox_bytes, 5);
+    gotox_cmd.msg_len = 5;
+    CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &gotox_cmd));
+    usleep(20000);
+
+    // Send repeated GotoX command, then wait > 15ms
+    if (Setup.GotoxRepeat) {
+      gotox_bytes[0] = 0xe1;
+      memcpy(gotox_cmd.msg, gotox_bytes, 5);
+      CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &gotox_cmd));
+      usleep(20000);
+    }
+    
+ 	 {
+ 	   char mess_move[60];
+        snprintf(mess_move,sizeof(mess_move),"Moving dish to %d.%d%s - %ds", \
+       	(new_source & 0x0800)?-satlong/10:satlong/10, \
+       	(new_source & 0x0800)?-satlong%10:satlong%10, \
+       	(new_source & 0x0800)?"E":"W", (int)waitseconds);
+      // Wait for dish movement and display message approx. for that time
+      Skins.QueueMessage(mtWarning, mess_move, int(1 + waitseconds - Setup.ChannelInfoTime), 0);
+      while (waitseconds > 0.0) {
+        usleep(100000); // 100ms
+        waitseconds = waitseconds - 100e-3;
+      }
+    }
+
+#endif
+
+    Setup.GotoxPrevSource = new_source;
+    dsyslog("DiSEqC GotoX done.");
+  }
+}
+
+
 bool cDvbTuner::SetFrontend(void)
 {
 #define MAXFRONTENDCMDS 16
@@ -394,7 +491,9 @@  bool cDvbTuner::SetFrontend(void)
      if (Setup.DiSEqC) {
         cDiseqc *diseqc = Diseqcs.Get(device, channel.Source(), channel.Frequency(), dtp.Polarization());
         if (diseqc) {
-           if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) {
+           if (diseqc->Commands() && (!diseqcCommands || \
+           		 (strcmp(diseqcCommands, diseqc->Commands())) || \
+           		  (strrchr(diseqc->Commands(),'G') && (channel.Source() != lastSource )))) {
               cDiseqc::eDiseqcActions da;
               for (char *CurrentAction = NULL; (da = diseqc->Execute(&CurrentAction)) != cDiseqc::daNone; ) {
                   switch (da) {
@@ -405,6 +504,7 @@  bool cDvbTuner::SetFrontend(void)
                     case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
                     case cDiseqc::daMiniA:     CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
                     case cDiseqc::daMiniB:     CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
+                    case cDiseqc::daGotoX:     HandleGotox(fd_frontend, channel.Source()); break;
                     case cDiseqc::daCodes: {
                          int n = 0;
                          uchar *codes = diseqc->Codes(n);
@@ -420,6 +520,7 @@  bool cDvbTuner::SetFrontend(void)
                     }
                   }
               diseqcCommands = diseqc->Commands();
+              lastSource = channel.Source();
               }
            frequency -= diseqc->Lof();
            }
@@ -429,6 +530,10 @@  bool cDvbTuner::SetFrontend(void)
            }
         }
      else {
+         // Send GotoX DiSEqC command if activated in vdr setup. Then wait with high LNB voltage
+         // estimated time for dish movement
+        HandleGotox(fd_frontend, channel.Source()); 
+
         int tone = SEC_TONE_OFF;
         if (frequency < (unsigned int)Setup.LnbSLOF) {
            frequency -= Setup.LnbFrequLo;
@@ -541,6 +646,7 @@  void cDvbTuner::Action(void)
                if (Timer.TimedOut()) {
                   tunerStatus = tsSet;
                   diseqcCommands = NULL;
+                  lastSource = 0;
                   if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
                      isyslog("frontend %d/%d timed out while tuning to channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder());
                      lastTimeoutReport = time(NULL);
@@ -551,6 +657,7 @@  void cDvbTuner::Action(void)
                if (Status & FE_REINIT) {
                   tunerStatus = tsSet;
                   diseqcCommands = NULL;
+                  lastSource = 0;
                   isyslog("frontend %d/%d was reinitialized", adapter, frontend);
                   lastTimeoutReport = 0;
                   continue;
diff -rup a/menu.c b/menu.c
--- a/menu.c	2010-06-06 11:56:16.000000000 +0200
+++ b/menu.c	2011-01-28 14:50:08.625533824 +0100
@@ -2890,8 +2890,15 @@  void cMenuSetupLNB::Setup(void)
      Add(new cMenuEditIntItem( tr("Setup.LNB$SLOF (MHz)"),               &data.LnbSLOF));
      Add(new cMenuEditIntItem( tr("Setup.LNB$Low LNB frequency (MHz)"),  &data.LnbFrequLo));
      Add(new cMenuEditIntItem( tr("Setup.LNB$High LNB frequency (MHz)"), &data.LnbFrequHi));
-     }
-
+  }
+  Add(new cMenuEditBoolItem(tr("Setup.LNB$Use GotoX dish positioning"), &data.UseGotox));
+  if (data.UseGotox) {
+     Add(new cMenuEditBoolItem(tr("Setup.LNB$Repeat GotoX commands"), &data.GotoxRepeat));
+     Add(new cMenuEditIntpItem(tr("Setup.LNB$Latitude"), &data.GotoxLat,0,900,&data.GotoxSN,tr("North"),tr("South")));
+     Add(new cMenuEditIntpItem(tr("Setup.LNB$Longitude"), &data.GotoxLong,0,1800,&data.GotoxEW,tr("West"),tr("East")));
+     Add(new cMenuEditIntItem(tr("Setup.LNB$GotoxMaxSwing"), &data.GotoxMaxSwing,0,180));
+     Add(new cMenuEditIntdItem(tr("Setup.LNB$Rotor speed (deg/s)"), &data.GotoxSpeed, 1, 100));
+  }
   SetCurrent(Get(current));
   Display();
 }
@@ -2899,10 +2906,15 @@  void cMenuSetupLNB::Setup(void)
 eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
 {
   int oldDiSEqC = data.DiSEqC;
+  int oldUseGotox = data.UseGotox;
   eOSState state = cMenuSetupBase::ProcessKey(Key);
 
   if (Key != kNone && data.DiSEqC != oldDiSEqC)
      Setup();
+
+  if (Key != kNone && data.UseGotox != oldUseGotox)
+     Setup();
+
   return state;
 }
 
diff -rup a/menuitems.c b/menuitems.c
--- a/menuitems.c	2010-06-06 12:37:08.000000000 +0200
+++ b/menuitems.c	2011-01-28 14:50:21.357534700 +0100
@@ -1113,3 +1113,121 @@  void cMenuSetupPage::SetupStore(const ch
   if (plugin)
      plugin->SetupStore(Name, Value);
 }
+
+// cMenuEditIntpItem & cMenuEditIntdItem for GotoX function
+
+void cMenuEditIntpItem::Set(void)
+{
+  char buf[16];
+  snprintf(buf, sizeof(buf), "%d.%d %s", *value/10, *value % 10, *value2 ? trueString : falseString);
+  SetValue(buf);
+}
+
+void cMenuEditIntdItem::Set(void)
+{
+  char buf[16];
+  snprintf(buf, sizeof(buf), "%d.%d", *value/10, *value % 10);
+  SetValue(buf);
+}
+
+
+cMenuEditIntpItem::cMenuEditIntpItem(const char *Name, int *Value, int Min, int Max,int *Value2, const char *FalseString,const char *TrueString):cMenuEditIntItem(Name, Value, Min, Max)
+{
+  value = Value;
+  value2= Value2;
+  trueString = TrueString;
+  falseString = FalseString;
+  min = Min;
+  max = Max;
+  Set();
+}
+
+cMenuEditIntdItem::cMenuEditIntdItem(const char *Name, int *Value, int Min, int Max):cMenuEditIntItem(Name, Value, Min, Max)
+{
+  value = Value;
+  min = Min;
+  max = Max;
+  Set();
+}
+
+eOSState cMenuEditIntpItem::ProcessKey(eKeys Key)
+{
+  eOSState state = cMenuEditItem::ProcessKey(Key);
+  if (state == osUnknown)
+  {
+    int newValue = *value;
+    int newValue2= *value2;
+    Key = NORMALKEY(Key);
+    switch (Key) {
+      case kNone  : break;
+      case k0...k9:
+                    if (fresh)
+                    {
+                      *value = 0;
+                      fresh = false;
+                    }
+                    newValue = *value * 10 + (Key - k0);
+                    break;
+      case kLeft  :
+                    newValue2 = 0;
+                    fresh = true;
+                    break;
+      case kRight :
+                    newValue2 = 1;
+                    fresh = true;
+                    break;
+      default     :
+                    if (*value < min) { *value = min; Set(); }
+                    if (*value > max) { *value = max; Set(); }
+                    return state;
+                 }
+    if ((!fresh || min <= newValue) && newValue <= max)
+    {
+      *value = newValue;
+      *value2 = newValue2;
+      Set();
+    }
+ state = osContinue;
+  }
+  return state;
+}
+
+eOSState cMenuEditIntdItem::ProcessKey(eKeys Key)
+{
+  eOSState state = cMenuEditItem::ProcessKey(Key);
+  if (state == osUnknown)
+  {
+    int newValue = *value;
+    Key = NORMALKEY(Key);
+    switch (Key) {
+      case kNone  : break;
+      case k0...k9:
+                    if (fresh)
+                    {
+                      *value = 0;
+                      fresh = false;
+                    }
+                    newValue = *value * 10 + (Key - k0);
+                    break;
+      case kLeft  :
+	            newValue = *value - 1;
+                    fresh = true;
+                    break;
+      case kRight :
+                    newValue = *value + 1;
+                    fresh = true;
+                    break;
+      default     :
+                    if (*value < min) { *value = min; Set(); }
+                    if (*value > max) { *value = max; Set(); }
+                    return state;
+                 }
+    if ((!fresh || min <= newValue) && newValue <= max)
+    {
+      *value = newValue;
+      Set();
+    }
+    state = osContinue;
+  }
+  return state;
+}
diff -rup a/menuitems.h b/menuitems.h
--- a/menuitems.h	2010-06-06 12:32:38.000000000 +0200
+++ b/menuitems.h	2011-01-28 14:50:36.217533731 +0100
@@ -201,4 +201,22 @@  public:
   void SetPlugin(cPlugin *Plugin);
   };
 
+class cMenuEditIntpItem : public cMenuEditIntItem {
+protected:
+  virtual void Set(void);
+  const char *falseString, *trueString;
+  int *value2;
+public:
+  cMenuEditIntpItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX, int *Value2=0, const char *FalseString = "", const char *TrueSting = NULL);
+  virtual eOSState ProcessKey(eKeys Key);
+};
+
+class cMenuEditIntdItem : public cMenuEditIntItem {
+protected:
+  virtual void Set(void);
+public:
+  cMenuEditIntdItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX);
+  virtual eOSState ProcessKey(eKeys Key);
+};
+
 #endif //__MENUITEMS_H