MythTV master
diseqc.cpp
Go to the documentation of this file.
1/* -*- Mode: c++ -*-
2 * \file dvbdevtree.cpp
3 * \brief DVB-S Device Tree Control Classes.
4 * \author Copyright (C) 2006, Yeasah Pell
5 */
6
7// Std C headers
8#include <cstring>
9#include <cmath>
10#include <thread>
11
12// POSIX headers
13#include <sys/time.h>
14
15// Qt headers
16#include <QString>
17
18// MythTV headers
19#include "libmythbase/mythconfig.h"
20#include "libmythbase/compat.h"
23#include "libmythbase/mythdb.h"
25
26#include "diseqc.h"
27#include "dtvmultiplex.h"
28
29#if CONFIG_DVB
30# include "recorders/dvbtypes.h"
31#else
32static constexpr uint8_t SEC_VOLTAGE_13 { 0 };
33static constexpr uint8_t SEC_VOLTAGE_18 { 1 };
34static constexpr uint8_t SEC_VOLTAGE_OFF { 2 };
35#endif
36
37// DiSEqC sleep intervals per eutelsat spec
38static constexpr std::chrono::milliseconds DISEQC_SHORT_WAIT { 15ms };
39static constexpr std::chrono::milliseconds DISEQC_LONG_WAIT { 100ms };
40static constexpr std::chrono::milliseconds DISEQC_POWER_ON_WAIT { 500ms };
41static constexpr std::chrono::milliseconds DISEQC_POWER_OFF_WAIT { 1s };
42
43#if CONFIG_DVB
44// Number of times to retry ioctls after receiving ETIMEDOUT before giving up
45static constexpr uint8_t TIMEOUT_RETRIES { 10 };
46static constexpr std::chrono::milliseconds TIMEOUT_WAIT { 250ms };
47
48// Framing byte
49static constexpr uint8_t DISEQC_FRM { 0xe0 };
50static constexpr uint8_t DISEQC_FRM_REPEAT {1 << 0};
51//static constexpr uint8_t DISEQC_FRM_REPLY_REQ {1 << 1};
52#endif
53
54// Address byte
55enum DISEQC_ADRS : std::uint8_t {
68};
69
70// Command byte
71enum DISEQC_CMDS : std::uint8_t {
88};
89
90static constexpr double TO_RADS { M_PI / 180.0 };
91static constexpr double TO_DEC { 180.0 / M_PI };
92
93static constexpr double EPS { 1E-4 };
94
95#define LOC QString("DiSEqCDevTree: ")
96
98
100{
101 for (const auto &item : table)
102 if (type == item.value)
103 return item.name;
104 return {};
105}
106
108 const TypeTableVec &table)
109{
110 for (const auto &item : table)
111 if (type == item.name)
112 return item.value;
113 return table[0].value;
114}
115
117
130bool DiSEqCDevSettings::Load(uint card_input_id)
131{
132 if (card_input_id == m_inputId)
133 return true;
134
135 m_config.clear();
136
137 // load settings from DB
139 query.prepare(
140 "SELECT diseqcid, value "
141 "FROM diseqc_config "
142 "WHERE cardinputid = :INPUTID");
143
144 query.bindValue(":INPUTID", card_input_id);
145 if (!query.exec() || !query.isActive())
146 {
147 MythDB::DBError("DiSEqCDevSettings::Load", query);
148 return false;
149 }
150
151 while (query.next())
152 m_config[query.value(0).toUInt()] = query.value(1).toDouble();
153
154 m_inputId = card_input_id;
155
156 return true;
157}
158
164bool DiSEqCDevSettings::Store(uint card_input_id) const
165{
167
168 // clear out previous settings
169 query.prepare(
170 "DELETE from diseqc_config "
171 "WHERE cardinputid = :INPUTID");
172 query.bindValue(":INPUTID", card_input_id);
173
174 if (!query.exec() || !query.isActive())
175 {
176 MythDB::DBError("DiSEqCDevSettings::Store 1", query);
177 return false;
178 }
179
180 // insert new settings
181 query.prepare(
182 "INSERT INTO diseqc_config "
183 " ( cardinputid, diseqcid, value) "
184 "VALUES (:INPUTID, :DEVID, :VALUE) ");
185
186 for (auto it = m_config.cbegin(); it != m_config.cend(); ++it)
187 {
188 query.bindValue(":INPUTID", card_input_id);
189 query.bindValue(":DEVID", it.key());
190 query.bindValue(":VALUE", *it);
191 if (!query.exec() || !query.isActive())
192 {
193 MythDB::DBError("DiSEqCDevSettings::Store 2", query);
194 return false;
195 }
196 }
197
198 return true;
199}
200
207{
208 uint_to_dbl_t::const_iterator it = m_config.find(devid);
209
210 if (it != m_config.end())
211 return *it;
212
213 return 0.0;
214}
215
221void DiSEqCDevSettings::SetValue(uint devid, double value)
222{
223 m_config[devid] = value;
224 m_inputId = UINT_MAX;
225}
226
228
234
240{
241 return s_trees.FindTree(cardid);
242}
243
248{
250}
251
253
259{
261}
262
268{
269 QMutexLocker lock(&m_treesLock);
270
271 cardid_to_diseqc_tree_t::iterator it = m_trees.find(cardid);
272 if (it != m_trees.end())
273 return *it;
274
275 auto *tree = new DiSEqCDevTree;
276 tree->Load(cardid);
277 m_trees[cardid] = tree;
278
279 return tree;
280}
281
286{
287 QMutexLocker lock(&m_treesLock);
288
289 for (auto & tree : m_trees)
290 delete tree;
291
292 m_trees.clear();
293}
294
296
301const uint DiSEqCDevTree::kFirstFakeDiSEqCID = 0xf0000000;
302
304{
305 delete m_root;
306}
307
313bool DiSEqCDevTree::Load(const QString &device)
314{
315 // lookup configuration for this card
317 query.prepare(
318 "SELECT cardid "
319 "FROM capturecard "
320 "WHERE hostname = :HOSTNAME AND "
321 " videodevice = :VIDEODEVICE "
322 "LIMIT 1");
323 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
324 query.bindValue(":VIDEODEVICE", device);
325
326 uint cardid = 0;
327
328 if (!query.exec())
329 {
330 MythDB::DBError("DiSEqCDevTree::Load", query);
331 }
332 else if (query.next())
333 {
334 cardid = query.value(0).toUInt();
335 }
336
337 return Load(cardid);
338}
339
346{
347 // clear children
348
349 // TODO find root cause so that "delete m_root" can be enabled again, see ticket #13465
350 // Not doing the "delete m_root" fixes a segfault but creates a memory leak
351#if 0
352 delete m_root;
353#endif
354 m_delete.clear();
355 m_root = nullptr;
356
357 // lookup configuration for this card
359 query.prepare(
360 "SELECT diseqcid, cardtype, inputname "
361 "FROM capturecard "
362 "WHERE cardid = :CARDID");
363 query.bindValue(":CARDID", cardid);
364
365 if (!query.exec())
366 {
367 MythDB::DBError("DiSEqCDevTree::Load", query);
368 }
369 else if (!query.next())
370 {
371 return m_root;
372 }
373
374 if (query.value(0).toBool())
375 {
377 *this, query.value(0).toUInt());
378 }
379 else if ((query.value(1).toString().toUpper() == "DVB") &&
380 ((query.value(2).toString().toUpper() == "DVB-S" ) ||
381 (query.value(2).toString().toUpper() == "DVB-S2") ))
382 {
383 LOG(VB_GENERAL, LOG_WARNING, LOC +
384 QString("No device tree for cardid %1").arg(cardid));
385 }
386
387 return m_root;
388}
389
395bool DiSEqCDevTree::Exists(int cardid)
396{
397 // lookup configuration for this card
399 query.prepare(
400 "SELECT diseqcid "
401 "FROM capturecard "
402 "WHERE cardid = :CARDID");
403 query.bindValue(":CARDID", cardid);
404
405 if (!query.exec())
406 {
407 MythDB::DBError("DiSEqCDevTree::Load", query);
408 }
409 else if (query.next())
410 {
411 if (query.value(0).toUInt() > 0)
412 return true;
413 }
414
415 return false;
416}
417
424bool DiSEqCDevTree::Store(uint cardid, const QString &device)
425{
427
428 // apply pending node deletions
429 if (!m_delete.empty())
430 {
432
433 query0.prepare(
434 "DELETE FROM diseqc_tree "
435 "WHERE diseqcid = :DEVID");
436 query1.prepare(
437 "DELETE FROM diseqc_config "
438 "WHERE diseqcid = :DEVID");
439
440 for (uint devid : m_delete)
441 {
442 query0.bindValue(":DEVID", devid);
443 if (!query0.exec())
444 MythDB::DBError("DiSEqCDevTree::Store 1", query0);
445
446 query1.bindValue(":DEVID", devid);
447 if (!query1.exec())
448 MythDB::DBError("DiSEqCDevTree::Store 2", query1);
449
450 }
451 m_delete.clear();
452 }
453
454 // store changed and new nodes
455 uint devid = 0;
456 if (m_root && m_root->Store())
457 devid = m_root->GetDeviceID();
458 else if (m_root)
459 {
460 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to save DiSEqC tree.");
461 return false;
462 }
463
464 // update capture card to point to tree, or 0 if there is no tree
465 query0.prepare(
466 "UPDATE capturecard "
467 "SET diseqcid = :DEVID "
468 "WHERE (hostname = :HOSTNAME AND "
469 " videodevice = :VIDEODEVICE) "
470 " OR cardid = :CARDID");
471 query0.bindValue(":DEVID", devid);
472 query0.bindValue(":HOSTNAME", gCoreContext->GetHostName());
473 query0.bindValue(":VIDEODEVICE", device);
474 query0.bindValue(":CARDID", cardid);
475 if (!query0.exec())
476 {
477 MythDB::DBError("DiSEqCDevTree::Store 3", query0);
478 return false;
479 }
480
481 return true;
482}
483
484bool DiSEqCDevTree::SetTone([[maybe_unused]] bool on) const
485{
486 bool success = false;
487
488#if CONFIG_DVB
489 for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
490 {
491 if (ioctl(m_fdFrontend, FE_SET_TONE,
492 on ? SEC_TONE_ON : SEC_TONE_OFF) == 0)
493 success = true;
494 else
495 std::this_thread::sleep_for(TIMEOUT_WAIT);
496 }
497#endif // CONFIG_DVB
498
499 if (!success)
500 LOG(VB_GENERAL, LOG_ERR, LOC + "FE_SET_TONE failed" + ENO);
501
502 return success;
503}
504
512 const DTVMultiplex &tuning)
513{
514 if (!m_root)
515 {
516 LOG(VB_GENERAL, LOG_ERR, LOC + "No root device tree node!");
517 return false;
518 }
519
520 // apply any voltage change
521 ApplyVoltage(settings, tuning);
522
523 // turn off tone burst first if commands need to be sent
524 if (m_root->IsCommandNeeded(settings, tuning))
525 {
526 SetTone(false);
527 std::this_thread::sleep_for(DISEQC_SHORT_WAIT);
528 }
529
530 return m_root->Execute(settings, tuning);
531}
532
539{
540 if (m_root)
541 m_root->Reset();
542
543 m_lastVoltage = UINT_MAX;
544}
545
553{
554 DiSEqCDevDevice *node = m_root;
555 DiSEqCDevRotor *rotor = nullptr;
556
557 for (uint count = 0; node;)
558 {
559 rotor = dynamic_cast<DiSEqCDevRotor*>(node);
560
561 if (rotor && (++count > index))
562 break;
563
564 node = node->GetSelectedChild(settings);
565 }
566
567 return rotor;
568}
569
576{
577 DiSEqCDevDevice *node = m_root;
578 DiSEqCDevLNB *lnb = nullptr;
579
580 while (node)
581 {
582 lnb = dynamic_cast<DiSEqCDevLNB*>(node);
583
584 if (lnb)
585 break;
586
587 node = node->GetSelectedChild(settings);
588 }
589
590 return lnb;
591}
592
599{
600 DiSEqCDevDevice *node = m_root;
601 DiSEqCDevSCR *scr = nullptr;
602
603 while (node)
604 {
605 scr = dynamic_cast<DiSEqCDevSCR*>(node);
606
607 if (scr)
608 break;
609
610 node = node->GetSelectedChild(settings);
611 }
612
613 return scr;
614}
615
616
623{
624 if (m_root)
625 return m_root->FindDevice(dev_id);
626
627 return nullptr;
628}
629
635{
636 DiSEqCDevDevice *old_root = m_root;
637
638 m_root = root;
639
640 delete old_root;
641}
642
643#if CONFIG_DVB
644static bool send_diseqc(int fd, const dvb_diseqc_master_cmd cmd)
645{
646 bool success = false;
647
648 for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
649 {
650 if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd) == 0)
651 success = true;
652 else
653 std::this_thread::sleep_for(TIMEOUT_WAIT);
654 }
655
656 if (!success)
657 {
658 LOG(VB_GENERAL, LOG_ERR, LOC +
659 "send_diseqc FE_DISEQC_SEND_MASTER_CMD failed" + ENO);
660 }
661
662 return success;
663}
664#endif // CONFIG_DVB
665
674bool DiSEqCDevTree::SendCommand([[maybe_unused]] uint adr,
675 [[maybe_unused]] uint cmd,
676 [[maybe_unused]] uint repeats,
677 cmd_vec_t &data) const
678{
679 // check payload validity
680 if (data.size() > 3)
681 {
682 LOG(VB_GENERAL, LOG_ERR, LOC + "Bad DiSEqC command");
683 return false;
684 }
685
686#if !CONFIG_DVB
687
688 return false;
689
690#else // if CONFIG_DVB
691
692 bool resend_cmd = false;
693
694 // prepare command
695 dvb_diseqc_master_cmd mcmd = {};
696 mcmd.msg[0] = DISEQC_FRM;
697 mcmd.msg[1] = adr;
698 mcmd.msg[2] = cmd;
699 mcmd.msg_len = data.size() + 3;
700
701 if (!data.empty())
702 std::copy(data.cbegin(), data.cend(), mcmd.msg + 3);
703
704 // diagnostic
705 QString cmdstr;
706 for (uint byte = 0; byte < mcmd.msg_len; byte++)
707 cmdstr += QString("%1 ").arg(mcmd.msg[byte], 2, 16);
708
709 LOG(VB_CHANNEL, LOG_INFO, LOC + "Sending DiSEqC Command: " + cmdstr);
710
711 if (repeats >= 10)
712 {
713 repeats = repeats - 10;
714 resend_cmd = true;
715 }
716
717 // send the command
718 for (uint i = 0; i <= repeats; i++)
719 {
720 if (!send_diseqc(GetFD(), mcmd))
721 {
722 LOG(VB_GENERAL, LOG_ERR, LOC + "DiSEqC command failed" + ENO);
723 return false;
724 }
725
726 if (!resend_cmd)
727 mcmd.msg[0] |= DISEQC_FRM_REPEAT;
728
729 std::this_thread::sleep_for(DISEQC_SHORT_WAIT);
730 }
731
732 return true;
733
734#endif // CONFIG_DVB
735}
736
743bool DiSEqCDevTree::ResetDiseqc(bool hard_reset, bool is_SCR)
744{
745 Reset();
746
747 // power cycle the bus if requested
748 // tests show that the wait times required can be very long (~1sec)
749 if (hard_reset)
750 {
751 LOG(VB_CHANNEL, LOG_INFO, LOC + "Power-cycling DiSEqC Bus");
752
754 std::this_thread::sleep_for(DISEQC_POWER_OFF_WAIT);
756 }
757
758 if (!diseqc_bus_already_reset || !is_SCR)
759 {
760 // make sure the bus is powered
762 std::this_thread::sleep_for(DISEQC_POWER_ON_WAIT);
763 // some DiSEqC devices need more time. see #8465
764 std::this_thread::sleep_for(DISEQC_POWER_ON_WAIT);
765
766 // issue a global reset command
767 LOG(VB_CHANNEL, LOG_INFO, LOC + "Resetting DiSEqC Bus");
769 {
770 LOG(VB_GENERAL, LOG_ERR, LOC + "DiSEqC reset failed" + ENO);
771 return false;
772 }
773
774 if (is_SCR)
776 }
777 else
778 {
779 LOG(VB_CHANNEL, LOG_INFO, LOC + "Skipping reset: already done for this SCR bus");
780 }
781
782 std::this_thread::sleep_for(DISEQC_LONG_WAIT);
783
784 return true;
785}
786
792void DiSEqCDevTree::Open(int fd_frontend, bool is_SCR)
793{
794 m_fdFrontend = fd_frontend;
795
796 // issue reset command
797 ResetDiseqc(false, is_SCR);
798}
799
801{
802 if (voltage == m_lastVoltage)
803 return true;
804
805 int volts {0};
806 if (voltage == SEC_VOLTAGE_18)
807 volts = 18;
808 else if (voltage == SEC_VOLTAGE_13)
809 volts = 13;
810
811 LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing LNB voltage to " +
812 QString("%1V").arg(volts));
813
814 bool success = false;
815
816#if CONFIG_DVB
817 for (uint retry = 0; !success && retry < TIMEOUT_RETRIES; retry++)
818 {
819 if (ioctl(m_fdFrontend, FE_SET_VOLTAGE, voltage) == 0)
820 success = true;
821 else
822 std::this_thread::sleep_for(TIMEOUT_WAIT);
823 }
824#endif // CONFIG_DVB
825
826 if (!success)
827 {
828 LOG(VB_GENERAL, LOG_ERR, LOC + "FE_SET_VOLTAGE failed" + ENO);
829 return false;
830 }
831
832 m_lastVoltage = voltage;
833 return true;
834}
835
837{
838 if (m_root)
840
841 return false;
842}
843
845 const DTVMultiplex &tuning)
846{
847 uint voltage = SEC_VOLTAGE_18;
848
849 if (m_root)
850 voltage = m_root->GetVoltage(settings, tuning);
851
852 return SetVoltage(voltage);
853}
854
856
861const std::vector<DiSEqCDevDevice::TypeTable> DiSEqCDevDevice::kDvbdevLookup
862{
863 { "switch", kTypeSwitch },
864 { "rotor", kTypeRotor },
865 { "scr", kTypeSCR },
866 { "lnb", kTypeLNB },
867 { QString(), kTypeLNB },
868};
869
870
872{
873 if (IsRealDeviceID())
875}
876
878{
879 DiSEqCDevDevice *dev = nullptr;
880
881 if (GetDeviceID() == dev_id)
882 dev = this;
883
884 uint num_children = GetChildCount();
885
886 for (uint ch = 0; !dev && ch < num_children; ch++)
887 {
888 DiSEqCDevDevice *child = GetChild(ch);
889 if (child)
890 {
891 if (child->GetDeviceID() == dev_id)
892 dev = child;
893 else
894 dev = child->FindDevice(dev_id);
895 }
896 }
897
898 return dev;
899}
900
902{
903 // load settings from DB
905 query.prepare(
906 "SELECT type, description "
907 "FROM diseqc_tree "
908 "WHERE diseqcid = :DEVID");
909 query.bindValue(":DEVID", devid);
910
911 if (!query.exec() || !query.isActive())
912 {
913 MythDB::DBError("DiSEqCDevDevice::CreateById", query);
914 return nullptr;
915 }
916 if (!query.next())
917 {
918 LOG(VB_GENERAL, LOG_ERR, LOC + "CreateById failed to find dtv dev " +
919 QString("%1").arg(devid));
920
921 return nullptr;
922 }
923
924 dvbdev_t type = DevTypeFromString(query.value(0).toString());
925 QString desc = query.value(1).toString();
926 DiSEqCDevDevice *node = CreateByType(tree, type, devid);
927
928 if (node)
929 {
930 node->SetDescription(desc);
931 node->Load();
932 }
933
934 return node;
935}
936
939 uint dev_id)
940{
941 if (!dev_id)
942 dev_id = tree.CreateFakeDiSEqCID();
943
944 DiSEqCDevDevice *node = nullptr;
945 switch (type)
946 {
947 case kTypeSwitch:
948 node = new DiSEqCDevSwitch(tree, dev_id);
949 if (node)
950 node->SetDescription("Switch");
951 break;
952 case kTypeRotor:
953 node = new DiSEqCDevRotor(tree, dev_id);
954 if (node)
955 node->SetDescription("Rotor");
956 break;
957 case kTypeSCR:
958 node = new DiSEqCDevSCR(tree, dev_id);
959 if (node)
960 node->SetDescription("Unicable");
961 break;
962 case kTypeLNB:
963 node = new DiSEqCDevLNB(tree, dev_id);
964 if (node)
965 node->SetDescription("LNB");
966 break;
967 default:
968 break;
969 }
970
971 if (node)
972 node->SetDeviceType(type);
973
974 return node;
975}
976
1047
1052const std::vector<DiSEqCDevDevice::TypeTable> DiSEqCDevSwitch::kSwitchTypeTable
1053{
1054 { "legacy_sw21", kTypeLegacySW21 },
1055 { "legacy_sw42", kTypeLegacySW42 },
1056 { "legacy_sw64", kTypeLegacySW64 },
1057 { "tone", kTypeTone },
1058 { "diseqc", kTypeDiSEqCCommitted },
1059 { "diseqc_uncom", kTypeDiSEqCUncommitted },
1060 { "voltage", kTypeVoltage },
1061 { "mini_diseqc", kTypeMiniDiSEqC },
1062 { QString(), kTypeTone },
1063};
1064
1066 : DiSEqCDevDevice(tree, devid)
1067{
1068 m_children.resize(m_numPorts);
1069
1070 for (uint i = 0; i < m_numPorts; i++)
1071 m_children[i] = nullptr;
1072
1074}
1075
1077{
1078 for (auto & child : m_children)
1079 delete child;
1080}
1081
1083 const DTVMultiplex &tuning)
1084{
1085 bool success = true;
1086
1087 // sanity check switch position
1088 int pos = GetPosition(settings);
1089 if (pos < 0)
1090 return false;
1091
1092 // perform switching
1093 if (ShouldSwitch(settings, tuning))
1094 {
1095 switch (m_type)
1096 {
1097 case kTypeTone:
1098 success = ExecuteTone(settings, tuning, pos);
1099 break;
1102 success = ExecuteDiseqc(settings, tuning, pos);
1103 break;
1104 case kTypeLegacySW21:
1105 case kTypeLegacySW42:
1106 case kTypeLegacySW64:
1107 success = ExecuteLegacy(settings, tuning, pos);
1108 break;
1109 case kTypeVoltage:
1110 success = ExecuteVoltage(settings, tuning, pos);
1111 break;
1112 case kTypeMiniDiSEqC:
1113 success = ExecuteMiniDiSEqC(settings, tuning, pos);
1114 break;
1115 default:
1116 success = false;
1117 LOG(VB_GENERAL, LOG_ERR, LOC +
1118 QString("Unknown switch type (%1)").arg((uint)m_type));
1119 break;
1120 }
1121
1122 // if a child device will be sending a diseqc command, wait 100ms
1123 if (m_children[pos]->IsCommandNeeded(settings, tuning))
1124 {
1125 LOG(VB_CHANNEL, LOG_INFO, LOC + "Waiting for switch");
1126 std::this_thread::sleep_for(DISEQC_LONG_WAIT);
1127 }
1128
1129 m_lastPos = pos;
1130 }
1131
1132 // chain to child if the switch was successful
1133 if (success)
1134 success = m_children[pos]->Execute(settings, tuning);
1135
1136 return success;
1137}
1138
1140{
1141 m_lastPos = UINT_MAX;
1142 m_lastHighBand = UINT_MAX;
1143 m_lastHorizontal = UINT_MAX;
1144 for (auto & child : m_children)
1145 {
1146 if (child)
1147 child->Reset();
1148 }
1149}
1150
1152 const DTVMultiplex &tuning) const
1153{
1154 int pos = GetPosition(settings);
1155 if (pos < 0)
1156 return false;
1157
1158 return (ShouldSwitch(settings, tuning) ||
1159 m_children[pos]->IsCommandNeeded(settings, tuning));
1160}
1161
1163{
1164 // sanity check switch position
1165 int pos = GetPosition(settings);
1166 if (pos < 0)
1167 return nullptr;
1168
1169 return m_children[pos];
1170}
1171
1173{
1174 return m_numPorts;
1175}
1176
1178{
1179 if (ordinal < m_children.size())
1180 return m_children[ordinal];
1181
1182 return nullptr;
1183}
1184
1186{
1187 if (ordinal >= m_children.size())
1188 return false;
1189
1190 if (m_children[ordinal])
1191 delete m_children[ordinal];
1192
1193 m_children[ordinal] = device;
1194 if (device)
1195 {
1196 device->SetOrdinal(ordinal);
1197 device->SetParent(this);
1198 }
1199
1200 return true;
1201}
1202
1204 const DTVMultiplex &tuning) const
1205{
1206 uint voltage = SEC_VOLTAGE_18;
1207 DiSEqCDevDevice *child = GetSelectedChild(settings);
1208
1209 if (child)
1210 voltage = child->GetVoltage(settings, tuning);
1211
1212 return voltage;
1213}
1214
1216{
1217 // clear old children
1218 for (auto & child : m_children)
1219 delete child;
1220
1221 m_children.clear();
1222
1223 // populate switch parameters from db
1225 query.prepare(
1226 "SELECT subtype, address, switch_ports, cmd_repeat "
1227 "FROM diseqc_tree "
1228 "WHERE diseqcid = :DEVID");
1229 query.bindValue(":DEVID", GetDeviceID());
1230
1231 if (!query.exec() || !query.isActive())
1232 {
1233 MythDB::DBError("DiSEqCDevSwitch::Load 1", query);
1234 return false;
1235 }
1236 if (query.next())
1237 {
1238 m_type = SwitchTypeFromString(query.value(0).toString());
1239 m_address = query.value(1).toUInt();
1240 m_numPorts = query.value(2).toUInt();
1241 m_repeat = query.value(3).toUInt();
1242 m_children.resize(m_numPorts);
1243 for (uint i = 0; i < m_numPorts; i++)
1244 m_children[i] = nullptr;
1245 }
1246
1247 // load children from db
1248 query.prepare(
1249 "SELECT diseqcid, ordinal "
1250 "FROM diseqc_tree "
1251 "WHERE parentid = :DEVID");
1252 query.bindValue(":DEVID", GetDeviceID());
1253 if (!query.exec() || !query.isActive())
1254 {
1255 MythDB::DBError("DiSEqCDevSwitch::Load 2", query);
1256 return false;
1257 }
1258
1259 while (query.next())
1260 {
1261 uint child_dev_id = query.value(0).toUInt();
1262 uint ordinal = query.value(1).toUInt();
1263 DiSEqCDevDevice *child = CreateById(m_tree, child_dev_id);
1264 if (child && !SetChild(ordinal, child))
1265 {
1266 LOG(VB_GENERAL, LOG_ERR, LOC +
1267 QString("Switch port out of range (%1 > %2)")
1268 .arg(ordinal + 1).arg(m_numPorts));
1269 delete child;
1270 }
1271 }
1272
1273 return true;
1274}
1275
1277{
1278 QString type = SwitchTypeToString(m_type);
1280
1281 // insert new or update old
1282 if (IsRealDeviceID())
1283 {
1284 query.prepare(
1285 "UPDATE diseqc_tree "
1286 "SET parentid = :PARENT, "
1287 " ordinal = :ORDINAL, "
1288 " type = 'switch', "
1289 " description = :DESC, "
1290 " subtype = :TYPE, "
1291 " address = :ADDRESS, "
1292 " switch_ports = :PORTS, "
1293 " cmd_repeat = :REPEAT "
1294 "WHERE diseqcid = :DEVID");
1295 query.bindValue(":DEVID", GetDeviceID());
1296 }
1297 else
1298 {
1299 query.prepare(
1300 "INSERT INTO diseqc_tree"
1301 " ( parentid, ordinal, type, "
1302 " description, address, subtype, "
1303 " switch_ports, cmd_repeat ) "
1304 "VALUES "
1305 " (:PARENT, :ORDINAL, 'switch', "
1306 " :DESC, :ADDRESS, :TYPE, "
1307 " :PORTS, :REPEAT )");
1308 }
1309
1310 if (m_parent)
1311 query.bindValue(":PARENT", m_parent->GetDeviceID());
1312
1313 query.bindValue(":ORDINAL", m_ordinal);
1314 query.bindValue(":DESC", GetDescription());
1315 query.bindValue(":ADDRESS", m_address);
1316 query.bindValue(":TYPE", type);
1317 query.bindValue(":PORTS", m_numPorts);
1318 query.bindValue(":REPEAT", m_repeat);
1319
1320 if (!query.exec())
1321 {
1322 MythDB::DBError("DiSEqCDevSwitch::Store", query);
1323 return false;
1324 }
1325
1326 // figure out devid if we did an insert
1327 if (!IsRealDeviceID())
1328 SetDeviceID(query.lastInsertId().toUInt());
1329
1330 // chain to children
1331 bool success = true;
1332 for (auto *child : m_children)
1333 {
1334 if (child)
1335 success &= child->Store();
1336 }
1337
1338 return success;
1339}
1340
1342{
1343 uint old_num = m_children.size();
1344
1345 if (old_num > num_ports)
1346 {
1347 for (uint ch = num_ports; ch < old_num; ch++)
1348 {
1349 if (m_children[ch])
1350 delete m_children[ch];
1351 }
1352 m_children.resize(num_ports);
1353 }
1354 else if (old_num < num_ports)
1355 {
1356 m_children.resize(num_ports);
1357 for (uint ch = old_num; ch < num_ports; ch++)
1358 m_children[ch] = nullptr;
1359 }
1360
1361 m_numPorts = num_ports;
1362}
1363
1364bool DiSEqCDevSwitch::ExecuteLegacy([[maybe_unused]] const DiSEqCDevSettings &settings,
1365 [[maybe_unused]] const DTVMultiplex &tuning,
1366 [[maybe_unused]] uint pos)
1367{
1368#if CONFIG_DVB && defined(FE_DISHNETWORK_SEND_LEGACY_CMD)
1369 static const cmd_vec_t kSw21Cmds { 0x34, 0x65, };
1370 static const cmd_vec_t kSw42Cmds { 0x46, 0x17, };
1371 static const cmd_vec_t kSw64VCmds { 0x39, 0x4b, 0x0d, };
1372 static const cmd_vec_t kSw64HCmds { 0x1a, 0x5c, 0x2e, };
1373
1374 cmd_vec_t cmds {};
1375 unsigned char horizcmd = 0x00;
1376
1377 // determine polarity from lnb
1378 bool horizontal = false;
1379 DiSEqCDevLNB *lnb = m_tree.FindLNB(settings);
1380 if (lnb)
1381 horizontal = lnb->IsHorizontal(tuning);
1382
1383 // get command table for this switch
1384 switch (m_type)
1385 {
1386 case kTypeLegacySW21:
1387 cmds = kSw21Cmds;
1388 if (horizontal)
1389 horizcmd = 0x80;
1390 break;
1391 case kTypeLegacySW42:
1392 cmds = kSw42Cmds;
1393 break;
1394 case kTypeLegacySW64:
1395 if (horizontal)
1396 cmds = kSw64HCmds;
1397 else
1398 cmds = kSw64VCmds;
1399 break;
1400 default:
1401 return false;
1402 }
1403 pos %= cmds.size();
1404
1405 LOG(VB_CHANNEL, LOG_INFO, LOC +
1406 QString("Changing to Legacy switch port %1/%2")
1407 .arg(pos + 1).arg(cmds.size()));
1408
1409 // send command
1410 if (ioctl(m_tree.GetFD(), FE_DISHNETWORK_SEND_LEGACY_CMD,
1411 cmds[pos] | horizcmd) == -1)
1412 {
1413 LOG(VB_GENERAL, LOG_ERR, LOC +
1414 "FE_DISHNETWORK_SEND_LEGACY_CMD failed" + ENO);
1415
1416 return false;
1417 }
1418
1419 return true;
1420
1421#else // !FE_DISHNETWORK_SEND_LEGACY_CMD
1422
1423 LOG(VB_GENERAL, LOG_ERR, LOC + "You must compile with a newer "
1424 "version of the linux headers for DishNet Legacy switch support.");
1425 return false;
1426
1427#endif // !FE_DISHNETWORK_SEND_LEGACY_CMD
1428}
1429
1430#if CONFIG_DVB
1431static bool set_tone(int fd, fe_sec_tone_mode tone)
1432{
1433 bool success = false;
1434
1435 for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
1436 {
1437 if (ioctl(fd, FE_SET_TONE, tone) == 0)
1438 success = true;
1439 else
1440 std::this_thread::sleep_for(TIMEOUT_WAIT);
1441 }
1442
1443 if (!success)
1444 {
1445 LOG(VB_GENERAL, LOG_ERR, LOC + "set_tone failed" + ENO);
1446 }
1447
1448 return success;
1449}
1450#endif // CONFIG_DVB
1451
1452#if CONFIG_DVB
1453static bool set_voltage(int fd, fe_sec_voltage volt)
1454{
1455 bool success = false;
1456
1457 for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
1458 {
1459 if (0 == ioctl(fd, FE_SET_VOLTAGE, volt))
1460 success = true;
1461 else
1462 std::this_thread::sleep_for(TIMEOUT_WAIT);
1463 }
1464
1465 if (!success)
1466 {
1467 LOG(VB_GENERAL, LOG_ERR, LOC + "FE_SET_VOLTAGE failed" + ENO);
1468 }
1469
1470 return success;
1471}
1472#endif // CONFIG_DVB
1473
1474#if CONFIG_DVB
1475static bool mini_diseqc(int fd, fe_sec_mini_cmd cmd)
1476{
1477 bool success = false;
1478
1479 for (uint retry = 0; !success && (retry < TIMEOUT_RETRIES); retry++)
1480 {
1481 if (ioctl(fd, FE_DISEQC_SEND_BURST, cmd) == 0)
1482 success = true;
1483 else
1484 std::this_thread::sleep_for(TIMEOUT_WAIT);
1485 }
1486
1487 if (!success)
1488 {
1489 LOG(VB_GENERAL, LOG_ERR, LOC +
1490 "mini_diseqc FE_DISEQC_SEND_BURST failed" + ENO);
1491 }
1492
1493 return success;
1494}
1495#endif // CONFIG_DVB
1496
1498 const DTVMultiplex &/*tuning*/,
1499 uint pos)
1500{
1501 LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing to Tone switch port " +
1502 QString("%1/2").arg(pos + 1));
1503
1504#if CONFIG_DVB
1505 if (set_tone(m_tree.GetFD(), (0 == pos) ? SEC_TONE_OFF : SEC_TONE_ON))
1506 return true;
1507#endif // CONFIG_DVB
1508
1509 LOG(VB_GENERAL, LOG_ERR, LOC + "Setting Tone Switch failed." + ENO);
1510 return false;
1511}
1512
1513bool DiSEqCDevSwitch::ExecuteVoltage([[maybe_unused]] const DiSEqCDevSettings &settings,
1514 [[maybe_unused]] const DTVMultiplex &tuning,
1515 uint pos)
1516{
1517 LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing to Voltage Switch port " +
1518 QString("%1/2").arg(pos + 1));
1519
1520#if CONFIG_DVB
1521 if (set_voltage(m_tree.GetFD(),
1522 (0 == pos) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18))
1523 {
1524 return true;
1525 }
1526#endif // CONFIG_DVB
1527
1528 LOG(VB_GENERAL, LOG_ERR, LOC + "Setting Voltage Switch failed." + ENO);
1529
1530 return false;
1531}
1532
1533bool DiSEqCDevSwitch::ExecuteMiniDiSEqC([[maybe_unused]] const DiSEqCDevSettings &settings,
1534 [[maybe_unused]] const DTVMultiplex &tuning,
1535 uint pos)
1536{
1537 LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing to MiniDiSEqC Switch port " +
1538 QString("%1/2").arg(pos + 1));
1539
1540#if CONFIG_DVB
1541 if (mini_diseqc(m_tree.GetFD(), (0 == pos) ? SEC_MINI_A : SEC_MINI_B))
1542 return true;
1543#endif // CONFIG_DVB
1544
1545 LOG(VB_GENERAL, LOG_ERR, LOC + "Setting Mini DiSEqC Switch failed." + ENO);
1546
1547 return false;
1548}
1549
1551 const DTVMultiplex &tuning) const
1552{
1553 int pos = GetPosition(settings);
1554 if (pos < 0)
1555 return false;
1556
1557 // committed switch should change for band and polarity as well
1559 {
1560 // retrieve LNB info
1561 bool high_band = false;
1562 bool horizontal = false;
1563 DiSEqCDevLNB *lnb = m_tree.FindLNB(settings);
1564 if (lnb)
1565 {
1566 high_band = lnb->IsHighBand(tuning);
1567 horizontal = lnb->IsHorizontal(tuning);
1568 }
1569
1570 if(static_cast<uint>(high_band) != m_lastHighBand ||
1571 static_cast<uint>(horizontal) != m_lastHorizontal)
1572 return true;
1573 }
1574 else if (kTypeLegacySW42 == m_type ||
1576 {
1577 // retrieve LNB info
1578 bool horizontal = false;
1579 DiSEqCDevLNB *lnb = m_tree.FindLNB(settings);
1580 if (lnb)
1581 horizontal = lnb->IsHorizontal(tuning);
1582
1583 if (static_cast<unsigned int>(horizontal) != m_lastHorizontal)
1584 return true;
1585 }
1586 else if (kTypeVoltage == m_type ||
1587 kTypeTone == m_type)
1588 {
1589 return true;
1590 }
1591
1592 return m_lastPos != (uint)pos;
1593}
1594
1596 const DTVMultiplex &tuning,
1597 uint pos32)
1598{
1599 auto pos = static_cast<uint8_t>(pos32);
1600 // retrieve LNB info
1601 bool high_band = false;
1602 bool horizontal = false;
1603 DiSEqCDevLNB *lnb = m_tree.FindLNB(settings);
1604 if (lnb)
1605 {
1606 high_band = lnb->IsHighBand(tuning);
1607 horizontal = lnb->IsHorizontal(tuning);
1608 }
1609
1610 // check number of ports
1611 if (((kTypeDiSEqCCommitted == m_type) && (m_numPorts > 4)) ||
1612 ((kTypeDiSEqCUncommitted == m_type) && (m_numPorts > 16)))
1613 {
1614 LOG(VB_GENERAL, LOG_ERR, LOC +
1615 QString("Invalid number of ports for DiSEqC 1.x Switch (%1)")
1616 .arg(m_numPorts));
1617 return false;
1618 }
1619
1620 // build command
1622 cmd_vec_t data { pos };
1624 {
1625 cmd = DISEQC_CMD_WRITE_N0;
1626 data[0] = ((pos << 2) | (horizontal ? 2 : 0) | (high_band ? 1 : 0));
1627 }
1628 data[0] |= 0xf0;
1629
1630 LOG(VB_CHANNEL, LOG_INFO, LOC + "Changing to DiSEqC switch port " +
1631 QString("%1/%2").arg(pos + 1).arg(m_numPorts));
1632
1633 bool ret = m_tree.SendCommand(m_address, cmd, m_repeat, data);
1634 if(ret)
1635 {
1636 m_lastHighBand = static_cast<uint>(high_band);
1637 m_lastHorizontal = static_cast<uint>(horizontal);
1638 }
1639 return ret;
1640}
1641
1643{
1644 int pos = (int) settings.GetValue(GetDeviceID());
1645
1646 if (pos >= (int)m_numPorts)
1647 {
1648 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Port %1 ").arg(pos + 1) +
1649 QString("is not in range [0..%1)").arg(m_numPorts));
1650
1651 return -1;
1652 }
1653
1654 if ((pos >= 0) && !m_children[pos])
1655 {
1656 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Port %1 ").arg(pos + 1) +
1657 "has no connected devices configured.");
1658
1659 return -1;
1660 }
1661
1662 return pos;
1663}
1664
1666
1667static double GetCurTimeFloating(void)
1668{
1669 struct timeval curtime {};
1670 gettimeofday(&curtime, nullptr);
1671 return (double)curtime.tv_sec + (((double)curtime.tv_usec) / 1000000);
1672}
1673
1678const std::vector<DiSEqCDevDevice::TypeTable> DiSEqCDevRotor::kRotorTypeTable
1679{
1680 { "diseqc_1_2", kTypeDiSEqC_1_2 },
1681 { "diseqc_1_3", kTypeDiSEqC_1_3 },
1682 { nullptr, kTypeDiSEqC_1_3 }
1683};
1684
1686{
1687 delete m_child;
1688}
1689
1691 const DTVMultiplex &tuning)
1692{
1693 bool success = true;
1694
1695 double position = settings.GetValue(GetDeviceID());
1696 if (m_reset || (position != m_lastPosition))
1697 {
1698 switch (m_type)
1699 {
1700 case kTypeDiSEqC_1_2:
1701 success = ExecuteRotor(settings, tuning, position);
1702 break;
1703 case kTypeDiSEqC_1_3:
1704 success = ExecuteUSALS(settings, tuning, position);
1705 break;
1706 default:
1707 success = false;
1708 LOG(VB_GENERAL, LOG_ERR, LOC + "Unknown rotor type " +
1709 QString("(%1)").arg((uint) m_type));
1710 break;
1711 }
1712
1713 m_lastPosition = position;
1714 m_reset = false;
1715 if (success)
1716 // prevent tuning parameters overriding rotor parameters
1717 std::this_thread::sleep_for(DISEQC_LONG_WAIT);
1718 }
1719
1720 // chain to child
1721 if (success && m_child)
1722 success = m_child->Execute(settings, tuning);
1723
1724 return success;
1725}
1726
1728{
1729 m_reset = true;
1730 if (m_child)
1731 m_child->Reset();
1732}
1733
1735 const DTVMultiplex &tuning) const
1736{
1737 double position = settings.GetValue(GetDeviceID());
1738
1739 if (m_reset || (position != m_lastPosition))
1740 return true;
1741
1742 if (m_child)
1743 return m_child->IsCommandNeeded(settings, tuning);
1744
1745 return false;
1746}
1747
1749{
1750 return m_child;
1751}
1752
1754{
1755 if (ordinal)
1756 return false;
1757
1758 DiSEqCDevDevice *old_child = m_child;
1759 m_child = nullptr;
1760 delete old_child;
1761
1762 m_child = device;
1763 if (m_child)
1764 {
1765 m_child->SetOrdinal(ordinal);
1766 m_child->SetParent(this);
1767 }
1768
1769 return true;
1770}
1771
1773{
1774 double position = settings.GetValue(GetDeviceID());
1775 double completed = GetProgress();
1776 bool moving = (completed < 1.0) || (position != m_lastPosition);
1777
1778 return (m_lastPosKnown && moving);
1779}
1780
1782 const DTVMultiplex &tuning) const
1783{
1784 // override voltage if the last position is known and the rotor is moving
1785 if (IsMoving(settings))
1786 {
1787 LOG(VB_CHANNEL, LOG_INFO, LOC +
1788 "Overriding voltage to 18V for faster rotor movement");
1789 }
1790 else if (m_child)
1791 {
1792 return m_child->GetVoltage(settings, tuning);
1793 }
1794
1795 return SEC_VOLTAGE_18;
1796}
1797
1799{
1800 // populate switch parameters from db
1802 query.prepare(
1803 "SELECT subtype, rotor_positions, "
1804 " rotor_hi_speed, rotor_lo_speed, "
1805 " cmd_repeat "
1806 "FROM diseqc_tree "
1807 "WHERE diseqcid = :DEVID");
1808 query.bindValue(":DEVID", GetDeviceID());
1809
1810 if (!query.exec() || !query.isActive())
1811 {
1812 MythDB::DBError("DiSEqCDevRotor::Load 1", query);
1813 return false;
1814 }
1815 if (query.next())
1816 {
1817 m_type = RotorTypeFromString(query.value(0).toString());
1818 m_speedHi = query.value(2).toDouble();
1819 m_speedLo = query.value(3).toDouble();
1820 m_repeat = query.value(4).toUInt();
1821
1822 // form of "angle1=index1:angle2=index2:..."
1823 QString positions = query.value(1).toString();
1824 QStringList pos = positions.split(":", Qt::SkipEmptyParts);
1825 for (const auto & kv : std::as_const(pos))
1826 {
1827 const QStringList eq = kv.split("=", Qt::SkipEmptyParts);
1828 if (eq.size() == 2)
1829 m_posmap[eq[0].toFloat()] = eq[1].toUInt();
1830 }
1831 }
1832
1833 // load children from db
1834 if (m_child)
1835 {
1836 delete m_child;
1837 m_child = nullptr;
1838 }
1839
1840 query.prepare(
1841 "SELECT diseqcid "
1842 "FROM diseqc_tree "
1843 "WHERE parentid = :DEVID");
1844 query.bindValue(":DEVID", GetDeviceID());
1845
1846 if (!query.exec() || !query.isActive())
1847 {
1848 MythDB::DBError("DiSEqCDevRotor::Load 2", query);
1849 return false;
1850 }
1851 if (query.next())
1852 {
1853 uint child_dev_id = query.value(0).toUInt();
1854 SetChild(0, CreateById(m_tree, child_dev_id));
1855 }
1856
1857 return true;
1858}
1859
1861{
1862 QString posmap = "";
1863 QString type = RotorTypeToString(m_type);
1864
1865 if (!m_posmap.empty())
1866 {
1867 QStringList pos;
1868
1869 dbl_to_uint_t::const_iterator it = m_posmap.begin();
1870 for (; it != m_posmap.end(); ++it)
1871 pos.push_back(QString("%1=%2").arg(it.key()).arg(*it));
1872
1873 posmap = pos.join(":");
1874 }
1875
1877
1878 // insert new or update old
1879 if (IsRealDeviceID())
1880 {
1881 query.prepare(
1882 "UPDATE diseqc_tree "
1883 "SET parentid = :PARENT, "
1884 " ordinal = :ORDINAL, "
1885 " type = 'rotor', "
1886 " description = :DESC, "
1887 " subtype = :TYPE, "
1888 " rotor_hi_speed = :HISPEED, "
1889 " rotor_lo_speed = :LOSPEED, "
1890 " rotor_positions = :POSMAP, "
1891 " cmd_repeat = :REPEAT "
1892 "WHERE diseqcid = :DEVID");
1893 query.bindValue(":DEVID", GetDeviceID());
1894 }
1895 else
1896 {
1897 query.prepare(
1898 "INSERT INTO diseqc_tree "
1899 " ( parentid, ordinal, type, "
1900 " description, subtype, rotor_hi_speed, "
1901 " rotor_lo_speed, rotor_positions, cmd_repeat ) "
1902 "VALUES "
1903 " (:PARENT, :ORDINAL, 'rotor', "
1904 " :DESC, :TYPE, :HISPEED, "
1905 " :LOSPEED, :POSMAP, :REPEAT )");
1906 }
1907
1908 if (m_parent)
1909 query.bindValue(":PARENT", m_parent->GetDeviceID());
1910
1911 query.bindValue(":ORDINAL", m_ordinal);
1912 query.bindValue(":DESC", GetDescription());
1913 query.bindValue(":TYPE", type);
1914 query.bindValue(":HISPEED", m_speedHi);
1915 query.bindValue(":LOSPEED", m_speedLo);
1916 query.bindValue(":POSMAP", posmap);
1917 query.bindValue(":REPEAT", m_repeat);
1918
1919 if (!query.exec())
1920 {
1921 MythDB::DBError("DiSEqCDevRotor::Store", query);
1922 return false;
1923 }
1924
1925 // figure out devid if we did an insert
1926 if (!IsRealDeviceID())
1927 SetDeviceID(query.lastInsertId().toUInt());
1928
1929 // chain to child
1930 if (m_child)
1931 return m_child->Store();
1932
1933 return true;
1934}
1935
1942{
1943 if (m_moveTime == 0.0)
1944 return 1.0;
1945
1946 // calculate duration of move
1947 double speed = ((m_tree.GetVoltage() == SEC_VOLTAGE_18) ?
1949 double change = abs(m_desiredAzimuth - m_lastAzimuth);
1950 double duration = change / speed;
1951
1952 // determine completion percentage
1953 double time_since_move = GetCurTimeFloating() - m_moveTime;
1954 double completed = time_since_move / duration;
1955 if(completed > 1.0)
1956 {
1958 completed = 1.0;
1959 }
1960
1961 return completed;
1962}
1963
1972{
1973 return m_lastPosKnown;
1974}
1975
1977{
1978 uint_to_dbl_t inv_posmap;
1979 dbl_to_uint_t::const_iterator it;
1980 for (it = m_posmap.begin(); it != m_posmap.end(); ++it)
1981 inv_posmap[*it] = it.key();
1982
1983 return inv_posmap;
1984}
1985
1987{
1988 m_posmap.clear();
1989
1990 uint_to_dbl_t::const_iterator it;
1991 for (it = inv_posmap.begin(); it != inv_posmap.end(); ++it)
1992 m_posmap[*it] = it.key();
1993}
1994
1996 const DTVMultiplex& /*tuning*/,
1997 double angle)
1998{
1999 // determine stored position from position map
2000 dbl_to_uint_t::const_iterator it =
2001 m_posmap.lowerBound(angle - EPS); // clazy:exclude=strict-iterators
2002 cmd_vec_t index { static_cast<uint8_t>(angle) };
2003 if (it != m_posmap.cend())
2004 {
2005 index[0] = *it;
2007 }
2008
2009 LOG(VB_CHANNEL, LOG_INFO, LOC + "Rotor - " +
2010 QString("Goto Stored Position %1").arg(index[0]));
2011
2013 m_repeat, index);
2014}
2015
2017 const DTVMultiplex& /*tuning*/,
2018 double angle)
2019{
2020 double azimuth = CalculateAzimuth(angle);
2022
2023 LOG(VB_CHANNEL, LOG_INFO, LOC + "USALS Rotor - " +
2024 QString("Goto %1 (Azimuth %2)").arg(angle).arg(azimuth));
2025
2026 uint az16 = (uint) (abs(azimuth) * 16.0);
2027 cmd_vec_t cmd {
2028 static_cast<uint8_t>(((azimuth > 0.0) ? 0xE0 : 0xD0) | ((az16 >> 8) & 0x0f)),
2029 static_cast<uint8_t>(az16 & 0xff) };
2030
2032 m_repeat, cmd);
2033}
2034
2036{
2037 // Azimuth Calculation references:
2038 // http://engr.nmsu.edu/~etti/3_2/3_2e.html
2039 // http://www.angelfire.com/trek/ismail/theory.html
2040
2041 // Earth Station Latitude and Longitude in radians
2042 double P = gCoreContext->GetSetting("Latitude", "").toDouble() * TO_RADS;
2043 double Ue = gCoreContext->GetSetting("Longitude", "").toDouble() * TO_RADS;
2044
2045 // Satellite Longitude in radians
2046 double Us = angle * TO_RADS;
2047
2048 return TO_DEC * atan( tan(Us - Ue) / sin(P) );
2049}
2050
2052{
2053 if (m_moveTime == 0.0)
2054 return m_lastAzimuth;
2055
2056 double change = m_desiredAzimuth - m_lastAzimuth;
2057 return m_lastAzimuth + (change * GetProgress());
2058}
2059
2061{
2062 // save time and angle of this command
2063 m_desiredAzimuth = azimuth;
2064
2065 // set last to approximate current position (or worst case if unknown)
2066 if (m_lastPosKnown || m_moveTime > 0.0)
2068 else
2069 m_lastAzimuth = azimuth > 0.0 ? -75.0 : 75.0;
2070
2072}
2073
2075{
2076 m_moveTime = 0.0;
2077 m_lastPosKnown = true;
2079}
2080
2082
2087const std::vector<DiSEqCDevDevice::TypeTable> DiSEqCDevSCR::kSCRPositionTable
2088{
2089 { "A", kTypeScrPosA },
2090 { "B", kTypeScrPosB },
2091 { QString(), kTypeScrPosA },
2092};
2093
2095{
2096 delete m_child;
2097}
2098
2100{
2101 if (m_child)
2102 m_child->Reset();
2103}
2104
2105bool DiSEqCDevSCR::Execute(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning)
2106{
2107 // retrieve LNB info
2108 DiSEqCDevLNB *lnb = m_tree.FindLNB(settings);
2109 if (!lnb)
2110 {
2111 LOG(VB_GENERAL, LOG_ERR, LOC + "SCR: No LNB for this configuration!");
2112 return false;
2113 }
2114
2115 bool high_band = lnb->IsHighBand(tuning);
2116 bool horizontal = lnb->IsHorizontal(tuning);
2117 uint32_t frequency = lnb->GetIntermediateFrequency(settings, tuning);
2118 uint t = ((frequency / 1000 + m_scrFrequency + 2) / 4) - 350;
2119
2120 // retrieve position settings (value should be 0 or 1)
2121 auto scr_position = (dvbdev_pos_t)int(settings.GetValue(GetDeviceID()));
2122
2123 // check parameters
2124 if (m_scrUserband > 7)
2125 {
2126 LOG(VB_GENERAL, LOG_ERR, LOC + QString("SCR: Userband ID=%1 is out of range (0-7)!")
2127 .arg(m_scrUserband));
2128 return false;
2129 }
2130
2131 if (t >= 1024)
2132 {
2133 LOG(VB_GENERAL, LOG_ERR, LOC + QString("SCR: T=%1 is out of range!").arg(t));
2134 return false;
2135 }
2136
2137 LOG(VB_GENERAL, LOG_INFO, QString("SCR: Tuning to %1kHz, %2, %3 using UB=%4, FREQ=%5MHz, POS=%6%7")
2138 .arg(tuning.m_frequency)
2139 .arg(high_band ? "HiBand" : "LoBand",
2140 horizontal ? "H" : "V")
2141 .arg(m_scrUserband)
2142 .arg(m_scrFrequency)
2143 .arg((scr_position) ? "B" : "A",
2144 (m_scrPin >= 0 && m_scrPin <= 255) ?
2145 QString(", PIN=%1").arg(m_scrPin) : QString("")));
2146
2147 // build command
2148 cmd_vec_t data {
2149 static_cast<uint8_t>(t >> 8 | m_scrUserband << 5),
2150 static_cast<uint8_t>(t & 0x00FF) };
2151
2152 if (high_band)
2153 data[0] |= (1 << 2);
2154
2155 if (horizontal)
2156 data[0] |= (1 << 3);
2157
2158 if (scr_position)
2159 data[0] |= (1 << 4);
2160
2161 // send command
2162 if (m_scrPin >= 0 && m_scrPin <= 255)
2163 data.push_back(m_scrPin);
2164 return SendCommand(DISEQC_CMD_ODU, m_repeat, data);
2165}
2166
2168{
2169 // check parameters
2170 if (m_scrUserband > 7)
2171 {
2172 LOG(VB_GENERAL, LOG_ERR, LOC + QString("SCR: Userband ID=%1 is out of range (0-7)!")
2173 .arg(m_scrUserband));
2174 return false;
2175 }
2176
2177 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SCR: Power off UB=%1%7")
2178 .arg(m_scrUserband)
2179 .arg((m_scrPin >= 0 && m_scrPin <= 255)
2180 ? QString(", PIN=%1").arg(m_scrPin)
2181 : QString("")));
2182
2183 // build command
2184 cmd_vec_t data {
2185 static_cast<uint8_t>(m_scrUserband << 5), 0x00 };
2186
2187 // send command
2188 if (m_scrPin >= 0 && m_scrPin <= 255)
2189 data.push_back(m_scrPin);
2190 return SendCommand(DISEQC_CMD_ODU, m_repeat, data);
2191}
2192
2194 [[maybe_unused]] uint repeats,
2195 cmd_vec_t &data) const
2196{
2197 // power on bus
2199 return false;
2200 std::this_thread::sleep_for(DISEQC_LONG_WAIT);
2201
2202 // send command
2203 bool ret = m_tree.SendCommand(DISEQC_ADR_SW_ALL, cmd, repeats, data);
2204
2205 // power off bus
2207 return false;
2208
2209 return ret;
2210}
2211
2213 const DTVMultiplex &/*tuning*/) const
2214{
2215 return SEC_VOLTAGE_13;
2216}
2217
2218uint32_t DiSEqCDevSCR::GetIntermediateFrequency(const uint32_t frequency) const
2219{
2220 uint t = ((frequency / 1000 + m_scrFrequency + 2) / 4) - 350;
2221 return (((t + 350) * 4) * 1000) - frequency;
2222}
2223
2225{
2226 // populate scr parameters from db
2228 query.prepare(
2229 "SELECT scr_userband, scr_frequency, "
2230 " scr_pin, cmd_repeat "
2231 "FROM diseqc_tree "
2232 "WHERE diseqcid = :DEVID");
2233 query.bindValue(":DEVID", GetDeviceID());
2234
2235 if (!query.exec() || !query.isActive())
2236 {
2237 MythDB::DBError("DiSEqCDevSCR::Load 1", query);
2238 return false;
2239 }
2240 if (query.next())
2241 {
2242 m_scrUserband = query.value(0).toUInt();
2243 m_scrFrequency = query.value(1).toUInt();
2244 m_scrPin = query.value(2).toInt();
2245 m_repeat = query.value(3).toUInt();
2246 }
2247
2248 // load children from db
2249 if (m_child)
2250 {
2251 delete m_child;
2252 m_child = nullptr;
2253 }
2254
2255 query.prepare(
2256 "SELECT diseqcid "
2257 "FROM diseqc_tree "
2258 "WHERE parentid = :DEVID");
2259 query.bindValue(":DEVID", GetDeviceID());
2260
2261 if (!query.exec() || !query.isActive())
2262 {
2263 MythDB::DBError("DiSEqCDevSCR::Load 2", query);
2264 return false;
2265 }
2266 if (query.next())
2267 {
2268 uint child_dev_id = query.value(0).toUInt();
2269 SetChild(0, CreateById(m_tree, child_dev_id));
2270 }
2271
2272 return true;
2273}
2274
2275bool DiSEqCDevSCR::Store(void) const
2276{
2278
2279 // insert new or update old
2280 if (IsRealDeviceID())
2281 {
2282 query.prepare(
2283 "UPDATE diseqc_tree "
2284 "SET parentid = :PARENT, "
2285 " ordinal = :ORDINAL, "
2286 " type = 'scr', "
2287 " description = :DESC, "
2288 " scr_userband = :USERBAND, "
2289 " scr_frequency = :FREQUENCY, "
2290 " scr_pin = :PIN, "
2291 " cmd_repeat = :REPEAT "
2292 "WHERE diseqcid = :DEVID");
2293 query.bindValue(":DEVID", GetDeviceID());
2294 }
2295 else
2296 {
2297 query.prepare(
2298 "INSERT INTO diseqc_tree"
2299 " ( parentid, ordinal, type, "
2300 " description, scr_userband, scr_frequency, "
2301 " scr_pin, cmd_repeat) "
2302 "VALUES "
2303 " (:PARENT, :ORDINAL, 'scr', "
2304 " :DESC, :USERBAND, :FREQUENCY,"
2305 " :PIN, :REPEAT) ");
2306 }
2307
2308 if (m_parent)
2309 query.bindValue(":PARENT", m_parent->GetDeviceID());
2310
2311 query.bindValue(":ORDINAL", m_ordinal);
2312 query.bindValue(":DESC", GetDescription());
2313 query.bindValue(":USERBAND", m_scrUserband);
2314 query.bindValue(":FREQUENCY", m_scrFrequency);
2315 query.bindValue(":PIN", m_scrPin);
2316 query.bindValue(":REPEAT", m_repeat);
2317
2318 // update dev_id
2319 if (!query.exec())
2320 {
2321 MythDB::DBError("DiSEqCDevSCR::Store", query);
2322 return false;
2323 }
2324
2325 // figure out devid if we did an insert
2326 if (!IsRealDeviceID())
2327 SetDeviceID(query.lastInsertId().toUInt());
2328
2329 // chain to child
2330 if (m_child)
2331 return m_child->Store();
2332
2333 return true;
2334}
2335
2337{
2338 if (ordinal)
2339 return false;
2340
2341 DiSEqCDevDevice *old_child = m_child;
2342 m_child = nullptr;
2343 delete old_child;
2344
2345 m_child = device;
2346 if (m_child)
2347 {
2348 m_child->SetOrdinal(ordinal);
2349 m_child->SetParent(this);
2350 }
2351
2352 return true;
2353}
2354
2356
2361const std::vector<DiSEqCDevDevice::TypeTable> DiSEqCDevLNB::kLNBTypeTable
2362{
2363 { "fixed", kTypeFixed },
2364 { "voltage", kTypeVoltageControl },
2365 { "voltage_tone", kTypeVoltageAndToneControl },
2366 { "bandstacked", kTypeBandstacked },
2367 { QString(), kTypeVoltageAndToneControl },
2368};
2369
2370bool DiSEqCDevLNB::Execute(const DiSEqCDevSettings& /*settings*/, const DTVMultiplex &tuning)
2371{
2372 // set tone for bandselect
2374 m_tree.SetTone(IsHighBand(tuning));
2375
2376 return true;
2377}
2378
2380 const DTVMultiplex &tuning) const
2381{
2382 uint voltage = SEC_VOLTAGE_18;
2383
2384 if ((kTypeVoltageControl == m_type) ||
2386 {
2387 voltage = (IsHorizontal(tuning) ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13);
2388 }
2389
2390 return voltage;
2391}
2392
2394{
2395 // populate lnb parameters from db
2397 query.prepare(
2398 "SELECT subtype, lnb_lof_switch, "
2399 " lnb_lof_hi, lnb_lof_lo, "
2400 " lnb_pol_inv, cmd_repeat "
2401 "FROM diseqc_tree "
2402 "WHERE diseqcid = :DEVID");
2403 query.bindValue(":DEVID", GetDeviceID());
2404
2405 if (!query.exec() || !query.isActive())
2406 {
2407 MythDB::DBError("DiSEqCDevLNB::Load", query);
2408 return false;
2409 }
2410 if (query.next())
2411 {
2412 m_type = LNBTypeFromString(query.value(0).toString());
2413 m_lofSwitch = query.value(1).toInt();
2414 m_lofHi = query.value(2).toInt();
2415 m_lofLo = query.value(3).toInt();
2416 m_polInv = query.value(4).toBool();
2417 m_repeat = query.value(5).toUInt();
2418 }
2419
2420 return true;
2421}
2422
2423bool DiSEqCDevLNB::Store(void) const
2424{
2425 QString type = LNBTypeToString(m_type);
2427
2428 // insert new or update old
2429 if (IsRealDeviceID())
2430 {
2431 query.prepare(
2432 "UPDATE diseqc_tree "
2433 "SET parentid = :PARENT, "
2434 " ordinal = :ORDINAL, "
2435 " type = 'lnb', "
2436 " description = :DESC, "
2437 " subtype = :TYPE, "
2438 " lnb_lof_switch = :LOFSW, "
2439 " lnb_lof_lo = :LOFLO, "
2440 " lnb_lof_hi = :LOFHI, "
2441 " lnb_pol_inv = :POLINV, "
2442 " cmd_repeat = :REPEAT "
2443 "WHERE diseqcid = :DEVID");
2444 query.bindValue(":DEVID", GetDeviceID());
2445 }
2446 else
2447 {
2448 query.prepare(
2449 "INSERT INTO diseqc_tree"
2450 " ( parentid, ordinal, type, "
2451 " description, subtype, lnb_lof_switch, "
2452 " lnb_lof_lo, lnb_lof_hi, lnb_pol_inv, "
2453 " cmd_repeat ) "
2454 "VALUES "
2455 " (:PARENT, :ORDINAL, 'lnb', "
2456 " :DESC, :TYPE, :LOFSW, "
2457 " :LOFLO, :LOFHI, :POLINV, "
2458 " :REPEAT ) ");
2459 }
2460
2461 if (m_parent)
2462 query.bindValue(":PARENT", m_parent->GetDeviceID());
2463
2464 query.bindValue(":ORDINAL", m_ordinal);
2465 query.bindValue(":DESC", GetDescription());
2466 query.bindValue(":TYPE", type);
2467 query.bindValue(":LOFSW", m_lofSwitch);
2468 query.bindValue(":LOFLO", m_lofLo);
2469 query.bindValue(":LOFHI", m_lofHi);
2470 query.bindValue(":POLINV", m_polInv);
2471 query.bindValue(":REPEAT", m_repeat);
2472
2473 // update dev_id
2474 if (!query.exec())
2475 {
2476 MythDB::DBError("DiSEqCDevLNB::Store", query);
2477 return false;
2478 }
2479
2480 // figure out devid if we did an insert
2481 if (!IsRealDeviceID())
2482 SetDeviceID(query.lastInsertId().toUInt());
2483
2484 return true;
2485}
2486
2494{
2495 switch (m_type)
2496 {
2498 return (tuning.m_frequency > m_lofSwitch);
2499 case kTypeBandstacked:
2500 return IsHorizontal(tuning);
2501 default:
2502 return false;
2503 }
2504
2505 return false;
2506}
2507
2514{
2515 QString pol = tuning.m_polarity.toString().toLower();
2516 return (pol == "h" || pol == "l") ^ IsPolarityInverted();
2517}
2518
2526 const DiSEqCDevSettings& /*settings*/, const DTVMultiplex &tuning) const
2527{
2528 uint64_t abs_freq = tuning.m_frequency;
2529 uint lof = (IsHighBand(tuning)) ? m_lofHi : m_lofLo;
2530
2531 return (lof > abs_freq) ? (lof - abs_freq) : (abs_freq - lof);
2532}
uint64_t m_frequency
Definition: dtvmultiplex.h:94
DTVPolarity m_polarity
Definition: dtvmultiplex.h:104
QString toString() const
Represents a node in a DVB-S device network.
Definition: diseqc.h:140
uint GetDeviceID(void) const
Definition: diseqc.h:170
std::vector< TypeTable > TypeTableVec
Definition: diseqc.h:214
virtual void Reset(void)
Resets to the last known settings for this device.
Definition: diseqc.h:147
bool IsRealDeviceID(void) const
Definition: diseqc.h:171
virtual bool Store(void) const =0
Stores this device to the database.
DiSEqCDevTree & m_tree
Definition: diseqc.h:208
virtual uint GetChildCount(void) const
Retrieves the proper number of children for this node.
Definition: diseqc.h:177
virtual DiSEqCDevDevice * GetSelectedChild(const DiSEqCDevSettings &) const
Retrieves the selected child for this configuration, if any.
Definition: diseqc.h:186
virtual DiSEqCDevDevice * GetChild(uint)
Retrieves the nth child of this node.
Definition: diseqc.h:188
uint m_ordinal
Definition: diseqc.h:210
static DiSEqCDevDevice * CreateById(DiSEqCDevTree &tree, uint devid)
Definition: diseqc.cpp:901
virtual bool Execute(const DiSEqCDevSettings &, const DTVMultiplex &)=0
Applies DiSEqC settings to this node and any children.
QString GetDescription(void) const
Definition: diseqc.h:175
void SetDescription(const QString &desc)
Definition: diseqc.h:163
void SetParent(DiSEqCDevDevice *parent)
Definition: diseqc.h:161
virtual uint GetVoltage(const DiSEqCDevSettings &, const DTVMultiplex &) const =0
Retrives the desired voltage for this config.
void SetOrdinal(uint ordinal)
Definition: diseqc.h:162
static QString TableToString(uint type, const TypeTableVec &table)
Definition: diseqc.cpp:99
uint m_repeat
Definition: diseqc.h:211
void SetDeviceID(uint devid) const
Definition: diseqc.h:203
virtual ~DiSEqCDevDevice()
Definition: diseqc.cpp:871
static DiSEqCDevDevice * CreateByType(DiSEqCDevTree &tree, dvbdev_t type, uint dev_id=0)
Definition: diseqc.cpp:937
DiSEqCDevDevice * FindDevice(uint dev_id)
Returns a device by ID.
Definition: diseqc.cpp:877
virtual bool Load(void)=0
Loads this device from the database.
void SetDeviceType(dvbdev_t type)
Definition: diseqc.h:160
static const TypeTableVec kDvbdevLookup
Definition: diseqc.h:220
static uint TableFromString(const QString &type, const TypeTableVec &table)
Definition: diseqc.cpp:107
DiSEqCDevDevice * m_parent
Definition: diseqc.h:209
dvbdev_t GetDeviceType(void) const
Definition: diseqc.h:169
virtual bool IsCommandNeeded(const DiSEqCDevSettings &, const DTVMultiplex &) const
Determines if this device or any child will be sending a command for the given configuration chain.
Definition: diseqc.h:178
static dvbdev_t DevTypeFromString(const QString &type)
Definition: diseqc.h:193
LNB Class.
Definition: diseqc.h:447
static dvbdev_lnb_t LNBTypeFromString(const QString &type)
Definition: diseqc.h:488
bool IsHighBand(const DTVMultiplex &tuning) const
Determine if the high frequency band is active (for switchable LNBs).
Definition: diseqc.cpp:2493
bool IsPolarityInverted(void) const
Definition: diseqc.h:476
uint32_t GetIntermediateFrequency(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) const
Calculate proper intermediate frequency for the given settings and tuning parameters.
Definition: diseqc.cpp:2525
bool Store(void) const override
Stores this device to the database.
Definition: diseqc.cpp:2423
uint m_lofLo
Definition: diseqc.h:495
uint m_lofHi
Definition: diseqc.h:494
uint m_lofSwitch
Definition: diseqc.h:493
@ kTypeVoltageAndToneControl
Definition: diseqc.h:462
@ kTypeBandstacked
Definition: diseqc.h:463
@ kTypeVoltageControl
Definition: diseqc.h:461
static QString LNBTypeToString(dvbdev_lnb_t type)
Definition: diseqc.h:485
bool Execute(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) override
Applies DiSEqC settings to this node and any children.
Definition: diseqc.cpp:2370
bool Load(void) override
Loads this device from the database.
Definition: diseqc.cpp:2393
static const TypeTableVec kLNBTypeTable
Definition: diseqc.h:501
bool m_polInv
If a signal is circularly polarized the polarity will flip on each reflection, so antenna systems wit...
Definition: diseqc.h:499
dvbdev_lnb_t m_type
Definition: diseqc.h:492
bool IsHorizontal(const DTVMultiplex &tuning) const
Determine if horizontal polarity is active (for switchable LNBs).
Definition: diseqc.cpp:2513
uint GetVoltage(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) const override
Retrives the desired voltage for this config.
Definition: diseqc.cpp:2379
Rotor class.
Definition: diseqc.h:303
@ kTypeDiSEqC_1_3
Definition: diseqc.h:316
@ kTypeDiSEqC_1_2
Definition: diseqc.h:316
void StartRotorPositionTracking(double azimuth)
Definition: diseqc.cpp:2060
bool ExecuteUSALS(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning, double angle)
Definition: diseqc.cpp:2016
static dvbdev_rotor_t RotorTypeFromString(const QString &type)
Definition: diseqc.h:347
uint_to_dbl_t GetPosMap(void) const
Definition: diseqc.cpp:1976
dvbdev_rotor_t m_type
Definition: diseqc.h:362
double GetProgress(void) const
Returns an indication of rotor progress.
Definition: diseqc.cpp:1941
uint GetVoltage(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) const override
Retrives the desired voltage for this config.
Definition: diseqc.cpp:1781
bool SetChild(uint ordinal, DiSEqCDevDevice *device) override
Changes the nth child of this node.
Definition: diseqc.cpp:1753
bool ExecuteRotor(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning, double angle)
Definition: diseqc.cpp:1995
~DiSEqCDevRotor() override
Definition: diseqc.cpp:1685
bool m_lastPosKnown
Definition: diseqc.h:375
bool IsMoving(const DiSEqCDevSettings &settings) const
Definition: diseqc.cpp:1772
bool m_reset
Definition: diseqc.h:371
static const TypeTableVec kRotorTypeTable
Definition: diseqc.h:379
static double CalculateAzimuth(double angle)
Definition: diseqc.cpp:2035
bool Store(void) const override
Stores this device to the database.
Definition: diseqc.cpp:1860
bool IsCommandNeeded(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) const override
Determines if this device or any child will be sending a command for the given configuration chain.
Definition: diseqc.cpp:1734
bool Execute(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) override
Applies DiSEqC settings to this node and any children.
Definition: diseqc.cpp:1690
void SetPosMap(const uint_to_dbl_t &posmap)
Definition: diseqc.cpp:1986
double m_moveTime
Definition: diseqc.h:374
void RotationComplete(void) const
Definition: diseqc.cpp:2074
dbl_to_uint_t m_posmap
Definition: diseqc.h:365
double m_speedHi
Definition: diseqc.h:363
double m_lastPosition
Definition: diseqc.h:369
bool IsPositionKnown(void) const
Returns true if there is reasonable confidence in the value returned by GetProgress().
Definition: diseqc.cpp:1971
double m_lastAzimuth
Definition: diseqc.h:376
DiSEqCDevDevice * m_child
Definition: diseqc.h:366
static QString RotorTypeToString(dvbdev_rotor_t type)
Definition: diseqc.h:345
bool Load(void) override
Loads this device from the database.
Definition: diseqc.cpp:1798
double GetApproxAzimuth(void) const
Definition: diseqc.cpp:2051
void Reset(void) override
Resets to the last known settings for this device.
Definition: diseqc.cpp:1727
double m_desiredAzimuth
Definition: diseqc.h:370
double m_speedLo
Definition: diseqc.h:364
DiSEqCDevDevice * GetSelectedChild(const DiSEqCDevSettings &setting) const override
Retrieves the selected child for this configuration, if any.
Definition: diseqc.cpp:1748
Unicable / SCR Class.
Definition: diseqc.h:383
bool SendCommand(uint cmd, uint repeats, cmd_vec_t &data) const
Definition: diseqc.cpp:2193
uint32_t GetIntermediateFrequency(uint32_t frequency) const
Definition: diseqc.cpp:2218
~DiSEqCDevSCR() override
Definition: diseqc.cpp:2094
uint m_scrFrequency
Definition: diseqc.h:438
uint GetVoltage(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) const override
Retrives the desired voltage for this config.
Definition: diseqc.cpp:2212
int m_scrPin
Definition: diseqc.h:439
uint m_scrUserband
Definition: diseqc.h:437
bool PowerOff(void) const
Definition: diseqc.cpp:2167
DiSEqCDevDevice * m_child
Definition: diseqc.h:441
void Reset(void) override
Resets to the last known settings for this device.
Definition: diseqc.cpp:2099
bool Store(void) const override
Stores this device to the database.
Definition: diseqc.cpp:2275
bool SetChild(uint ordinal, DiSEqCDevDevice *device) override
Changes the nth child of this node.
Definition: diseqc.cpp:2336
bool Load(void) override
Loads this device from the database.
Definition: diseqc.cpp:2224
static const TypeTableVec kSCRPositionTable
Definition: diseqc.h:443
bool Execute(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) override
Applies DiSEqC settings to this node and any children.
Definition: diseqc.cpp:2105
DVB-S device settings class.
Definition: diseqc.h:37
double GetValue(uint devid) const
Retrieves a value from this configuration chain by device id.
Definition: diseqc.cpp:206
void SetValue(uint devid, double value)
Sets a value for this configuration chain by device id.
Definition: diseqc.cpp:221
bool Store(uint card_input_id) const
Stores configuration chain to DB for specified card input id.
Definition: diseqc.cpp:164
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
Definition: diseqc.cpp:130
uint m_inputId
current input id
Definition: diseqc.h:48
uint_to_dbl_t m_config
Definition: diseqc.h:47
Switch class, including tone, legacy and DiSEqC switches.
Definition: diseqc.h:224
static QString SwitchTypeToString(dvbdev_switch_t type)
Definition: diseqc.h:270
uint m_lastHorizontal
Definition: diseqc.h:296
dvbdev_vec_t m_children
Definition: diseqc.h:297
DiSEqCDevDevice * GetChild(uint ordinal) override
Retrieves the nth child of this node.
Definition: diseqc.cpp:1177
DiSEqCDevSwitch(DiSEqCDevTree &tree, uint devid)
Definition: diseqc.cpp:1065
uint m_lastPos
Definition: diseqc.h:294
dvbdev_switch_t m_type
Definition: diseqc.h:291
DiSEqCDevDevice * GetSelectedChild(const DiSEqCDevSettings &settings) const override
Retrieves the selected child for this configuration, if any.
Definition: diseqc.cpp:1162
bool SetChild(uint ordinal, DiSEqCDevDevice *device) override
Changes the nth child of this node.
Definition: diseqc.cpp:1185
bool ShouldSwitch(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) const
Definition: diseqc.cpp:1550
@ kTypeDiSEqCUncommitted
Definition: diseqc.h:241
@ kTypeDiSEqCCommitted
Definition: diseqc.h:240
bool IsCommandNeeded(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) const override
Determines if this device or any child will be sending a command for the given configuration chain.
Definition: diseqc.cpp:1151
bool Execute(const DiSEqCDevSettings &, const DTVMultiplex &) override
Applies DiSEqC settings to this node and any children.
Definition: diseqc.cpp:1082
int GetPosition(const DiSEqCDevSettings &settings) const
Definition: diseqc.cpp:1642
static const TypeTableVec kSwitchTypeTable
Definition: diseqc.h:299
uint GetChildCount(void) const override
Retrieves the proper number of children for this node.
Definition: diseqc.cpp:1172
bool ExecuteTone(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning, uint pos)
Definition: diseqc.cpp:1497
bool ExecuteLegacy(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning, uint pos)
Definition: diseqc.cpp:1364
void SetNumPorts(uint num_ports)
Definition: diseqc.cpp:1341
bool Store(void) const override
Stores this device to the database.
Definition: diseqc.cpp:1276
static dvbdev_switch_t SwitchTypeFromString(const QString &type)
Definition: diseqc.h:272
~DiSEqCDevSwitch() override
Definition: diseqc.cpp:1076
uint m_lastHighBand
Definition: diseqc.h:295
bool Load(void) override
Loads this device from the database.
Definition: diseqc.cpp:1215
bool ExecuteMiniDiSEqC(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning, uint pos)
Definition: diseqc.cpp:1533
void Reset(void) override
Resets to the last known settings for this device.
Definition: diseqc.cpp:1139
bool ExecuteDiseqc(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning, uint pos)
Definition: diseqc.cpp:1595
bool ExecuteVoltage(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning, uint pos)
Definition: diseqc.cpp:1513
uint GetVoltage(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning) const override
Retrives the desired voltage for this config.
Definition: diseqc.cpp:1203
uint m_numPorts
Definition: diseqc.h:293
uint m_address
Definition: diseqc.h:292
DVB-S device tree class.
Definition: diseqc.h:75
uint GetVoltage(void) const
Definition: diseqc.h:116
bool Execute(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning)
Applies settings to the entire tree.
Definition: diseqc.cpp:511
bool Load(const QString &device)
Loads the device tree from the database.
Definition: diseqc.cpp:313
void AddDeferredDelete(uint dev_id)
Definition: diseqc.h:120
int GetFD(void) const
Definition: diseqc.h:109
void SetRoot(DiSEqCDevDevice *root)
Changes the root node of the tree.
Definition: diseqc.cpp:634
DiSEqCDevDevice * FindDevice(uint dev_id)
Returns a device by ID.
Definition: diseqc.cpp:622
uint CreateFakeDiSEqCID(void)
Definition: diseqc.h:121
void Reset(void)
Reset state of nodes in tree, forcing updates on the next Execute command.
Definition: diseqc.cpp:538
static const uint kFirstFakeDiSEqCID
Definition: diseqc.h:136
bool Store(uint cardid, const QString &device="")
Stores the device tree to the database.
Definition: diseqc.cpp:424
DiSEqCDevDevice * m_root
Definition: diseqc.h:131
bool ResetDiseqc(bool hard_reset, bool is_SCR)
Resets the DiSEqC bus.
Definition: diseqc.cpp:743
DiSEqCDevSCR * FindSCR(const DiSEqCDevSettings &settings)
Returns the SCR device object selected by the configuration chain.
Definition: diseqc.cpp:598
DiSEqCDevLNB * FindLNB(const DiSEqCDevSettings &settings)
Returns the LNB device object selected by the configuration chain.
Definition: diseqc.cpp:575
DiSEqCDevRotor * FindRotor(const DiSEqCDevSettings &settings, uint index=0)
Returns the nth rotor device object in the tree.
Definition: diseqc.cpp:552
bool SendCommand(uint adr, uint cmd, uint repeats, cmd_vec_t &data) const
Definition: diseqc.cpp:674
static bool Exists(int cardid)
Check if a Diseqc device tree exists.
Definition: diseqc.cpp:395
bool SetVoltage(uint voltage)
Definition: diseqc.cpp:800
bool SetTone(bool on) const
Definition: diseqc.cpp:484
int m_fdFrontend
Definition: diseqc.h:130
bool ApplyVoltage(const DiSEqCDevSettings &settings, const DTVMultiplex &tuning)
Definition: diseqc.cpp:844
uint m_lastVoltage
Definition: diseqc.h:132
std::vector< uint > m_delete
Definition: diseqc.h:134
bool IsInNeedOfConf(void) const
Definition: diseqc.cpp:836
void Open(int fd_frontend, bool is_SCR)
Retrieve device tree.
Definition: diseqc.cpp:792
Static-scoped locked tree list class.
Definition: diseqc.h:62
DiSEqCDevTree * FindTree(uint cardid)
Retrieve device tree.
Definition: diseqc.cpp:267
QMutex m_treesLock
Definition: diseqc.h:71
void InvalidateTrees(void)
Invalidate cached trees.
Definition: diseqc.cpp:285
cardid_to_diseqc_tree_t m_trees
Definition: diseqc.h:70
static DiSEqCDevTree * FindTree(uint cardid)
Retrieve device tree.
Definition: diseqc.cpp:239
static void InvalidateTrees(void)
Invalidate cached trees.
Definition: diseqc.cpp:247
static DiSEqCDevTrees s_trees
Definition: diseqc.h:58
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
QVariant lastInsertId()
Return the id of the last inserted row.
Definition: mythdbcon.cpp:936
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
unsigned int uint
Definition: compat.h:60
#define LOC
Definition: diseqc.cpp:95
static constexpr std::chrono::milliseconds DISEQC_POWER_ON_WAIT
Definition: diseqc.cpp:40
static constexpr std::chrono::milliseconds DISEQC_SHORT_WAIT
Definition: diseqc.cpp:38
static constexpr double EPS
Definition: diseqc.cpp:93
DISEQC_ADRS
Definition: diseqc.cpp:55
@ DISEQC_ADR_ALL
Definition: diseqc.cpp:56
@ DISEQC_ADR_SW
Definition: diseqc.cpp:61
@ DISEQC_ADR_SW_ALL
Definition: diseqc.cpp:57
@ DISEQC_ADR_LNB
Definition: diseqc.cpp:58
@ DISEQC_ADR_SMATV
Definition: diseqc.cpp:62
@ DISEQC_ADR_POS_EL
Definition: diseqc.cpp:67
@ DISEQC_ADR_LNB_SW
Definition: diseqc.cpp:59
@ DISEQC_ADR_POS_AZ
Definition: diseqc.cpp:66
@ DISEQC_ADR_SW_BLK
Definition: diseqc.cpp:60
@ DISEQC_ADR_POL_LIN
Definition: diseqc.cpp:64
@ DISEQC_ADR_POL_ALL
Definition: diseqc.cpp:63
@ DISEQC_ADR_POS_ALL
Definition: diseqc.cpp:65
bool diseqc_bus_already_reset
Definition: diseqc.cpp:97
static constexpr uint8_t SEC_VOLTAGE_18
Definition: diseqc.cpp:33
static constexpr uint8_t SEC_VOLTAGE_OFF
Definition: diseqc.cpp:34
static constexpr uint8_t SEC_VOLTAGE_13
Definition: diseqc.cpp:32
static constexpr double TO_DEC
Definition: diseqc.cpp:91
static double GetCurTimeFloating(void)
Definition: diseqc.cpp:1667
static constexpr std::chrono::milliseconds DISEQC_LONG_WAIT
Definition: diseqc.cpp:39
static constexpr std::chrono::milliseconds DISEQC_POWER_OFF_WAIT
Definition: diseqc.cpp:41
DISEQC_CMDS
Definition: diseqc.cpp:71
@ DISEQC_CMD_ODU
Definition: diseqc.cpp:77
@ DISEQC_CMD_DRIVE_E
Definition: diseqc.cpp:83
@ DISEQC_CMD_LMT_OFF
Definition: diseqc.cpp:80
@ DISEQC_CMD_STORE_POS
Definition: diseqc.cpp:85
@ DISEQC_CMD_GOTO_X
Definition: diseqc.cpp:87
@ DISEQC_CMD_WRITE_FREQ
Definition: diseqc.cpp:76
@ DISEQC_CMD_RESET
Definition: diseqc.cpp:72
@ DISEQC_CMD_GOTO_POS
Definition: diseqc.cpp:86
@ DISEQC_CMD_WRITE_N0
Definition: diseqc.cpp:74
@ DISEQC_CMD_ODU_MDU
Definition: diseqc.cpp:78
@ DISEQC_CMD_DRIVE_W
Definition: diseqc.cpp:84
@ DISEQC_CMD_LMT_W
Definition: diseqc.cpp:82
@ DISEQC_CMD_HALT
Definition: diseqc.cpp:79
@ DISEQC_CMD_LMT_E
Definition: diseqc.cpp:81
@ DISEQC_CMD_WRITE_N1
Definition: diseqc.cpp:75
@ DISEQC_CMD_CLR_RESET
Definition: diseqc.cpp:73
static constexpr double TO_RADS
Definition: diseqc.cpp:90
std::vector< uint8_t > cmd_vec_t
Definition: diseqc.h:34
QMap< uint, double > uint_to_dbl_t
Definition: diseqc.h:30
static constexpr double M_PI
Definition: goom_tools.h:9
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.