mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-08-30 08:51:10 +02:00
time_monotonic_ms values did wrap every ~24 days, leading to problems in code that did not cope with that. Instead of fixing all places where relative comparisons with time_monotonic_ms() are made, just use a bigger datatype. Convert all users to the new type.
2226 lines
55 KiB
C++
2226 lines
55 KiB
C++
/*
|
|
* $Id: frontend.cpp,v 1.55 2004/04/20 14:47:15 derget Exp $
|
|
*
|
|
* (C) 2002-2003 Andreas Oberritter <obi@tuxbox.org>
|
|
*
|
|
* Copyright (C) 2011 CoolStream International Ltd
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/* system c */
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <cmath>
|
|
/* zapit */
|
|
#include <config.h>
|
|
#include <zapit/debug.h>
|
|
#include <zapit/settings.h>
|
|
#include <zapit/getservices.h>
|
|
#include <connection/basicserver.h>
|
|
#include <zapit/client/msgtypes.h>
|
|
#include <zapit/frontend_c.h>
|
|
#include <zapit/satconfig.h>
|
|
#include <driver/abstime.h>
|
|
#include <linux/dvb/version.h>
|
|
|
|
extern transponder_list_t transponders;
|
|
extern int zapit_debug;
|
|
|
|
#define SOUTH 0
|
|
#define NORTH 1
|
|
#define EAST 0
|
|
#define WEST 1
|
|
#define USALS
|
|
|
|
#define CLEAR 0
|
|
// Common properties
|
|
#define FREQUENCY 1
|
|
#define MODULATION 2
|
|
#define INVERSION 3
|
|
// common to S/S2/C
|
|
#define SYMBOL_RATE 4
|
|
#define DELIVERY_SYSTEM 5
|
|
#define INNER_FEC 6
|
|
// DVB-S/S2 specific
|
|
#define PILOTS 7
|
|
#define ROLLOFF 8
|
|
// DVB-T specific
|
|
#define BANDWIDTH 4
|
|
#define CODE_RATE_HP 6
|
|
#define CODE_RATE_LP 7
|
|
#define TRANSMISSION_MODE 8
|
|
#define GUARD_INTERVAL 9
|
|
#define HIERARCHY 10
|
|
|
|
#define FE_COMMON_PROPS 2
|
|
#define FE_DVBS_PROPS 6
|
|
#define FE_DVBS2_PROPS 8
|
|
#define FE_DVBC_PROPS 6
|
|
#define FE_DVBT_PROPS 10
|
|
|
|
/* stolen from dvb.c from vlc */
|
|
static const struct dtv_property dvbs_cmdargs[] = {
|
|
{ DTV_CLEAR, {0,0,0}, { 0 },0 },
|
|
{ DTV_FREQUENCY, {0,0,0}, { 0 },0 },
|
|
{ DTV_MODULATION, {0,0,0}, { QPSK },0 },
|
|
{ DTV_INVERSION, {0,0,0}, { INVERSION_AUTO },0 },
|
|
{ DTV_SYMBOL_RATE, {0,0,0}, { 27500000 },0 },
|
|
{ DTV_DELIVERY_SYSTEM, {0,0,0}, { SYS_DVBS },0 },
|
|
{ DTV_INNER_FEC, {0,0,0}, { FEC_AUTO },0 },
|
|
{ DTV_TUNE, {0,0,0}, { 0 },0 },
|
|
};
|
|
|
|
static const struct dtv_property dvbs2_cmdargs[] = {
|
|
{ DTV_CLEAR, {0,0,0}, { 0 },0 },
|
|
{ DTV_FREQUENCY, {}, { 0 },0 },
|
|
{ DTV_MODULATION, {}, { PSK_8 } ,0},
|
|
{ DTV_INVERSION, {}, { INVERSION_AUTO } ,0},
|
|
{ DTV_SYMBOL_RATE, {}, { 27500000 } ,0},
|
|
{ DTV_DELIVERY_SYSTEM, {}, { SYS_DVBS2 } ,0},
|
|
{ DTV_INNER_FEC, {}, { FEC_AUTO } ,0},
|
|
{ DTV_PILOT, {}, { PILOT_AUTO } ,0},
|
|
{ DTV_ROLLOFF, {}, { ROLLOFF_AUTO } ,0},
|
|
{ DTV_TUNE, {}, { 0 } ,0 },
|
|
};
|
|
|
|
static const struct dtv_property dvbc_cmdargs[] = {
|
|
{ DTV_CLEAR, {0,0,0}, { 0 } ,0},
|
|
{ DTV_FREQUENCY, {}, { 0 } ,0},
|
|
{ DTV_MODULATION, {}, { QAM_AUTO } ,0},
|
|
{ DTV_INVERSION, {}, { INVERSION_AUTO } ,0},
|
|
{ DTV_SYMBOL_RATE, {}, { 27500000 } ,0},
|
|
{ DTV_DELIVERY_SYSTEM, {}, { SYS_DVBC_ANNEX_AC } ,0},
|
|
{ DTV_INNER_FEC, {}, { FEC_AUTO } ,0},
|
|
{ DTV_TUNE, {}, { 0 }, 0},
|
|
};
|
|
|
|
static const struct dtv_property dvbt_cmdargs[] = {
|
|
{ DTV_CLEAR, {0,0,0}, { 0 } ,0},
|
|
{ DTV_FREQUENCY, {}, { 0 } ,0},
|
|
{ DTV_MODULATION, {}, { QAM_AUTO } ,0},
|
|
{ DTV_INVERSION, {}, { INVERSION_AUTO } ,0},
|
|
{ DTV_BANDWIDTH_HZ, {}, { 8000000 } ,0},
|
|
{ DTV_DELIVERY_SYSTEM, {}, { SYS_DVBT } ,0},
|
|
{ DTV_CODE_RATE_HP, {}, { FEC_AUTO } ,0},
|
|
{ DTV_CODE_RATE_LP, {}, { FEC_AUTO } ,0},
|
|
{ DTV_TRANSMISSION_MODE,{}, { TRANSMISSION_MODE_AUTO}, 0},
|
|
{ DTV_GUARD_INTERVAL, {}, { GUARD_INTERVAL_AUTO}, 0},
|
|
{ DTV_HIERARCHY, {}, { HIERARCHY_AUTO }, 0},
|
|
{ DTV_TUNE, {}, { 0 }, 0},
|
|
};
|
|
|
|
#define diff(x,y) (max(x,y) - min(x,y))
|
|
|
|
#define FE_TIMER_INIT() \
|
|
int64_t timer_start; \
|
|
static uint32_t tmin = 2000, tmax = 0; \
|
|
uint32_t timer_msec = 0;
|
|
|
|
#define FE_TIMER_START() \
|
|
timer_start = time_monotonic_ms();
|
|
|
|
#define FE_TIMER_STOP(label) \
|
|
timer_msec = (uint32_t)(time_monotonic_ms() - \
|
|
timer_start); \
|
|
if(tmin > timer_msec) tmin = timer_msec; \
|
|
if(tmax < timer_msec) tmax = timer_msec; \
|
|
printf("[fe%d] %s: %u msec (min %u max %u)\n", \
|
|
fenumber, label, timer_msec, tmin, tmax);
|
|
|
|
#define SETCMD(c, d) { \
|
|
prop[cmdseq.num].cmd = (c); \
|
|
prop[cmdseq.num].u.data = (d); \
|
|
if (cmdseq.num++ > DTV_IOCTL_MAX_MSGS) { \
|
|
printf("ERROR: too many tuning commands on frontend %d/%d", adapter, fenumber);\
|
|
return; \
|
|
} \
|
|
}
|
|
|
|
// Internal Inner FEC representation
|
|
typedef enum dvb_fec {
|
|
fAuto,
|
|
f1_2,
|
|
f2_3,
|
|
f3_4,
|
|
f5_6,
|
|
f7_8,
|
|
f8_9,
|
|
f3_5,
|
|
f4_5,
|
|
f9_10,
|
|
fNone = 15
|
|
} dvb_fec_t;
|
|
|
|
#define TIME_STEP 200
|
|
#define TIMEOUT_MAX_MS (feTimeout*100)
|
|
|
|
/*********************************************************************************************************/
|
|
|
|
CFrontend::CFrontend(int Number, int Adapter)
|
|
{
|
|
printf("[fe%d] New frontend on adapter %d\n", Number, Adapter);
|
|
fd = -1;
|
|
fenumber = Number;
|
|
adapter = Adapter;
|
|
slave = false; //(Number != 0); //false;
|
|
standby = true;
|
|
locked = false;
|
|
usecount = 0;
|
|
|
|
femode = FE_MODE_INDEPENDENT;
|
|
masterkey = 0;
|
|
|
|
tuned = false;
|
|
uncommitedInput = 255;
|
|
|
|
currentDiseqc = 255;
|
|
config.diseqcType = NO_DISEQC;
|
|
config.diseqcRepeats = 0;
|
|
config.uni_scr = 0; /* the unicable SCR address 0-7 */
|
|
config.uni_qrg = 0; /* the unicable frequency in MHz */
|
|
config.uni_lnb = 0; /* for two-position switches */
|
|
config.highVoltage = false;
|
|
config.motorRotationSpeed = 0; //in 0.1 degrees per second
|
|
|
|
feTimeout = 40;
|
|
currentVoltage = SEC_VOLTAGE_OFF;
|
|
currentToneMode = SEC_TONE_ON;
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
deliverySystemMask = UNKNOWN_DS;
|
|
}
|
|
|
|
CFrontend::~CFrontend(void)
|
|
{
|
|
printf("[fe%d] close frontend fd %d\n", fenumber, fd);
|
|
if(fd >= 0)
|
|
Close();
|
|
}
|
|
|
|
bool CFrontend::Open(bool init)
|
|
{
|
|
if(!standby)
|
|
return false;
|
|
|
|
char filename[128];
|
|
snprintf(filename, sizeof(filename), "/dev/dvb/adapter%d/frontend%d", adapter, fenumber);
|
|
printf("[fe%d] open %s\n", fenumber, filename);
|
|
|
|
mutex.lock();
|
|
if (fd < 0) {
|
|
if ((fd = open(filename, O_RDWR | O_NONBLOCK | O_CLOEXEC)) < 0) {
|
|
ERROR(filename);
|
|
mutex.unlock();
|
|
return false;
|
|
}
|
|
|
|
getFEInfo();
|
|
}
|
|
|
|
currentTransponder.setTransponderId(0);
|
|
standby = false;
|
|
|
|
mutex.unlock();
|
|
|
|
if (init)
|
|
Init();
|
|
|
|
return true;
|
|
}
|
|
|
|
void CFrontend::getFEInfo(void)
|
|
{
|
|
fop(ioctl, FE_GET_INFO, &info);
|
|
|
|
//FIXME if (fenumber > 1) info.type = FE_QAM;
|
|
|
|
printf("[fe%d] frontend fd %d type %d\n", fenumber, fd, info.type);
|
|
bool legacy = true;
|
|
|
|
deliverySystemMask = UNKNOWN_DS;
|
|
|
|
#if (DVB_API_VERSION >= 5) && (DVB_API_VERSION_MINOR >= 5)
|
|
dtv_property prop[1];
|
|
dtv_properties cmdseq;
|
|
|
|
memset(prop, 0, sizeof(prop));
|
|
memset(&cmdseq, 0, sizeof(cmdseq));
|
|
cmdseq.props = prop;
|
|
|
|
SETCMD(DTV_ENUM_DELSYS, 0);
|
|
int ret = fop(ioctl, FE_GET_PROPERTY, &cmdseq);
|
|
if (ret == 0) {
|
|
for (uint32_t i = 0; i < prop[0].u.buffer.len; i++) {
|
|
if (i >= MAX_DELSYS) {
|
|
printf("ERROR: too many delivery systems on frontend %d/%d", adapter, fenumber);
|
|
break;
|
|
}
|
|
|
|
switch ((fe_delivery_system_t)prop[0].u.buffer.data[i]) {
|
|
case SYS_DVBC_ANNEX_A:
|
|
case SYS_DVBC_ANNEX_B:
|
|
case SYS_DVBC_ANNEX_C:
|
|
deliverySystemMask |= DVB_C;
|
|
break;
|
|
case SYS_DVBT:
|
|
deliverySystemMask |= DVB_T;
|
|
break;
|
|
case SYS_DVBT2:
|
|
deliverySystemMask |= DVB_T2;
|
|
break;
|
|
case SYS_DVBS:
|
|
deliverySystemMask |= DVB_S;
|
|
break;
|
|
case SYS_DVBS2:
|
|
deliverySystemMask |= DVB_S2;
|
|
break;
|
|
case SYS_DTMB:
|
|
deliverySystemMask |= DTMB;
|
|
break;
|
|
default:
|
|
printf("ERROR: too many delivery systems on frontend %d/%d", adapter, fenumber);
|
|
continue;
|
|
}
|
|
|
|
}
|
|
legacy = false;
|
|
} else {
|
|
printf("WARNING: can't query delivery systems on frontend %d/%d - falling back to legacy mode\n", adapter, fenumber);
|
|
}
|
|
#endif
|
|
if (legacy) {
|
|
// Legacy mode (DVB-API < 5.5):
|
|
switch (info.type) {
|
|
case FE_QPSK:
|
|
deliverySystemMask |= DVB_S;
|
|
#ifndef BOXMODEL_CS_HD1
|
|
if (info.caps & FE_CAN_2G_MODULATION)
|
|
#endif
|
|
deliverySystemMask |= DVB_S2;
|
|
break;
|
|
case FE_OFDM:
|
|
deliverySystemMask |= DVB_T;
|
|
#ifdef SYS_DVBT2
|
|
if (info.caps & FE_CAN_2G_MODULATION)
|
|
deliverySystemMask |= DVB_T2;
|
|
#endif
|
|
break;
|
|
case FE_QAM:
|
|
deliverySystemMask |= DVB_C;
|
|
break;
|
|
default:
|
|
printf("ERROR: unknown frontend type %d on frontend %d/%d", info.type, adapter, fenumber);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CFrontend::Init(void)
|
|
{
|
|
mutex.lock();
|
|
// Set the voltage to On, and wait voltage to become stable
|
|
// and wait for diseqc equipment to be ready.
|
|
secSetVoltage(SEC_VOLTAGE_13, 100);
|
|
secSetTone(SEC_TONE_OFF, 20);
|
|
setDiseqcType((diseqc_t) config.diseqcType, true);
|
|
setTsidOnid(0);
|
|
mutex.unlock();
|
|
}
|
|
|
|
void CFrontend::Close(void)
|
|
{
|
|
if(standby)
|
|
return;
|
|
|
|
printf("[fe%d] close frontend\n", fenumber);
|
|
|
|
if (!slave && config.diseqcType > MINI_DISEQC)
|
|
sendDiseqcStandby();
|
|
// Disable tone first as it's imposed on voltage
|
|
secSetTone(SEC_TONE_OFF, 20);
|
|
// Disable voltage immediately and wait 50ms to prevent
|
|
// a fast power-off -> power-on sequence where diseqc equipment
|
|
// might not reset properly.
|
|
secSetVoltage(SEC_VOLTAGE_OFF, 50);
|
|
|
|
tuned = false;
|
|
standby = true;;
|
|
close(fd);
|
|
fd = -1;
|
|
}
|
|
|
|
void CFrontend::setMasterSlave(bool _slave)
|
|
{
|
|
if(slave == _slave)
|
|
return;
|
|
|
|
if(_slave) {
|
|
// Disable tone first as it's imposed on voltage
|
|
secSetTone(SEC_TONE_OFF, 20);
|
|
// Disable voltage immediately and wait 50ms to prevent
|
|
// a fast power-off -> power-on sequence where diseqc equipment
|
|
// might not reset properly.
|
|
secSetVoltage(SEC_VOLTAGE_OFF, 50);
|
|
}
|
|
slave = _slave;
|
|
if(!slave)
|
|
Init();
|
|
}
|
|
|
|
void CFrontend::reset(void)
|
|
{
|
|
// No-op
|
|
}
|
|
|
|
void CFrontend::Lock()
|
|
{
|
|
usecount++;
|
|
INFO("[fe%d] usecount %d tp %" PRIx64 "\n", fenumber, usecount, getTsidOnid());
|
|
}
|
|
|
|
void CFrontend::Unlock()
|
|
{
|
|
if(usecount > 0)
|
|
usecount--;
|
|
INFO("[fe%d] usecount %d tp %" PRIx64 "\n", fenumber, usecount, getTsidOnid());
|
|
}
|
|
|
|
fe_code_rate_t CFrontend::getCFEC()
|
|
{
|
|
if (isSat(currentTransponder.feparams.delsys) || isCable(currentTransponder.feparams.delsys))
|
|
return currentTransponder.feparams.fec_inner;
|
|
else
|
|
return FEC_NONE;
|
|
}
|
|
|
|
fe_code_rate_t CFrontend::getCodeRate(const uint8_t fec_inner, delivery_system_t delsys)
|
|
{
|
|
dvb_fec_t f = (dvb_fec_t) fec_inner;
|
|
fe_code_rate_t fec;
|
|
|
|
if (delsys == DVB_S || delsys == DVB_C || delsys == DVB_T) {
|
|
switch (f) {
|
|
case fNone:
|
|
fec = FEC_NONE;
|
|
break;
|
|
case f1_2:
|
|
fec = FEC_1_2;
|
|
break;
|
|
case f2_3:
|
|
fec = FEC_2_3;
|
|
break;
|
|
case f3_4:
|
|
fec = FEC_3_4;
|
|
break;
|
|
case f5_6:
|
|
fec = FEC_5_6;
|
|
break;
|
|
case f7_8:
|
|
fec = FEC_7_8;
|
|
break;
|
|
default:
|
|
if (zapit_debug)
|
|
printf("no valid fec for DVB-%c set.. assume auto\n", (delsys == DVB_S ? 'S' : (delsys == DVB_C ? 'C' : 'T')));
|
|
/* fall through */
|
|
case fAuto:
|
|
fec = FEC_AUTO;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (f) {
|
|
case f1_2:
|
|
fec = FEC_1_2;
|
|
break;
|
|
case f2_3:
|
|
fec = FEC_2_3;
|
|
break;
|
|
case f3_4:
|
|
fec = FEC_3_4;
|
|
break;
|
|
case f3_5:
|
|
fec = FEC_3_5;
|
|
break;
|
|
case f4_5:
|
|
fec = FEC_4_5;
|
|
break;
|
|
case f5_6:
|
|
fec = FEC_5_6;
|
|
break;
|
|
case f7_8:
|
|
fec = FEC_7_8;
|
|
break;
|
|
case f8_9:
|
|
fec = FEC_8_9;
|
|
break;
|
|
case f9_10:
|
|
fec = FEC_9_10;
|
|
break;
|
|
default:
|
|
if (zapit_debug)
|
|
printf("no valid fec for DVB-S2 set.. !!\n");
|
|
/* fall through */
|
|
case fAuto:
|
|
fec = FEC_AUTO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fec;
|
|
}
|
|
|
|
fe_hierarchy_t CFrontend::getHierarchy(const uint8_t hierarchy)
|
|
{
|
|
switch (hierarchy) {
|
|
case 0x00:
|
|
return HIERARCHY_NONE;
|
|
case 0x01:
|
|
return HIERARCHY_1;
|
|
case 0x02:
|
|
return HIERARCHY_2;
|
|
case 0x03:
|
|
return HIERARCHY_4;
|
|
default:
|
|
return HIERARCHY_AUTO;
|
|
}
|
|
}
|
|
|
|
fe_rolloff_t CFrontend::getRolloff(const uint8_t rolloff)
|
|
{
|
|
switch (rolloff) {
|
|
case 0x00:
|
|
return ROLLOFF_35;
|
|
case 0x01:
|
|
return ROLLOFF_25;
|
|
case 0x02:
|
|
return ROLLOFF_20;
|
|
default:
|
|
return ROLLOFF_AUTO;
|
|
}
|
|
}
|
|
|
|
fe_modulation_t CFrontend::getModulation(const uint8_t modulation)
|
|
{
|
|
switch (modulation) {
|
|
case 0x00:
|
|
return QPSK;
|
|
case 0x01:
|
|
return QAM_16;
|
|
case 0x02:
|
|
return QAM_32;
|
|
case 0x03:
|
|
return QAM_64;
|
|
case 0x04:
|
|
return QAM_128;
|
|
case 0x05:
|
|
return QAM_256;
|
|
default:
|
|
return QAM_AUTO;
|
|
}
|
|
}
|
|
|
|
fe_bandwidth_t CFrontend::getBandwidth(const uint8_t bandwidth)
|
|
{
|
|
switch (bandwidth) {
|
|
case 0x00:
|
|
return BANDWIDTH_8_MHZ;
|
|
case 0x01:
|
|
return BANDWIDTH_7_MHZ;
|
|
case 0x02:
|
|
return BANDWIDTH_6_MHZ;
|
|
case 0x03:
|
|
return BANDWIDTH_5_MHZ;
|
|
default:
|
|
return BANDWIDTH_AUTO; // AUTO
|
|
}
|
|
}
|
|
|
|
fe_guard_interval_t CFrontend::getGuardInterval(const uint8_t guard_interval)
|
|
{
|
|
switch (guard_interval) {
|
|
case 0x00:
|
|
return GUARD_INTERVAL_1_32;
|
|
case 0x01:
|
|
return GUARD_INTERVAL_1_16;
|
|
case 0x02:
|
|
return GUARD_INTERVAL_1_8;
|
|
case 0x03:
|
|
return GUARD_INTERVAL_1_4;
|
|
default:
|
|
return GUARD_INTERVAL_AUTO;
|
|
}
|
|
}
|
|
|
|
fe_modulation_t CFrontend::getConstellation(const uint8_t constellation)
|
|
{
|
|
switch (constellation) {
|
|
case 0x00:
|
|
return QPSK;
|
|
case 0x01:
|
|
return QAM_16;
|
|
case 0x02:
|
|
return QAM_64;
|
|
default:
|
|
return QAM_AUTO;
|
|
}
|
|
}
|
|
|
|
fe_transmit_mode_t CFrontend::getTransmissionMode(const uint8_t transmission_mode)
|
|
{
|
|
switch (transmission_mode) {
|
|
case 0x00:
|
|
return TRANSMISSION_MODE_2K;
|
|
case 0x01:
|
|
return TRANSMISSION_MODE_8K;
|
|
case 0x02:
|
|
return TRANSMISSION_MODE_4K;
|
|
default:
|
|
return TRANSMISSION_MODE_AUTO;
|
|
}
|
|
}
|
|
|
|
uint8_t CFrontend::getPolarization(void) const
|
|
{
|
|
return currentTransponder.getPolarization();
|
|
}
|
|
|
|
uint32_t CFrontend::getRate() const
|
|
{
|
|
return currentTransponder.getSymbolRate();
|
|
}
|
|
|
|
fe_status_t CFrontend::getStatus(void) const
|
|
{
|
|
struct dvb_frontend_event event;
|
|
event.status = FE_REINIT;
|
|
fop(ioctl, FE_READ_STATUS, &event.status);
|
|
return (fe_status_t) (event.status & FE_HAS_LOCK);
|
|
}
|
|
|
|
#if 0
|
|
//never used
|
|
FrontendParameters CFrontend::getFrontend(void) const
|
|
{
|
|
return currentTransponder.feparams;
|
|
}
|
|
#endif
|
|
|
|
uint32_t CFrontend::getBitErrorRate(void) const
|
|
{
|
|
uint32_t ber = 0;
|
|
fop(ioctl, FE_READ_BER, &ber);
|
|
|
|
return ber;
|
|
}
|
|
|
|
uint16_t CFrontend::getSignalStrength(void) const
|
|
{
|
|
uint16_t strength = 0;
|
|
fop(ioctl, FE_READ_SIGNAL_STRENGTH, &strength);
|
|
|
|
return strength;
|
|
}
|
|
|
|
uint16_t CFrontend::getSignalNoiseRatio(void) const
|
|
{
|
|
uint16_t snr = 0;
|
|
fop(ioctl, FE_READ_SNR, &snr);
|
|
return snr;
|
|
}
|
|
|
|
#if 0
|
|
//never used
|
|
uint32_t CFrontend::getUncorrectedBlocks(void) const
|
|
{
|
|
uint32_t blocks = 0;
|
|
fop(ioctl, FE_READ_UNCORRECTED_BLOCKS, &blocks);
|
|
|
|
return blocks;
|
|
}
|
|
#endif
|
|
|
|
struct dvb_frontend_event CFrontend::getEvent(void)
|
|
{
|
|
struct dvb_frontend_event event;
|
|
struct pollfd pfd;
|
|
static unsigned int timedout = 0;
|
|
|
|
FE_TIMER_INIT();
|
|
|
|
pfd.fd = fd;
|
|
pfd.events = POLLIN | POLLPRI;
|
|
pfd.revents = 0;
|
|
|
|
memset(&event, 0, sizeof(struct dvb_frontend_event));
|
|
|
|
FE_TIMER_START();
|
|
|
|
while ((int) timer_msec < TIMEOUT_MAX_MS) {
|
|
int ret = poll(&pfd, 1, TIMEOUT_MAX_MS - timer_msec);
|
|
if (ret < 0) {
|
|
perror("CFrontend::getEvent poll");
|
|
continue;
|
|
}
|
|
if (ret == 0) {
|
|
FE_TIMER_STOP("############################## poll timeout, time");
|
|
continue;
|
|
}
|
|
|
|
if (pfd.revents & (POLLIN | POLLPRI)) {
|
|
FE_TIMER_STOP("poll has event after");
|
|
memset(&event, 0, sizeof(struct dvb_frontend_event));
|
|
|
|
ret = ioctl(fd, FE_GET_EVENT, &event);
|
|
if (ret < 0) {
|
|
perror("CFrontend::getEvent ioctl");
|
|
continue;
|
|
}
|
|
//printf("[fe%d] poll events %d status %x\n", fenumber, pfd.revents, event.status);
|
|
|
|
if (event.status & FE_HAS_LOCK) {
|
|
printf("[fe%d] ****************************** FE_HAS_LOCK: freq %lu\n", fenumber, (long unsigned int)event.parameters.frequency);
|
|
tuned = true;
|
|
break;
|
|
} else if (event.status & FE_TIMEDOUT) {
|
|
if(timedout < timer_msec)
|
|
timedout = timer_msec;
|
|
printf("[fe%d] ############################## FE_TIMEDOUT (max %u)\n", fenumber, timedout);
|
|
/*break;*/
|
|
} else {
|
|
if (event.status & FE_HAS_SIGNAL)
|
|
printf("[fe%d] FE_HAS_SIGNAL\n", fenumber);
|
|
if (event.status & FE_HAS_CARRIER)
|
|
printf("[fe%d] FE_HAS_CARRIER\n", fenumber);
|
|
if (event.status & FE_HAS_VITERBI)
|
|
printf("[fe%d] FE_HAS_VITERBI\n", fenumber);
|
|
if (event.status & FE_HAS_SYNC)
|
|
printf("[fe%d] FE_HAS_SYNC\n", fenumber);
|
|
if (event.status & FE_REINIT)
|
|
printf("[fe%d] FE_REINIT\n", fenumber);
|
|
/* msec = TIME_STEP; */
|
|
}
|
|
} else if (pfd.revents & POLLHUP) {
|
|
FE_TIMER_STOP("poll hup after");
|
|
reset();
|
|
}
|
|
}
|
|
//printf("[fe%d] event after: %d\n", fenumber, tmsec);
|
|
return event;
|
|
}
|
|
|
|
void CFrontend::getDelSys(int f, int m, char *&fec, char *&sys, char *&mod)
|
|
{
|
|
return getDelSys(getCurrentDeliverySystem(), f, m, fec, sys, mod);
|
|
}
|
|
|
|
void CFrontend::getXMLDelsysFEC(fe_code_rate_t xmlfec, delivery_system_t & delsys, fe_modulation_t &mod, fe_code_rate_t & fec)
|
|
{
|
|
if ((int)xmlfec < FEC_S2_QPSK_1_2) {
|
|
delsys = DVB_S;
|
|
mod = QPSK;
|
|
} else if ((int)xmlfec < FEC_S2_8PSK_1_2) {
|
|
delsys = DVB_S2;
|
|
mod = QPSK;
|
|
} else {
|
|
delsys = DVB_S2;
|
|
mod = PSK_8;
|
|
}
|
|
|
|
switch ((int)xmlfec) {
|
|
case FEC_1_2:
|
|
case FEC_S2_QPSK_1_2:
|
|
case FEC_S2_8PSK_1_2:
|
|
fec = FEC_1_2;
|
|
break;
|
|
case FEC_2_3:
|
|
case FEC_S2_QPSK_2_3:
|
|
case FEC_S2_8PSK_2_3:
|
|
fec = FEC_2_3;
|
|
break;
|
|
case FEC_3_4:
|
|
case FEC_S2_QPSK_3_4:
|
|
case FEC_S2_8PSK_3_4:
|
|
fec = FEC_3_4;
|
|
break;
|
|
case FEC_S2_QPSK_3_5:
|
|
case FEC_S2_8PSK_3_5:
|
|
fec = FEC_3_5;
|
|
break;
|
|
case FEC_4_5:
|
|
case FEC_S2_QPSK_4_5:
|
|
case FEC_S2_8PSK_4_5:
|
|
fec = FEC_4_5;
|
|
break;
|
|
case FEC_5_6:
|
|
case FEC_S2_QPSK_5_6:
|
|
case FEC_S2_8PSK_5_6:
|
|
fec = FEC_5_6;
|
|
break;
|
|
case FEC_6_7:
|
|
fec = FEC_6_7;
|
|
break;
|
|
case FEC_7_8:
|
|
case FEC_S2_QPSK_7_8:
|
|
case FEC_S2_8PSK_7_8:
|
|
fec = FEC_7_8;
|
|
break;
|
|
case FEC_8_9:
|
|
case FEC_S2_QPSK_8_9:
|
|
case FEC_S2_8PSK_8_9:
|
|
fec = FEC_8_9;
|
|
break;
|
|
case FEC_S2_QPSK_9_10:
|
|
case FEC_S2_8PSK_9_10:
|
|
fec = FEC_9_10;
|
|
break;
|
|
default:
|
|
printf("[frontend] getXMLDelsysFEC: unknown FEC: %d !!!\n", xmlfec);
|
|
/* fall through */
|
|
case FEC_S2_AUTO:
|
|
case FEC_AUTO:
|
|
fec = FEC_AUTO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CFrontend::getDelSys(delivery_system_t delsys, int f, int m, char *&fec, char *&sys, char *&mod)
|
|
{
|
|
switch (delsys) {
|
|
case DVB_S:
|
|
sys = (char *)"DVB";
|
|
mod = (char *)"QPSK";
|
|
break;
|
|
case DVB_S2:
|
|
sys = (char *)"DVB-S2";
|
|
switch (m) {
|
|
case QPSK:
|
|
mod = (char *)"QPSK";
|
|
break;
|
|
case PSK_8:
|
|
mod = (char *)"8PSK";
|
|
break;
|
|
default:
|
|
printf("[frontend] unknown modulation %d!\n", m);
|
|
mod = (char *)"UNKNOWN";
|
|
}
|
|
break;
|
|
case DVB_C:
|
|
case DVB_T:
|
|
case DTMB:
|
|
switch(delsys) {
|
|
case DVB_C:
|
|
sys = (char *)"DVB-C(Annex A)";
|
|
break;
|
|
case DVB_T:
|
|
sys = (char *)"DVB-T";
|
|
break;
|
|
case DVB_T2:
|
|
sys = (char *)"DVB-T2";
|
|
break;
|
|
case DTMB:
|
|
sys = (char *)"DTMB";
|
|
break;
|
|
default:
|
|
printf("[frontend] unknown delsys %d!\n", delsys);
|
|
sys = (char *)"UNKNOWN";
|
|
break;
|
|
}
|
|
|
|
switch (m) {
|
|
case QAM_16:
|
|
mod = (char *)"QAM_16";
|
|
break;
|
|
case QAM_32:
|
|
mod = (char *)"QAM_32";
|
|
break;
|
|
case QAM_64:
|
|
mod = (char *)"QAM_64";
|
|
break;
|
|
case QAM_128:
|
|
mod = (char *)"QAM_128";
|
|
break;
|
|
case QAM_256:
|
|
mod = (char *)"QAM_256";
|
|
break;
|
|
case QAM_4_NR:
|
|
mod = (char *)"QAM_4_NR";
|
|
break;
|
|
case QPSK:
|
|
if (delsys == DVB_T || delsys == DVB_T2 || delsys == DTMB) {
|
|
mod = (char *)"QPSK"; // AKA QAM_4
|
|
break;
|
|
}
|
|
/* fall through */
|
|
case QAM_AUTO:
|
|
default:
|
|
mod = (char *)"QAM_AUTO";
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
printf("[frontend] unknown delsys %d!\n", delsys);
|
|
sys = (char *)"UNKNOWN";
|
|
mod = (char *)"UNKNOWN";
|
|
break;
|
|
}
|
|
|
|
switch (f) {
|
|
case FEC_1_2:
|
|
fec = (char *)"1/2";
|
|
break;
|
|
case FEC_2_3:
|
|
fec = (char *)"2/3";
|
|
break;
|
|
case FEC_3_4:
|
|
fec = (char *)"3/4";
|
|
break;
|
|
case FEC_4_5:
|
|
fec = (char *)"4/5";
|
|
break;
|
|
case FEC_5_6:
|
|
fec = (char *)"5/6";
|
|
break;
|
|
case FEC_6_7:
|
|
fec = (char *)"6/7";
|
|
break;
|
|
case FEC_7_8:
|
|
fec = (char *)"7/8";
|
|
break;
|
|
case FEC_8_9:
|
|
fec = (char *)"8/9";
|
|
break;
|
|
case FEC_3_5:
|
|
fec = (char *)"3/5";
|
|
break;
|
|
case FEC_9_10:
|
|
fec = (char *)"9/10";
|
|
break;
|
|
case FEC_2_5:
|
|
fec = (char *)"2/3";
|
|
break;
|
|
default:
|
|
printf("[frontend] getDelSys: unknown FEC: %d !!!\n", f);
|
|
case FEC_AUTO:
|
|
fec = (char *)"AUTO";
|
|
break;
|
|
}
|
|
}
|
|
|
|
fe_delivery_system_t CFrontend::getFEDeliverySystem(delivery_system_t Delsys)
|
|
{
|
|
fe_delivery_system_t delsys;
|
|
|
|
switch (Delsys) {
|
|
case DVB_S:
|
|
delsys = SYS_DVBS;
|
|
break;
|
|
case DVB_S2:
|
|
delsys = SYS_DVBS2;
|
|
break;
|
|
case DVB_T:
|
|
delsys = SYS_DVBT;
|
|
break;
|
|
case DVB_T2:
|
|
delsys = SYS_DVBT2;
|
|
break;
|
|
case DVB_C:
|
|
delsys = SYS_DVBC_ANNEX_A;
|
|
break;
|
|
//case DVB_C2: // not supported yet
|
|
// delsys = SYS_DVBC2;
|
|
//break;
|
|
case ISDBT:
|
|
delsys = SYS_ISDBT;
|
|
break;
|
|
case ISDBC:
|
|
delsys = SYS_ISDBC;
|
|
break;
|
|
case ISDBS:
|
|
delsys = SYS_ISDBS;
|
|
break;
|
|
case DTMB:
|
|
delsys = SYS_DTMB;
|
|
break;
|
|
case DSS:
|
|
delsys = SYS_DSS;
|
|
break;
|
|
case TURBO:
|
|
delsys = SYS_TURBO;
|
|
break;
|
|
default:
|
|
delsys = SYS_UNDEFINED;
|
|
break;
|
|
}
|
|
|
|
return delsys;
|
|
}
|
|
|
|
delivery_system_t CFrontend::getZapitDeliverySystem(uint32_t delnr)
|
|
{
|
|
return (delivery_system_t)ZAPIT_DS_BIT_MASK(delnr);
|
|
}
|
|
|
|
uint32_t CFrontend::getXMLDeliverySystem(delivery_system_t delsys)
|
|
{
|
|
// WARNING: this nr is directly mapped to the bit mask specified in frontend_types.h
|
|
uint32_t delnr = 0;
|
|
|
|
switch (delsys) {
|
|
case DVB_S:
|
|
delnr = 0;
|
|
break;
|
|
case DVB_S2:
|
|
delnr = 1;
|
|
break;
|
|
case DVB_C:
|
|
delnr = 2;
|
|
break;
|
|
//case DVB_C2: // not supported yet
|
|
// delnr = SYS_DVBC2;
|
|
//break;
|
|
case DVB_T:
|
|
delnr = 4;
|
|
break;
|
|
case DVB_T2:
|
|
delnr = 5;
|
|
break;
|
|
case DTMB:
|
|
delnr = 6;
|
|
break;
|
|
case DSS:
|
|
delnr = 7;
|
|
break;
|
|
case TURBO:
|
|
delnr = 8;
|
|
break;
|
|
case ISDBS:
|
|
delnr = 9;
|
|
break;
|
|
case ISDBC:
|
|
delnr = 10;
|
|
break;
|
|
case ISDBT:
|
|
delnr = 11;
|
|
break;
|
|
default:
|
|
printf("%s: unknown delivery system (%d)\n", __FUNCTION__, delsys);
|
|
delnr = 0;
|
|
break;
|
|
}
|
|
|
|
return delnr;
|
|
}
|
|
|
|
uint32_t CFrontend::getFEBandwidth(fe_bandwidth_t bandwidth)
|
|
{
|
|
uint32_t bandwidth_hz;
|
|
|
|
switch (bandwidth) {
|
|
case BANDWIDTH_8_MHZ:
|
|
default:
|
|
bandwidth_hz = 8000000;
|
|
break;
|
|
case BANDWIDTH_7_MHZ:
|
|
bandwidth_hz = 7000000;
|
|
break;
|
|
case BANDWIDTH_6_MHZ:
|
|
bandwidth_hz = 6000000;
|
|
break;
|
|
case BANDWIDTH_5_MHZ:
|
|
bandwidth_hz = 5000000;
|
|
break;
|
|
}
|
|
|
|
return bandwidth_hz;
|
|
}
|
|
|
|
bool CFrontend::buildProperties(const FrontendParameters *feparams, struct dtv_properties& cmdseq)
|
|
{
|
|
fe_pilot_t pilot = PILOT_OFF;
|
|
int fec;
|
|
fe_code_rate_t fec_inner = feparams->fec_inner;
|
|
|
|
/* cast to int is ncesessary because many of the FEC_S2 values are not
|
|
* properly defined in the enum, thus the compiler complains... :-( */
|
|
switch ((int)fec_inner) {
|
|
case FEC_1_2:
|
|
fec = FEC_1_2;
|
|
break;
|
|
case FEC_2_3:
|
|
fec = FEC_2_3;
|
|
if (feparams->delsys == DVB_S2 && feparams->modulation == PSK_8)
|
|
pilot = PILOT_ON;
|
|
break;
|
|
case FEC_3_4:
|
|
fec = FEC_3_4;
|
|
if (feparams->delsys == DVB_S2 && feparams->modulation == PSK_8)
|
|
pilot = PILOT_ON;
|
|
break;
|
|
case FEC_4_5:
|
|
fec = FEC_4_5;
|
|
break;
|
|
case FEC_5_6:
|
|
fec = FEC_5_6;
|
|
if (feparams->delsys == DVB_S2 && feparams->modulation == PSK_8)
|
|
pilot = PILOT_ON;
|
|
break;
|
|
case FEC_6_7:
|
|
fec = FEC_6_7;
|
|
break;
|
|
case FEC_7_8:
|
|
fec = FEC_7_8;
|
|
break;
|
|
case FEC_8_9:
|
|
fec = FEC_8_9;
|
|
break;
|
|
case FEC_3_5:
|
|
fec = FEC_3_5;
|
|
if (feparams->delsys == DVB_S2 && feparams->modulation == PSK_8)
|
|
pilot = PILOT_ON;
|
|
break;
|
|
case FEC_9_10:
|
|
fec = FEC_9_10;
|
|
break;
|
|
case FEC_2_5:
|
|
fec = FEC_2_5;
|
|
break;
|
|
default:
|
|
printf("[fe%d] DEMOD: unknown FEC: %d\n", fenumber, fec_inner);
|
|
case FEC_AUTO:
|
|
fec = FEC_AUTO;
|
|
break;
|
|
}
|
|
switch(feparams->pilot) {
|
|
case ZPILOT_ON:
|
|
pilot = PILOT_ON;
|
|
break;
|
|
case ZPILOT_OFF:
|
|
pilot = PILOT_OFF;
|
|
break;
|
|
case ZPILOT_AUTO:
|
|
pilot = PILOT_AUTO;
|
|
break;
|
|
case ZPILOT_AUTO_SW:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
int nrOfProps = 0;
|
|
|
|
switch (feparams->delsys) {
|
|
case DVB_S:
|
|
case DVB_S2:
|
|
if (feparams->delsys == DVB_S2) {
|
|
nrOfProps = FE_DVBS2_PROPS;
|
|
memcpy(cmdseq.props, dvbs2_cmdargs, sizeof(dvbs2_cmdargs));
|
|
|
|
cmdseq.props[MODULATION].u.data = feparams->modulation;
|
|
cmdseq.props[ROLLOFF].u.data = feparams->rolloff;
|
|
cmdseq.props[PILOTS].u.data = pilot;
|
|
printf("[fe%d] tuner pilot %d (feparams %d)\n", fenumber, pilot, feparams->pilot);
|
|
} else {
|
|
memcpy(cmdseq.props, dvbs_cmdargs, sizeof(dvbs_cmdargs));
|
|
nrOfProps = FE_DVBS_PROPS;
|
|
}
|
|
cmdseq.props[FREQUENCY].u.data = feparams->frequency;
|
|
cmdseq.props[SYMBOL_RATE].u.data= feparams->symbol_rate;
|
|
cmdseq.props[INNER_FEC].u.data = fec; /*_inner*/ ;
|
|
break;
|
|
case DVB_C:
|
|
memcpy(cmdseq.props, dvbc_cmdargs, sizeof(dvbc_cmdargs));
|
|
cmdseq.props[FREQUENCY].u.data = feparams->frequency;
|
|
cmdseq.props[MODULATION].u.data = feparams->modulation;
|
|
cmdseq.props[SYMBOL_RATE].u.data= feparams->symbol_rate;
|
|
cmdseq.props[INNER_FEC].u.data = fec_inner;
|
|
nrOfProps = FE_DVBC_PROPS;
|
|
break;
|
|
case DVB_T:
|
|
case DVB_T2:
|
|
case DTMB:
|
|
memcpy(cmdseq.props, dvbt_cmdargs, sizeof(dvbt_cmdargs));
|
|
nrOfProps = FE_DVBT_PROPS;
|
|
cmdseq.props[FREQUENCY].u.data = feparams->frequency;
|
|
cmdseq.props[MODULATION].u.data = feparams->modulation;
|
|
cmdseq.props[INVERSION].u.data = feparams->inversion;
|
|
cmdseq.props[CODE_RATE_HP].u.data = feparams->code_rate_HP;
|
|
cmdseq.props[CODE_RATE_LP].u.data = feparams->code_rate_LP;
|
|
cmdseq.props[TRANSMISSION_MODE].u.data = feparams->transmission_mode;
|
|
cmdseq.props[GUARD_INTERVAL].u.data = feparams->guard_interval;
|
|
cmdseq.props[HIERARCHY].u.data = feparams->hierarchy;
|
|
cmdseq.props[DELIVERY_SYSTEM].u.data = getFEDeliverySystem(feparams->delsys);
|
|
cmdseq.props[BANDWIDTH].u.data = getFEBandwidth(feparams->bandwidth);
|
|
break;
|
|
default:
|
|
printf("frontend: unknown frontend type, exiting\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
if (config.diseqcType == DISEQC_UNICABLE)
|
|
cmdseq.props[FREQUENCY].u.data = sendEN50494TuningCommand(feparams->frequency,
|
|
currentToneMode == SEC_TONE_ON,
|
|
currentVoltage == SEC_VOLTAGE_18,
|
|
!!config.uni_lnb);
|
|
|
|
if (config.diseqcType == DISEQC_UNICABLE2)
|
|
cmdseq.props[FREQUENCY].u.data = sendEN50607TuningCommand(feparams->frequency,
|
|
currentToneMode == SEC_TONE_ON,
|
|
currentVoltage == SEC_VOLTAGE_18,
|
|
config.uni_lnb);
|
|
|
|
cmdseq.num += nrOfProps;
|
|
|
|
return true;
|
|
}
|
|
|
|
int CFrontend::setFrontend(const FrontendParameters *feparams, bool nowait)
|
|
{
|
|
struct dtv_property cmdargs[FE_COMMON_PROPS + FE_DVBT_PROPS]; // WARNING: increase when needed more space
|
|
struct dtv_properties cmdseq;
|
|
|
|
cmdseq.num = FE_COMMON_PROPS;
|
|
cmdseq.props = cmdargs;
|
|
|
|
tuned = false;
|
|
|
|
struct dvb_frontend_event ev;
|
|
{
|
|
// Erase previous events
|
|
while (1) {
|
|
if (ioctl(fd, FE_GET_EVENT, &ev) < 0)
|
|
break;
|
|
//printf("[fe%d] DEMOD: event status %d\n", fenumber, ev.status);
|
|
}
|
|
}
|
|
|
|
if (!buildProperties(feparams, cmdseq))
|
|
return 0;
|
|
|
|
{
|
|
FE_TIMER_INIT();
|
|
FE_TIMER_START();
|
|
if ((ioctl(fd, FE_SET_PROPERTY, &cmdseq)) < 0) {
|
|
perror("FE_SET_PROPERTY failed");
|
|
return false;
|
|
}
|
|
FE_TIMER_STOP("FE_SET_PROPERTY took");
|
|
}
|
|
if (nowait)
|
|
return 0;
|
|
{
|
|
FE_TIMER_INIT();
|
|
FE_TIMER_START();
|
|
|
|
struct dvb_frontend_event event;
|
|
event = getEvent();
|
|
|
|
if(tuned) {
|
|
FE_TIMER_STOP("tuning took");
|
|
}
|
|
}
|
|
|
|
return tuned;
|
|
}
|
|
|
|
void CFrontend::secSetTone(const fe_sec_tone_mode_t toneMode, const uint32_t ms)
|
|
{
|
|
if (slave || info.type != FE_QPSK)
|
|
return;
|
|
|
|
if (currentToneMode == toneMode)
|
|
return;
|
|
|
|
if (config.diseqcType == DISEQC_UNICABLE) {
|
|
/* this is too ugly for words. the "currentToneMode" is the only place
|
|
where the global "highband" state is saved. So we need to fake it for
|
|
unicable and still set the tone on... */
|
|
currentToneMode = toneMode; /* need to know polarization for unicable */
|
|
fop(ioctl, FE_SET_TONE, SEC_TONE_OFF); /* tone must be off except for the time
|
|
of sending commands */
|
|
return;
|
|
}
|
|
|
|
printf("[fe%d] tone %s\n", fenumber, toneMode == SEC_TONE_ON ? "on" : "off");
|
|
FE_TIMER_INIT();
|
|
FE_TIMER_START();
|
|
if (fop(ioctl, FE_SET_TONE, toneMode) == 0) {
|
|
currentToneMode = toneMode;
|
|
FE_TIMER_STOP("FE_SET_TONE took");
|
|
usleep(1000 * ms);
|
|
}
|
|
}
|
|
|
|
void CFrontend::secSetVoltage(const fe_sec_voltage_t voltage, const uint32_t ms)
|
|
{
|
|
if (slave || info.type != FE_QPSK)
|
|
return;
|
|
|
|
if (currentVoltage == voltage)
|
|
return;
|
|
|
|
printf("[fe%d] voltage %s\n", fenumber, voltage == SEC_VOLTAGE_OFF ? "OFF" : voltage == SEC_VOLTAGE_13 ? "13" : "18");
|
|
if (config.diseqcType == DISEQC_UNICABLE) {
|
|
/* see my comment in secSetTone... */
|
|
currentVoltage = voltage; /* need to know polarization for unicable */
|
|
fop(ioctl, FE_SET_VOLTAGE, SEC_VOLTAGE_13); /* voltage must not be 18V */
|
|
return;
|
|
}
|
|
|
|
if (fop(ioctl, FE_SET_VOLTAGE, voltage) == 0) {
|
|
currentVoltage = voltage;
|
|
usleep(1000 * ms);
|
|
}
|
|
}
|
|
|
|
void CFrontend::sendDiseqcCommand(const struct dvb_diseqc_master_cmd *cmd, const uint32_t ms)
|
|
{
|
|
if (slave || info.type != FE_QPSK)
|
|
return;
|
|
|
|
printf("[fe%d] Diseqc cmd: ", fenumber);
|
|
for (int i = 0; i < cmd->msg_len; i++)
|
|
printf("0x%X ", cmd->msg[i]);
|
|
printf("\n");
|
|
|
|
if (fop(ioctl, FE_DISEQC_SEND_MASTER_CMD, cmd) == 0)
|
|
usleep(1000 * ms);
|
|
}
|
|
|
|
void CFrontend::sendToneBurst(const fe_sec_mini_cmd_t burst, const uint32_t ms)
|
|
{
|
|
if (slave || info.type != FE_QPSK)
|
|
return;
|
|
if (fop(ioctl, FE_DISEQC_SEND_BURST, burst) == 0)
|
|
usleep(1000 * ms);
|
|
}
|
|
|
|
void CFrontend::setDiseqcType(const diseqc_t newDiseqcType, bool force)
|
|
{
|
|
switch (newDiseqcType) {
|
|
case NO_DISEQC:
|
|
INFO("fe%d: NO_DISEQC", fenumber);
|
|
break;
|
|
case MINI_DISEQC:
|
|
INFO("fe%d: MINI_DISEQC", fenumber);
|
|
break;
|
|
case SMATV_REMOTE_TUNING:
|
|
INFO("fe%d: SMATV_REMOTE_TUNING", fenumber);
|
|
break;
|
|
case DISEQC_1_0:
|
|
INFO("fe%d: DISEQC_1_0", fenumber);
|
|
break;
|
|
case DISEQC_1_1:
|
|
INFO("fe%d: DISEQC_1_1", fenumber);
|
|
break;
|
|
case DISEQC_1_2:
|
|
INFO("fe%d: DISEQC_1_2", fenumber);
|
|
break;
|
|
case DISEQC_ADVANCED:
|
|
INFO("fe%d: DISEQC_ADVANCED", fenumber);
|
|
break;
|
|
case DISEQC_UNICABLE:
|
|
INFO("fe%d: DISEQC_UNICABLE", fenumber);
|
|
break;
|
|
case DISEQC_UNICABLE2:
|
|
INFO("fe%d: DISEQC_UNICABLE2", fenumber);
|
|
break;
|
|
#if 0
|
|
case DISEQC_2_0:
|
|
INFO("DISEQC_2_0");
|
|
break;
|
|
case DISEQC_2_1:
|
|
INFO("DISEQC_2_1");
|
|
break;
|
|
case DISEQC_2_2:
|
|
INFO("DISEQC_2_2");
|
|
break;
|
|
#endif
|
|
default:
|
|
WARN("Invalid DiSEqC type");
|
|
return;
|
|
}
|
|
|
|
if (newDiseqcType == DISEQC_UNICABLE || newDiseqcType == DISEQC_UNICABLE2) {
|
|
secSetTone(SEC_TONE_OFF, 0);
|
|
secSetVoltage(SEC_VOLTAGE_13, 0);
|
|
}
|
|
else if ((force && (newDiseqcType != NO_DISEQC)) ||
|
|
((config.diseqcType <= MINI_DISEQC) && (newDiseqcType > MINI_DISEQC))) {
|
|
secSetTone(SEC_TONE_OFF, 15);
|
|
sendDiseqcReset();
|
|
sendDiseqcPowerOn();
|
|
secSetTone(SEC_TONE_ON, 50);
|
|
}
|
|
|
|
config.diseqcType = newDiseqcType;
|
|
}
|
|
|
|
void CFrontend::setLnbOffsets(int32_t _lnbOffsetLow, int32_t _lnbOffsetHigh, int32_t _lnbSwitch)
|
|
{
|
|
lnbOffsetLow = _lnbOffsetLow * 1000;
|
|
lnbOffsetHigh = _lnbOffsetHigh * 1000;
|
|
lnbSwitch = _lnbSwitch * 1000;
|
|
//printf("[fe%d] setLnbOffsets %d/%d/%d\n", fenumber, lnbOffsetLow, lnbOffsetHigh, lnbSwitch);
|
|
}
|
|
|
|
void CFrontend::sendMotorCommand(uint8_t cmdtype, uint8_t address, uint8_t command, uint8_t num_parameters, uint8_t parameter1, uint8_t parameter2, int repeat)
|
|
{
|
|
struct dvb_diseqc_master_cmd cmd;
|
|
int i;
|
|
fe_sec_tone_mode_t oldTone = currentToneMode;
|
|
|
|
printf("[fe%d] sendMotorCommand: cmdtype = %x, address = %x, cmd = %x\n", fenumber, cmdtype, address, command);
|
|
printf("[fe%d] sendMotorCommand: num_parms = %d, parm1 = %x, parm2 = %x\n", fenumber, num_parameters, parameter1, parameter2);
|
|
|
|
cmd.msg[0] = cmdtype; //command type
|
|
cmd.msg[1] = address; //address
|
|
cmd.msg[2] = command; //command
|
|
cmd.msg[3] = parameter1;
|
|
cmd.msg[4] = parameter2;
|
|
cmd.msg_len = 3 + num_parameters;
|
|
|
|
secSetTone(SEC_TONE_OFF, 15);
|
|
#if 0
|
|
fe_sec_voltage_t oldVoltage = currentVoltage;
|
|
//secSetVoltage(config.highVoltage ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13, 15);
|
|
//secSetVoltage(SEC_VOLTAGE_13, 100);
|
|
#endif
|
|
|
|
for(i = 0; i <= repeat; i++)
|
|
sendDiseqcCommand(&cmd, 50);
|
|
|
|
//secSetVoltage(oldVoltage, 15);
|
|
secSetTone(oldTone, 15);
|
|
printf("[fe%d] motor command sent.\n", fenumber);
|
|
|
|
}
|
|
|
|
void CFrontend::positionMotor(uint8_t motorPosition)
|
|
{
|
|
struct dvb_diseqc_master_cmd cmd = {
|
|
{0xE0, 0x31, 0x6B, 0x00, 0x00, 0x00}, 4
|
|
};
|
|
|
|
if (motorPosition != 0) {
|
|
secSetTone(SEC_TONE_OFF, 25);
|
|
secSetVoltage(config.highVoltage ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13, 15);
|
|
cmd.msg[3] = motorPosition;
|
|
|
|
for (int i = 0; i <= repeatUsals; ++i)
|
|
sendDiseqcCommand(&cmd, 50);
|
|
|
|
printf("[fe%d] motor positioning command sent.\n", fenumber);
|
|
}
|
|
}
|
|
|
|
bool CFrontend::setInput(CZapitChannel * channel, bool nvod)
|
|
{
|
|
transponder_list_t::iterator tpI;
|
|
transponder_id_t ct = nvod ? (channel->getTransponderId() & 0xFFFFFFFFULL) : channel->getTransponderId();
|
|
transponder_id_t current_id = nvod ? (currentTransponder.getTransponderId() & 0xFFFFFFFFULL) : currentTransponder.getTransponderId();
|
|
//printf("CFrontend::setInput tuned %d nvod %d current_id %llx new %llx\n\n", tuned, nvod, current_id, ct);
|
|
|
|
if (tuned && (ct == current_id))
|
|
return false;
|
|
|
|
if (nvod) {
|
|
for (tpI = transponders.begin(); tpI != transponders.end(); ++tpI) {
|
|
if ((ct & 0xFFFFFFFFULL) == (tpI->first & 0xFFFFFFFFULL))
|
|
break;
|
|
}
|
|
} else
|
|
tpI = transponders.find(channel->getTransponderId());
|
|
|
|
if (tpI == transponders.end()) {
|
|
printf("Transponder %" PRIx64 " for channel %" PRIx64 " not found\n", ct, channel->getChannelID());
|
|
return false;
|
|
}
|
|
|
|
currentTransponder.setTransponderId(tpI->first);
|
|
|
|
currentSatellitePosition = channel->getSatellitePosition();
|
|
setInput(channel->getSatellitePosition(), tpI->second.getFrequency(), tpI->second.getPolarization());
|
|
return true;
|
|
}
|
|
|
|
void CFrontend::setInput(t_satellite_position satellitePosition, uint32_t frequency, uint8_t polarization)
|
|
{
|
|
sat_iterator_t sit = satellites.find(satellitePosition);
|
|
|
|
/* unicable uses diseqc parameter for input selection */
|
|
config.uni_lnb = sit->second.diseqc;
|
|
|
|
setLnbOffsets(sit->second.lnbOffsetLow, sit->second.lnbOffsetHigh, sit->second.lnbSwitch);
|
|
if (config.diseqcType == DISEQC_UNICABLE || config.diseqcType == DISEQC_UNICABLE2)
|
|
return;
|
|
|
|
|
|
if (config.diseqcType != DISEQC_ADVANCED) {
|
|
setDiseqc(sit->second.diseqc, polarization, frequency);
|
|
return;
|
|
}
|
|
if (config.diseqc_order /*sit->second.diseqc_order*/ == COMMITED_FIRST) {
|
|
if (setDiseqcSimple(sit->second.commited, polarization, frequency))
|
|
uncommitedInput = 255;
|
|
sendUncommittedSwitchesCommand(sit->second.uncommited);
|
|
} else {
|
|
if (sendUncommittedSwitchesCommand(sit->second.uncommited))
|
|
currentDiseqc = -1;
|
|
setDiseqcSimple(sit->second.commited, polarization, frequency);
|
|
}
|
|
}
|
|
|
|
/* frequency is the IF-frequency (950-2100), what a stupid spec...
|
|
high_band, horizontal, bank are actually bool (0/1)
|
|
bank specifies the "switch bank" (as in Mini-DiSEqC A/B) */
|
|
uint32_t CFrontend::sendEN50494TuningCommand(const uint32_t frequency, const int high_band,
|
|
const int horizontal, const int bank)
|
|
{
|
|
uint32_t bpf = config.uni_qrg;
|
|
struct dvb_diseqc_master_cmd cmd = {
|
|
{0xe0, 0x10, 0x5a, 0x00, 0x00, 0x00}, 5
|
|
};
|
|
unsigned int t = (frequency / 1000 + bpf + 2) / 4 - 350;
|
|
if (t < 1024 && config.uni_scr >= 0 && config.uni_scr < 8)
|
|
{
|
|
uint32_t ret = (t + 350) * 4000 - frequency;
|
|
INFO("[unicable] 18V=%d TONE=%d, freq=%d qrg=%d scr=%d bank=%d ret=%d", currentVoltage == SEC_VOLTAGE_18, currentToneMode == SEC_TONE_ON, frequency, bpf, config.uni_scr, bank, ret);
|
|
if (!slave && info.type == FE_QPSK) {
|
|
cmd.msg[3] = (t >> 8) | /* highest 3 bits of t */
|
|
(config.uni_scr << 5) | /* adress */
|
|
(bank << 4) | /* input 0/1 */
|
|
(horizontal << 3) | /* horizontal == 0x08 */
|
|
(high_band) << 2; /* high_band == 0x04 */
|
|
cmd.msg[4] = t & 0xFF;
|
|
fop(ioctl, FE_SET_VOLTAGE, SEC_VOLTAGE_18);
|
|
usleep(15 * 1000); /* en50494 says: >4ms and < 22 ms */
|
|
sendDiseqcCommand(&cmd, 50); /* en50494 says: >2ms and < 60 ms */
|
|
fop(ioctl, FE_SET_VOLTAGE, SEC_VOLTAGE_13);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
WARN("ooops. t > 1024? (%d) or uni_scr out of range? (%d)", t, config.uni_scr);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t CFrontend::sendEN50607TuningCommand(const uint32_t frequency, const int high_band, const int horizontal, const int bank)
|
|
{
|
|
uint32_t bpf = config.uni_qrg;
|
|
struct dvb_diseqc_master_cmd cmd = { {0x70, 0x00, 0x00, 0x00, 0x00, 0x00}, 4 };
|
|
|
|
unsigned int t = frequency / 1000 - 100;
|
|
if (t < 0x800 && config.uni_scr >= 0 && config.uni_scr < 32)
|
|
{
|
|
uint32_t ret = bpf * 1000;
|
|
INFO("[unicable-JESS] 18V=%d TONE=%d, freq=%d qrg=%d scr=%d bank=%d ret=%d", currentVoltage == SEC_VOLTAGE_18, currentToneMode == SEC_TONE_ON, frequency, bpf, config.uni_scr, bank, ret);
|
|
if (!slave && info.type == FE_QPSK)
|
|
{
|
|
cmd.msg[1] = ((config.uni_scr & 0x1F) << 3) | /* user band adress ( 0 to 31) */
|
|
/* max. possible tuning word = 0x7FF */
|
|
((t >> 8) & 0x07); /* highest 3 bits of t (MSB) */
|
|
cmd.msg[2] = t & 0xFF; /* tuning word (LSB) */
|
|
cmd.msg[3] = (0 << 4) | /* no uncommited switch */
|
|
/* I really don't know if the combines of option and position bits are right here,
|
|
because I can'test it, assuming here 4 sat positions */
|
|
((bank & 0x03) << 2) | /* input 0/1/2/3 */
|
|
(horizontal << 1) | /* horizontal == 0x02 */
|
|
high_band; /* high_band == 0x01 */
|
|
fop(ioctl, FE_SET_VOLTAGE, SEC_VOLTAGE_18);
|
|
usleep(15 * 1000); /* en50494 says: >4ms and < 22 ms */
|
|
sendDiseqcCommand(&cmd, 50); /* en50494 says: >2ms and < 60 ms */
|
|
fop(ioctl, FE_SET_VOLTAGE, SEC_VOLTAGE_13);
|
|
}
|
|
return ret;
|
|
}
|
|
WARN("ooops. t > 2047? (%d) or uni_scr out of range? (%d)", t, config.uni_scr);
|
|
return 0;
|
|
}
|
|
|
|
bool CFrontend::tuneChannel(CZapitChannel * /*channel*/, bool /*nvod*/)
|
|
{
|
|
transponder_list_t::iterator transponder = transponders.find(currentTransponder.getTransponderId());
|
|
if (transponder == transponders.end())
|
|
return false;
|
|
return tuneFrequency(&transponder->second.feparams, false);
|
|
}
|
|
|
|
#if 0
|
|
bool CFrontend::retuneChannel(void)
|
|
{
|
|
mutex.lock();
|
|
setInput(currentSatellitePosition, currentTransponder.feparams.frequency, currentTransponder.feparams.polarization);
|
|
transponder_list_t::iterator transponder = transponders.find(currentTransponder.TP_id);
|
|
if (transponder == transponders.end())
|
|
return false;
|
|
mutex.unlock();
|
|
return tuneFrequency(&transponder->second.feparams, transponder->second.feparams.polarization, true);
|
|
}
|
|
#endif
|
|
|
|
int CFrontend::tuneFrequency(FrontendParameters *feparams, bool nowait)
|
|
{
|
|
transponder TP;
|
|
|
|
currentTransponder.feparams = TP.feparams = *feparams;
|
|
|
|
return setParameters(&TP, nowait);
|
|
}
|
|
|
|
int CFrontend::setParameters(transponder *TP, bool nowait)
|
|
{
|
|
int freq_offset = 0, freq;
|
|
FrontendParameters feparams;
|
|
|
|
/* Copy the data for local use */
|
|
feparams = TP->feparams;
|
|
|
|
if (!supportsDelivery(feparams.delsys)) {
|
|
printf("[fe%d]: does not support delivery system %d\n", fenumber, feparams.delsys);
|
|
return 0;
|
|
}
|
|
|
|
|
|
freq = (int) feparams.frequency;
|
|
char * f, *s, *m;
|
|
bool high_band;
|
|
|
|
switch (feparams.delsys) {
|
|
case DVB_S:
|
|
case DVB_S2:
|
|
if (freq < lnbSwitch) {
|
|
high_band = false;
|
|
freq_offset = lnbOffsetLow;
|
|
} else {
|
|
high_band = true;
|
|
freq_offset = lnbOffsetHigh;
|
|
}
|
|
|
|
feparams.frequency = abs(freq - freq_offset);
|
|
setSec(TP->feparams.polarization, high_band);
|
|
getDelSys(feparams.delsys, feparams.fec_inner, feparams.modulation, f, s, m);
|
|
break;
|
|
case DVB_C:
|
|
if (freq < 1000*1000)
|
|
feparams.frequency = freq * 1000;
|
|
getDelSys(feparams.delsys, feparams.fec_inner, feparams.modulation, f, s, m);
|
|
break;
|
|
case DVB_T:
|
|
case DVB_T2:
|
|
case DTMB:
|
|
if (freq < 1000*1000)
|
|
feparams.frequency = freq * 1000;
|
|
getDelSys(feparams.delsys, feparams.fec_inner, feparams.modulation, f, s, m);
|
|
break;
|
|
default:
|
|
printf("[fe%d] unknown delsys %d\n", fenumber, feparams.delsys);
|
|
break;
|
|
}
|
|
|
|
printf("[fe%d] tune to %d %s %s %s %s srate %d (tuner %d offset %d timeout %d)\n", fenumber, freq, s, m, f,
|
|
feparams.polarization & 1 ? "V/R" : "H/L", feparams.symbol_rate, feparams.frequency, freq_offset, TIMEOUT_MAX_MS);
|
|
setFrontend(&feparams, nowait);
|
|
|
|
return tuned;
|
|
}
|
|
|
|
bool CFrontend::sendUncommittedSwitchesCommand(int input)
|
|
{
|
|
struct dvb_diseqc_master_cmd cmd = {
|
|
{0xe0, 0x10, 0x39, 0x00, 0x00, 0x00}, 4
|
|
};
|
|
|
|
printf("[fe%d] uncommitted %d -> %d\n", fenumber, uncommitedInput, input);
|
|
if ((input < 0) /* || (uncommitedInput == input) */)
|
|
return false;
|
|
|
|
uncommitedInput = input;
|
|
cmd.msg[3] = 0xF0 + input;
|
|
|
|
secSetTone(SEC_TONE_OFF, 20);
|
|
sendDiseqcCommand(&cmd, 125);
|
|
return true;
|
|
}
|
|
|
|
/* off = low band, on - hi band , vertical 13v, horizontal 18v */
|
|
bool CFrontend::setDiseqcSimple(int sat_no, const uint8_t pol, const uint32_t frequency)
|
|
{
|
|
fe_sec_voltage_t v = (pol & 1) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
|
|
//fe_sec_mini_cmd_t b = (sat_no & 1) ? SEC_MINI_B : SEC_MINI_A;
|
|
bool high_band = ((int)frequency >= lnbSwitch);
|
|
|
|
struct dvb_diseqc_master_cmd cmd = {
|
|
{0xe0, 0x10, 0x38, 0x00, 0x00, 0x00}, 4
|
|
};
|
|
|
|
INFO("[fe%d] diseqc input %d -> %d", fenumber, currentDiseqc, sat_no);
|
|
currentDiseqc = sat_no;
|
|
if (slave)
|
|
return true;
|
|
if ((sat_no >= 0) /* && (diseqc != sat_no)*/) {
|
|
cmd.msg[3] = 0xf0 | (((sat_no * 4) & 0x0f) | (high_band ? 1 : 0) | ((pol & 1) ? 0 : 2));
|
|
|
|
secSetTone(SEC_TONE_OFF, 20);
|
|
secSetVoltage(v, 100);
|
|
sendDiseqcCommand(&cmd, 100);
|
|
return true;
|
|
}
|
|
return false;
|
|
#if 0 //do we need this in advanced setup ?
|
|
if (config.diseqcType == SMATV_REMOTE_TUNING)
|
|
sendDiseqcSmatvRemoteTuningCommand(frequency);
|
|
|
|
if (config.diseqcType == MINI_DISEQC)
|
|
sendToneBurst(b, 15);
|
|
currentDiseqc = sat_no;
|
|
#endif
|
|
}
|
|
|
|
void CFrontend::setDiseqc(int sat_no, const uint8_t pol, const uint32_t frequency)
|
|
{
|
|
uint8_t loop;
|
|
bool high_band = ((int)frequency >= lnbSwitch);
|
|
struct dvb_diseqc_master_cmd cmd = { {0xE0, 0x10, 0x38, 0xF0, 0x00, 0x00}, 4 };
|
|
fe_sec_mini_cmd_t b = (sat_no & 1) ? SEC_MINI_B : SEC_MINI_A;
|
|
int delay = 0;
|
|
|
|
if ((config.diseqcType == NO_DISEQC) || sat_no < 0)
|
|
return;
|
|
|
|
printf("[fe%d] diseqc input %d -> %d\n", fenumber, currentDiseqc, sat_no);
|
|
currentDiseqc = sat_no;
|
|
if (slave)
|
|
return;
|
|
|
|
// Disable tone for at least 15ms
|
|
secSetTone(SEC_TONE_OFF, 20);
|
|
// Set wanted voltage and wait at least 100 ms, in case voltage was off.
|
|
secSetVoltage((pol & 1) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18, 100);
|
|
|
|
#if 0 /* FIXME: is this necessary ? */
|
|
sendDiseqcReset();
|
|
sendDiseqcPowerOn();
|
|
#endif
|
|
|
|
for (loop = 0; loop <= config.diseqcRepeats; loop++) {
|
|
delay = 0;
|
|
|
|
if (config.diseqcType == DISEQC_1_1) { /* setup the uncommited switch first */
|
|
|
|
delay = 100; // delay for 1.0 after 1.1 command
|
|
cmd.msg[2] = 0x39; /* port group = uncommited switches */
|
|
#if 1
|
|
/* for 16 inputs */
|
|
cmd.msg[3] = 0xF0 | ((sat_no / 4) & 0x03);
|
|
//send the command to setup second uncommited switch and
|
|
// wait 100 ms.
|
|
sendDiseqcCommand(&cmd, 100);
|
|
#else
|
|
/* for 64 inputs */
|
|
uint8_t cascade_input[16] = {0xF0, 0xF4, 0xF8, 0xFC, 0xF1, 0xF5, 0xF9, 0xFD, 0xF2, 0xF6, 0xFA,
|
|
0xFE, 0xF3, 0xF7, 0xFB, 0xFF
|
|
};
|
|
cmd.msg[3] = cascade_input[(sat_no / 4) & 0xFF];
|
|
sendDiseqcCommand(&cmd, 100); /* send the command to setup first uncommited switch and wait 100 ms !!! */
|
|
cmd.msg[3] &= 0xCF;
|
|
sendDiseqcCommand(&cmd, 100); /* send the command to setup second uncommited switch and wait 100 ms !!! */
|
|
#endif
|
|
}
|
|
|
|
if (config.diseqcType >= DISEQC_1_0) { /* DISEQC 1.0 */
|
|
usleep(delay * 1000);
|
|
//cmd.msg[0] |= 0x01; /* repeated transmission */
|
|
cmd.msg[2] = 0x38; /* port group = commited switches */
|
|
cmd.msg[3] = 0xF0 | ((sat_no & 0x0F) << 2) | ((pol & 1) ? 0 : 2) | (high_band ? 1 : 0);
|
|
sendDiseqcCommand(&cmd, 100); /* send the command to setup second commited switch */
|
|
}
|
|
cmd.msg[0] = 0xE1;
|
|
usleep(50 * 1000); /* sleep at least 50 milli seconds */
|
|
}
|
|
|
|
// Toneburst should not be repeated and should be sent AFTER
|
|
// all diseqc, this means we don't support a toneburst switch
|
|
// before any diseqc equipment.
|
|
if (config.diseqcType == MINI_DISEQC)
|
|
sendToneBurst(b, 1);
|
|
|
|
usleep(25 * 1000);
|
|
|
|
if (config.diseqcType == SMATV_REMOTE_TUNING)
|
|
sendDiseqcSmatvRemoteTuningCommand(frequency);
|
|
|
|
usleep(25 * 1000);
|
|
}
|
|
|
|
void CFrontend::setSec(const uint8_t pol, const bool high_band)
|
|
{
|
|
fe_sec_voltage_t v = (pol & 1) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
|
|
fe_sec_tone_mode_t t = high_band ? SEC_TONE_ON : SEC_TONE_OFF;
|
|
|
|
// set tone off first
|
|
secSetTone(SEC_TONE_OFF, 20);
|
|
// set the desired voltage
|
|
secSetVoltage(v, 100);
|
|
// set tone according to what's needed.
|
|
secSetTone(t, 15);
|
|
}
|
|
|
|
void CFrontend::sendDiseqcPowerOn(uint32_t ms)
|
|
{
|
|
printf("[fe%d] diseqc power on\n", fenumber);
|
|
// Send power on to 'all' equipment
|
|
sendDiseqcZeroByteCommand(0xe0, 0x00, 0x03, ms);
|
|
}
|
|
|
|
void CFrontend::sendDiseqcReset(uint32_t ms)
|
|
{
|
|
printf("[fe%d] diseqc reset\n", fenumber);
|
|
// Send reset to 'all' equipment
|
|
sendDiseqcZeroByteCommand(0xe0, 0x00, 0x00, ms);
|
|
}
|
|
|
|
void CFrontend::sendDiseqcStandby(uint32_t ms)
|
|
{
|
|
printf("[fe%d] diseqc standby\n", fenumber);
|
|
if (config.diseqcType > DISEQC_ADVANCED)
|
|
{
|
|
/* use ODU_Power_OFF command for unicable or jess here
|
|
to set the used UB frequency of the frontend to standby */
|
|
struct dvb_diseqc_master_cmd cmd = {{0}, 6};
|
|
printf("[fe%d] standby scr: %d\n", fenumber, config.uni_scr);
|
|
if (config.diseqcType == DISEQC_UNICABLE)
|
|
{
|
|
cmd.msg[0] = 0xe0;
|
|
cmd.msg[1] = 0x10;
|
|
cmd.msg[2] = 0x5a;
|
|
cmd.msg[3] = ((config.uni_scr & 0x07) << 5);
|
|
cmd.msg_len = 5;
|
|
}
|
|
if (config.diseqcType == DISEQC_UNICABLE2)
|
|
{
|
|
cmd.msg[0] = 0x70;
|
|
cmd.msg[1] = ((config.uni_scr & 0x1F) << 3);
|
|
cmd.msg_len = 4;
|
|
}
|
|
fop(ioctl, FE_SET_VOLTAGE, SEC_VOLTAGE_18);
|
|
usleep(15 * 1000);
|
|
sendDiseqcCommand(&cmd, ms);
|
|
fop(ioctl, FE_SET_VOLTAGE, SEC_VOLTAGE_13);
|
|
return;
|
|
}
|
|
// Send power off to 'all' equipment
|
|
sendDiseqcZeroByteCommand(0xe0, 0x00, 0x02, ms);
|
|
}
|
|
|
|
void CFrontend::sendDiseqcZeroByteCommand(uint8_t framing_byte, uint8_t address, uint8_t command, uint32_t ms)
|
|
{
|
|
struct dvb_diseqc_master_cmd diseqc_cmd = {
|
|
{framing_byte, address, command, 0x00, 0x00, 0x00}, 3
|
|
};
|
|
|
|
sendDiseqcCommand(&diseqc_cmd, ms);
|
|
}
|
|
|
|
void CFrontend::sendDiseqcSmatvRemoteTuningCommand(const uint32_t frequency)
|
|
{
|
|
/* [0] from master, no reply, 1st transmission
|
|
* [1] intelligent slave interface for multi-master bus
|
|
* [2] write channel frequency
|
|
* [3] frequency
|
|
* [4] frequency
|
|
* [5] frequency */
|
|
|
|
struct dvb_diseqc_master_cmd cmd = {
|
|
{0xe0, 0x71, 0x58, 0x00, 0x00, 0x00}, 6
|
|
};
|
|
|
|
cmd.msg[3] = (((frequency / 10000000) << 4) & 0xF0) | ((frequency / 1000000) & 0x0F);
|
|
cmd.msg[4] = (((frequency / 100000) << 4) & 0xF0) | ((frequency / 10000) & 0x0F);
|
|
cmd.msg[5] = (((frequency / 1000) << 4) & 0xF0) | ((frequency / 100) & 0x0F);
|
|
|
|
sendDiseqcCommand(&cmd, 15);
|
|
}
|
|
|
|
int CFrontend::driveToSatellitePosition(t_satellite_position satellitePosition, bool from_scan)
|
|
{
|
|
int waitForMotor = 0;
|
|
int new_position = 0, old_position = 0;
|
|
bool use_usals = 0;
|
|
|
|
if (CFrontend::linked(femode) || femode == CFrontend::FE_MODE_UNUSED) {
|
|
rotorSatellitePosition = satellitePosition;
|
|
return 0;
|
|
}
|
|
//if(config.diseqcType == DISEQC_ADVANCED) //FIXME testing
|
|
{
|
|
bool moved = false;
|
|
|
|
sat_iterator_t sit = satellites.find(satellitePosition);
|
|
if (sit == satellites.end()) {
|
|
printf("[fe%d] satellite position %d not found!\n", fenumber, satellitePosition);
|
|
return 0;
|
|
} else {
|
|
new_position = sit->second.motor_position;
|
|
use_usals = sit->second.use_usals;
|
|
}
|
|
sit = satellites.find(rotorSatellitePosition);
|
|
if (sit != satellites.end())
|
|
old_position = sit->second.motor_position;
|
|
|
|
printf("[fe%d] sat pos %d -> %d motor pos %d -> %d usals %s\n", fenumber, rotorSatellitePosition, satellitePosition, old_position, new_position, use_usals ? "on" : "off");
|
|
|
|
if (rotorSatellitePosition == satellitePosition)
|
|
return 0;
|
|
|
|
if (use_usals || config.use_usals) {
|
|
gotoXX(satellitePosition);
|
|
moved = true;
|
|
} else {
|
|
if (new_position > 0) {
|
|
positionMotor(new_position);
|
|
moved = true;
|
|
}
|
|
}
|
|
|
|
if (from_scan || (new_position > 0 && old_position > 0)) {
|
|
waitForMotor = config.motorRotationSpeed ? 2 + abs(satellitePosition - rotorSatellitePosition) / config.motorRotationSpeed : 0;
|
|
}
|
|
if (moved) {
|
|
waitForMotor = config.motorRotationSpeed ? 2 + abs(satellitePosition - rotorSatellitePosition) / config.motorRotationSpeed : 0;
|
|
rotorSatellitePosition = satellitePosition;
|
|
}
|
|
}
|
|
|
|
return waitForMotor;
|
|
}
|
|
|
|
static const int gotoXTable[10] = { 0x00, 0x02, 0x03, 0x05, 0x06, 0x08, 0x0A, 0x0B, 0x0D, 0x0E };
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
double factorial_div(double value, int x)
|
|
{
|
|
if (!x)
|
|
return 1;
|
|
else {
|
|
while (x > 1) {
|
|
value = value / x--;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
double powerd(double x, int y)
|
|
{
|
|
int i = 0;
|
|
double ans = 1.0;
|
|
|
|
if (!y)
|
|
return 1.000;
|
|
else {
|
|
while (i < y) {
|
|
i++;
|
|
ans = ans * x;
|
|
}
|
|
}
|
|
return ans;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
double SIN(double x)
|
|
{
|
|
int i = 0;
|
|
int j = 1;
|
|
int sign = 1;
|
|
double y1 = 0.0;
|
|
double diff = 1000.0;
|
|
|
|
if (x < 0.0) {
|
|
x = -1 * x;
|
|
sign = -1;
|
|
}
|
|
|
|
while (x > 360.0 * M_PI / 180) {
|
|
x = x - 360 * M_PI / 180;
|
|
}
|
|
|
|
if (x > (270.0 * M_PI / 180)) {
|
|
sign = sign * -1;
|
|
x = 360.0 * M_PI / 180 - x;
|
|
} else if (x > (180.0 * M_PI / 180)) {
|
|
sign = sign * -1;
|
|
x = x - 180.0 * M_PI / 180;
|
|
} else if (x > (90.0 * M_PI / 180)) {
|
|
x = 180.0 * M_PI / 180 - x;
|
|
}
|
|
|
|
while (powerd(diff, 2) > 1.0E-16) {
|
|
i++;
|
|
diff = j * factorial_div(powerd(x, (2 * i - 1)), (2 * i - 1));
|
|
y1 = y1 + diff;
|
|
j = -1 * j;
|
|
}
|
|
return (sign * y1);
|
|
}
|
|
|
|
double COS(double x)
|
|
{
|
|
return SIN(90 * M_PI / 180 - x);
|
|
}
|
|
|
|
double ATAN(double x)
|
|
{
|
|
int i = 0; /* counter for terms in binomial series */
|
|
int j = 1; /* sign of nth term in series */
|
|
int k = 0;
|
|
int sign = 1; /* sign of the input x */
|
|
double y = 0.0; /* the output */
|
|
double deltay = 1.0; /* the value of the next term in the series */
|
|
double addangle = 0.0; /* used if arctan > 22.5 degrees */
|
|
|
|
if (x < 0.0) {
|
|
x = -1 * x;
|
|
sign = -1;
|
|
}
|
|
|
|
while (x > 0.3249196962) {
|
|
k++;
|
|
x = (x - 0.3249196962) / (1 + x * 0.3249196962);
|
|
}
|
|
addangle = k * 18.0 * M_PI / 180;
|
|
|
|
while (powerd(deltay, 2) > 1.0E-16) {
|
|
i++;
|
|
deltay = j * powerd(x, (2 * i - 1)) / (2 * i - 1);
|
|
y = y + deltay;
|
|
j = -1 * j;
|
|
}
|
|
return (sign * (y + addangle));
|
|
}
|
|
|
|
double ASIN(double x)
|
|
{
|
|
return 2 * ATAN(x / (1 + std::sqrt(1.0 - x * x)));
|
|
}
|
|
|
|
double Radians(double number)
|
|
{
|
|
return number * M_PI / 180;
|
|
}
|
|
|
|
double Deg(double number)
|
|
{
|
|
return number * 180 / M_PI;
|
|
}
|
|
|
|
double Rev(double number)
|
|
{
|
|
return number - std::floor(number / 360.0) * 360;
|
|
}
|
|
|
|
double calcElevation(double SatLon, double SiteLat, double SiteLon, int Height_over_ocean = 0)
|
|
{
|
|
const double a0 = 0.58804392, a1 = -0.17941557, a2 = 0.29906946E-1, a3 = -0.25187400E-2, a4 = 0.82622101E-4;
|
|
const double f = 1.00 / 298.257; // Earth flattning factor
|
|
const double r_sat = 42164.57; // Distance from earth centre to satellite
|
|
const double r_eq = 6378.14; // Earth radius
|
|
double sinRadSiteLat = SIN(Radians(SiteLat)), cosRadSiteLat = COS(Radians(SiteLat));
|
|
double Rstation = r_eq / (std::sqrt(1.00 - f * (2.00 - f) * sinRadSiteLat * sinRadSiteLat));
|
|
double Ra = (Rstation + Height_over_ocean) * cosRadSiteLat;
|
|
double Rz = Rstation * (1.00 - f) * (1.00 - f) * sinRadSiteLat;
|
|
double alfa_rx = r_sat * COS(Radians(SatLon - SiteLon)) - Ra;
|
|
double alfa_ry = r_sat * SIN(Radians(SatLon - SiteLon));
|
|
double alfa_rz = -Rz, alfa_r_north = -alfa_rx * sinRadSiteLat + alfa_rz * cosRadSiteLat;
|
|
double alfa_r_zenith = alfa_rx * cosRadSiteLat + alfa_rz * sinRadSiteLat;
|
|
double El_geometric = Deg(ATAN(alfa_r_zenith / std::sqrt(alfa_r_north * alfa_r_north + alfa_ry * alfa_ry)));
|
|
double x = std::fabs(El_geometric + 0.589);
|
|
double refraction = std::fabs(a0 + a1 * x + a2 * x * x + a3 * x * x * x + a4 * x * x * x * x);
|
|
double El_observed = 0.00;
|
|
|
|
if (El_geometric > 10.2)
|
|
El_observed = El_geometric + 0.01617 * (COS(Radians(std::fabs(El_geometric))) / SIN(Radians(std::fabs(El_geometric))));
|
|
else {
|
|
El_observed = El_geometric + refraction;
|
|
}
|
|
|
|
if (alfa_r_zenith < -3000)
|
|
El_observed = -99;
|
|
|
|
return El_observed;
|
|
}
|
|
|
|
double calcAzimuth(double SatLon, double SiteLat, double SiteLon, int Height_over_ocean = 0)
|
|
{
|
|
const double f = 1.00 / 298.257; // Earth flattning factor
|
|
const double r_sat = 42164.57; // Distance from earth centre to satellite
|
|
const double r_eq = 6378.14; // Earth radius
|
|
|
|
double sinRadSiteLat = SIN(Radians(SiteLat)), cosRadSiteLat = COS(Radians(SiteLat));
|
|
double Rstation = r_eq / (std::sqrt(1 - f * (2 - f) * sinRadSiteLat * sinRadSiteLat));
|
|
double Ra = (Rstation + Height_over_ocean) * cosRadSiteLat;
|
|
double Rz = Rstation * (1 - f) * (1 - f) * sinRadSiteLat;
|
|
double alfa_rx = r_sat * COS(Radians(SatLon - SiteLon)) - Ra;
|
|
double alfa_ry = r_sat * SIN(Radians(SatLon - SiteLon));
|
|
double alfa_rz = -Rz;
|
|
double alfa_r_north = -alfa_rx * sinRadSiteLat + alfa_rz * cosRadSiteLat;
|
|
double Azimuth = 0.00;
|
|
|
|
if (alfa_r_north < 0)
|
|
Azimuth = 180 + Deg(ATAN(alfa_ry / alfa_r_north));
|
|
else
|
|
Azimuth = Rev(360 + Deg(ATAN(alfa_ry / alfa_r_north)));
|
|
|
|
return Azimuth;
|
|
}
|
|
|
|
double calcDeclination(double SiteLat, double Azimuth, double Elevation)
|
|
{
|
|
return Deg(ASIN(SIN(Radians(Elevation)) * SIN(Radians(SiteLat)) + COS(Radians(Elevation)) * COS(Radians(SiteLat)) + COS(Radians(Azimuth))
|
|
)
|
|
);
|
|
}
|
|
|
|
double calcSatHourangle(double Azimuth, double Elevation, double Declination, double Lat)
|
|
{
|
|
double a = -COS(Radians(Elevation)) * SIN(Radians(Azimuth));
|
|
double b = SIN(Radians(Elevation)) * COS(Radians(Lat)) - COS(Radians(Elevation)) * SIN(Radians(Lat)) * COS(Radians(Azimuth));
|
|
|
|
// Works for all azimuths (northern & sourhern hemisphere)
|
|
double returnvalue = 180 + Deg(ATAN(a / b));
|
|
|
|
(void)Declination;
|
|
|
|
if (Azimuth > 270) {
|
|
returnvalue = ((returnvalue - 180) + 360);
|
|
if (returnvalue > 360)
|
|
returnvalue = 360 - (returnvalue - 360);
|
|
}
|
|
|
|
if (Azimuth < 90)
|
|
returnvalue = (180 - returnvalue);
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
void CFrontend::gotoXX(t_satellite_position pos)
|
|
{
|
|
int RotorCmd;
|
|
int satDir = pos < 0 ? WEST : EAST;
|
|
double SatLon = abs(pos) / 10.00;
|
|
double SiteLat = gotoXXLatitude;
|
|
double SiteLon = gotoXXLongitude;
|
|
|
|
if (gotoXXLaDirection == SOUTH)
|
|
SiteLat = -SiteLat;
|
|
if (gotoXXLoDirection == WEST)
|
|
SiteLon = 360 - SiteLon;
|
|
if (satDir == WEST)
|
|
SatLon = 360 - SatLon;
|
|
printf("siteLatitude = %f, siteLongitude = %f, %f degrees\n", SiteLat, SiteLon, SatLon);
|
|
|
|
double azimuth = calcAzimuth(SatLon, SiteLat, SiteLon);
|
|
double elevation = calcElevation(SatLon, SiteLat, SiteLon);
|
|
double declination = calcDeclination(SiteLat, azimuth, elevation);
|
|
double satHourAngle = calcSatHourangle(azimuth, elevation, declination, SiteLat);
|
|
printf("azimuth=%f, elevation=%f, declination=%f, PolarmountHourAngle=%f\n", azimuth, elevation, declination, satHourAngle);
|
|
if (SiteLat >= 0) {
|
|
int tmp = (int)round(fabs(180 - satHourAngle) * 10.0);
|
|
RotorCmd = (tmp / 10) * 0x10 + gotoXTable[tmp % 10];
|
|
if (satHourAngle < 180) // the east
|
|
RotorCmd |= 0xE000;
|
|
else
|
|
RotorCmd |= 0xD000;
|
|
} else {
|
|
if (satHourAngle < 180) {
|
|
int tmp = (int)round(fabs(satHourAngle) * 10.0);
|
|
RotorCmd = (tmp / 10) * 0x10 + gotoXTable[tmp % 10];
|
|
RotorCmd |= 0xD000;
|
|
} else {
|
|
int tmp = (int)round(fabs(360 - satHourAngle) * 10.0);
|
|
RotorCmd = (tmp / 10) * 0x10 + gotoXTable[tmp % 10];
|
|
RotorCmd |= 0xE000;
|
|
}
|
|
}
|
|
|
|
printf("RotorCmd = %04x\n", RotorCmd);
|
|
if (config.highVoltage)
|
|
secSetVoltage(SEC_VOLTAGE_18, 100);
|
|
sendMotorCommand(0xE0, 0x31, 0x6E, 2, ((RotorCmd & 0xFF00) / 0x100), RotorCmd & 0xFF, repeatUsals);
|
|
//secSetVoltage(config.highVoltage ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13, 15); //FIXME ?
|
|
}
|
|
|
|
bool CFrontend::isCable(delivery_system_t delsys)
|
|
{
|
|
return ZAPIT_DS_IS_CABLE(delsys);
|
|
}
|
|
|
|
bool CFrontend::isSat(delivery_system_t delsys)
|
|
{
|
|
return ZAPIT_DS_IS_SAT(delsys);
|
|
}
|
|
|
|
bool CFrontend::isTerr(delivery_system_t delsys)
|
|
{
|
|
return ZAPIT_DS_IS_TERR(delsys);
|
|
}
|
|
|
|
bool CFrontend::hasCable(void)
|
|
{
|
|
return ZAPIT_DS_IS_CABLE(deliverySystemMask);
|
|
}
|
|
|
|
bool CFrontend::hasSat(void)
|
|
{
|
|
return ZAPIT_DS_IS_SAT(deliverySystemMask);
|
|
}
|
|
|
|
bool CFrontend::hasTerr(void)
|
|
{
|
|
return ZAPIT_DS_IS_TERR(deliverySystemMask);
|
|
}
|
|
|
|
bool CFrontend::isHybrid(void)
|
|
{
|
|
if (hasSat() && hasCable())
|
|
return true;
|
|
if (hasSat() && hasTerr())
|
|
return true;
|
|
if (hasCable() && hasTerr())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CFrontend::supportsDelivery(delivery_system_t delsys)
|
|
{
|
|
return (deliverySystemMask & delsys) != 0;
|
|
}
|
|
|
|
delivery_system_t CFrontend::getCurrentDeliverySystem(void)
|
|
{
|
|
// FIXME: this should come from demod information
|
|
//if (tuned)
|
|
return currentTransponder.feparams.delsys;
|
|
//else
|
|
// return UNKNOWN_DS;
|
|
}
|
|
|
|
uint32_t CFrontend::getSupportedDeliverySystems(void) const
|
|
{
|
|
return deliverySystemMask;
|
|
}
|