From 89c636e216ca3d916ff451625b84f704b9e41ced Mon Sep 17 00:00:00 2001 From: CrazyCat Date: Tue, 2 Feb 2021 19:28:02 +0200 Subject: [PATCH] Added tune-s2 util. --- tune-s2/Makefile | 26 +++ tune-s2/README.md | 61 ++++++ tune-s2/diseqc.c | 176 ++++++++++++++++ tune-s2/diseqc.h | 40 ++++ tune-s2/kb.c | 62 ++++++ tune-s2/kb.h | 14 ++ tune-s2/tune-s2.c | 515 ++++++++++++++++++++++++++++++++++++++++++++++ tune-s2/tune-s2.h | 174 ++++++++++++++++ 8 files changed, 1068 insertions(+) create mode 100644 tune-s2/Makefile create mode 100644 tune-s2/README.md create mode 100644 tune-s2/diseqc.c create mode 100644 tune-s2/diseqc.h create mode 100644 tune-s2/kb.c create mode 100644 tune-s2/kb.h create mode 100644 tune-s2/tune-s2.c create mode 100644 tune-s2/tune-s2.h diff --git a/tune-s2/Makefile b/tune-s2/Makefile new file mode 100644 index 0000000..a5d5126 --- /dev/null +++ b/tune-s2/Makefile @@ -0,0 +1,26 @@ +SRC=kb.c diseqc.c tune-s2.c +HED=kb.h diseqc.h tune-s2.h +OBJ=kb.o diseqc.o tune-s2.o + +BIND=/usr/local/bin/ + +TARGET=tune-s2 + +all: $(TARGET) + +$(TARGET): $(OBJ) + $(CC) $(CFLG) $(OBJ) -o $(TARGET) $(CLIB) -lm + +$(OBJ): $(HED) + +install: all + cp $(TARGET) $(BIND) + +uninstall: + rm $(BIND)$(TARGET) + +clean: + rm -f $(OBJ) $(TARGET) *~ ._* + +%.o: %.c + $(CC) $(INCLUDE) -c $< -o $@ diff --git a/tune-s2/README.md b/tune-s2/README.md new file mode 100644 index 0000000..808bf9f --- /dev/null +++ b/tune-s2/README.md @@ -0,0 +1,61 @@ +# README # + + tune-s2 + +tune-s2 is a small linux app I've wrote to be able to tune my dvb devices, all I wanted it todo is control my motor, +diseqc and 22khz switch's and tune a signal. From there it waits for the user to press the 'q' button to quit the app. +This allows me to use other apps like my demux app or other apps like dvbsnoop and dvbtraffic. + +clone repo: + +git clone https://bitbucket.org/updatelee/tune-s2.git + +compile: + +make + +install it: + +sudo make install + +./tune-s2 -help + +usage: tune-s2 12224 V 20000 [options] + + -adapter N : use given adapter (default 0) + + -frontend N : use given frontend (default 0) + + -2 : use 22khz tone + + -committed N : use DiSEqC COMMITTED switch position N (1-4) + + -uncommitted N : use DiSEqC uncommitted switch position N (1-4) + + -servo N : servo delay in milliseconds (20-1000, default 20) + + -gotox NN : Drive Motor to Satellite Position NN (0-99) + + -usals N.N : orbital position + + -long N.N : site long + + -lat N.N : site lat + + -lnb lnb-type : STANDARD, UNIVERSAL, DBS, CBAND or + + -system : System DVB-S or DVB-S2 + + -modulation : modulation BPSK QPSK 8PSK + + -fec : fec 1/2, 2/3, 3/4, 3/5, 4/5, 5/6, 6/7, 8/9, 9/10, AUTO + + -rolloff : rolloff 35=0.35 25=0.25 20=0.20 0=UNKNOWN + + -inversion N : spectral inversion (OFF / ON / AUTO [default]) + + -pilot N : pilot (OFF / ON / AUTO [default]) + + -mis N : MIS # + + -help : help \ No newline at end of file diff --git a/tune-s2/diseqc.c b/tune-s2/diseqc.c new file mode 100644 index 0000000..c66d5ca --- /dev/null +++ b/tune-s2/diseqc.c @@ -0,0 +1,176 @@ +/* diseqc -- simple diseqc commands for the Linux DVB S2 API + * + * UDL (updatelee@gmail.com) + * Derived from work by: + * Igor M. Liplianin (liplianin@me.by) + * Alex Betis + * Pendragon + * + * 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. + */ + +#include "diseqc.h" + +struct dvb_diseqc_master_cmd committed_switch_cmds[] = { + { { 0xE0, 0x10, 0x38, 0xF0, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x38, 0xF4, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x38, 0xF8, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x38, 0xFC, 0x00, 0x00 }, 4 } +}; + +struct dvb_diseqc_master_cmd uncommitted_switch_cmds[] = { + { { 0xE0, 0x10, 0x39, 0xF0, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x39, 0xF1, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x39, 0xF2, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x39, 0xF3, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x39, 0xF4, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x39, 0xF5, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x39, 0xF6, 0x00, 0x00 }, 4 }, + { { 0xE0, 0x10, 0x39, 0xF7, 0x00, 0x00 }, 4 } +}; + +struct dvb_diseqc_master_cmd dir_cmd[] = +{ + { { 0xe0, 0x31, 0x68, 0xFF, 0x00, 0x00 }, 4 }, // Drive Motor East 1 step + { { 0xe0, 0x31, 0x69, 0xFF, 0x00, 0x00 }, 4 } // Drive Motor West 1 step +}; + +double radian( double number ) +{ + return number*M_PI/180; +} + +double degree( double number ) +{ + return number*180/M_PI; +} + +void motor_usals(int frontend_fd, double site_lat, double site_long, double sat_long) +{ + if (ioctl(frontend_fd, FE_SET_TONE, SEC_TONE_OFF) == -1) + perror("FE_SET_TONE ERROR!"); + usleep(20000); + + if (ioctl(frontend_fd, FE_SET_VOLTAGE, SEC_VOLTAGE_18) == -1) + perror("FE_SET_VOLTAGE ERROR!"); + usleep(20000); + + double r_eq = 6378.14; // Earth radius + double r_sat = 42164.57; // Distance from earth centre to satellite + + site_lat = radian(site_lat); + site_long = radian(site_long); + sat_long = radian(sat_long); + + double declination = degree( atan( r_eq * sin(site_lat) / ( (r_sat - r_eq) + (r_eq * (1 - cos(site_lat))) ) ) ); + + // x = [0], y = [1], z = [2] + double dishVector[3] = { r_eq * cos(site_lat), 0, r_eq * sin(site_lat) }; + double satVector[3] = { r_sat * cos(site_long - sat_long), r_sat * sin(site_long - sat_long), 0 }; + double satPointing[3] = { satVector[0] - dishVector[0], satVector[1] - dishVector[1], satVector[2] - dishVector[2] } ; + + double motor_angle = degree( atan( satPointing[1]/satPointing[0] ) ); + + int sixteenths = fabs(motor_angle) * 16.0 + 0.5; + int angle_1, angle_2; + angle_1 = motor_angle > 0.0 ? 0xd0 : 0xe0; + angle_1 |= sixteenths >> 8; + angle_2 = sixteenths & 0xff; + + printf("Long: %.2f, Lat: %.2f, Orbital Pos: %.2f, RotorCmd: %02x %02x, motor_angle: %.2f declination: %.2f\n", degree(site_long), degree(site_lat), degree(sat_long), angle_1, angle_2, motor_angle, declination); + + struct dvb_diseqc_master_cmd usals_cmd = { { 0xe0, 0x31, 0x6e, angle_1, angle_2, 0x00 }, 5 }; + + diseqc_send_msg(frontend_fd, usals_cmd); + + printf("Waiting for motor to move, either wait 45sec or hit 's' to skip\n"); + + int c; + int sec = time(NULL); + set_conio_terminal_mode(); + do { + sleep(1); + if ( kbhit() ) + c = kbgetchar(); /* consume the character */ + } while( (char)c != 's' && sec+45 > time(NULL) ); + reset_terminal_mode(); +} + +void motor_dir(int frontend_fd, int dir) +{ + diseqc_send_msg(frontend_fd, dir_cmd[dir]); + usleep(20000); +} + +void motor_gotox(int frontend_fd, int pmem) +{ + struct dvb_diseqc_master_cmd gotox_cmd = { { 0xe0, 0x31, 0x6B, pmem, 0x00, 0x00 }, 4 }; + diseqc_send_msg(frontend_fd, gotox_cmd); + printf("Waiting for motor to move, either wait 45sec or hit 's' to skip\n"); + int c; + int sec = time(NULL); + set_conio_terminal_mode(); + do { + sleep(1); + if ( kbhit() ) + c = kbgetchar(); /* consume the character */ + } while( (char)c != 's' && sec+45 > time(NULL) ); + reset_terminal_mode(); +} + +void motor_gotox_save(int frontend_fd, int pmem) +{ + struct dvb_diseqc_master_cmd gotox_save_cmd = { { 0xe0, 0x31, 0x6A, pmem, 0x00, 0x00 }, 4 }; + diseqc_send_msg(frontend_fd, gotox_save_cmd); + usleep(20000); +} + +void diseqc_send_msg(int frontend_fd, struct dvb_diseqc_master_cmd cmd) +{ + printf("DiSEqC: %02x %02x %02x %02x %02x %02x length: %d\n", + cmd.msg[0], cmd.msg[1], + cmd.msg[2], cmd.msg[3], + cmd.msg[4], cmd.msg[5], cmd.msg_len); + + if (ioctl(frontend_fd, FE_DISEQC_SEND_MASTER_CMD, &cmd) == -1) + perror("FE_DISEQC_SEND_MASTER_CMD ERROR!"); + usleep(20000); +} + +void setup_switch (int frontend_fd, fe_sec_voltage_t voltage, fe_sec_tone_mode_t tone, int committed, int uncommitted, int servo) +{ + if (tone) + printf("22khz OFF\n"); + else + printf("22khz ON\n"); + + if (ioctl(frontend_fd, FE_SET_TONE, SEC_TONE_OFF) == -1) + perror("FE_SET_TONE ERROR!"); + usleep(20000); + + if (ioctl(frontend_fd, FE_SET_VOLTAGE, voltage) == -1) + perror("FE_SET_VOLTAGE ERROR!"); + usleep(servo*1000); + + if (uncommitted) + diseqc_send_msg(frontend_fd, uncommitted_switch_cmds[uncommitted-1]); + + if (committed) + diseqc_send_msg(frontend_fd, committed_switch_cmds[committed-1]); + + if (ioctl(frontend_fd, FE_SET_TONE, tone) == -1) + perror("FE_SET_TONE ERROR!"); + usleep(20000); +} diff --git a/tune-s2/diseqc.h b/tune-s2/diseqc.h new file mode 100644 index 0000000..b6034ff --- /dev/null +++ b/tune-s2/diseqc.h @@ -0,0 +1,40 @@ +/* diseqc -- simple diseqc commands for the Linux DVB S2 API + * + * UDL (updatelee@gmail.com) + * Derived from work by: + * Igor M. Liplianin (liplianin@me.by) + * Alex Betis + * Pendragon + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "kb.h" + +extern void diseqc_send_msg (int frontend_fd, struct dvb_diseqc_master_cmd cmd); +extern void setup_switch (int frontend_fd, fe_sec_voltage_t voltage, fe_sec_tone_mode_t tone, int committed, int uncommitted, int servo); +extern void motor_usals(int frontend_fd, double site_lat, double site_long, double sat_long); +extern double radian( double number ); +extern double degree( double number ); +extern void motor_dir(int frontend_fd, int dir); +extern void motor_gotox(int frontend_fd, int pmem); +extern void motor_gotox_save(int frontend_fd, int pmem); diff --git a/tune-s2/kb.c b/tune-s2/kb.c new file mode 100644 index 0000000..b114049 --- /dev/null +++ b/tune-s2/kb.c @@ -0,0 +1,62 @@ +#include "kb.h" + +struct termios orig_termios; + +void reset_terminal_mode() +{ + tcsetattr(0, TCSANOW, &orig_termios); +} + +void set_conio_terminal_mode() +{ + struct termios new_termios; + + /* take two copies - one for now, one for later */ + tcgetattr(0, &orig_termios); + memcpy(&new_termios, &orig_termios, sizeof(new_termios)); + + /* register cleanup handler, and set the new terminal mode */ + atexit(reset_terminal_mode); + cfmakeraw(&new_termios); + tcsetattr(0, TCSANOW, &new_termios); +} + +int kbhit() +{ + struct timeval tv = { 0L, 0L }; + fd_set fds; + FD_SET(0, &fds); + return select(1, &fds, NULL, NULL, &tv); +} + +int kbgetchar() +{ + int r; + unsigned char c; + if ((r = read(0, &c, sizeof(c))) < 0) { + return r; + } else { + return c; + } +} + +char getch(void) +{ + char buf = 0; + struct termios old = {0}; + if (tcgetattr(0, &old) < 0) + perror("tcsetattr()"); + old.c_lflag &= ~ICANON; + old.c_lflag &= ~ECHO; + old.c_cc[VMIN] = 1; + old.c_cc[VTIME] = 0; + if (tcsetattr(0, TCSANOW, &old) < 0) + perror("tcsetattr ICANON"); + if (read(0, &buf, 1) < 0) + perror ("read()"); + old.c_lflag |= ICANON; + old.c_lflag |= ECHO; + if (tcsetattr(0, TCSADRAIN, &old) < 0) + perror ("tcsetattr ~ICANON"); + return (buf); +} diff --git a/tune-s2/kb.h b/tune-s2/kb.h new file mode 100644 index 0000000..ea54860 --- /dev/null +++ b/tune-s2/kb.h @@ -0,0 +1,14 @@ +#include +#include +#include +#include +#include +#include +#include + +void reset_terminal_mode(); +void set_conio_terminal_mode(); +int kbhit(); +int kbgetchar(); +char getch(void); + diff --git a/tune-s2/tune-s2.c b/tune-s2/tune-s2.c new file mode 100644 index 0000000..0cad2cb --- /dev/null +++ b/tune-s2/tune-s2.c @@ -0,0 +1,515 @@ +/* tune-s2 -- simple zapping tool for the Linux DVB S2 API + * + * UDL (updatelee@gmail.com) + * Derived from work by: + * Igor M. Liplianin (liplianin@me.by) + * Alex Betis + * + * 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. + */ + +#include "tune-s2.h" + +#if DVB_API_VERSION < 5 || DVB_API_VERSION_MINOR < 2 +#error szap-s2 requires Linux DVB driver API version 5.2 and newer! +#endif + +#ifndef DTV_STREAM_ID + #define DTV_STREAM_ID DTV_ISDBS_TS_ID +#endif + +#ifndef NO_STREAM_ID_FILTER + #define NO_STREAM_ID_FILTER (~0U) +#endif + +int name2value(char *name, struct options *table) +{ + while (table->name) { + if (!strcmp(table->name, name)) + return table->value; + table++; + } +} + +char * value2name(int value, struct options *table) +{ + while (table->name) { + if (table->value == value) + return table->name; + table++; + } +} + +int check_frontend (int frontend_fd) +{ + fe_status_t status; + uint32_t ber; + unsigned int ber_scale; + float snr; + unsigned int snr_scale; + float lvl; + unsigned int lvl_scale; + + if (ioctl(frontend_fd, FE_READ_STATUS, &status) == -1) { + perror("FE_READ_STATUS failed"); + } + + struct dtv_property p[3]; + p[0].cmd = DTV_STAT_SIGNAL_STRENGTH; + p[1].cmd = DTV_STAT_CNR; + p[2].cmd = DTV_STAT_POST_ERROR_BIT_COUNT; + + struct dtv_properties p_status; + p_status.num = 3; + p_status.props = p; + + if (ioctl(frontend_fd, FE_GET_PROPERTY, &p_status) == -1) { + perror("FE_GET_PROPERTY failed"); + } + + lvl_scale = p_status.props[0].u.st.stat[0].scale; + if (lvl_scale == FE_SCALE_DECIBEL) { + lvl = p_status.props[0].u.st.stat[0].svalue * 0.001; + } else { + int lvl2; + if (ioctl(frontend_fd, FE_READ_SIGNAL_STRENGTH, &lvl2) == -1) { + lvl = 0; + } else { + lvl = (float)lvl2 * 100 / 0xffff; + if (lvl < 0) { + lvl = 0; + } + } + } + snr_scale = p_status.props[1].u.st.stat[0].scale; + if (snr_scale == FE_SCALE_DECIBEL) { + snr = p_status.props[1].u.st.stat[0].svalue * .001; + } else { + unsigned int snr2 = 0; + if (ioctl(frontend_fd, FE_READ_SNR, &snr2) == -1) { + snr2 = 0; + } + snr = (float)snr2/10; + } + ber_scale = p_status.props[2].u.st.stat[0].scale; + if (ber_scale == FE_SCALE_COUNTER) { + ber = p_status.props[2].u.st.stat[0].uvalue; + } else { + ber = 0; + if (ioctl(frontend_fd, FE_READ_BER, &ber) == -1) { + ber = 0; + } + } + printf ("status %s | signal %2.1f %s | snr %2.1f dB | ber %u | ", + (status & FE_HAS_LOCK) ? "Locked" : "Unlocked", lvl, (lvl_scale == FE_SCALE_DECIBEL) ? "dBm" : "%", snr, ber); + if (status & FE_HAS_LOCK) { + printf("FE_HAS_LOCK \n"); + } else printf("\n"); + + return 0; +} + +int tune(int frontend_fd, struct tune_p *t) +{ + struct dtv_property p_clear[] = { + { .cmd = DTV_CLEAR } + }; + + struct dtv_properties cmdseq_clear = { + .num = 1, + .props = p_clear + }; + + if ((ioctl(frontend_fd, FE_SET_PROPERTY, &cmdseq_clear)) == -1) { + perror("FE_SET_PROPERTY DTV_CLEAR failed"); + return -1; + } + usleep (20000); + + // discard stale QPSK events + struct dvb_frontend_event ev; + while (1) { + if (ioctl(frontend_fd, FE_GET_EVENT, &ev) == -1) + break; + } + + struct dtv_property p_tune[] = { + { .cmd = DTV_DELIVERY_SYSTEM, .u.data = t->system }, + { .cmd = DTV_FREQUENCY, .u.data = t->freq * 1000 }, + { .cmd = DTV_VOLTAGE, .u.data = t->voltage }, + { .cmd = DTV_SYMBOL_RATE, .u.data = t->sr * 1000}, + { .cmd = DTV_TONE, .u.data = t->tone }, + { .cmd = DTV_MODULATION, .u.data = t->modulation }, + { .cmd = DTV_INNER_FEC, .u.data = t->fec }, + { .cmd = DTV_INVERSION, .u.data = t->inversion }, + { .cmd = DTV_ROLLOFF, .u.data = t->rolloff }, + { .cmd = DTV_BANDWIDTH_HZ, .u.data = 0 }, + { .cmd = DTV_PILOT, .u.data = t->pilot }, + { .cmd = DTV_STREAM_ID, .u.data = t->mis }, + { .cmd = DTV_TUNE }, + }; + struct dtv_properties cmdseq_tune = { + .num = 13, + .props = p_tune + }; + + printf("\nTuning specs: \n"); + printf("System: %s \n", value2name(p_tune[0].u.data, dvb_system)); + printf("Frequency: %d %s %d \n", abs(p_tune[1].u.data/1000 + t->LO), value2name(p_tune[2].u.data, dvb_voltage) , p_tune[3].u.data / 1000); + printf("22khz: %s \n", value2name(p_tune[4].u.data, dvb_tone)); + printf("Modulation: %s \n", value2name(p_tune[5].u.data, dvb_modulation)); + printf("FEC: %s \n", value2name(p_tune[6].u.data, dvb_fec)); + printf("Inversion: %s \n", value2name(p_tune[7].u.data, dvb_inversion)); + printf("Rolloff: %s \n", value2name(p_tune[8].u.data, dvb_rolloff)); + printf("Pilot: %s \n", value2name(p_tune[10].u.data, dvb_pilot)); + printf("MIS: %d \n\n", p_tune[11].u.data); + + if (ioctl(frontend_fd, FE_SET_PROPERTY, &cmdseq_tune) == -1) { + perror("FE_SET_PROPERTY TUNE failed"); + } + usleep (200000); + + // wait for zero status indicating start of tunning + do { + ioctl(frontend_fd, FE_GET_EVENT, &ev); + } + while(ev.status != 0); + + if (ioctl(frontend_fd, FE_GET_EVENT, &ev) == -1) { + ev.status = 0; + } + + int i; + fe_status_t status; + for ( i = 0; i < 20; i++) + { + if (ioctl(frontend_fd, FE_READ_STATUS, &status) == -1) { + perror("FE_READ_STATUS failed"); + } + + if (status & FE_HAS_LOCK || status & FE_TIMEDOUT) + break; + else + sleep(1); + } + + struct dtv_property p[] = { + { .cmd = DTV_DELIVERY_SYSTEM }, + { .cmd = DTV_FREQUENCY }, + { .cmd = DTV_VOLTAGE }, + { .cmd = DTV_SYMBOL_RATE }, + { .cmd = DTV_TONE }, + { .cmd = DTV_MODULATION }, + { .cmd = DTV_INNER_FEC }, + { .cmd = DTV_INVERSION }, + { .cmd = DTV_ROLLOFF }, + { .cmd = DTV_BANDWIDTH_HZ }, + { .cmd = DTV_PILOT }, + { .cmd = DTV_STREAM_ID } + }; + + struct dtv_properties p_status = { + .num = 12, + .props = p + }; + + // get the actual parameters from the driver for that channel + if ((ioctl(frontend_fd, FE_GET_PROPERTY, &p_status)) == -1) { + perror("FE_GET_PROPERTY failed"); + return -1; + } + + int m,num,den; + double fec_result; + double bw; + double dr; + if (p_status.props[5].u.data < 1) + m = 2; + else if (p_status.props[5].u.data < 10) + m = 3; + else if (p_status.props[5].u.data < 11) + m = 4; + else + m = 5; + bw = (p_status.props[3].u.data) / 1000 * (1 + atof(value2name(p_status.props[8].u.data, dvb_rolloff))/100)/1000; + sscanf (value2name(p_status.props[6].u.data, dvb_fec), "%d/%d", &num, &den); + fec_result = ((float)num/den); + if (p_status.props[0].u.data < 6) + dr = ((p_status.props[3].u.data / 1000) * fec_result * 188/204 * m)/1000; + else + dr = ((p_status.props[3].u.data / 1000) * fec_result * 0.98 * m)/1000; + printf("Tuned specs: \n"); + printf("System: %s %d \n", value2name(p_status.props[0].u.data, dvb_system), p_status.props[0].u.data); + printf("Frequency: %d %s %d \n", abs(p_status.props[1].u.data/1000 + t->LO), value2name(p_status.props[2].u.data, dvb_voltage) , p_status.props[3].u.data / 1000); + printf("22khz: %s \n", value2name(p_status.props[4].u.data, dvb_tone)); + printf("Modulation: %s %d \n", value2name(p_status.props[5].u.data, dvb_modulation), p_status.props[5].u.data); + printf("FEC: %s %d \n", value2name(p_status.props[6].u.data, dvb_fec), p_status.props[6].u.data); + printf("Inversion: %s %d \n", value2name(p_status.props[7].u.data, dvb_inversion), p_status.props[7].u.data); + printf("Rolloff: %s %d \n", value2name(p_status.props[8].u.data, dvb_rolloff), p_status.props[8].u.data); + if(p_status.props[10].u.data) + printf("Pilot: ON %d \n",p_status.props[10].u.data); + else printf("Pilot: OFF %d \n",p_status.props[10].u.data); + printf("MIS: %d \n", p_status.props[11].u.data); + printf("Bandwidth: %3.4f MHz \n", bw); + printf("Data Rate: %3.4f Mbps \n", dr); + +// create a numerical representation of modcode + uint32_t current_modcode; + current_modcode=(p_status.props[0].u.data << 8) + (p_status.props[5].u.data << 4) + (p_status.props[6].u.data); +// printf("Modcode: %03x \n", current_modcode); +// cn failure table, data from http://www.satbroadcasts.com/news,81,Minimum_carrier_to_noise_ratio_values_CNR,_CN_for_DVB_S2_system.html +// and http://www.satbroadcasts.com/news,79,Minimum_carrier_to_noise_ratio_values_CNR,_CN_for_DVB_S_system.html + + typedef struct { float cn; uint32_t modcode; } cn_failure_t; + cn_failure_t cn[30] = + { + { 2.7, 0x501 }, // S1 qpsk 1/2 + { 4.4, 0x502 }, // S1 qpsk 2/3 + { 5.5, 0x503 }, // S1 qpsk 3/4 + { 6.5, 0x505 }, // S1 qpsk 5/6 + { 7.2, 0x507 }, // S1 qpsk 7/8 + { 1.0, 0x601 }, // S2 qpsk 1/2 + { 2.2, 0x60A }, // S2 qpsk 3/5 + { 3.1, 0x602 }, // S2 qpsk 2/3 + { 4.0, 0x603 }, // S2 qpsk 3/4 + { 4.6, 0x604 }, // S2 qpsk 4/5 + { 5.2, 0x605 }, // S2 qpsk 5/6 + { 6.2, 0x608 }, // S2 qpsk 8/9 + { 6.5, 0x60B }, // S2 qpsk 9/10 + { 5.5, 0x69A }, // S2 8psk 3/5 + { 6.6, 0x692 }, // S2 8psk 2/3 + { 7.9, 0x693 }, // S2 8psk 3/4 + { 9.45, 0x695 }, // S2 8psk 5/6 + { 10.7, 0x698 }, // S2 8psk 8/9 + { 11.0, 0x69B }, // S2 8psk 9/10 + { 9.0, 0x6A2 }, // S2 16APSK 2/3 + { 10.2, 0x6A3 }, // S2 16APSK 3/4 + { 11.0, 0x6A4 }, // S2 16APSK 4/5 + { 11.6, 0x6A5 }, // S2 16APSK 5/6 + { 12.9, 0x6A8 }, // S2 16APSK 8/9 + { 13.1, 0x6AB }, // S2 16APSk 9/10 + { 12.6, 0x6B3 }, // S2 32APSK 3/4 + { 13.6, 0x6B4 }, // S2 32APSK 4/5 + { 14.3, 0x6B5 }, // S2 32APSK 5/6 + { 15.7, 0x6B8 }, // S2 32APSK 8/9 + { 16.1, 0x6BB } // S2 32APSK 9/10 + }; +// based on the current modcode (system, modulation, fec) print the cn failure point + int j; + for (j = 0; j < 30; j++) + { + if(current_modcode == cn[j].modcode) { + printf("CN Failure: %2.1f dB \n\n", cn[j].cn); + } + else ; + } + char c; + do + { + check_frontend(frontend_fd); + if (t->quit != 1) + c = getch(); + switch ( c ) { + case 'e': + motor_dir(frontend_fd, 0); + break; + case 'w': + motor_dir(frontend_fd, 1); + break; + case 's':{ + int pmem = 0; + printf ("enter a position no. to save: "); + scanf("%d", &pmem); + motor_gotox_save(frontend_fd, pmem); + break; } + } + } while(c != 'q' && t->quit != 1); + return 0; +} + +char *usage = + "\nusage: tune-s2 12224 V 20000 [options]\n" + " -adapter N : use given adapter (default 0)\n" + " -frontend N : use given frontend (default 0)\n" + " -2 : use 22khz tone\n" + " -committed N : use DiSEqC COMMITTED switch position N (1-4)\n" + " -uncommitted N : use DiSEqC uncommitted switch position N (1-4)\n" + " -servo N : servo delay in milliseconds (20-1000, default 20)\n" + " -gotox NN : Drive Motor to Satellite Position NN (0-99)\n" + " -usals N.N : orbital position\n" + " -long N.N : site long\n" + " -lat N.N : site lat\n" + " -lnb lnb-type : STANDARD, UNIVERSAL, DBS, CBAND or \n" + " -system : System DVB-S or DVB-S2\n" + " -modulation : modulation BPSK QPSK 8PSK\n" + " -fec : fec 1/2, 2/3, 3/4, 3/5, 4/5, 5/6, 6/7, 8/9, 9/10, AUTO\n" + " -rolloff : rolloff 35=0.35 25=0.25 20=0.20 0=UNKNOWN\n" + " -inversion N : spectral inversion (OFF / ON / AUTO [default])\n" + " -pilot N : pilot (OFF / ON / AUTO [default])\n" + " -mis N : MIS #\n" + " -quit : quit after tuning, used to time lock aquisition" + " -help : help\n"; + +int main(int argc, char *argv[]) +{ + if (!argv[1] || strcmp(argv[1], "-help") == 0) + { + printf("%s", usage); + return -1; + } + + struct lnb_p lnb_DBS = { 11250, 0, 0 }; + struct lnb_p lnb_STANDARD = { 10750, 0, 0 }; + struct lnb_p lnb_10600 = { 10600, 0, 0 }; + struct lnb_p lnb_10745 = { 10745, 0, 0 }; + struct lnb_p lnb_UNIVERSAL = { 9750, 10600, 11700 }; + struct lnb_p lnb_CBAND = { -5150, 0, 0 }; + struct lnb_p lnb_IF = { 0, 0, 0 }; + struct lnb_p lnb = lnb_STANDARD; + + char frontend_devname[80]; + int adapter = 0, frontend = 0; + int committed = 0; + int uncommitted = 0; + int servo = 20; + int pmem = 0; + + double site_lat = 0; + double site_long = 0; + double sat_long = 0; + + struct tune_p t; + t.fec = FEC_AUTO; + t.system = SYS_DVBS; + t.modulation = QPSK; + t.rolloff = ROLLOFF_AUTO; + t.inversion = INVERSION_AUTO; + t.pilot = PILOT_AUTO; + t.tone = SEC_TONE_OFF; + t.freq = strtoul(argv[1], NULL, 0); + t.voltage = name2value(argv[2], dvb_voltage); + t.sr = strtoul(argv[3], NULL, 0); + t.mis = -1; + + int a; + for( a = 4; a < argc; a++ ) + { + if ( !strcmp(argv[a], "-quit") ) + t.quit = 1; + else + t.quit = 0; + + if ( !strcmp(argv[a], "-adapter") ) + adapter = strtoul(argv[a+1], NULL, 0); + if ( !strcmp(argv[a], "-frontend") ) + frontend = strtoul(argv[a+1], NULL, 0); + if ( !strcmp(argv[a], "-2") ) + t.tone = SEC_TONE_ON; + if ( !strcmp(argv[a], "-committed") ) + committed = strtoul(argv[a+1], NULL, 0); + if ( !strcmp(argv[a], "-uncommitted") ) + uncommitted = strtoul(argv[a+1], NULL, 0); + if ( !strcmp(argv[a], "-servo") ) + servo = strtoul(argv[a+1], NULL, 0); + if ( !strcmp(argv[a], "-gotox") ) + pmem = strtoul(argv[a+1], NULL, 0); + if ( !strcmp(argv[a], "-usals") ) + sat_long = strtod(argv[a+1], NULL); + if ( !strcmp(argv[a], "-long") ) + site_long = strtod(argv[a+1], NULL); + if ( !strcmp(argv[a], "-lat") ) + site_lat = strtod(argv[a+1], NULL); + if ( !strcmp(argv[a], "-lnb") ) + { + if (!strcmp(argv[a+1], "DBS")) lnb = lnb_DBS; + if (!strcmp(argv[a+1], "10600")) lnb = lnb_10600; + if (!strcmp(argv[a+1], "10745")) lnb = lnb_10745; + if (!strcmp(argv[a+1], "STANDARD")) lnb = lnb_STANDARD; + if (!strcmp(argv[a+1], "UNIVERSAL")) lnb = lnb_UNIVERSAL; + if (!strcmp(argv[a+1], "CBAND")) lnb = lnb_CBAND; + if (!strcmp(argv[a+1], "IF")) lnb = lnb_IF; + } + if ( !strcmp(argv[a], "-fec") ) + t.fec = name2value(argv[a+1], dvb_fec); + if ( !strcmp(argv[a], "-system") ) + t.system = name2value(argv[a+1], dvb_system); + if ( !strcmp(argv[a], "-modulation") ) + t.modulation = name2value(argv[a+1], dvb_modulation); + if ( !strcmp(argv[a], "-rolloff") ) + t.rolloff = name2value(argv[a+1], dvb_rolloff); + if ( !strcmp(argv[a], "-inversion") ) + t.inversion = name2value(argv[a+1], dvb_inversion); + if ( !strcmp(argv[a], "-pilot") ) + t.pilot = name2value(argv[a+1], dvb_pilot); + if ( !strcmp(argv[a], "-mis") ) + t.mis = strtoul(argv[a+1], NULL, 0); + if ( !strcmp(argv[a], "-help") ) + { + printf("%s", usage); + return -1; + } + } + + printf("LNB: low: %d high: %d switch: %d \n", lnb.low, lnb.high, lnb.threshold); + + snprintf(frontend_devname, 80, "/dev/dvb/adapter%d/frontend%d", adapter, frontend); + printf("opening: %s\n", frontend_devname); + int frontend_fd; + if ((frontend_fd = open(frontend_devname, O_RDWR | O_NONBLOCK)) < 0) + { + printf("failed to open '%s': %d %m\n", frontend_devname, errno); + return -1; + } + + struct dvb_frontend_info info; + if ((ioctl(frontend_fd, FE_GET_INFO, &info)) == -1) { + perror("FE_GET_INFO failed\n"); + return -1; + } + printf("frontend: (%s) \nfmin %d MHz \nfmax %d MHz \nmin_sr %d Ksps\nmax_sr %d Ksps\n", info.name, + info.type == 0 ? info.frequency_min / 1000: info.frequency_min / 1000000, + info.type == 0 ? info.frequency_max / 1000: info.frequency_max / 1000000, + info.type == 0 ? info.symbol_rate_min / 1000: info.symbol_rate_min /1000000, + info.type == 0 ? info.symbol_rate_max / 1000: info.symbol_rate_max /1000000); + + if (lnb.threshold && lnb.high && t.freq > lnb.threshold) + { + printf("HIGH band\n"); + t.tone = SEC_TONE_ON; + t.LO = lnb.high; + t.freq = abs(t.freq - abs(t.LO)); + } else { + printf("LOW band\n"); + t.LO = lnb.low; + t.freq = abs(t.freq - abs(t.LO));; + } + if(servo >= 1000) { + servo = 1000; + } else if(servo <= 20) { + servo = 20; + } + if (sat_long) + motor_usals(frontend_fd, site_lat, site_long, sat_long); + if (!t.tone || committed || uncommitted) + setup_switch (frontend_fd, t.voltage, t.tone, committed, uncommitted, servo); + + if(pmem) + motor_gotox(frontend_fd, pmem); + tune(frontend_fd, &t); + + printf("Closing frontend ... \n"); + close (frontend_fd); + return 0; +} diff --git a/tune-s2/tune-s2.h b/tune-s2/tune-s2.h new file mode 100644 index 0000000..39127db --- /dev/null +++ b/tune-s2/tune-s2.h @@ -0,0 +1,174 @@ +/* tune-s2 -- simple zapping tool for the Linux DVB S2 API + * + * UDL (updatelee@gmail.com) + * Derived from work by: + * Igor M. Liplianin (liplianin@me.by) + * Alex Betis + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diseqc.h" +#include "kb.h" + +struct lnb_p +{ + int low; + int high; + int threshold; +}; + +struct tune_p +{ + int freq; + int sr; + int fec; + int system; + int modulation; + int inversion; + int rolloff; + int pilot; + int mis; + int voltage; + int tone; + int LO; + int quit; +}; + +struct options +{ + char *name; + int value; +}; + +struct options dvb_fec[] = +{ + { "1/2", FEC_1_2 }, + { "2/3", FEC_2_3 }, + { "3/4", FEC_3_4 }, + { "4/5", FEC_4_5 }, + { "5/6", FEC_5_6 }, + { "6/7", FEC_6_7 }, + { "7/8", FEC_7_8} , + { "8/9", FEC_8_9 }, + { "AUTO", FEC_AUTO }, + { "3/5", FEC_3_5 }, + { "9/10", FEC_9_10 }, + { NULL, 0 } +}; + +struct options dvb_system[] = +{ + { "UNDEFINED", SYS_UNDEFINED }, + { "DVB-C_ANNEX_AC", SYS_DVBC_ANNEX_AC }, + { "DVB-C_ANNEX_B", SYS_DVBC_ANNEX_B }, + { "DVB-T", SYS_DVBT }, + { "DSS", SYS_DSS }, + { "DVB-S", SYS_DVBS }, + { "DVB-S2", SYS_DVBS2 }, + { "DVB-H", SYS_DVBH }, + { "ISDBT", SYS_ISDBT }, + { "ISDBS", SYS_ISDBS }, + { "ISDBC", SYS_ISDBC }, + { "ATSC", SYS_ATSC }, + { "ATSCMH", SYS_ATSCMH }, + { "DMBTH", SYS_DMBTH }, + { "CMMB", SYS_CMMB }, + { "DAB", SYS_DAB }, + { "ATSC", SYS_ATSC }, + { "TURBO", SYS_TURBO }, + { NULL, 0 } +}; + +struct options dvb_modulation[] = +{ + { "QPSK", QPSK }, + { "QAM_16", QAM_16 }, + { "QAM_32", QAM_32 }, + { "QAM_64", QAM_64 }, + { "QAM_128", QAM_128 }, + { "QAM_256", QAM_256 }, + { "QAM_AUTO", QAM_AUTO }, + { "VSB_8", VSB_8 }, + { "VSB_16", VSB_16 }, + { "8PSK", PSK_8 }, + { "APSK_16", APSK_16 }, + { "APSK_32", APSK_32 }, + { "DQPSK", DQPSK }, + { NULL, 0 } +}; + +struct options dvb_rolloff[] = +{ + { "20", ROLLOFF_20 }, + { "25", ROLLOFF_25 }, + { "35", ROLLOFF_35 }, + { "AUTO", ROLLOFF_AUTO }, + { NULL, 0 } +}; + +struct options dvb_inversion[] = +{ + { "OFF", INVERSION_OFF }, + { "ON", INVERSION_ON }, + { "AUTO", INVERSION_AUTO }, + { NULL, 0 } +}; + +struct options dvb_pilot[] = +{ + { "OFF", PILOT_OFF }, + { "ON", PILOT_ON }, + { "AUTO", PILOT_AUTO }, + { NULL, 0 } +}; + +struct options dvb_voltage[] = +{ + { "V", SEC_VOLTAGE_13 }, + { "H", SEC_VOLTAGE_18 }, + { "R", SEC_VOLTAGE_13 }, + { "L", SEC_VOLTAGE_18 }, + { "N", SEC_VOLTAGE_OFF }, + { NULL, 0 } +}; + +struct options dvb_tone[] = +{ + { "ON", SEC_TONE_ON }, + { "OFF", SEC_TONE_OFF }, + { NULL, 0 } +}; + +int name2value(char *name, struct options *table); +char * value2name(int value, struct options *table);