@@ -389,6 +389,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;
@@ -583,6 +589,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);
@@ -680,6 +695,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);
@@ -248,6 +248,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;
@@ -250,6 +250,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 'S': *CurrentAction = GetScrBank(*CurrentAction); break;
case '[': *CurrentAction = GetCodes(*CurrentAction, Codes, MaxCodes);
@@ -267,6 +268,22 @@ cDiseqc::eDiseqcActions cDiseqc::Execute
return daNone;
}
+cDiseqc::eDiseqcActions cDiseqc::TestForGotoXX(const char **CurrentAction) const
+{
+ if (!*CurrentAction)
+ *CurrentAction = commands;
+ while (*CurrentAction && **CurrentAction) {
+ switch (*(*CurrentAction)++) {
+ case ' ': break;
+ case 'G': return daGotoX;
+ default: return daNone;
+ }
+ }
+ return daNone;
+}
+
+
+
// --- cDiseqcs --------------------------------------------------------------
cDiseqcs Diseqcs;
@@ -50,6 +50,7 @@ public:
daVoltage18,
daMiniA,
daMiniB,
+ daGotoX,
daScr,
daCodes,
};
@@ -88,6 +89,7 @@ public:
///< be a pointer returned from a previous call to cDiseqcs::Get().
///< Frequency must be the frequency the tuner will be tuned to, and will be
///< set to the proper SCR frequency upon return (if SCR is used).
+ eDiseqcActions TestForGotoXX(const char **CurrentAction) const;
int Devices(void) const { return devices; }
int Source(void) const { return source; }
int Slof(void) const { return slof; }
@@ -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"
@@ -271,6 +273,7 @@ private:
fe_delivery_system frontendType;
cChannel channel;
const cDiseqc *lastDiseqc;
+ int lastSource;
const cScr *scr;
eTunerStatus tunerStatus;
cMutex mutex;
@@ -283,6 +286,7 @@ private:
void ClearEventQueue(void) const;
bool GetFrontendStatus(fe_status_t &Status) const;
void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency);
+ bool TestDiseqcForGotoXX(const cDiseqc *Diseqc);
void ResetToneAndVoltage(void);
bool SetFrontend(void);
virtual void Action(void);
@@ -316,6 +320,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *D
lockTimeout = 0;
lastTimeoutReport = 0;
lastDiseqc = NULL;
+ lastSource = 0;
scr = NULL;
tunerStatus = tsIdle;
bondedTuner = NULL;
@@ -455,6 +460,97 @@ bool cDvbTuner::IsTunedTo(const cChannel
return strcmp(channel.Parameters(), Channel->Parameters()) == 0;
}
+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));
+ cCondWait::SleepMs(20);
+
+ // 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));
+ cCondWait::SleepMs(20);
+
+ // 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));
+ cCondWait::SleepMs(20);
+ }
+
+ {
+ 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) {
+ cCondWait::SleepMs(100); // 100ms
+ waitseconds = waitseconds - 100e-3;
+ }
+ }
+#endif
+
+ Setup.GotoxPrevSource = new_source;
+ dsyslog("DiSEqC GotoX done.");
+ }
+}
+
void cDvbTuner::SetChannel(const cChannel *Channel)
{
if (Channel) {
@@ -648,6 +744,7 @@ void cDvbTuner::ExecuteDiseqc(const cDis
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: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); break;
default: esyslog("ERROR: unknown diseqc command %d", da);
}
@@ -656,6 +753,21 @@ void cDvbTuner::ExecuteDiseqc(const cDis
ResetToneAndVoltage(); // makes sure we don't block the bus!
}
+bool cDvbTuner::TestDiseqcForGotoXX(const cDiseqc *Diseqc)
+{
+ struct dvb_diseqc_master_cmd cmd;
+ const char *CurrentAction = NULL;
+ for (;;) {
+ cmd.msg_len = sizeof(cmd.msg);
+ cDiseqc::eDiseqcActions da = Diseqc->TestForGotoXX(&CurrentAction);
+ if (da == cDiseqc::daNone)
+ break;
+ if (da == cDiseqc::daGotoX)
+ return true;
+ }
+ return false;
+}
+
void cDvbTuner::ResetToneAndVoltage(void)
{
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13));
@@ -691,7 +803,8 @@ bool cDvbTuner::SetFrontend(void)
if (Setup.DiSEqC) {
if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, channel.Source(), frequency, dtp.Polarization(), &scr)) {
frequency -= diseqc->Lof();
- if (diseqc != lastDiseqc || diseqc->IsScr()) {
+ if (diseqc != lastDiseqc || diseqc->IsScr() || \
+ ((TestDiseqcForGotoXX(diseqc) && (channel.Source() != lastSource )))) {
if (GetBondedMaster() == this) {
ExecuteDiseqc(diseqc, &frequency);
if (frequency == 0)
@@ -700,6 +813,7 @@ bool cDvbTuner::SetFrontend(void)
else
ResetToneAndVoltage();
lastDiseqc = diseqc;
+ lastSource = channel.Source();
}
}
else {
@@ -708,6 +822,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;
@@ -824,6 +942,7 @@ void cDvbTuner::Action(void)
if (Timer.TimedOut()) {
tunerStatus = tsSet;
lastDiseqc = 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);
@@ -837,6 +956,7 @@ void cDvbTuner::Action(void)
if (Status & FE_REINIT) {
tunerStatus = tsSet;
lastDiseqc = NULL;
+ lastSource = 0;
isyslog("frontend %d/%d was reinitialized", adapter, frontend);
lastTimeoutReport = 0;
continue;
@@ -2925,6 +2925,15 @@ void cMenuSetupLNB::Setup(void)
}
}
+ 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();
}
@@ -2932,6 +2941,7 @@ void cMenuSetupLNB::Setup(void)
eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
{
int oldDiSEqC = data.DiSEqC;
+ int oldUseGotox = data.UseGotox;
bool DeviceBondingsChanged = false;
if (Key == kOk) {
cString NewDeviceBondings = satCableNumbers.ToString();
@@ -2944,6 +2954,10 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys
Setup();
else if (DeviceBondingsChanged)
cDvbDevice::BondDevices(data.DeviceBondings);
+
+ if (Key != kNone && data.UseGotox != oldUseGotox)
+ Setup();
+
return state;
}
@@ -1132,3 +1132,122 @@ 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;
+}
+
@@ -204,4 +204,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