|
MythTV
0.26-pre
|
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 }
1.7.6.1