mirror of
https://github.com/DigitalDevices/dddvb.git
synced 2023-10-10 13:37:43 +02:00
add basic CI support and signal quality
This commit is contained in:
parent
31a833acfd
commit
a8c7d06316
@ -5,10 +5,10 @@ all: libdddvb.so.1.0.1
|
|||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) $(LIB_FLAGS) $(CFLAGS) -c $<
|
$(CC) $(LIB_FLAGS) $(CFLAGS) -c $<
|
||||||
|
|
||||||
libdddvb.a: dvb.o dddvb.o tools.o config.o
|
libdddvb.a: dvb.o dddvb.o tools.o config.o ca.o
|
||||||
$(AR) -cvq libdddvb.a $^
|
$(AR) -cvq libdddvb.a $^
|
||||||
|
|
||||||
libdddvb.so.1.0.1: dvb.o dddvb.o tools.o config.o
|
libdddvb.so.1.0.1: dvb.o dddvb.o tools.o config.o ca.o
|
||||||
$(CC) $(LIB_FLAGS) $(CFLAGS) -shared -Wl,-soname,libdddvb.so.1 -o libdddvb.so.1.0.1 $^ -lc
|
$(CC) $(LIB_FLAGS) $(CFLAGS) -shared -Wl,-soname,libdddvb.so.1 -o libdddvb.so.1.0.1 $^ -lc
|
||||||
ln -sf libdddvb.so.1.0.1 libdddvb.so.1
|
ln -sf libdddvb.so.1.0.1 libdddvb.so.1
|
||||||
ln -sf libdddvb.so.1.0.1 libdddvb.so
|
ln -sf libdddvb.so.1.0.1 libdddvb.so
|
||||||
@ -17,4 +17,4 @@ dddvb_test: dddvb_test.o
|
|||||||
$(CC) -o dddvb_test $< -L . -l dddvb
|
$(CC) -o dddvb_test $< -L . -l dddvb
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm *.o
|
-rm -f *.o
|
||||||
|
636
lib/src/ca.c
Normal file
636
lib/src/ca.c
Normal file
@ -0,0 +1,636 @@
|
|||||||
|
#include "libdddvb.h"
|
||||||
|
#include "dddvb.h"
|
||||||
|
#include "tools.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include <linux/dvb/dmx.h>
|
||||||
|
#include <linux/dvb/frontend.h>
|
||||||
|
#include <linux/dvb/video.h>
|
||||||
|
#include <linux/dvb/net.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/ethernet.h>
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
|
#define MMI_STATE_CLOSED 0
|
||||||
|
#define MMI_STATE_OPEN 1
|
||||||
|
#define MMI_STATE_ENQ 2
|
||||||
|
#define MMI_STATE_MENU 3
|
||||||
|
|
||||||
|
|
||||||
|
int set_nonblock(int fd)
|
||||||
|
{
|
||||||
|
int fl = fcntl(fd, F_GETFL);
|
||||||
|
|
||||||
|
if (fl < 0)
|
||||||
|
return fl;
|
||||||
|
return fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
int streamsock(const char *port, int family, struct sockaddr *sadr)
|
||||||
|
{
|
||||||
|
int one=1, sock;
|
||||||
|
struct addrinfo *ais, *ai, hints = {
|
||||||
|
.ai_flags = AI_PASSIVE,
|
||||||
|
.ai_family = family,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
.ai_protocol = 0, .ai_addrlen = 0,
|
||||||
|
.ai_addr = NULL, .ai_canonname = NULL, .ai_next = NULL,
|
||||||
|
};
|
||||||
|
if (getaddrinfo(NULL, port, &hints, &ais) < 0)
|
||||||
|
return -1;
|
||||||
|
for (ai = ais; ai; ai = ai->ai_next) {
|
||||||
|
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||||
|
if (sock == -1)
|
||||||
|
continue;
|
||||||
|
if (!setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) &&
|
||||||
|
!bind(sock, ai->ai_addr, ai->ai_addrlen)) {
|
||||||
|
*sadr = *ai->ai_addr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
close(sock);
|
||||||
|
sock = -1;
|
||||||
|
}
|
||||||
|
freeaddrinfo(ais);
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ai_callback(void *arg, uint8_t slot_id, uint16_t session_number,
|
||||||
|
uint8_t application_type, uint16_t application_manufacturer,
|
||||||
|
uint16_t manufacturer_code, uint8_t menu_string_length,
|
||||||
|
uint8_t *menu_string)
|
||||||
|
{
|
||||||
|
struct dddvb_ca *ca = arg;
|
||||||
|
|
||||||
|
dbgprintf(DEBUG_CA, "Application type: %02x\n", application_type);
|
||||||
|
dbgprintf(DEBUG_CA, "Application manufacturer: %04x\n", application_manufacturer);
|
||||||
|
dbgprintf(DEBUG_CA, "Manufacturer code: %04x\n", manufacturer_code);
|
||||||
|
dbgprintf(DEBUG_CA, "Menu string: %.*s\n", menu_string_length, menu_string);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ca_info_callback(void *arg, uint8_t slot_id, uint16_t snum,
|
||||||
|
uint32_t id_count, uint16_t *ids)
|
||||||
|
{
|
||||||
|
struct dddvb_ca *ca = arg;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
dbgprintf(DEBUG_CA, "CAM supports the following ca system ids:\n");
|
||||||
|
for (i = 0; i < id_count; i++) {
|
||||||
|
dbgprintf(DEBUG_CA, " 0x%04x\n", ids[i]);
|
||||||
|
}
|
||||||
|
ca->resource_ready = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int handle_pmt(struct dvbca *ca, uint8_t *buf, int size)
|
||||||
|
{
|
||||||
|
int listmgmt = CA_LIST_MANAGEMENT_ONLY;
|
||||||
|
uint8_t capmt[4096];
|
||||||
|
struct section *section = section_codec(buf, size);
|
||||||
|
struct section_ext *section_ext = section_ext_decode(section, 0);
|
||||||
|
struct mpeg_pmt_section *pmt = mpeg_pmt_section_codec(section_ext);
|
||||||
|
|
||||||
|
dbgprintf(DEBUG_CA, "handle pmt\n");
|
||||||
|
if (section_ext->version_number == ca->ca_pmt_version &&
|
||||||
|
ca->pmt == ca->pmt_old)
|
||||||
|
return;
|
||||||
|
if (ca->pmt != ca->pmt_old) {
|
||||||
|
ca->pmt_old = ca->pmt;
|
||||||
|
ca->sentpmt = 0;
|
||||||
|
}
|
||||||
|
if (ca->resource_ready) {
|
||||||
|
ca->data_pmt_version = pmt->head.version_number;
|
||||||
|
|
||||||
|
if (ca->sentpmt) {
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_UPDATE;
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
ca->sentpmt = 1;
|
||||||
|
dbgprintf(DEBUG_CA, "set ca_pmt\n");
|
||||||
|
|
||||||
|
if ((size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), ca->moveca, listmgmt,
|
||||||
|
CA_PMT_CMD_ID_OK_DESCRAMBLING)) < 0) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to format PMT\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (en50221_app_ca_pmt(ca->stdcam->ca_resource, ca->stdcam->ca_session_number, capmt, size)) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to send PMT\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static void handle_tdt(struct dddvb_ca *ca)
|
||||||
|
{
|
||||||
|
struct section *section;
|
||||||
|
struct dvb_tdt_section *tdt;
|
||||||
|
uint8_t sec[4096];
|
||||||
|
time_t dvb_time;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (ca->stdcam == NULL)
|
||||||
|
return;
|
||||||
|
if (ca->stdcam->dvbtime == NULL)
|
||||||
|
return;
|
||||||
|
len = getsec(ca->input, 0x14, 0, 0x70, sec);
|
||||||
|
if (len < 0)
|
||||||
|
return;
|
||||||
|
dbgprintf(DEBUG_CA, "got tdt\n");
|
||||||
|
|
||||||
|
section = section_codec(sec, len);
|
||||||
|
if (section == NULL)
|
||||||
|
return;
|
||||||
|
tdt = dvb_tdt_section_codec(section);
|
||||||
|
if (tdt == NULL)
|
||||||
|
return;
|
||||||
|
dvb_time = dvbdate_to_unixtime(tdt->utc_time);
|
||||||
|
|
||||||
|
dbgprintf(DEBUG_CA, "set dvbtime\n");
|
||||||
|
if (ca->stdcam->dvbtime)
|
||||||
|
ca->stdcam->dvbtime(ca->stdcam, dvb_time);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int handle_pmts(struct dddvb_ca *ca)
|
||||||
|
{
|
||||||
|
int listmgmt = CA_LIST_MANAGEMENT_ONLY;
|
||||||
|
uint8_t sec[4096], capmt[4096];
|
||||||
|
struct section *section;
|
||||||
|
struct section_ext *section_ext;
|
||||||
|
struct mpeg_pmt_section *pmt;
|
||||||
|
int i, size, num, len;
|
||||||
|
|
||||||
|
if (!ca->resource_ready)
|
||||||
|
return 0;
|
||||||
|
dbgprintf(DEBUG_CA, "handle pmts\n");
|
||||||
|
for (i = num = 0; i < MAX_PMT; i++)
|
||||||
|
if (ca->pmt[i])
|
||||||
|
num++;
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
len = -1;//getsec(ca->input, ca->pmt[i] & 0xffff, ca->pmt[i] >> 16, 2, sec);
|
||||||
|
if (len < 0)
|
||||||
|
continue;
|
||||||
|
section = section_codec(sec, len);
|
||||||
|
section_ext = section_ext_decode(section, 0);
|
||||||
|
pmt = mpeg_pmt_section_codec(section_ext);
|
||||||
|
|
||||||
|
ca->ca_pmt_version[i] = section_ext->version_number;
|
||||||
|
if (ca->sentpmt) {
|
||||||
|
//return 0;
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_UPDATE;
|
||||||
|
} else {
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_ONLY;
|
||||||
|
if (num > 1) {
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_MORE;
|
||||||
|
if (i == 0)
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_FIRST;
|
||||||
|
if (i == num - 1)
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_LAST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dbgprintf(DEBUG_CA, "set ca_pmt\n");
|
||||||
|
|
||||||
|
if ((size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), ca->moveca, listmgmt,
|
||||||
|
CA_PMT_CMD_ID_OK_DESCRAMBLING)) < 0) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to format PMT\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
//dump(capmt, size);
|
||||||
|
if (en50221_app_ca_pmt(ca->stdcam->ca_resource, ca->stdcam->ca_session_number, capmt, size)) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to send PMT\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num)
|
||||||
|
ca->sentpmt = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_pmts(struct dddvb_ca *ca, uint8_t **pmts)
|
||||||
|
{
|
||||||
|
int listmgmt = CA_LIST_MANAGEMENT_ONLY;
|
||||||
|
uint8_t sec[4096], capmt[4096];
|
||||||
|
struct section *section;
|
||||||
|
struct section_ext *section_ext;
|
||||||
|
struct mpeg_pmt_section *pmt;
|
||||||
|
int i, size, num, len;
|
||||||
|
|
||||||
|
if (!ca->resource_ready)
|
||||||
|
return 0;
|
||||||
|
dbgprintf(DEBUG_CA, "handle pmts\n");
|
||||||
|
for (i = num = 0; i < MAX_PMT; i++)
|
||||||
|
if (pmts[i])
|
||||||
|
num++;
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
section = section_codec(pmts[i], len);
|
||||||
|
section_ext = section_ext_decode(section, 0);
|
||||||
|
pmt = mpeg_pmt_section_codec(section_ext);
|
||||||
|
|
||||||
|
ca->ca_pmt_version[i] = section_ext->version_number;
|
||||||
|
if (ca->sentpmt) {
|
||||||
|
//return 0;
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_UPDATE;
|
||||||
|
} else {
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_ONLY;
|
||||||
|
if (num > 1) {
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_MORE;
|
||||||
|
if (i == 0)
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_FIRST;
|
||||||
|
if (i == num - 1)
|
||||||
|
listmgmt = CA_LIST_MANAGEMENT_LAST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dbgprintf(DEBUG_CA, "set ca_pmt\n");
|
||||||
|
|
||||||
|
if ((size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), ca->moveca, listmgmt,
|
||||||
|
CA_PMT_CMD_ID_OK_DESCRAMBLING)) < 0) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to format PMT\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
//dump(capmt, size);
|
||||||
|
if (en50221_app_ca_pmt(ca->stdcam->ca_resource, ca->stdcam->ca_session_number, capmt, size)) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to send PMT\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num)
|
||||||
|
ca->sentpmt = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void proc_csock_msg(struct dddvb_ca *ca, uint8_t *buf, int len)
|
||||||
|
{
|
||||||
|
if (*buf == '\r') {
|
||||||
|
return;
|
||||||
|
} else if (*buf == '\n') {
|
||||||
|
switch(ca->mmi_state) {
|
||||||
|
case MMI_STATE_CLOSED:
|
||||||
|
case MMI_STATE_OPEN:
|
||||||
|
if ((ca->mmi_bufp == 0) && (ca->resource_ready)) {
|
||||||
|
en50221_app_ai_entermenu(ca->stdcam->ai_resource,
|
||||||
|
ca->stdcam->ai_session_number);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MMI_STATE_ENQ:
|
||||||
|
if (ca->mmi_bufp == 0) {
|
||||||
|
en50221_app_mmi_answ(ca->stdcam->mmi_resource,
|
||||||
|
ca->stdcam->mmi_session_number,
|
||||||
|
MMI_ANSW_ID_CANCEL, NULL, 0);
|
||||||
|
} else {
|
||||||
|
en50221_app_mmi_answ(ca->stdcam->mmi_resource,
|
||||||
|
ca->stdcam->mmi_session_number,
|
||||||
|
MMI_ANSW_ID_ANSWER,
|
||||||
|
ca->mmi_buf, ca->mmi_bufp);
|
||||||
|
}
|
||||||
|
ca->mmi_state = MMI_STATE_OPEN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MMI_STATE_MENU:
|
||||||
|
ca->mmi_buf[ca->mmi_bufp] = 0;
|
||||||
|
en50221_app_mmi_menu_answ(ca->stdcam->mmi_resource,
|
||||||
|
ca->stdcam->mmi_session_number,
|
||||||
|
atoi(ca->mmi_buf));
|
||||||
|
ca->mmi_state = MMI_STATE_OPEN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ca->mmi_bufp = 0;
|
||||||
|
} else {
|
||||||
|
if (ca->mmi_bufp < (sizeof(ca->mmi_buf) - 1)) {
|
||||||
|
ca->mmi_buf[ca->mmi_bufp++] = *buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int proc_csock(struct dddvb_ca *ca)
|
||||||
|
{
|
||||||
|
uint8_t buf[1024];
|
||||||
|
int len, i, res;
|
||||||
|
|
||||||
|
if (ca->stdcam == NULL)
|
||||||
|
return -1;
|
||||||
|
while ((len = recv(ca->sock, buf, 1, 0)) >= 0) {
|
||||||
|
if (len == 0)
|
||||||
|
goto release;
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno != EAGAIN)
|
||||||
|
goto release;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
proc_csock_msg(ca, buf, len);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
release:
|
||||||
|
close(ca->sock);
|
||||||
|
ca->sock = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_ci(struct dddvb_ca *ca)
|
||||||
|
{
|
||||||
|
uint8_t sec[4096];
|
||||||
|
uint32_t pmt_count, tdt_count;
|
||||||
|
int len;
|
||||||
|
int sock, i;
|
||||||
|
struct sockaddr sadr;
|
||||||
|
char port[6];
|
||||||
|
|
||||||
|
snprintf(port, sizeof(port), "%u", (uint16_t) (8888 + ca->nr));
|
||||||
|
sock = streamsock(port, AF_INET, &sadr);
|
||||||
|
if (listen(sock, 4) < 0) {
|
||||||
|
dbgprintf(DEBUG_CA, "listen error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca->sock = -1;
|
||||||
|
|
||||||
|
while (1) {//!ca->exit) {
|
||||||
|
struct timeval timeout;
|
||||||
|
uint32_t count = 0;
|
||||||
|
int num;
|
||||||
|
int mfd;
|
||||||
|
fd_set fds;
|
||||||
|
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 200000;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
if (ca->sock < 0) {
|
||||||
|
FD_SET(sock, &fds);
|
||||||
|
num = select(sock + 1, &fds, NULL, NULL, &timeout);
|
||||||
|
} else {
|
||||||
|
FD_SET(ca->sock, &fds);
|
||||||
|
num = select(ca->sock + 1, &fds, NULL, NULL, &timeout);
|
||||||
|
}
|
||||||
|
if (num > 0) {
|
||||||
|
if (ca->sock < 0) {
|
||||||
|
if (FD_ISSET(sock, &fds)) {
|
||||||
|
socklen_t len;
|
||||||
|
struct sockaddr cadr;
|
||||||
|
|
||||||
|
ca->sock = accept(sock, &cadr, &len);
|
||||||
|
if (ca->sock >= 0) {
|
||||||
|
set_nonblock(ca->sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (FD_ISSET(ca->sock, &fds)) {
|
||||||
|
proc_csock(ca);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&ca->mutex);
|
||||||
|
if (!ca->state) {
|
||||||
|
pthread_mutex_unlock(&ca->mutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ca->setpmt) {
|
||||||
|
dbgprintf(DEBUG_CA, "got new PMT %08x\n", ca->pmt_new);
|
||||||
|
memcpy(ca->pmt, ca->pmt_new, sizeof(ca->pmt));
|
||||||
|
memset(ca->pmt_old, 0, sizeof(ca->pmt_old));
|
||||||
|
for (i = 0; i < MAX_PMT; i++)
|
||||||
|
ca->ca_pmt_version[i] = -1;
|
||||||
|
ca->sentpmt = 0;
|
||||||
|
ca->setpmt = 0;
|
||||||
|
pmt_count = 0;
|
||||||
|
tdt_count = 0;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&ca->mutex);
|
||||||
|
|
||||||
|
if (!ca->sentpmt)
|
||||||
|
handle_pmts(ca);
|
||||||
|
else {
|
||||||
|
pmt_count++;
|
||||||
|
if (pmt_count == 10) {
|
||||||
|
//handle_pmts(ca);
|
||||||
|
pmt_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tdt_count++;
|
||||||
|
if (tdt_count == 10) {
|
||||||
|
//handle_tdt(ca);
|
||||||
|
tdt_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int set_pmt(struct dddvb_ca *ca, uint32_t *pmt)
|
||||||
|
{
|
||||||
|
dbgprintf(DEBUG_CA, "set_pmt %08x %08x %08x\n", pmt[0], pmt[1], pmt[2]);
|
||||||
|
pthread_mutex_lock(&ca->mutex);
|
||||||
|
ca->setpmt = 1;
|
||||||
|
memcpy(ca->pmt_new, pmt, sizeof(ca->pmt_new));
|
||||||
|
pthread_mutex_unlock(&ca->mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ci_poll(struct dddvb_ca *ca)
|
||||||
|
{
|
||||||
|
while (!ca->dd->exit) {
|
||||||
|
ca->stdcam->poll(ca->stdcam);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int mmi_close_callback(void *arg, uint8_t slot_id, uint16_t snum,
|
||||||
|
uint8_t cmd_id, uint8_t delay)
|
||||||
|
{
|
||||||
|
struct dddvb_ca *ca = arg;
|
||||||
|
|
||||||
|
ca->mmi_state = MMI_STATE_CLOSED;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mmi_display_control_callback(void *arg, uint8_t slot_id, uint16_t snum,
|
||||||
|
uint8_t cmd_id, uint8_t mmi_mode)
|
||||||
|
{
|
||||||
|
struct dddvb_ca *ca = arg;
|
||||||
|
struct en50221_app_mmi_display_reply_details reply;
|
||||||
|
|
||||||
|
if (cmd_id != MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE) {
|
||||||
|
en50221_app_mmi_display_reply(ca->stdcam->mmi_resource, snum,
|
||||||
|
MMI_DISPLAY_REPLY_ID_UNKNOWN_CMD_ID, &reply);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only support high level mode
|
||||||
|
if (mmi_mode != MMI_MODE_HIGH_LEVEL) {
|
||||||
|
en50221_app_mmi_display_reply(ca->stdcam->mmi_resource, snum,
|
||||||
|
MMI_DISPLAY_REPLY_ID_UNKNOWN_MMI_MODE, &reply);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.u.mode_ack.mmi_mode = mmi_mode;
|
||||||
|
en50221_app_mmi_display_reply(ca->stdcam->mmi_resource, snum,
|
||||||
|
MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK, &reply);
|
||||||
|
ca->mmi_state = MMI_STATE_OPEN;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mmi_enq_callback(void *arg, uint8_t slot_id, uint16_t snum,
|
||||||
|
uint8_t blind_answer, uint8_t expected_answer_length,
|
||||||
|
uint8_t *text, uint32_t text_size)
|
||||||
|
{
|
||||||
|
struct dddvb_ca *ca = arg;
|
||||||
|
|
||||||
|
if (ca->sock >= 0) {
|
||||||
|
sendstring(ca->sock, "%.*s: ", text_size, text);
|
||||||
|
}
|
||||||
|
//mmi_enq_blind = blind_answer;
|
||||||
|
//mmi_enq_length = expected_answer_length;
|
||||||
|
ca->mmi_state = MMI_STATE_ENQ;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mmi_menu_callback(void *arg, uint8_t slot_id, uint16_t snum,
|
||||||
|
struct en50221_app_mmi_text *title,
|
||||||
|
struct en50221_app_mmi_text *sub_title,
|
||||||
|
struct en50221_app_mmi_text *bottom,
|
||||||
|
uint32_t item_count, struct en50221_app_mmi_text *items,
|
||||||
|
uint32_t item_raw_length, uint8_t *items_raw)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
struct dddvb_ca *ca = arg;
|
||||||
|
|
||||||
|
if (ca->sock >= 0) {
|
||||||
|
if (title->text_length)
|
||||||
|
sendstring(ca->sock, "%.*s\n", title->text_length, title->text);
|
||||||
|
if (sub_title->text_length)
|
||||||
|
sendstring(ca->sock, "%.*s\n", sub_title->text_length, sub_title->text);
|
||||||
|
for (i = 0; i < item_count; i++)
|
||||||
|
sendstring(ca->sock, "%i. %.*s\n", i + 1, items[i].text_length, items[i].text);
|
||||||
|
if (bottom->text_length)
|
||||||
|
sendstring(ca->sock, "%.*s\n", bottom->text_length, bottom->text);
|
||||||
|
}
|
||||||
|
ca->mmi_state = MMI_STATE_MENU;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int init_ca_stack(struct dddvb_ca *ca)
|
||||||
|
{
|
||||||
|
ca->tl = en50221_tl_create(1, 16);
|
||||||
|
if (ca->tl == NULL) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to create transport layer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ca->sl = en50221_sl_create(ca->tl, 16);
|
||||||
|
if (ca->sl == NULL) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to create session layer\n");
|
||||||
|
en50221_tl_destroy(ca->tl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ca->stdcam = en50221_stdcam_llci_create(ca->fd, 0, ca->tl, ca->sl);
|
||||||
|
if (!ca->stdcam) {
|
||||||
|
dbgprintf(DEBUG_CA, "Failed to create stdcam\n");
|
||||||
|
en50221_sl_destroy(ca->sl);
|
||||||
|
en50221_tl_destroy(ca->tl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ca->stdcam->ai_resource) {
|
||||||
|
en50221_app_ai_register_callback(ca->stdcam->ai_resource, ai_callback, ca);
|
||||||
|
}
|
||||||
|
if (ca->stdcam->ca_resource) {
|
||||||
|
en50221_app_ca_register_info_callback(ca->stdcam->ca_resource, ca_info_callback, ca);
|
||||||
|
}
|
||||||
|
if (ca->stdcam->mmi_resource) {
|
||||||
|
en50221_app_mmi_register_close_callback(ca->stdcam->mmi_resource, mmi_close_callback, ca);
|
||||||
|
en50221_app_mmi_register_display_control_callback(ca->stdcam->mmi_resource,
|
||||||
|
mmi_display_control_callback, ca);
|
||||||
|
en50221_app_mmi_register_enq_callback(ca->stdcam->mmi_resource, mmi_enq_callback, ca);
|
||||||
|
en50221_app_mmi_register_menu_callback(ca->stdcam->mmi_resource, mmi_menu_callback, ca);
|
||||||
|
en50221_app_mmi_register_list_callback(ca->stdcam->mmi_resource, mmi_menu_callback, ca);
|
||||||
|
} else {
|
||||||
|
dbgprintf(DEBUG_CA,
|
||||||
|
"CAM Menus are not supported by this interface hardware\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int init_ca(struct dddvb *dd, int a, int f, int fd)
|
||||||
|
{
|
||||||
|
struct dddvb_ca *ca;
|
||||||
|
char fname[80];
|
||||||
|
|
||||||
|
ca = &dd->dvbca[dd->dvbca_num];
|
||||||
|
ca->dd = dd;
|
||||||
|
ca->anum = a;
|
||||||
|
ca->fnum = f;
|
||||||
|
ca->nr = dd->dvbca_num + 1;
|
||||||
|
ca->fd = fd;
|
||||||
|
pthread_mutex_init(&ca->mutex, 0);
|
||||||
|
|
||||||
|
init_ca_stack(ca);
|
||||||
|
|
||||||
|
pthread_create(&ca->poll_pt, NULL, (void *) ci_poll, ca);
|
||||||
|
pthread_create(&ca->pt, NULL, (void *) handle_ci, ca);
|
||||||
|
|
||||||
|
sprintf(fname, "/dev/dvb/adapter%d/ci%d", a, f);
|
||||||
|
ca->ci_wfd = open(fname, O_WRONLY);
|
||||||
|
ca->ci_rfd = open(fname, O_RDONLY);
|
||||||
|
dd->dvbca_num++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dddvb_ca_write(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len)
|
||||||
|
{
|
||||||
|
dbgprintf(DEBUG_CA, "ca_write\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dddvb_ca_read(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len)
|
||||||
|
{
|
||||||
|
dbgprintf(DEBUG_CA, "ca_read\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dddvb_ca_set_pmts(struct dddvb *dd, uint32_t nr, uint8_t **pmts)
|
||||||
|
{
|
||||||
|
struct dddvb_ca *ca = &dd->dvbca[nr];
|
||||||
|
|
||||||
|
dbgprintf(DEBUG_CA, "ca_set_pmt\n");
|
||||||
|
return set_pmts(ca, pmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int scan_dvbca(struct dddvb *dd)
|
||||||
|
{
|
||||||
|
int a, f, fd;
|
||||||
|
char fname[80];
|
||||||
|
|
||||||
|
for (a = 0; a < 16; a++) {
|
||||||
|
for (f = 0; f < 16; f++) {
|
||||||
|
sprintf(fname, "/dev/dvb/adapter%d/ca%d", a, f);
|
||||||
|
fd = open(fname, O_RDWR);
|
||||||
|
if (fd >= 0) {
|
||||||
|
init_ca(dd, a, f, fd);
|
||||||
|
//close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dbgprintf(DEBUG_CA, "Found %d CA interfaces\n", dd->dvbca_num);
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
LIBDDDVB_EXPORTED uint32_t dddvb_debug;
|
LIBDDDVB_EXPORTED uint32_t dddvb_debug;
|
||||||
LIBDDDVB_EXPORTED struct dddvb *global_dd = NULL;
|
LIBDDDVB_EXPORTED struct dddvb *global_dd = NULL;
|
||||||
LIBDDDVB_EXPORTED pthread_mutex_t dddvb_mutex = PTHREAD_MUTEX_INITIALIZER;
|
LIBDDDVB_EXPORTED pthread_mutex_t dddvb_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
@ -13,7 +13,10 @@
|
|||||||
#include <linux/dvb/net.h>
|
#include <linux/dvb/net.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <libdvben50221/en50221_stdcam.h>
|
||||||
|
|
||||||
#define DDDVB_MAX_DVB_FE 256
|
#define DDDVB_MAX_DVB_FE 256
|
||||||
|
#define DDDVB_MAX_DVB_CA 256
|
||||||
|
|
||||||
#define DDDVB_MAX_SOURCE 4
|
#define DDDVB_MAX_SOURCE 4
|
||||||
|
|
||||||
@ -74,6 +77,7 @@ struct dddvb_fe {
|
|||||||
uint32_t quality;
|
uint32_t quality;
|
||||||
int64_t strength;
|
int64_t strength;
|
||||||
int64_t cnr;
|
int64_t cnr;
|
||||||
|
int64_t ber;
|
||||||
int first;
|
int first;
|
||||||
|
|
||||||
uint32_t tune;
|
uint32_t tune;
|
||||||
@ -85,6 +89,44 @@ struct dddvb_fe {
|
|||||||
struct dddvb_status status;
|
struct dddvb_status status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dddvb_ca {
|
||||||
|
struct dddvb *dd;
|
||||||
|
struct osstrm *stream;
|
||||||
|
int fd;
|
||||||
|
int ci_rfd;
|
||||||
|
int ci_wfd;
|
||||||
|
uint32_t type;
|
||||||
|
int anum;
|
||||||
|
int fnum;
|
||||||
|
int state;
|
||||||
|
int nr;
|
||||||
|
int input;
|
||||||
|
|
||||||
|
pthread_t pt;
|
||||||
|
pthread_t poll_pt;
|
||||||
|
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
struct en50221_transport_layer *tl;
|
||||||
|
struct en50221_session_layer *sl;
|
||||||
|
struct en50221_stdcam *stdcam;
|
||||||
|
int resource_ready;
|
||||||
|
int sentpmt;
|
||||||
|
int moveca;
|
||||||
|
int ca_pmt_version[MAX_PMT];
|
||||||
|
int data_pmt_version;
|
||||||
|
|
||||||
|
int setpmt;
|
||||||
|
uint32_t pmt[MAX_PMT];
|
||||||
|
uint32_t pmt_new[MAX_PMT];
|
||||||
|
uint32_t pmt_old[MAX_PMT];
|
||||||
|
|
||||||
|
int mmi_state;
|
||||||
|
uint8_t mmi_buf[16];
|
||||||
|
int mmi_bufp;
|
||||||
|
int sock;
|
||||||
|
};
|
||||||
|
|
||||||
struct dddvb {
|
struct dddvb {
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
pthread_mutex_t uni_lock;
|
pthread_mutex_t uni_lock;
|
||||||
@ -103,7 +145,11 @@ struct dddvb {
|
|||||||
uint32_t dvbfe_num;
|
uint32_t dvbfe_num;
|
||||||
uint32_t scif_type;
|
uint32_t scif_type;
|
||||||
|
|
||||||
|
uint32_t dvbca_num;
|
||||||
|
int exit;
|
||||||
|
|
||||||
struct dddvb_fe dvbfe[DDDVB_MAX_DVB_FE];
|
struct dddvb_fe dvbfe[DDDVB_MAX_DVB_FE];
|
||||||
|
struct dddvb_ca dvbca[DDDVB_MAX_DVB_CA];
|
||||||
};
|
};
|
||||||
|
|
||||||
int dddvb_dvb_init(struct dddvb *dd);
|
int dddvb_dvb_init(struct dddvb *dd);
|
||||||
@ -112,7 +158,7 @@ int parse_config(struct dddvb *dd, char *name, char *sec,
|
|||||||
void dddvb_fe_handle(struct dddvb_fe *fe);
|
void dddvb_fe_handle(struct dddvb_fe *fe);
|
||||||
int dddvb_fe_tune(struct dddvb_fe *fe, struct dddvb_params *p);
|
int dddvb_fe_tune(struct dddvb_fe *fe, struct dddvb_params *p);
|
||||||
int dddvb_fe_start(struct dddvb_fe *fe);
|
int dddvb_fe_start(struct dddvb_fe *fe);
|
||||||
|
int scan_dvbca(struct dddvb *dd);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _DDDVB_H_ */
|
#endif /* _DDDVB_H_ */
|
||||||
|
@ -533,6 +533,9 @@ static int open_fe(struct dddvb_fe *fe)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include "dvb_quality.c"
|
||||||
|
|
||||||
static void get_stats(struct dddvb_fe *fe)
|
static void get_stats(struct dddvb_fe *fe)
|
||||||
{
|
{
|
||||||
uint16_t sig = 0, snr = 0;
|
uint16_t sig = 0, snr = 0;
|
||||||
@ -545,7 +548,7 @@ static void get_stats(struct dddvb_fe *fe)
|
|||||||
ioctl(fe->fd, FE_READ_STATUS, &stat);
|
ioctl(fe->fd, FE_READ_STATUS, &stat);
|
||||||
fe->stat = stat;
|
fe->stat = stat;
|
||||||
fe->lock = (stat == 0x1f) ? 1 : 0;
|
fe->lock = (stat == 0x1f) ? 1 : 0;
|
||||||
//calc_lq(fe);
|
calc_lq(fe);
|
||||||
if (!get_stat(fe->fd, DTV_STAT_SIGNAL_STRENGTH, &st)) {
|
if (!get_stat(fe->fd, DTV_STAT_SIGNAL_STRENGTH, &st)) {
|
||||||
|
|
||||||
fe->strength = str = st.stat[0].svalue;
|
fe->strength = str = st.stat[0].svalue;
|
||||||
@ -886,6 +889,7 @@ int dddvb_dvb_init(struct dddvb *dd)
|
|||||||
parse_config(dd, "", "scif", &scif_config);
|
parse_config(dd, "", "scif", &scif_config);
|
||||||
set_lnb(dd, 0, 0, 9750000, 10600000, 11700000);
|
set_lnb(dd, 0, 0, 9750000, 10600000, 11700000);
|
||||||
parse_config(dd, "", "LNB", &lnb_config);
|
parse_config(dd, "", "LNB", &lnb_config);
|
||||||
|
scan_dvbca(dd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
388
lib/src/dvb_quality.c
Normal file
388
lib/src/dvb_quality.c
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
static int32_t Log10x100(uint32_t x)
|
||||||
|
{
|
||||||
|
static uint32_t LookupTable[100] = {
|
||||||
|
101157945, 103514217, 105925373, 108392691, 110917482,
|
||||||
|
113501082, 116144861, 118850223, 121618600, 124451461, // 800.5 - 809.5
|
||||||
|
127350308, 130316678, 133352143, 136458314, 139636836,
|
||||||
|
142889396, 146217717, 149623566, 153108746, 156675107, // 810.5 - 819.5
|
||||||
|
160324539, 164058977, 167880402, 171790839, 175792361,
|
||||||
|
179887092, 184077200, 188364909, 192752491, 197242274, // 820.5 - 829.5
|
||||||
|
201836636, 206538016, 211348904, 216271852, 221309471,
|
||||||
|
226464431, 231739465, 237137371, 242661010, 248313311, // 830.5 - 839.5
|
||||||
|
254097271, 260015956, 266072506, 272270131, 278612117,
|
||||||
|
285101827, 291742701, 298538262, 305492111, 312607937, // 840.5 - 849.5
|
||||||
|
319889511, 327340695, 334965439, 342767787, 350751874,
|
||||||
|
358921935, 367282300, 375837404, 384591782, 393550075, // 850.5 - 859.5
|
||||||
|
402717034, 412097519, 421696503, 431519077, 441570447,
|
||||||
|
451855944, 462381021, 473151259, 484172368, 495450191, // 860.5 - 869.5
|
||||||
|
506990708, 518800039, 530884444, 543250331, 555904257,
|
||||||
|
568852931, 582103218, 595662144, 609536897, 623734835, // 870.5 - 879.5
|
||||||
|
638263486, 653130553, 668343918, 683911647, 699841996,
|
||||||
|
716143410, 732824533, 749894209, 767361489, 785235635, // 880.5 - 889.5
|
||||||
|
803526122, 822242650, 841395142, 860993752, 881048873,
|
||||||
|
901571138, 922571427, 944060876, 966050879, 988553095, // 890.5 - 899.5
|
||||||
|
};
|
||||||
|
int32_t y = 800;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if( x == 0 ) return 0;
|
||||||
|
|
||||||
|
if( x >= 1000000000 ) {
|
||||||
|
x /= 10;
|
||||||
|
y += 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (x < 100000000 ) {
|
||||||
|
x *= 10;
|
||||||
|
y -= 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( i < 100 && x > LookupTable[i] ) i += 1;
|
||||||
|
y += i;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t berq_rs(uint32_t BERNumerator, uint32_t BERDenominator)
|
||||||
|
{
|
||||||
|
int32_t LogBER = Log10x100(BERDenominator) - Log10x100(BERNumerator);
|
||||||
|
int32_t BERQual = 100;
|
||||||
|
|
||||||
|
if ( BERNumerator == 0 )
|
||||||
|
return 100;
|
||||||
|
if (LogBER < 700) {
|
||||||
|
if (LogBER < 300)
|
||||||
|
BERQual = 0;
|
||||||
|
else
|
||||||
|
BERQual = (LogBER + 5) / 5 - 40;
|
||||||
|
}
|
||||||
|
return BERQual;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t berq_bch(uint32_t BERNumerator, uint32_t BERDenominator)
|
||||||
|
{
|
||||||
|
int32_t LogBER = Log10x100(BERDenominator) - Log10x100(BERNumerator);
|
||||||
|
int32_t BERQual = 100;
|
||||||
|
|
||||||
|
if (BERNumerator == 0 )
|
||||||
|
return 100;
|
||||||
|
if (LogBER < 700) {
|
||||||
|
if( LogBER < 400 )
|
||||||
|
BERQual = 0;
|
||||||
|
else
|
||||||
|
BERQual = 40;
|
||||||
|
}
|
||||||
|
return BERQual;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32_t ber_quality(struct dddvb_fe *fe)
|
||||||
|
{
|
||||||
|
struct dtv_fe_stats ber;
|
||||||
|
|
||||||
|
get_stat(fe->fd, DTV_STAT_PRE_ERROR_BIT_COUNT, &ber);
|
||||||
|
get_stat(fe->fd, DTV_STAT_PRE_TOTAL_BIT_COUNT, &ber);
|
||||||
|
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t dvbsq(uint32_t snr, uint32_t fec,
|
||||||
|
uint32_t ber_num, uint32_t ber_den)
|
||||||
|
{
|
||||||
|
int32_t SignalToNoiseRel = -1000;
|
||||||
|
int32_t Quality = 0;
|
||||||
|
int32_t BERQuality = berq_rs(ber_num, ber_den);
|
||||||
|
|
||||||
|
// SNR Values for quasi errorfree reception from Nordig 2.2
|
||||||
|
static const int32_t DVBS_SN[] = {
|
||||||
|
// 1/2 2/3 3/4 5/6 7/8
|
||||||
|
0, 38, 56, 67, 0, 77, 0, 84
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fec >= FEC_NONE && fec <= FEC_7_8 )
|
||||||
|
SignalToNoiseRel = snr / 100 - DVBS_SN[fec];
|
||||||
|
|
||||||
|
if( SignalToNoiseRel < -70 )
|
||||||
|
Quality = 0;
|
||||||
|
else if( SignalToNoiseRel < 30 )
|
||||||
|
Quality = ((SignalToNoiseRel + 70) * BERQuality) / 100;
|
||||||
|
else
|
||||||
|
Quality = BERQuality;
|
||||||
|
return Quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t dvbs2q(int32_t snr, uint32_t fec, uint32_t mod,
|
||||||
|
uint32_t ber_num, uint32_t ber_den)
|
||||||
|
{
|
||||||
|
static const int32_t DVBS2_SN_QPSK[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5
|
||||||
|
0, 20, 41, 50, 57, 62, 0, 0, 72, 0, 32, 74, 7,
|
||||||
|
};
|
||||||
|
static const int32_t DVBS2_SN_8PSK[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5
|
||||||
|
0, 0, 76, 89, 0, 104, 0, 0, 117, 0, 65, 120, 0,
|
||||||
|
};
|
||||||
|
static const int32_t DVBS2_SN_16APSK[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5
|
||||||
|
0, 0, 100, 112, 120, 126, 0, 0, 139, 0, 0, 141, 0,
|
||||||
|
};
|
||||||
|
static const int32_t DVBS2_SN_32APSK[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5
|
||||||
|
0, 0, 0, 137, 146, 153, 0, 0, 167, 0, 0, 171, 0,
|
||||||
|
};
|
||||||
|
int32_t BERQuality = berq_bch(ber_num, ber_den);
|
||||||
|
int32_t Quality = 0;
|
||||||
|
int32_t SignalToNoiseRel = -1000, snc = 0;
|
||||||
|
|
||||||
|
if (fec > FEC_2_5 )
|
||||||
|
return 0;
|
||||||
|
switch (mod) {
|
||||||
|
case QPSK:
|
||||||
|
snc = DVBS2_SN_QPSK[fec];
|
||||||
|
break;
|
||||||
|
case PSK_8:
|
||||||
|
snc = DVBS2_SN_8PSK[fec];
|
||||||
|
break;
|
||||||
|
case APSK_16:
|
||||||
|
snc = DVBS2_SN_16APSK[fec];
|
||||||
|
break;
|
||||||
|
case APSK_32:
|
||||||
|
snc = DVBS2_SN_32APSK[fec];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
SignalToNoiseRel = snr / 100 - snc;
|
||||||
|
|
||||||
|
if (SignalToNoiseRel < -30 )
|
||||||
|
Quality = 0;
|
||||||
|
else if( SignalToNoiseRel < 30 )
|
||||||
|
Quality = ((SignalToNoiseRel + 30) * BERQuality) / 60;
|
||||||
|
else
|
||||||
|
Quality = 100;
|
||||||
|
return Quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t dvbcq(int32_t snr, uint32_t mod,
|
||||||
|
uint32_t ber_num, uint32_t ber_den)
|
||||||
|
{
|
||||||
|
int32_t SignalToNoiseRel = 0;
|
||||||
|
int32_t Quality = 0;
|
||||||
|
int32_t BERQuality = berq_rs(ber_num, ber_den);
|
||||||
|
|
||||||
|
switch (mod) {
|
||||||
|
case QAM_16: SignalToNoiseRel = snr - 200; break;
|
||||||
|
case QAM_32: SignalToNoiseRel = snr - 230; break;
|
||||||
|
case QAM_64: SignalToNoiseRel = snr - 260; break;
|
||||||
|
case QAM_128: SignalToNoiseRel = snr - 290; break;
|
||||||
|
case QAM_256: SignalToNoiseRel = snr - 320; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SignalToNoiseRel < -70)
|
||||||
|
Quality = 0;
|
||||||
|
else if (SignalToNoiseRel < 30)
|
||||||
|
Quality = ((SignalToNoiseRel + 70) * BERQuality) / 100;
|
||||||
|
else
|
||||||
|
Quality = BERQuality;
|
||||||
|
return Quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t dvbtq(int32_t snr, uint32_t mod, uint32_t fec,
|
||||||
|
uint32_t ber_num, uint32_t ber_den)
|
||||||
|
{
|
||||||
|
int32_t Quality = 0;
|
||||||
|
int32_t BERQuality = berq_rs(ber_num, ber_den);
|
||||||
|
int32_t SignalToNoiseRel = -1000, snc = 0;
|
||||||
|
|
||||||
|
// SNR Values for quasi error free reception from Nordig 2.2
|
||||||
|
static const int32_t DVBT_SN_QPSK[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5
|
||||||
|
0, 51, 69, 79, 0, 89, 0, 97, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
static const int32_t DVBT_SN_QAM16[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5
|
||||||
|
0, 108, 131, 146, 0, 156, 0, 160, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
static const int32_t DVBT_SN_QAM64[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5
|
||||||
|
0, 165, 187, 202, 0, 216, 0, 225, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fec > FEC_2_5 )
|
||||||
|
return 0;
|
||||||
|
switch (mod) {
|
||||||
|
case QPSK:
|
||||||
|
snc = DVBT_SN_QPSK[fec];
|
||||||
|
break;
|
||||||
|
case QAM_16:
|
||||||
|
snc = DVBT_SN_QAM16[fec];
|
||||||
|
break;
|
||||||
|
case QAM_64:
|
||||||
|
snc = DVBT_SN_QAM64[fec];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SignalToNoiseRel = snr / 100 - snc;
|
||||||
|
|
||||||
|
if (SignalToNoiseRel < -70 )
|
||||||
|
Quality = 0;
|
||||||
|
else if (SignalToNoiseRel < 30)
|
||||||
|
Quality = ((SignalToNoiseRel + 70) * BERQuality)/100;
|
||||||
|
else
|
||||||
|
Quality = BERQuality;
|
||||||
|
|
||||||
|
return Quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t dvbt2q(int32_t snr, uint32_t mod, uint32_t fec, uint32_t trans, uint32_t pilot,
|
||||||
|
uint32_t ber_num, uint32_t ber_den)
|
||||||
|
{
|
||||||
|
int32_t Quality = 0;
|
||||||
|
int32_t BERQuality = berq_bch(ber_num, ber_den);
|
||||||
|
int32_t SignalToNoiseRel = -1000, snc = 0;
|
||||||
|
|
||||||
|
static const int32_t QE_SN_16K_QPSK[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3
|
||||||
|
0, 32, 59, 68, 74, 80, 0, 0, 0, 0, 49, 0, 24, 0, 15 };
|
||||||
|
static const int32_t QE_SN_16K_16QAM[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3
|
||||||
|
0, 82,116,130,136,141, 0, 0, 0, 0, 104, 0, 74, 0, 62 };
|
||||||
|
static const int32_t QE_SN_16K_64QAM[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3
|
||||||
|
0,123,165,181,190,197, 0, 0, 0, 0, 151, 0,114, 0,101 };
|
||||||
|
static const int32_t QE_SN_16K_256QAM[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3
|
||||||
|
0,164,211,232,246,255, 0, 0, 0, 0, 202, 0,153, 0,137 };
|
||||||
|
static const int32_t QE_SN_64K_QPSK[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3
|
||||||
|
0, 35, 56, 66, 72, 77, 0, 0, 0, 0, 47, 0, 22, 0, 13 };
|
||||||
|
static const int32_t QE_SN_64K_16QAM[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3
|
||||||
|
0, 87,114,125,133,138, 0, 0, 0, 0, 101, 0, 72, 0, 60 };
|
||||||
|
static const int32_t QE_SN_64K_64QAM[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3
|
||||||
|
0,130,162,177,187,194, 0, 0, 0, 0, 148, 0,111, 0, 98 };
|
||||||
|
static const int32_t QE_SN_64K_256QAM[] = {
|
||||||
|
// 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3
|
||||||
|
0,170,208,229,243,251, 0, 0, 0, 0, 194, 0,148, 0,132 };
|
||||||
|
|
||||||
|
if (trans == TRANSMISSION_MODE_16K) {
|
||||||
|
switch (mod) {
|
||||||
|
case QPSK:
|
||||||
|
snc = QE_SN_16K_QPSK[fec];
|
||||||
|
break;
|
||||||
|
case QAM_16:
|
||||||
|
snc = QE_SN_16K_16QAM[fec];
|
||||||
|
break;
|
||||||
|
case QAM_64:
|
||||||
|
snc = QE_SN_16K_64QAM[fec];
|
||||||
|
break;
|
||||||
|
case QAM_256:
|
||||||
|
snc = QE_SN_16K_256QAM[fec];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trans == TRANSMISSION_MODE_C3780 + 1) { /* TRANSMISSION_MODE_64K */
|
||||||
|
switch (mod) {
|
||||||
|
case QPSK:
|
||||||
|
snc = QE_SN_64K_QPSK[fec];
|
||||||
|
break;
|
||||||
|
case QAM_16:
|
||||||
|
snc = QE_SN_64K_16QAM[fec];
|
||||||
|
break;
|
||||||
|
case QAM_64:
|
||||||
|
snc = QE_SN_64K_64QAM[fec];
|
||||||
|
break;
|
||||||
|
case QAM_256:
|
||||||
|
snc = QE_SN_64K_256QAM[fec];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snc) {
|
||||||
|
SignalToNoiseRel = snr - snc;
|
||||||
|
#if 0 //FIXME
|
||||||
|
if (PilotPattern >= DVBT2_PP3 &&
|
||||||
|
PilotPattern <= DVBT2_PP4 )
|
||||||
|
SignalToNoiseRel += 5;
|
||||||
|
else if
|
||||||
|
( PilotPattern >= DVBT2_PP5 && PilotPattern <= DVBT2_PP8 )
|
||||||
|
SignalToNoiseRel += 10;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if( SignalToNoiseRel < -30 )
|
||||||
|
Quality = 0;
|
||||||
|
else if (SignalToNoiseRel < 30 )
|
||||||
|
Quality = ((SignalToNoiseRel + 30) * BERQuality)/60;
|
||||||
|
else
|
||||||
|
Quality = 100;
|
||||||
|
|
||||||
|
return Quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calc_lq(struct dddvb_fe *fe)
|
||||||
|
{
|
||||||
|
struct dtv_fe_stats st;
|
||||||
|
int64_t str, snr;
|
||||||
|
uint32_t mod, fec, ber_num, ber_den, trans, pilot = 0, quality = 0;
|
||||||
|
|
||||||
|
get_stat(fe->fd, DTV_STAT_SIGNAL_STRENGTH, &st);
|
||||||
|
str = st.stat[0].svalue;
|
||||||
|
dbgprintf(DEBUG_DVB, "fe%d: str=%lld\n", fe->nr, str);
|
||||||
|
fe->strength = str;
|
||||||
|
str = (str * 48) / 10000 + 344;
|
||||||
|
if (str < 0)
|
||||||
|
str = 0;
|
||||||
|
if (str > 255)
|
||||||
|
str = 255;
|
||||||
|
fe->level = str;
|
||||||
|
// str: 0-255: -25dbm = 224, -65dbm = 32
|
||||||
|
// qual: 0-15 15=BER<2*10^-4 PER<10^-7
|
||||||
|
get_stat(fe->fd, DTV_STAT_CNR, &st);
|
||||||
|
snr = st.stat[0].svalue;
|
||||||
|
fe->cnr = snr;
|
||||||
|
get_property(fe->fd, DTV_INNER_FEC, &fec);
|
||||||
|
fe->param.param[PARAM_FEC] = fec;
|
||||||
|
get_property(fe->fd, DTV_MODULATION, &mod);
|
||||||
|
fe->param.param[PARAM_MTYPE] = mod;
|
||||||
|
|
||||||
|
get_stat(fe->fd, DTV_STAT_PRE_ERROR_BIT_COUNT, &st);
|
||||||
|
ber_num = st.stat[0].uvalue;
|
||||||
|
get_stat(fe->fd, DTV_STAT_PRE_TOTAL_BIT_COUNT, &st);
|
||||||
|
ber_den = st.stat[0].uvalue;
|
||||||
|
|
||||||
|
dbgprintf(DEBUG_DVB, "fe%d: snr=%lld ber=%llu/%llu\n",
|
||||||
|
fe->nr, snr, ber_num, ber_den);
|
||||||
|
dbgprintf(DEBUG_DVB, "fe%d: fec=%u mod=%u\n", fe->nr, fec, mod);
|
||||||
|
switch (fe->n_param.param[PARAM_MSYS]) {
|
||||||
|
case SYS_DVBS:
|
||||||
|
quality = dvbsq(snr, fec, ber_num, ber_den);
|
||||||
|
break;
|
||||||
|
case SYS_DVBS2:
|
||||||
|
quality = dvbs2q(snr, fec, mod, ber_num, ber_den);
|
||||||
|
break;
|
||||||
|
case SYS_DVBC_ANNEX_A:
|
||||||
|
quality = dvbcq(snr, mod, ber_num, ber_den);
|
||||||
|
break;
|
||||||
|
case SYS_DVBT:
|
||||||
|
quality = dvbtq(snr, mod, fec, ber_num, ber_den);
|
||||||
|
break;
|
||||||
|
case SYS_DVBT2:
|
||||||
|
get_property(fe->fd, DTV_TRANSMISSION_MODE, &trans);
|
||||||
|
dbgprintf(DEBUG_DVB, "fe%d: trans=%u pilot=%u\n", fe->nr, trans, pilot);
|
||||||
|
quality = dvbt2q(snr, mod, fec, trans, pilot, ber_num, ber_den);
|
||||||
|
break;
|
||||||
|
case SYS_DVBC2:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fe->quality = quality;
|
||||||
|
dbgprintf(DEBUG_DVB, "fe%d: level=%u quality=%u\n", fe->nr, fe->level, fe->quality);
|
||||||
|
}
|
||||||
|
|
@ -60,6 +60,10 @@ LIBDDDVB_EXPORTED struct dddvb *dddvb_init(char *config, uint32_t flags);
|
|||||||
LIBDDDVB_EXPORTED int dddvb_dvb_tune(struct dddvb_fe *fe, struct dddvb_params *p);
|
LIBDDDVB_EXPORTED int dddvb_dvb_tune(struct dddvb_fe *fe, struct dddvb_params *p);
|
||||||
LIBDDDVB_EXPORTED struct dddvb_fe *dddvb_fe_alloc(struct dddvb *dd, uint32_t type);
|
LIBDDDVB_EXPORTED struct dddvb_fe *dddvb_fe_alloc(struct dddvb *dd, uint32_t type);
|
||||||
LIBDDDVB_EXPORTED struct dddvb_fe *dddvb_fe_alloc_num(struct dddvb *dd, uint32_t type, uint32_t num);
|
LIBDDDVB_EXPORTED struct dddvb_fe *dddvb_fe_alloc_num(struct dddvb *dd, uint32_t type, uint32_t num);
|
||||||
|
LIBDDDVB_EXPORTED int dddvb_ca_write(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len);
|
||||||
|
LIBDDDVB_EXPORTED int dddvb_ca_read(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len);
|
||||||
|
LIBDDDVB_EXPORTED int dddvb_ca_set_pmts(struct dddvb *dd, uint32_t nr, uint8_t **pmts);
|
||||||
|
|
||||||
|
|
||||||
static inline void dddvb_set_frequency(struct dddvb_params *p, uint32_t freq) {
|
static inline void dddvb_set_frequency(struct dddvb_params *p, uint32_t freq) {
|
||||||
p->param[PARAM_FREQ] = freq;
|
p->param[PARAM_FREQ] = freq;
|
||||||
@ -109,6 +113,14 @@ static inline int64_t dddvb_get_cnr(struct dddvb_fe *fe) {
|
|||||||
return fe->cnr;
|
return fe->cnr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline int64_t dddvb_get_ber(struct dddvb_fe *fe) {
|
||||||
|
return fe->ber;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint32_t dddvb_get_quality(struct dddvb_fe *fe) {
|
||||||
|
return fe->quality;
|
||||||
|
};
|
||||||
|
|
||||||
static inline void dddvb_param_init(struct dddvb_params *p) {
|
static inline void dddvb_param_init(struct dddvb_params *p) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -116,4 +128,10 @@ static inline void dddvb_param_init(struct dddvb_params *p) {
|
|||||||
p->param[i] = DDDVB_UNDEF;
|
p->param[i] = DDDVB_UNDEF;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static inline int dddvb_ca_write(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len) {
|
||||||
|
return ca_write(dd, nr, buf, len);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _LIBDDDVB_H_ */
|
#endif /* _LIBDDDVB_H_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user