MythTV  0.26-pre
diseqc.cpp
Go to the documentation of this file.
00001 /* -*- Mode: c++ -*-
00002  * \file dvbdevtree.cpp
00003  * \brief DVB-S Device Tree Control Classes.
00004  * \author Copyright (C) 2006, Yeasah Pell
00005  */
00006 
00007 // Std C headers
00008 #include <cstring>
00009 #include <cmath>
00010 #include <unistd.h>
00011 
00012 // POSIX headers
00013 #include <sys/time.h>
00014 
00015 // Qt headers
00016 #include <QString>
00017 
00018 // MythTV headers
00019 #include "mythcorecontext.h"
00020 #include "mythdb.h"
00021 #include "mythlogging.h"
00022 #include "diseqc.h"
00023 #include "dtvmultiplex.h"
00024 #include "compat.h"
00025 
00026 #ifdef USING_DVB
00027 #   include "dvbtypes.h"
00028 #else
00029 #   define SEC_VOLTAGE_13  0
00030 #   define SEC_VOLTAGE_18  1
00031 #   define SEC_VOLTAGE_OFF 2
00032 #   define SEC_MINI_A      0
00033 #   define SEC_MINI_B      1
00034 #endif
00035 
00036 // DiSEqC sleep intervals per eutelsat spec
00037 #define DISEQC_SHORT_WAIT     (15 * 1000)
00038 #define DISEQC_LONG_WAIT      (100 * 1000)
00039 #define DISEQC_POWER_OFF_WAIT (1000 * 1000)
00040 #define DISEQC_POWER_ON_WAIT  (500 * 1000)
00041 
00042 // Number of times to retry ioctls after receiving ETIMEDOUT before giving up
00043 #define TIMEOUT_RETRIES       10
00044 #define TIMEOUT_WAIT          (250 * 1000)
00045 
00046 // Framing byte
00047 #define DISEQC_FRM            0xe0
00048 #define DISEQC_FRM_REPEAT     (1 << 0)
00049 #define DISEQC_FRM_REPLY_REQ  (1 << 1)
00050 
00051 // Address byte
00052 #define DISEQC_ADR_ALL        0x00
00053 #define DISEQC_ADR_SW_ALL     0x10
00054 #define DISEQC_ADR_LNB        0x11
00055 #define DISEQC_ADR_LNB_SW     0x12
00056 #define DISEQC_ADR_SW_BLK     0x14
00057 #define DISEQC_ADR_SW         0x15
00058 #define DISEQC_ADR_SMATV      0x18
00059 #define DISEQC_ADR_POL_ALL    0x20
00060 #define DISEQC_ADR_POL_LIN    0x21
00061 #define DISEQC_ADR_POS_ALL    0x30
00062 #define DISEQC_ADR_POS_AZ     0x31
00063 #define DISEQC_ADR_POS_EL     0x32
00064 
00065 // Command byte
00066 #define DISEQC_CMD_RESET      0x00
00067 #define DISEQC_CMD_CLR_RESET  0x01
00068 #define DISEQC_CMD_WRITE_N0   0x38
00069 #define DISEQC_CMD_WRITE_N1   0x39
00070 #define DISEQC_CMD_WRITE_FREQ 0x58
00071 #define DISEQC_CMD_HALT       0x60
00072 #define DISEQC_CMD_LMT_OFF    0x63
00073 #define DISEQC_CMD_LMT_E      0x66
00074 #define DISEQC_CMD_LMT_W      0x67
00075 #define DISEQC_CMD_DRIVE_E    0x68
00076 #define DISEQC_CMD_DRIVE_W    0x69
00077 #define DISEQC_CMD_STORE_POS  0x6a
00078 #define DISEQC_CMD_GOTO_POS   0x6b
00079 #define DISEQC_CMD_GOTO_X     0x6e
00080 
00081 #define TO_RADS (M_PI / 180.0)
00082 #define TO_DEC  (180.0 / M_PI)
00083 
00084 #define EPS     1E-4
00085 
00086 #define LOC      QString("DiSEqCDevTree: ")
00087 
00088 QString DiSEqCDevDevice::TableToString(uint type, const TypeTable *table)
00089 {
00090     for (; !table->name.isEmpty(); table++)
00091     {
00092         if (type == table->value)
00093         {
00094             QString tmp = table->name;
00095             tmp.detach();
00096             return tmp;
00097         }
00098     }
00099     return QString::null;
00100 }
00101 
00102 uint DiSEqCDevDevice::TableFromString(const QString   &type,
00103                                       const TypeTable *table)
00104 {
00105     uint first_val = table->value;
00106     for (; !table->name.isEmpty(); table++)
00107     {
00108         if (type == table->name)
00109             return table->value;
00110     }
00111     return first_val;
00112 }
00113 
00115 
00123 DiSEqCDevSettings::DiSEqCDevSettings()
00124     : m_input_id((uint) -1)
00125 {
00126 }
00127 
00133 bool DiSEqCDevSettings::Load(uint card_input_id)
00134 {
00135     if (card_input_id == m_input_id)
00136         return true;
00137 
00138     m_config.clear();
00139 
00140     // load settings from DB
00141     MSqlQuery query(MSqlQuery::InitCon());
00142     query.prepare(
00143         "SELECT diseqcid, value "
00144         "FROM diseqc_config "
00145         "WHERE cardinputid = :INPUTID");
00146 
00147     query.bindValue(":INPUTID", card_input_id);
00148     if (!query.exec() || !query.isActive())
00149     {
00150         MythDB::DBError("DiSEqCDevSettings::Load", query);
00151         return false;
00152     }
00153 
00154     while (query.next())
00155         m_config[query.value(0).toUInt()] = query.value(1).toDouble();
00156 
00157     m_input_id = card_input_id;
00158 
00159     return true;
00160 }
00161 
00167 bool DiSEqCDevSettings::Store(uint card_input_id) const
00168 {
00169     MSqlQuery query(MSqlQuery::InitCon());
00170 
00171     // clear out previous settings
00172     query.prepare(
00173         "DELETE from diseqc_config "
00174         "WHERE cardinputid = :INPUTID");
00175     query.bindValue(":INPUTID", card_input_id);
00176 
00177     if (!query.exec() || !query.isActive())
00178     {
00179         MythDB::DBError("DiSEqCDevSettings::Store 1", query);
00180         return false;
00181     }
00182 
00183     // insert new settings
00184     query.prepare(
00185         "INSERT INTO diseqc_config "
00186         "       ( cardinputid, diseqcid, value) "
00187         "VALUES (:INPUTID,    :DEVID,     :VALUE) ");
00188 
00189     uint_to_dbl_t::const_iterator it = m_config.begin();
00190     for (; it != m_config.end(); ++it)
00191     {
00192         query.bindValue(":INPUTID", card_input_id);
00193         query.bindValue(":DEVID",   it.key());
00194         query.bindValue(":VALUE",   *it);
00195         if (!query.exec() || !query.isActive())
00196         {
00197             MythDB::DBError("DiSEqCDevSettings::Store 2", query);
00198             return false;
00199         }
00200     }
00201 
00202     return true;
00203 }
00204 
00210 double DiSEqCDevSettings::GetValue(uint devid) const
00211 {
00212     uint_to_dbl_t::const_iterator it = m_config.find(devid);
00213 
00214     if (it != m_config.end())
00215         return *it;
00216 
00217     return 0.0;
00218 }
00219 
00225 void DiSEqCDevSettings::SetValue(uint devid, double value)
00226 {
00227     m_config[devid] = value;
00228     m_input_id = (uint) -1;
00229 }
00230 
00232 
00237 DiSEqCDevTrees DiSEqCDev::m_trees;
00238 
00244 DiSEqCDevTree *DiSEqCDev::FindTree(uint cardid)
00245 {
00246     return m_trees.FindTree(cardid);
00247 }
00248 
00252 void DiSEqCDev::InvalidateTrees(void)
00253 {
00254     m_trees.InvalidateTrees();
00255 }
00256 
00258 
00263 DiSEqCDevTrees::~DiSEqCDevTrees()
00264 {
00265     InvalidateTrees();
00266 }
00267 
00273 DiSEqCDevTree *DiSEqCDevTrees::FindTree(uint cardid)
00274 {
00275     QMutexLocker lock(&m_trees_lock);
00276 
00277     cardid_to_diseqc_tree_t::iterator it = m_trees.find(cardid);
00278     if (it != m_trees.end())
00279         return *it;
00280 
00281     DiSEqCDevTree *tree = new DiSEqCDevTree;
00282     tree->Load(cardid);
00283     m_trees[cardid] = tree;
00284 
00285     return tree;
00286 }
00287 
00291 void DiSEqCDevTrees::InvalidateTrees(void)
00292 {
00293     QMutexLocker lock(&m_trees_lock);
00294 
00295     cardid_to_diseqc_tree_t::iterator it = m_trees.begin();
00296     for (; it != m_trees.end(); ++it)
00297         delete *it;
00298 
00299     m_trees.clear();
00300 }
00301 
00303 
00308 const uint DiSEqCDevTree::kFirstFakeDiSEqCID = 0xf0000000;
00309 
00310 DiSEqCDevTree::DiSEqCDevTree() :
00311     m_fd_frontend(-1), m_root(NULL),
00312     m_previous_fake_diseqcid(kFirstFakeDiSEqCID)
00313 {
00314     Reset();
00315 }
00316 
00317 DiSEqCDevTree::~DiSEqCDevTree()
00318 {
00319     delete m_root;
00320 }
00321 
00327 bool DiSEqCDevTree::Load(uint cardid)
00328 {
00329     // clear children
00330     delete m_root;
00331     m_delete.clear();
00332     m_root = NULL;
00333 
00334     // lookup configuration for this card
00335     MSqlQuery query(MSqlQuery::InitCon());
00336     query.prepare(
00337         "SELECT diseqcid, cardtype "
00338         "FROM capturecard "
00339         "WHERE cardid = :CARDID");
00340     query.bindValue(":CARDID", cardid);
00341 
00342     if (!query.exec())
00343     {
00344         MythDB::DBError("DiSEqCDevTree::Load", query);
00345     }
00346     else if (!query.next())
00347     {
00348         return m_root;
00349     }
00350 
00351     if (query.value(0).toUInt())
00352     {
00353         m_root = DiSEqCDevDevice::CreateById(
00354             *this, query.value(0).toUInt());
00355     }
00356     else if (query.value(1).toString().toUpper() == "DVB")
00357     {
00358         LOG(VB_GENERAL, LOG_WARNING, LOC +
00359             QString("No device tree for cardid %1").arg(cardid));
00360     }
00361 
00362     return m_root;
00363 }
00364 
00370 bool DiSEqCDevTree::Exists(int cardid)
00371 {
00372     // lookup configuration for this card
00373     MSqlQuery query(MSqlQuery::InitCon());
00374     query.prepare(
00375         "SELECT diseqcid "
00376         "FROM capturecard "
00377         "WHERE cardid = :CARDID");
00378     query.bindValue(":CARDID", cardid);
00379 
00380     if (!query.exec())
00381     {
00382         MythDB::DBError("DiSEqCDevTree::Load", query);
00383     }
00384     else if (query.next())
00385     {
00386         if (query.value(0).toUInt() > 0)
00387             return true;
00388     }
00389 
00390     return false;
00391 }
00392 
00398 bool DiSEqCDevTree::Store(uint cardid)
00399 {
00400     MSqlQuery query0(MSqlQuery::InitCon());
00401 
00402     // apply pending node deletions
00403     if (!m_delete.empty())
00404     {
00405         MSqlQuery query1(MSqlQuery::InitCon());
00406 
00407         query0.prepare(
00408             "DELETE FROM diseqc_tree "
00409             "WHERE diseqcid = :DEVID");
00410         query1.prepare(
00411             "DELETE FROM diseqc_config "
00412             "WHERE diseqcid = :DEVID");
00413 
00414         vector<uint>::const_iterator it = m_delete.begin();
00415         for (; it != m_delete.end(); ++it)
00416         {
00417             query0.bindValue(":DEVID", *it);
00418             if (!query0.exec())
00419                 MythDB::DBError("DiSEqCDevTree::Store 1", query0);
00420 
00421             query1.bindValue(":DEVID", *it);
00422             if (!query1.exec())
00423                 MythDB::DBError("DiSEqCDevTree::Store 2", query1);
00424 
00425         }
00426         m_delete.clear();
00427     }
00428 
00429     // store changed and new nodes
00430     uint devid = 0;
00431     if (m_root && m_root->Store())
00432         devid = m_root->GetDeviceID();
00433     else if (m_root)
00434     {
00435         LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to save DiSEqC tree.");
00436         return false;
00437     }
00438 
00439     // update capture card to point to tree, or 0 if there is no tree
00440     query0.prepare(
00441         "UPDATE capturecard "
00442         "SET diseqcid = :DEVID "
00443         "WHERE cardid = :CARDID");
00444     query0.bindValue(":DEVID",  devid);
00445     query0.bindValue(":CARDID", cardid);
00446     if (!query0.exec())
00447     {
00448         MythDB::DBError("DiSEqCDevTree::Store 3", query0);
00449         return false;
00450     }
00451 
00452     return true;
00453 }
00454 
00455 bool DiSEqCDevTree::SetTone(bool on)
00456 {
00457     (void) on;
00458 
00459     bool success = false;
00460 
00461 #ifdef USING_DVB
00462     for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
00463     {
00464         if (ioctl(m_fd_frontend, FE_SET_TONE,
00465                   on ? SEC_TONE_ON : SEC_TONE_OFF) == 0)
00466             success = true;
00467         else
00468             usleep(TIMEOUT_WAIT);
00469     }
00470 #endif // USING_DVB
00471 
00472     if (!success)
00473         LOG(VB_GENERAL, LOG_ERR, LOC + "FE_SET_TONE failed" + ENO);
00474 
00475     return success;
00476 }
00477 
00484 bool DiSEqCDevTree::Execute(const DiSEqCDevSettings &settings,
00485                             const DTVMultiplex      &tuning)
00486 {
00487     if (!m_root)
00488     {
00489         LOG(VB_GENERAL, LOG_ERR, LOC + "No root device tree node!");
00490         return false;
00491     }
00492 
00493     // apply any voltage change
00494     ApplyVoltage(settings, tuning);
00495 
00496     // turn off tone burst first if commands need to be sent
00497     if (m_root->IsCommandNeeded(settings, tuning))
00498     {
00499         SetTone(false);
00500         usleep(DISEQC_SHORT_WAIT);
00501     }
00502 
00503     return m_root->Execute(settings, tuning);
00504 }
00505 
00511 void DiSEqCDevTree::Reset(void)
00512 {
00513     if (m_root)
00514         m_root->Reset();
00515 
00516     m_last_voltage = (uint) -1;
00517 }
00518 
00525 DiSEqCDevRotor *DiSEqCDevTree::FindRotor(const DiSEqCDevSettings &settings, uint index)
00526 {
00527     DiSEqCDevDevice *node  = m_root;
00528     DiSEqCDevRotor  *rotor = NULL;
00529 
00530     for (uint count = 0; node;)
00531     {
00532         rotor = dynamic_cast<DiSEqCDevRotor*>(node);
00533 
00534         if (rotor && (++count > index))
00535             break;
00536 
00537         node = node->GetSelectedChild(settings);
00538     }
00539 
00540     return rotor;
00541 }
00542 
00548 DiSEqCDevLNB *DiSEqCDevTree::FindLNB(const DiSEqCDevSettings &settings)
00549 {
00550     DiSEqCDevDevice *node = m_root;
00551     DiSEqCDevLNB    *lnb  = NULL;
00552 
00553     while (node)
00554     {
00555         lnb = dynamic_cast<DiSEqCDevLNB*>(node);
00556 
00557         if (lnb)
00558             break;
00559 
00560         node = node->GetSelectedChild(settings);
00561     }
00562 
00563     return lnb;
00564 }
00565 
00566 
00572 DiSEqCDevDevice *DiSEqCDevTree::FindDevice(uint dev_id)
00573 {
00574     if (m_root)
00575         return m_root->FindDevice(dev_id);
00576 
00577     return NULL;
00578 }
00579 
00584 void DiSEqCDevTree::SetRoot(DiSEqCDevDevice *root)
00585 {
00586     DiSEqCDevDevice *old_root = m_root;
00587 
00588     m_root = root;
00589 
00590     if (old_root)
00591         delete old_root;
00592 }
00593 
00594 #ifdef USING_DVB
00595 static bool send_diseqc(int fd, const dvb_diseqc_master_cmd &cmd)
00596 {
00597     (void) fd;
00598     (void) cmd;
00599 
00600     bool success = false;
00601 
00602     for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
00603     {
00604         if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd) == 0)
00605             success = true;
00606         else
00607             usleep(TIMEOUT_WAIT);
00608     }
00609 
00610     if (!success)
00611     {
00612         LOG(VB_GENERAL, LOG_ERR, LOC +
00613             "send_diseqc FE_DISEQC_SEND_MASTER_CMD failed" + ENO);
00614     }
00615 
00616     return success;
00617 }
00618 #endif //USING_DVB
00619 
00628 bool DiSEqCDevTree::SendCommand(uint adr, uint cmd, uint repeats,
00629                                 uint data_len, unsigned char *data)
00630 {
00631     // check payload validity
00632     if (data_len > 3 || (data_len > 0 && !data))
00633     {
00634         LOG(VB_GENERAL, LOG_ERR, LOC + "Bad DiSEqC command");
00635         return false;
00636     }
00637 
00638 #ifndef USING_DVB
00639 
00640     (void) adr;
00641     (void) cmd;
00642     (void) repeats;
00643     return false;
00644 
00645 #else // if USING_DVB
00646 
00647     // prepare command
00648     dvb_diseqc_master_cmd mcmd;
00649     mcmd.msg[0] = DISEQC_FRM;
00650     mcmd.msg[1] = adr;
00651     mcmd.msg[2] = cmd;
00652     mcmd.msg_len = data_len + 3;
00653 
00654     if (data_len > 0)
00655         memcpy(mcmd.msg + 3, data, data_len);
00656 
00657     // diagnostic
00658     QString cmdstr;
00659     for (uint byte = 0; byte < mcmd.msg_len; byte++)
00660         cmdstr += QString("%1 ").arg(mcmd.msg[byte], 2, 16);
00661 
00662     LOG(VB_CHANNEL, LOG_INFO, LOC + "Sending DiSEqC Command: " + cmdstr);
00663 
00664     // send the command
00665     for (uint i = 0; i <= repeats; i++)
00666     {
00667         if (!send_diseqc(GetFD(), mcmd))
00668         {
00669             LOG(VB_GENERAL, LOG_ERR, LOC + "DiSEqC command failed" + ENO);
00670             return false;
00671         }
00672 
00673         mcmd.msg[0] |= DISEQC_FRM_REPEAT;
00674         usleep(DISEQC_SHORT_WAIT);
00675     }
00676 
00677     return true;
00678 
00679 #endif // USING_DVB
00680 }
00681 
00687 bool DiSEqCDevTree::ResetDiseqc(bool hard_reset)
00688 {
00689     Reset();
00690 
00691     // power cycle the bus if requested
00692     // tests show that the wait times required can be very long (~1sec)
00693     if (hard_reset)
00694     {
00695         LOG(VB_CHANNEL, LOG_INFO, LOC + "Power-cycling DiSEqC Bus");
00696 
00697         SetVoltage(SEC_VOLTAGE_OFF);
00698         usleep(DISEQC_POWER_OFF_WAIT);
00699     }
00700 
00701     // make sure the bus is powered
00702     SetVoltage(SEC_VOLTAGE_18);
00703     usleep(DISEQC_POWER_ON_WAIT);
00704     // some DiSEqC devices need more time. see #8465
00705     usleep(DISEQC_POWER_ON_WAIT);
00706 
00707     // issue a global reset command
00708     LOG(VB_CHANNEL, LOG_INFO, LOC + "Resetting DiSEqC Bus");
00709     if (!SendCommand(DISEQC_ADR_ALL, DISEQC_CMD_RESET))
00710     {
00711         LOG(VB_GENERAL, LOG_ERR, LOC + "DiSEqC reset failed" + ENO);
00712         return false;
00713     }
00714 
00715     usleep(DISEQC_LONG_WAIT);
00716 
00717     return true;
00718 }
00719 
00720 void DiSEqCDevTree::Open(int fd_frontend)
00721 {
00722     m_fd_frontend = fd_frontend;
00723 
00724     // issue reset command
00725     ResetDiseqc(false /* do a soft reset */);
00726 }
00727 
00728 bool DiSEqCDevTree::SetVoltage(uint voltage)
00729 {
00730 
00731     if (voltage == m_last_voltage)
00732         return true;
00733 
00734     int volts = ((voltage == SEC_VOLTAGE_18) ? 18 :
00735                  ((voltage == SEC_VOLTAGE_13) ? 13 : 0));
00736 
00737     LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing LNB voltage to " +
00738             QString("%1V").arg(volts));
00739 
00740     bool success = false;
00741 
00742 #ifdef USING_DVB
00743     for (uint retry = 0; !success && retry < TIMEOUT_RETRIES; retry++)
00744     {
00745         if (ioctl(m_fd_frontend, FE_SET_VOLTAGE, voltage) == 0)
00746             success = true;
00747         else
00748             usleep(TIMEOUT_WAIT);
00749     }
00750 #endif // USING_DVB
00751 
00752     if (!success)
00753     {
00754         LOG(VB_GENERAL, LOG_ERR, LOC + "FE_SET_VOLTAGE failed" + ENO);
00755         return false;
00756     }
00757 
00758     m_last_voltage = voltage;
00759     return true;
00760 }
00761 
00762 bool DiSEqCDevTree::IsInNeedOfConf(void) const
00763 {
00764     if (m_root)
00765         return m_root->GetDeviceType() != DiSEqCDevDevice::kTypeLNB;
00766 
00767     return false;
00768 }
00769 
00770 bool DiSEqCDevTree::ApplyVoltage(const DiSEqCDevSettings &settings,
00771                                  const DTVMultiplex      &tuning)
00772 {
00773     uint voltage = SEC_VOLTAGE_18;
00774 
00775     if (m_root)
00776         voltage = m_root->GetVoltage(settings, tuning);
00777 
00778     return SetVoltage(voltage);
00779 }
00780 
00782 
00787 const DiSEqCDevDevice::TypeTable DiSEqCDevDevice::dvbdev_lookup[4] =
00788 {
00789     { "switch",      kTypeSwitch },
00790     { "rotor",       kTypeRotor  },
00791     { "lnb",         kTypeLNB    },
00792     { QString::null, kTypeLNB    },
00793 };
00794 
00795 
00800 DiSEqCDevDevice::DiSEqCDevDevice(DiSEqCDevTree &tree, uint devid)
00801     : m_devid(devid),           m_dev_type(kTypeLNB),
00802       m_desc(QString::null),    m_tree(tree),
00803       m_parent(NULL),           m_ordinal(0),
00804       m_repeat(1)
00805 {
00806 }
00807 
00808 DiSEqCDevDevice::~DiSEqCDevDevice()
00809 {
00810     if (IsRealDeviceID())
00811         m_tree.AddDeferredDelete(GetDeviceID());
00812 }
00813 
00814 DiSEqCDevDevice *DiSEqCDevDevice::FindDevice(uint dev_id)
00815 {
00816     DiSEqCDevDevice *dev = NULL;
00817 
00818     if (GetDeviceID() == dev_id)
00819         dev = this;
00820 
00821     uint num_children = GetChildCount();
00822 
00823     for (uint ch = 0; !dev && ch < num_children; ch++)
00824     {
00825         DiSEqCDevDevice *child = GetChild(ch);
00826         if (child)
00827         {
00828             if (child->GetDeviceID() == dev_id)
00829                 dev = child;
00830             else
00831                 dev = child->FindDevice(dev_id);
00832         }
00833     }
00834 
00835     return dev;
00836 }
00837 
00838 DiSEqCDevDevice *DiSEqCDevDevice::CreateById(DiSEqCDevTree &tree, uint devid)
00839 {
00840     // load settings from DB
00841     MSqlQuery query(MSqlQuery::InitCon());
00842     query.prepare(
00843         "SELECT type, description "
00844         "FROM diseqc_tree "
00845         "WHERE diseqcid = :DEVID");
00846     query.bindValue(":DEVID", devid);
00847 
00848     if (!query.exec() || !query.isActive())
00849     {
00850         MythDB::DBError("DiSEqCDevDevice::CreateById", query);
00851         return NULL;
00852     }
00853     else if (!query.next())
00854     {
00855         LOG(VB_GENERAL, LOG_ERR, LOC + "CreateById failed to find dtv dev " +
00856                 QString("%1").arg(devid));
00857 
00858         return NULL;
00859     }
00860 
00861     dvbdev_t      type = DevTypeFromString(query.value(0).toString());
00862     QString       desc = query.value(1).toString();
00863     DiSEqCDevDevice *node = CreateByType(tree, type, devid);
00864 
00865     if (node)
00866     {
00867         node->SetDescription(desc);
00868         node->Load();
00869     }
00870 
00871     return node;
00872 }
00873 
00874 DiSEqCDevDevice *DiSEqCDevDevice::CreateByType(DiSEqCDevTree &tree,
00875                                                dvbdev_t type,
00876                                                uint dev_id)
00877 {
00878     if (!dev_id)
00879         dev_id = tree.CreateFakeDiSEqCID();
00880 
00881     DiSEqCDevDevice *node = NULL;
00882     switch (type)
00883     {
00884         case kTypeSwitch:
00885             node = new DiSEqCDevSwitch(tree, dev_id);
00886             if (node)
00887                 node->SetDescription("Switch");
00888             break;
00889         case kTypeRotor:
00890             node = new DiSEqCDevRotor(tree, dev_id);
00891             if (node)
00892                 node->SetDescription("Rotor");
00893             break;
00894         case kTypeLNB:
00895             node = new DiSEqCDevLNB(tree, dev_id);
00896             if (node)
00897                 node->SetDescription("LNB");
00898             break;
00899         default:
00900             break;
00901     }
00902 
00903     if (node)
00904         node->SetDeviceType(type);
00905 
00906     return node;
00907 }
00908 
00977 
00978 
00983 const DiSEqCDevDevice::TypeTable DiSEqCDevSwitch::SwitchTypeTable[9] =
00984 {
00985     { "legacy_sw21",  kTypeLegacySW21        },
00986     { "legacy_sw42",  kTypeLegacySW42        },
00987     { "legacy_sw64",  kTypeLegacySW64        },
00988     { "tone",         kTypeTone          },
00989     { "diseqc",       kTypeDiSEqCCommitted   },
00990     { "diseqc_uncom", kTypeDiSEqCUncommitted },
00991     { "voltage",      kTypeVoltage           },
00992     { "mini_diseqc",  kTypeMiniDiSEqC        },
00993     { QString::null,  kTypeTone              },
00994 };
00995 
00996 DiSEqCDevSwitch::DiSEqCDevSwitch(DiSEqCDevTree &tree, uint devid)
00997     : DiSEqCDevDevice(tree, devid),
00998       m_type(kTypeTone), m_address(DISEQC_ADR_SW_ALL),
00999       m_num_ports(2)
01000 {
01001     m_children.resize(m_num_ports);
01002 
01003     for (uint i = 0; i < m_num_ports; i++)
01004         m_children[i] = NULL;
01005 
01006     Reset();
01007 }
01008 
01009 DiSEqCDevSwitch::~DiSEqCDevSwitch()
01010 {
01011     dvbdev_vec_t::iterator it = m_children.begin();
01012     for (; it != m_children.end(); ++it)
01013     {
01014         if (*it)
01015             delete *it;
01016     }
01017 }
01018 
01019 bool DiSEqCDevSwitch::Execute(const DiSEqCDevSettings &settings,
01020                               const DTVMultiplex      &tuning)
01021 {
01022     bool success = true;
01023 
01024     // sanity check switch position
01025     int pos = GetPosition(settings);
01026     if (pos < 0)
01027         return false;
01028 
01029     // perform switching
01030     if (ShouldSwitch(settings, tuning))
01031     {
01032         switch (m_type)
01033         {
01034             case kTypeTone:
01035                 success = ExecuteTone(settings, tuning, pos);
01036                 break;
01037             case kTypeDiSEqCCommitted:
01038             case kTypeDiSEqCUncommitted:
01039                 success = ExecuteDiseqc(settings, tuning, pos);
01040                 break;
01041             case kTypeLegacySW21:
01042             case kTypeLegacySW42:
01043             case kTypeLegacySW64:
01044                 success = ExecuteLegacy(settings, tuning, pos);
01045                 break;
01046             case kTypeVoltage:
01047                 success = ExecuteVoltage(settings, tuning, pos);
01048                 break;
01049             case kTypeMiniDiSEqC:
01050                 success = ExecuteMiniDiSEqC(settings, tuning, pos);
01051                 break;
01052             default:
01053                 success = false;
01054                 LOG(VB_GENERAL, LOG_ERR, LOC +
01055                     QString("Unknown switch type (%1)").arg((uint)m_type));
01056                 break;
01057         }
01058 
01059         // if a child device will be sending a diseqc command, wait 100ms
01060         if (m_children[pos]->IsCommandNeeded(settings, tuning))
01061         {
01062             LOG(VB_CHANNEL, LOG_INFO, LOC + "Waiting for switch");
01063             usleep(DISEQC_LONG_WAIT);
01064         }
01065 
01066         m_last_pos = pos;
01067     }
01068 
01069     // chain to child if the switch was successful
01070     if (success)
01071         success = m_children[pos]->Execute(settings, tuning);
01072 
01073     return success;
01074 }
01075 
01076 void DiSEqCDevSwitch::Reset(void)
01077 {
01078     m_last_pos = (uint) -1;
01079     m_last_high_band = (uint) -1;
01080     m_last_horizontal = (uint) -1;
01081     dvbdev_vec_t::iterator it = m_children.begin();
01082     for (; it != m_children.end(); ++it)
01083     {
01084         if (*it)
01085             (*it)->Reset();
01086     }
01087 }
01088 
01089 bool DiSEqCDevSwitch::IsCommandNeeded(const DiSEqCDevSettings &settings,
01090                                       const DTVMultiplex      &tuning) const
01091 {
01092     int pos = GetPosition(settings);
01093     if (pos < 0)
01094         return false;
01095 
01096     return (ShouldSwitch(settings, tuning) ||
01097             m_children[pos]->IsCommandNeeded(settings, tuning));
01098 }
01099 
01100 DiSEqCDevDevice *DiSEqCDevSwitch::GetSelectedChild(const DiSEqCDevSettings &settings) const
01101 {
01102     // sanity check switch position
01103     int pos = GetPosition(settings);
01104     if (pos < 0)
01105         return NULL;
01106 
01107     return m_children[pos];
01108 }
01109 
01110 uint DiSEqCDevSwitch::GetChildCount(void) const
01111 {
01112     return m_num_ports;
01113 }
01114 
01115 DiSEqCDevDevice *DiSEqCDevSwitch::GetChild(uint ordinal)
01116 {
01117     if (ordinal < m_children.size())
01118         return m_children[ordinal];
01119 
01120     return NULL;
01121 }
01122 
01123 bool DiSEqCDevSwitch::SetChild(uint ordinal, DiSEqCDevDevice *device)
01124 {
01125     if (ordinal >= m_children.size())
01126         return false;
01127 
01128     if (m_children[ordinal])
01129         delete m_children[ordinal];
01130 
01131     m_children[ordinal] = device;
01132     if (device)
01133     {
01134         device->SetOrdinal(ordinal);
01135         device->SetParent(this);
01136     }
01137 
01138     return true;
01139 }
01140 
01141 uint DiSEqCDevSwitch::GetVoltage(const DiSEqCDevSettings &settings,
01142                                  const DTVMultiplex         &tuning) const
01143 {
01144     uint voltage = SEC_VOLTAGE_18;
01145     DiSEqCDevDevice *child = GetSelectedChild(settings);
01146 
01147     if (child)
01148         voltage = child->GetVoltage(settings, tuning);
01149 
01150     return voltage;
01151 }
01152 
01153 bool DiSEqCDevSwitch::Load(void)
01154 {
01155     // clear old children
01156     dvbdev_vec_t::iterator it = m_children.begin();
01157     for (; it != m_children.end(); ++it)
01158     {
01159         if (*it)
01160             delete *it;
01161     }
01162 
01163     m_children.clear();
01164 
01165     // populate switch parameters from db
01166     MSqlQuery query(MSqlQuery::InitCon());
01167     query.prepare(
01168         "SELECT subtype, address, switch_ports, cmd_repeat "
01169         "FROM diseqc_tree "
01170         "WHERE diseqcid = :DEVID");
01171     query.bindValue(":DEVID", GetDeviceID());
01172 
01173     if (!query.exec() || !query.isActive())
01174     {
01175         MythDB::DBError("DiSEqCDevSwitch::Load 1", query);
01176         return false;
01177     }
01178     else if (query.next())
01179     {
01180         m_type = SwitchTypeFromString(query.value(0).toString());
01181         m_address = query.value(1).toUInt();
01182         m_num_ports = query.value(2).toUInt();
01183         m_repeat = query.value(3).toUInt();
01184         m_children.resize(m_num_ports);
01185         for (uint i = 0; i < m_num_ports; i++)
01186             m_children[i] = NULL;
01187     }
01188 
01189     // load children from db
01190     query.prepare(
01191         "SELECT diseqcid, ordinal "
01192         "FROM diseqc_tree "
01193         "WHERE parentid = :DEVID");
01194     query.bindValue(":DEVID", GetDeviceID());
01195     if (!query.exec() || !query.isActive())
01196     {
01197         MythDB::DBError("DiSEqCDevSwitch::Load 2", query);
01198         return false;
01199     }
01200 
01201     while (query.next())
01202     {
01203         uint          child_dev_id = query.value(0).toUInt();
01204         uint          ordinal      = query.value(1).toUInt();
01205         DiSEqCDevDevice *child        = CreateById(m_tree, child_dev_id);
01206         if (child && !SetChild(ordinal, child))
01207         {
01208             LOG(VB_GENERAL, LOG_ERR, LOC +
01209                 QString("Switch port out of range (%1 > %2)")
01210                     .arg(ordinal + 1).arg(m_num_ports));
01211             delete child;
01212         }
01213     }
01214 
01215     return true;
01216 }
01217 
01218 bool DiSEqCDevSwitch::Store(void) const
01219 {
01220     QString type = SwitchTypeToString(m_type);
01221     MSqlQuery query(MSqlQuery::InitCon());
01222 
01223     // insert new or update old
01224     if (IsRealDeviceID())
01225     {
01226         query.prepare(
01227             "UPDATE diseqc_tree "
01228             "SET parentid     = :PARENT, "
01229             "    ordinal      = :ORDINAL, "
01230             "    type         = 'switch', "
01231             "    description  = :DESC, "
01232             "    subtype      = :TYPE, "
01233             "    address      = :ADDRESS, "
01234             "    switch_ports = :PORTS, "
01235             "    cmd_repeat   = :REPEAT "
01236             "WHERE diseqcid = :DEVID");
01237         query.bindValue(":DEVID",   GetDeviceID());
01238     }
01239     else
01240     {
01241         query.prepare(
01242             "INSERT INTO diseqc_tree"
01243             " ( parentid,      ordinal,         type,    "
01244             "   description,   address,         subtype, "
01245             "   switch_ports,  cmd_repeat )              "
01246             "VALUES "
01247             " (:PARENT,       :ORDINAL,         'switch', "
01248             "  :DESC,         :ADDRESS,         :TYPE,    "
01249             "  :PORTS,        :REPEAT )");
01250     }
01251 
01252     if (m_parent)
01253         query.bindValue(":PARENT", m_parent->GetDeviceID());
01254 
01255     query.bindValue(":ORDINAL", m_ordinal);
01256     query.bindValue(":DESC",    GetDescription());
01257     query.bindValue(":ADDRESS", m_address);
01258     query.bindValue(":TYPE",    type);
01259     query.bindValue(":PORTS",   m_num_ports);
01260     query.bindValue(":REPEAT",  m_repeat);
01261 
01262     if (!query.exec())
01263     {
01264         MythDB::DBError("DiSEqCDevSwitch::Store", query);
01265         return false;
01266     }
01267 
01268     // figure out devid if we did an insert
01269     if (!IsRealDeviceID())
01270         SetDeviceID(query.lastInsertId().toUInt());
01271 
01272     // chain to children
01273     bool success = true;
01274     for (uint ch = 0; ch < m_children.size(); ch++)
01275     {
01276         if (m_children[ch])
01277             success &= m_children[ch]->Store();
01278     }
01279 
01280     return success;
01281 }
01282 
01283 void DiSEqCDevSwitch::SetNumPorts(uint num_ports)
01284 {
01285     uint old_num = m_children.size();
01286 
01287     if (old_num > num_ports)
01288     {
01289         for (uint ch = num_ports; ch < old_num; ch++)
01290         {
01291             if (m_children[ch])
01292                 delete m_children[ch];
01293         }
01294         m_children.resize(num_ports);
01295     }
01296     else if (old_num < num_ports)
01297     {
01298         m_children.resize(num_ports);
01299         for (uint ch = old_num; ch < num_ports; ch++)
01300             m_children[ch] = NULL;
01301     }
01302 
01303     m_num_ports = num_ports;
01304 }
01305 
01306 bool DiSEqCDevSwitch::ExecuteLegacy(const DiSEqCDevSettings &settings,
01307                                     const DTVMultiplex &tuning,
01308                                     uint pos)
01309 {
01310     (void) settings;
01311     (void) tuning;
01312     (void) pos;
01313 
01314 #if defined(USING_DVB) && defined(FE_DISHNETWORK_SEND_LEGACY_CMD)
01315     static const unsigned char sw21_cmds[]   = { 0x34, 0x65, };
01316     static const unsigned char sw42_cmds[]   = { 0x46, 0x17, };
01317     static const unsigned char sw64_v_cmds[] = { 0x39, 0x4b, 0x0d, };
01318     static const unsigned char sw64_h_cmds[] = { 0x1a, 0x5c, 0x2e, };
01319 
01320     const unsigned char *cmds = NULL;
01321     unsigned char horizcmd = 0x00;
01322     uint num_ports = 0;
01323 
01324     // determine polarity from lnb
01325     bool horizontal = false;
01326     DiSEqCDevLNB *lnb = m_tree.FindLNB(settings);
01327     if (lnb)
01328         horizontal = lnb->IsHorizontal(tuning);
01329 
01330     // get command table for this switch
01331     switch (m_type)
01332     {
01333         case kTypeLegacySW21:
01334             cmds = sw21_cmds;
01335             num_ports = 2;
01336             if (horizontal)
01337                 horizcmd = 0x80;
01338             break;
01339         case kTypeLegacySW42:
01340             cmds = sw42_cmds;
01341             num_ports = 2;
01342             break;
01343         case kTypeLegacySW64:
01344             if (horizontal)
01345                 cmds = sw64_h_cmds;
01346             else
01347                 cmds = sw64_v_cmds;
01348             num_ports = 3;
01349             break;
01350         default:
01351             return false;
01352     }
01353     if (num_ports)
01354         pos %= num_ports;
01355 
01356     LOG(VB_CHANNEL, LOG_INFO, LOC +
01357         QString("Changing to Legacy switch port %1/%2")
01358             .arg(pos + 1).arg(num_ports));
01359 
01360     // send command
01361     if (ioctl(m_tree.GetFD(), FE_DISHNETWORK_SEND_LEGACY_CMD,
01362               cmds[pos] | horizcmd) == -1)
01363     {
01364         LOG(VB_GENERAL, LOG_ERR, LOC +
01365             "FE_DISHNETWORK_SEND_LEGACY_CMD failed" + ENO);
01366 
01367         return false;
01368     }
01369 
01370     return true;
01371 
01372 #else // !FE_DISHNETWORK_SEND_LEGACY_CMD
01373 
01374     LOG(VB_GENERAL, LOG_ERR, LOC + "You must compile with a newer "
01375             "version of the linux headers for DishNet Legacy switch support.");
01376     return false;
01377 
01378 #endif // !FE_DISHNETWORK_SEND_LEGACY_CMD
01379 }
01380 
01381 #ifdef USING_DVB
01382 static bool set_tone(int fd, fe_sec_tone_mode tone)
01383 {
01384     (void) fd;
01385     (void) tone;
01386 
01387     bool success = false;
01388 
01389     for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
01390     {
01391         if (ioctl(fd, FE_SET_TONE, tone) == 0)
01392             success = true;
01393         else
01394             usleep(TIMEOUT_WAIT);
01395     }
01396 
01397     if (!success)
01398     {
01399         LOG(VB_GENERAL, LOG_ERR, LOC + "set_tone failed" + ENO);
01400     }
01401 
01402     return success;
01403 }
01404 #endif // USING_DVB
01405 
01406 #ifdef USING_DVB
01407 static bool set_voltage(int fd, fe_sec_voltage volt)
01408 {
01409     (void) fd;
01410     (void) volt;
01411 
01412     bool success = false;
01413 
01414     for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
01415     {
01416         if (0 == ioctl(fd, FE_SET_VOLTAGE, volt))
01417             success = true;
01418         else
01419             usleep(TIMEOUT_WAIT);
01420     }
01421 
01422     if (!success)
01423     {
01424         LOG(VB_GENERAL, LOG_ERR, LOC + "FE_SET_VOLTAGE failed" + ENO);
01425     }
01426 
01427     return success;
01428 }
01429 #endif // USING_DVB
01430 
01431 #ifdef USING_DVB
01432 static bool mini_diseqc(int fd, fe_sec_mini_cmd cmd)
01433 {
01434     (void) fd;
01435     (void) cmd;
01436 
01437     bool success = false;
01438 
01439     for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
01440     {
01441         if (ioctl(fd, FE_DISEQC_SEND_BURST, cmd) == 0)
01442             success = true;
01443         else
01444             usleep(TIMEOUT_WAIT);
01445     }
01446 
01447     if (!success)
01448     {
01449         LOG(VB_GENERAL, LOG_ERR, LOC +
01450             "mini_diseqc FE_DISEQC_SEND_BURST failed" + ENO);
01451     }
01452 
01453     return success;
01454 }
01455 #endif // USING_DVB
01456 
01457 bool DiSEqCDevSwitch::ExecuteTone(const DiSEqCDevSettings &/*settings*/,
01458                                   const DTVMultiplex &/*tuning*/,
01459                                   uint pos)
01460 {
01461     LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing to Tone switch port " +
01462             QString("%1/2").arg(pos + 1));
01463 
01464 #ifdef USING_DVB
01465     if (set_tone(m_tree.GetFD(), (0 == pos) ? SEC_TONE_OFF : SEC_TONE_ON))
01466         return true;
01467 #endif // USING_DVB
01468 
01469     LOG(VB_GENERAL, LOG_ERR, LOC + "Setting Tone Switch failed." + ENO);
01470     return false;
01471 }
01472 
01473 bool DiSEqCDevSwitch::ExecuteVoltage(const DiSEqCDevSettings &settings,
01474                                      const DTVMultiplex &tuning, uint pos)
01475 {
01476     (void) settings;
01477     (void) tuning;
01478 
01479     LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing to Voltage Switch port " +
01480             QString("%1/2").arg(pos + 1));
01481 
01482 #ifdef USING_DVB
01483     if (set_voltage(m_tree.GetFD(),
01484                     (0 == pos) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18))
01485     {
01486         return true;
01487     }
01488 #endif // USING_DVB
01489 
01490     LOG(VB_GENERAL, LOG_ERR, LOC + "Setting Voltage Switch failed." + ENO);
01491 
01492     return false;
01493 }
01494 
01495 bool DiSEqCDevSwitch::ExecuteMiniDiSEqC(const DiSEqCDevSettings &settings,
01496                                         const DTVMultiplex &tuning, uint pos)
01497 {
01498     (void) settings;
01499     (void) tuning;
01500 
01501     LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing to MiniDiSEqC Switch port " +
01502             QString("%1/2").arg(pos + 1));
01503 
01504 #ifdef USING_DVB
01505     if (mini_diseqc(m_tree.GetFD(), (0 == pos) ? SEC_MINI_A : SEC_MINI_B))
01506         return true;
01507 #endif // USING_DVB
01508 
01509     LOG(VB_GENERAL, LOG_ERR, LOC + "Setting Mini DiSEqC Switch failed." + ENO);
01510 
01511     return false;
01512 }
01513 
01514 bool DiSEqCDevSwitch::ShouldSwitch(const DiSEqCDevSettings &settings,
01515                                    const DTVMultiplex &tuning) const
01516 {
01517     int pos = GetPosition(settings);
01518     if (pos < 0)
01519         return false;
01520 
01521     // committed switch should change for band and polarity as well
01522     if (kTypeDiSEqCCommitted == m_type)
01523     {
01524         // retrieve LNB info
01525         bool high_band  = false;
01526         bool horizontal = false;
01527         DiSEqCDevLNB *lnb  = m_tree.FindLNB(settings);
01528         if (lnb)
01529         {
01530             high_band   = lnb->IsHighBand(tuning);
01531             horizontal  = lnb->IsHorizontal(tuning);
01532         }
01533 
01534         if(high_band != m_last_high_band ||
01535            horizontal != m_last_horizontal)
01536             return true;
01537     }
01538     else if (kTypeLegacySW42 == m_type ||
01539              kTypeLegacySW64 == m_type)
01540     {
01541         // retrieve LNB info
01542         bool horizontal = false;
01543         DiSEqCDevLNB *lnb  = m_tree.FindLNB(settings);
01544         if (lnb)
01545             horizontal  = lnb->IsHorizontal(tuning);
01546 
01547         if (horizontal != m_last_horizontal)
01548             return true;
01549     }
01550     else if (kTypeVoltage == m_type ||
01551              kTypeTone == m_type)
01552         return true;
01553 
01554     return m_last_pos != (uint)pos;
01555 }
01556 
01557 bool DiSEqCDevSwitch::ExecuteDiseqc(const DiSEqCDevSettings &settings,
01558                                     const DTVMultiplex &tuning,
01559                                     uint pos)
01560 {
01561     // retrieve LNB info
01562     bool high_band  = false;
01563     bool horizontal = false;
01564     DiSEqCDevLNB *lnb  = m_tree.FindLNB(settings);
01565     if (lnb)
01566     {
01567         high_band   = lnb->IsHighBand(tuning);
01568         horizontal  = lnb->IsHorizontal(tuning);
01569     }
01570 
01571     // check number of ports
01572     if (((kTypeDiSEqCCommitted   == m_type) && (m_num_ports > 4)) ||
01573         ((kTypeDiSEqCUncommitted == m_type) && (m_num_ports > 16)))
01574     {
01575         LOG(VB_GENERAL, LOG_ERR, LOC +
01576             QString("Invalid number of ports for DiSEqC 1.x Switch (%1)")
01577                 .arg(m_num_ports));
01578         return false;
01579     }
01580 
01581     // build command
01582     uint          cmd  = DISEQC_CMD_WRITE_N1;
01583     unsigned char data = pos;
01584     if (kTypeDiSEqCUncommitted != m_type)
01585     {
01586         cmd  = DISEQC_CMD_WRITE_N0;
01587         data = ((pos << 2) | (horizontal ? 2 : 0) | (high_band  ? 1 : 0));
01588     }
01589     data |= 0xf0;
01590 
01591     LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing to DiSEqC switch port " +
01592             QString("%1/%2").arg(pos + 1).arg(m_num_ports));
01593 
01594     bool ret = m_tree.SendCommand(m_address, cmd, m_repeat, 1, &data);
01595     if(ret)
01596     {
01597         m_last_high_band = high_band;
01598         m_last_horizontal = horizontal;
01599     }
01600     return ret;
01601 }
01602 
01603 int DiSEqCDevSwitch::GetPosition(const DiSEqCDevSettings &settings) const
01604 {
01605     int pos = (int) settings.GetValue(GetDeviceID());
01606 
01607     if (pos >= (int)m_num_ports)
01608     {
01609         LOG(VB_GENERAL, LOG_ERR, LOC + QString("Port %1 ").arg(pos + 1) +
01610                 QString("is not in range [0..%1)").arg(m_num_ports));
01611 
01612         return -1;
01613     }
01614 
01615     if ((pos >= 0) && !m_children[pos])
01616     {
01617         LOG(VB_GENERAL, LOG_ERR, LOC + QString("Port %1 ").arg(pos + 1) +
01618                 "has no connected devices configured.");
01619 
01620         return -1;
01621     }
01622 
01623     return pos;
01624 }
01625 
01627 
01628 static double GetCurTimeFloating(void)
01629 {
01630     struct timeval curtime;
01631     gettimeofday(&curtime, NULL);
01632     return (double)curtime.tv_sec + (((double)curtime.tv_usec) / 1000000);
01633 }
01634 
01639 const DiSEqCDevDevice::TypeTable DiSEqCDevRotor::RotorTypeTable[] =
01640 {
01641     { "diseqc_1_2", kTypeDiSEqC_1_2 },
01642     { "diseqc_1_3", kTypeDiSEqC_1_3 },
01643     { NULL, kTypeDiSEqC_1_3 }
01644 };
01645 
01646 DiSEqCDevRotor::DiSEqCDevRotor(DiSEqCDevTree &tree, uint devid)
01647     : DiSEqCDevDevice(tree, devid),
01648       m_type(kTypeDiSEqC_1_3),
01649       m_speed_hi(2.5),          m_speed_lo(1.9),
01650       m_child(NULL),
01651       m_last_position(0.0),     m_desired_azimuth(0.0),
01652       m_reset(true),            m_move_time(0.0),
01653       m_last_pos_known(false),  m_last_azimuth(0.0)
01654 {
01655     Reset();
01656 }
01657 
01658 DiSEqCDevRotor::~DiSEqCDevRotor()
01659 {
01660     if (m_child)
01661         delete m_child;
01662 }
01663 
01664 bool DiSEqCDevRotor::Execute(const DiSEqCDevSettings &settings,
01665                              const DTVMultiplex &tuning)
01666 {
01667     bool success = true;
01668 
01669     double position = settings.GetValue(GetDeviceID());
01670     if (m_reset || (position != m_last_position))
01671     {
01672         switch (m_type)
01673         {
01674             case kTypeDiSEqC_1_2:
01675                 success = ExecuteRotor(settings, tuning, position);
01676                 break;
01677             case kTypeDiSEqC_1_3:
01678                 success = ExecuteUSALS(settings, tuning, position);
01679                 break;
01680             default:
01681                 success = false;
01682                 LOG(VB_GENERAL, LOG_ERR, LOC + "Unknown rotor type " +
01683                         QString("(%1)").arg((uint) m_type));
01684                 break;
01685         }
01686 
01687         m_last_position = position;
01688         m_reset = false;
01689         if (success)
01690             // prevent tuning paramaters overiding rotor parameters
01691             usleep(DISEQC_LONG_WAIT);
01692     }
01693 
01694     // chain to child
01695     if (success && m_child)
01696         success = m_child->Execute(settings, tuning);
01697 
01698     return success;
01699 }
01700 
01701 void DiSEqCDevRotor::Reset(void)
01702 {
01703     m_reset = true;
01704     if (m_child)
01705         m_child->Reset();
01706 }
01707 
01708 bool DiSEqCDevRotor::IsCommandNeeded(const DiSEqCDevSettings &settings,
01709                                      const DTVMultiplex         &tuning) const
01710 {
01711     double position = settings.GetValue(GetDeviceID());
01712 
01713     if (m_reset || (position != m_last_position))
01714         return true;
01715 
01716     if (m_child)
01717         return m_child->IsCommandNeeded(settings, tuning);
01718 
01719     return false;
01720 }
01721 
01722 DiSEqCDevDevice *DiSEqCDevRotor::GetSelectedChild(const DiSEqCDevSettings&) const
01723 {
01724     return m_child;
01725 }
01726 
01727 bool DiSEqCDevRotor::SetChild(uint ordinal, DiSEqCDevDevice *device)
01728 {
01729     if (ordinal)
01730         return false;
01731 
01732     DiSEqCDevDevice *old_child = m_child;
01733     m_child = NULL;
01734     if (old_child)
01735         delete old_child;
01736 
01737     m_child = device;
01738     if (m_child)
01739     {
01740         m_child->SetOrdinal(ordinal);
01741         m_child->SetParent(this);
01742     }
01743 
01744     return true;
01745 }
01746 
01747 bool DiSEqCDevRotor::IsMoving(const DiSEqCDevSettings &settings) const
01748 {
01749     double position = settings.GetValue(GetDeviceID());
01750     double completed = GetProgress();
01751     bool   moving   = (completed < 1.0) || (position != m_last_position);
01752 
01753     return (m_last_pos_known && moving);
01754 }
01755 
01756 uint DiSEqCDevRotor::GetVoltage(const DiSEqCDevSettings &settings,
01757                                 const DTVMultiplex         &tuning) const
01758 {
01759     // override voltage if the last position is known and the rotor is moving
01760     if (IsMoving(settings))
01761     {
01762         LOG(VB_CHANNEL, LOG_INFO, LOC +
01763             "Overriding voltage to 18V for faster rotor movement");
01764     }
01765     else if (m_child)
01766     {
01767         return m_child->GetVoltage(settings, tuning);
01768     }
01769 
01770     return SEC_VOLTAGE_18;
01771 }
01772 
01773 bool DiSEqCDevRotor::Load(void)
01774 {
01775     // populate switch parameters from db
01776     MSqlQuery query(MSqlQuery::InitCon());
01777     query.prepare(
01778         "SELECT subtype,         rotor_positions, "
01779         "       rotor_hi_speed,  rotor_lo_speed, "
01780         "       cmd_repeat "
01781         "FROM diseqc_tree "
01782         "WHERE diseqcid = :DEVID");
01783     query.bindValue(":DEVID", GetDeviceID());
01784 
01785     if (!query.exec() || !query.isActive())
01786     {
01787         MythDB::DBError("DiSEqCDevRotor::Load 1", query);
01788         return false;
01789     }
01790     else if (query.next())
01791     {
01792         m_type     = RotorTypeFromString(query.value(0).toString());
01793         m_speed_hi = query.value(2).toDouble();
01794         m_speed_lo = query.value(3).toDouble();
01795         m_repeat   = query.value(4).toUInt();
01796 
01797         // form of "angle1=index1:angle2=index2:..."
01798         QString positions = query.value(1).toString();
01799         QStringList pos = positions.split(":", QString::SkipEmptyParts);
01800         QStringList::const_iterator it = pos.begin();
01801         for (; it != pos.end(); ++it)
01802         {
01803             const QStringList eq = (*it).split("=", QString::SkipEmptyParts);
01804             if (eq.size() == 2)
01805                 m_posmap[eq[0].toFloat()] = eq[1].toUInt();
01806         }
01807     }
01808 
01809     // load children from db
01810     if (m_child)
01811     {
01812         delete m_child;
01813         m_child = NULL;
01814     }
01815 
01816     query.prepare(
01817         "SELECT diseqcid "
01818         "FROM diseqc_tree "
01819         "WHERE parentid = :DEVID");
01820     query.bindValue(":DEVID", GetDeviceID());
01821 
01822     if (!query.exec() || !query.isActive())
01823     {
01824         MythDB::DBError("DiSEqCDevRotor::Load 2", query);
01825         return false;
01826     }
01827     else if (query.next())
01828     {
01829         uint child_dev_id = query.value(0).toUInt();
01830         SetChild(0, CreateById(m_tree, child_dev_id));
01831     }
01832 
01833     return true;
01834 }
01835 
01836 bool DiSEqCDevRotor::Store(void) const
01837 {
01838     QString posmap = "";
01839     QString type   = RotorTypeToString(m_type);
01840 
01841     if (!m_posmap.empty())
01842     {
01843         QStringList pos;
01844 
01845         dbl_to_uint_t::const_iterator it = m_posmap.begin();
01846         for (; it != m_posmap.end(); ++it)
01847             pos.push_back(QString("%1=%2").arg(it.key()).arg(*it));
01848 
01849         posmap = pos.join(":");
01850     }
01851 
01852     MSqlQuery query(MSqlQuery::InitCon());
01853 
01854     // insert new or update old
01855     if (IsRealDeviceID())
01856     {
01857         query.prepare(
01858             "UPDATE diseqc_tree "
01859             "SET parentid        = :PARENT,  "
01860             "    ordinal         = :ORDINAL, "
01861             "    type            = 'rotor',  "
01862             "    description     = :DESC,    "
01863             "    subtype         = :TYPE,    "
01864             "    rotor_hi_speed  = :HISPEED, "
01865             "    rotor_lo_speed  = :LOSPEED, "
01866             "    rotor_positions = :POSMAP,  "
01867             "    cmd_repeat      = :REPEAT   "
01868             "WHERE diseqcid = :DEVID");
01869         query.bindValue(":DEVID",   GetDeviceID());
01870     }
01871     else
01872     {
01873         query.prepare(
01874             "INSERT INTO diseqc_tree "
01875             " ( parentid,       ordinal,         type,   "
01876             "   description,    subtype,         rotor_hi_speed, "
01877             "   rotor_lo_speed, rotor_positions, cmd_repeat ) "
01878             "VALUES "
01879             " (:PARENT,         :ORDINAL,        'rotor',  "
01880             "  :DESC,           :TYPE,           :HISPEED, "
01881             "  :LOSPEED,        :POSMAP,         :REPEAT )");
01882     }
01883 
01884     if (m_parent)
01885         query.bindValue(":PARENT", m_parent->GetDeviceID());
01886 
01887     query.bindValue(":ORDINAL", m_ordinal);
01888     query.bindValue(":DESC",    GetDescription());
01889     query.bindValue(":TYPE",    type);
01890     query.bindValue(":HISPEED", m_speed_hi);
01891     query.bindValue(":LOSPEED", m_speed_lo);
01892     query.bindValue(":POSMAP",  posmap);
01893     query.bindValue(":REPEAT",  m_repeat);
01894 
01895     if (!query.exec())
01896     {
01897         MythDB::DBError("DiSEqCDevRotor::Store", query);
01898         return false;
01899     }
01900 
01901     // figure out devid if we did an insert
01902     if (!IsRealDeviceID())
01903         SetDeviceID(query.lastInsertId().toUInt());
01904 
01905     // chain to child
01906     if (m_child)
01907         return m_child->Store();
01908 
01909     return true;
01910 }
01911 
01917 double DiSEqCDevRotor::GetProgress(void) const
01918 {
01919     if (m_move_time == 0.0)
01920         return 1.0;
01921 
01922     // calculate duration of move
01923     double speed    = ((m_tree.GetVoltage() == SEC_VOLTAGE_18) ?
01924                        m_speed_hi : m_speed_lo);
01925     double change   = abs(m_desired_azimuth - m_last_azimuth);
01926     double duration = change / speed;
01927 
01928     // determine completion percentage
01929     double time_since_move = GetCurTimeFloating() - m_move_time;
01930     double completed = time_since_move / duration;
01931     if(completed > 1.0)
01932     {
01933         RotationComplete();
01934         completed = 1.0;
01935     }
01936 
01937     return completed;
01938 }
01939 
01947 bool DiSEqCDevRotor::IsPositionKnown(void) const
01948 {
01949     return m_last_pos_known;
01950 }
01951 
01952 uint_to_dbl_t DiSEqCDevRotor::GetPosMap(void) const
01953 {
01954     uint_to_dbl_t inv_posmap;
01955     dbl_to_uint_t::const_iterator it;
01956     for (it = m_posmap.begin(); it != m_posmap.end(); ++it)
01957         inv_posmap[*it] = it.key();
01958 
01959     return inv_posmap;
01960 }
01961 
01962 void DiSEqCDevRotor::SetPosMap(const uint_to_dbl_t &inv_posmap)
01963 {
01964     m_posmap.clear();
01965 
01966     uint_to_dbl_t::const_iterator it;
01967     for (it = inv_posmap.begin(); it != inv_posmap.end(); ++it)
01968         m_posmap[*it] = it.key();
01969 }
01970 
01971 bool DiSEqCDevRotor::ExecuteRotor(const DiSEqCDevSettings&, const DTVMultiplex&,
01972                                   double angle)
01973 {
01974     // determine stored position from position map
01975     dbl_to_uint_t::const_iterator it = m_posmap.lowerBound(angle - EPS);
01976     unsigned char index = (uint) angle;
01977     if (it != m_posmap.end())
01978     {
01979         index = *it;
01980         StartRotorPositionTracking(CalculateAzimuth(angle));
01981     }
01982 
01983     LOG(VB_CHANNEL, LOG_INFO, LOC + "Rotor - " +
01984             QString("Goto Stored Position %1").arg(index));
01985 
01986     return m_tree.SendCommand(DISEQC_ADR_POS_AZ, DISEQC_CMD_GOTO_POS,
01987                               m_repeat, 1, &index);
01988 }
01989 
01990 bool DiSEqCDevRotor::ExecuteUSALS(const DiSEqCDevSettings&, const DTVMultiplex&,
01991                                   double angle)
01992 {
01993     double azimuth = CalculateAzimuth(angle);
01994     StartRotorPositionTracking(azimuth);
01995 
01996     LOG(VB_CHANNEL, LOG_INFO, LOC + "USALS Rotor - " +
01997             QString("Goto %1 (Azimuth %2)").arg(angle).arg(azimuth));
01998 
01999     uint az16 = (uint) (abs(azimuth) * 16.0);
02000     unsigned char cmd[2];
02001     cmd[0] = ((azimuth > 0.0) ? 0xE0 : 0xD0) | ((az16 >> 8)  &0x0f);
02002     cmd[1] = (az16  &0xff);
02003 
02004     return m_tree.SendCommand(DISEQC_ADR_POS_AZ, DISEQC_CMD_GOTO_X,
02005                               m_repeat, 2, cmd);
02006 }
02007 
02008 double DiSEqCDevRotor::CalculateAzimuth(double angle) const
02009 {
02010     // Azimuth Calculation references:
02011     // http://engr.nmsu.edu/~etti/3_2/3_2e.html
02012     // http://www.angelfire.com/trek/ismail/theory.html
02013 
02014     // Earth Station Latitude and Longitude in radians
02015     double P  = gCoreContext->GetSetting("Latitude",  "").toFloat() * TO_RADS;
02016     double Ue = gCoreContext->GetSetting("Longitude", "").toFloat() * TO_RADS;
02017 
02018     // Satellite Longitude in radians
02019     double Us = angle * TO_RADS;
02020 
02021     return TO_DEC * atan( tan(Us - Ue) / sin(P) );
02022 }
02023 
02024 double DiSEqCDevRotor::GetApproxAzimuth(void) const
02025 {
02026     if (m_move_time == 0.0)
02027         return m_last_azimuth;
02028 
02029     double change = m_desired_azimuth - m_last_azimuth;
02030     return m_last_azimuth + (change * GetProgress());
02031 }
02032 
02033 void DiSEqCDevRotor::StartRotorPositionTracking(double azimuth)
02034 {
02035     // save time and angle of this command
02036     m_desired_azimuth = azimuth;
02037 
02038     // set last to approximate current position (or worst case if unknown)
02039     if (m_last_pos_known || m_move_time > 0.0)
02040         m_last_azimuth = GetApproxAzimuth();
02041     else
02042         m_last_azimuth = azimuth > 0.0 ? -75.0 : 75.0;
02043 
02044     m_move_time = GetCurTimeFloating();
02045 }
02046 
02047 void DiSEqCDevRotor::RotationComplete(void) const
02048 {
02049     m_move_time = 0.0;
02050     m_last_pos_known = true;
02051     m_last_azimuth = m_desired_azimuth;
02052 }
02053 
02055 
02060 const DiSEqCDevDevice::TypeTable DiSEqCDevLNB::LNBTypeTable[5] =
02061 {
02062     { "fixed",        kTypeFixed                 },
02063     { "voltage",      kTypeVoltageControl        },
02064     { "voltage_tone", kTypeVoltageAndToneControl },
02065     { "bandstacked",  kTypeBandstacked           },
02066     { QString::null,  kTypeVoltageAndToneControl },
02067 };
02068 
02069 DiSEqCDevLNB::DiSEqCDevLNB(DiSEqCDevTree &tree, uint devid)
02070     : DiSEqCDevDevice(tree, devid),
02071       m_type(kTypeVoltageAndToneControl), m_lof_switch(11700000),
02072       m_lof_hi(10600000),       m_lof_lo(9750000),
02073       m_pol_inv(false)
02074 {
02075     Reset();
02076 }
02077 
02078 bool DiSEqCDevLNB::Execute(const DiSEqCDevSettings&, const DTVMultiplex &tuning)
02079 {
02080     // set tone for bandselect
02081     if (m_type == kTypeVoltageAndToneControl)
02082         m_tree.SetTone(IsHighBand(tuning));
02083 
02084     return true;
02085 }
02086 
02087 uint DiSEqCDevLNB::GetVoltage(const DiSEqCDevSettings&,
02088                               const DTVMultiplex &tuning) const
02089 {
02090     uint voltage = SEC_VOLTAGE_18;
02091 
02092     if ((kTypeVoltageControl        == m_type) ||
02093         (kTypeVoltageAndToneControl == m_type))
02094     {
02095         voltage = (IsHorizontal(tuning) ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13);
02096     }
02097 
02098     return voltage;
02099 }
02100 
02101 bool DiSEqCDevLNB::Load(void)
02102 {
02103     // populate lnb parameters from db
02104     MSqlQuery query(MSqlQuery::InitCon());
02105     query.prepare(
02106         "SELECT subtype,         lnb_lof_switch, "
02107         "       lnb_lof_hi,      lnb_lof_lo, "
02108         "       lnb_pol_inv,     cmd_repeat "
02109         "FROM diseqc_tree "
02110         "WHERE diseqcid = :DEVID");
02111     query.bindValue(":DEVID", GetDeviceID());
02112 
02113     if (!query.exec() || !query.isActive())
02114     {
02115         MythDB::DBError("DiSEqCDevLNB::Load", query);
02116         return false;
02117     }
02118     else if (query.next())
02119     {
02120         m_type       = LNBTypeFromString(query.value(0).toString());
02121         m_lof_switch = query.value(1).toInt();
02122         m_lof_hi     = query.value(2).toInt();
02123         m_lof_lo     = query.value(3).toInt();
02124         m_pol_inv    = query.value(4).toUInt();
02125         m_repeat     = query.value(5).toUInt();
02126     }
02127 
02128     return true;
02129 }
02130 
02131 bool DiSEqCDevLNB::Store(void) const
02132 {
02133     QString type = LNBTypeToString(m_type);
02134     MSqlQuery query(MSqlQuery::InitCon());
02135 
02136     // insert new or update old
02137     if (IsRealDeviceID())
02138     {
02139         query.prepare(
02140             "UPDATE diseqc_tree "
02141             "SET parentid        = :PARENT,  "
02142             "    ordinal         = :ORDINAL, "
02143             "    type            = 'lnb',    "
02144             "    description     = :DESC,    "
02145             "    subtype         = :TYPE,    "
02146             "    lnb_lof_switch  = :LOFSW,   "
02147             "    lnb_lof_lo      = :LOFLO,   "
02148             "    lnb_lof_hi      = :LOFHI,   "
02149             "    lnb_pol_inv     = :POLINV,  "
02150             "    cmd_repeat      = :REPEAT   "
02151             "WHERE diseqcid = :DEVID");
02152         query.bindValue(":DEVID",   GetDeviceID());
02153     }
02154     else
02155     {
02156         query.prepare(
02157             "INSERT INTO diseqc_tree"
02158             " ( parentid,      ordinal,         type, "
02159             "   description,   subtype,         lnb_lof_switch, "
02160             "   lnb_lof_lo,    lnb_lof_hi,      lnb_pol_inv, "
02161             "   cmd_repeat ) "
02162             "VALUES "
02163             " (:PARENT,       :ORDINAL,         'lnb', "
02164             "  :DESC,         :TYPE,            :LOFSW, "
02165             "  :LOFLO,        :LOFHI,           :POLINV, "
02166             "  :REPEAT ) ");
02167     }
02168 
02169     if (m_parent)
02170         query.bindValue(":PARENT", m_parent->GetDeviceID());
02171 
02172     query.bindValue(":ORDINAL", m_ordinal);
02173     query.bindValue(":DESC",    GetDescription());
02174     query.bindValue(":TYPE",    type);
02175     query.bindValue(":LOFSW",   m_lof_switch);
02176     query.bindValue(":LOFLO",   m_lof_lo);
02177     query.bindValue(":LOFHI",   m_lof_hi);
02178     query.bindValue(":POLINV",  m_pol_inv);
02179     query.bindValue(":REPEAT",  m_repeat);
02180 
02181     // update dev_id
02182     if (!query.exec())
02183     {
02184         MythDB::DBError("DiSEqCDevLNB::Store", query);
02185         return false;
02186     }
02187 
02188     // figure out devid if we did an insert
02189     if (!IsRealDeviceID())
02190         SetDeviceID(query.lastInsertId().toUInt());
02191 
02192     return true;
02193 }
02194 
02201 bool DiSEqCDevLNB::IsHighBand(const DTVMultiplex &tuning) const
02202 {
02203     switch (m_type)
02204     {
02205         case kTypeVoltageAndToneControl:
02206             return (tuning.frequency > m_lof_switch);
02207         case kTypeBandstacked:
02208             return IsHorizontal(tuning);
02209         default:
02210             return false;
02211     }
02212 
02213     return false;
02214 }
02215 
02221 bool DiSEqCDevLNB::IsHorizontal(const DTVMultiplex &tuning) const
02222 {
02223     QString pol = tuning.polarity.toString().toLower();
02224     return (pol == "h" || pol == "l") ^ IsPolarityInverted();
02225 }
02226 
02235 uint32_t DiSEqCDevLNB::GetIntermediateFrequency(
02236     const DiSEqCDevSettings&, const DTVMultiplex &tuning) const
02237 {
02238     (void) tuning;
02239 
02240     uint64_t abs_freq = tuning.frequency;
02241     uint lof = (IsHighBand(tuning)) ? m_lof_hi : m_lof_lo;
02242 
02243     return (lof > abs_freq) ? (lof - abs_freq) : (abs_freq - lof);
02244 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends