diff --git a/apps/modtest.c b/apps/modtest.c new file mode 100644 index 0000000..71784c5 --- /dev/null +++ b/apps/modtest.c @@ -0,0 +1,1313 @@ + +// for systems without O_LARGEFILE +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef uint64_t u64; + +#include "../ddbridge/ddbridge-mci.h" +#include "../ddbridge/ddbridge-ioctl.h" + +#define NIT_PID 0x0010 +#define MAXNIT 1024 +#define DEFAULT_BIT_RATE_C (50870588ULL) +#define DEFAULT_BIT_RATE_T (31668449ULL) +#define TS_SIZE (188) +#define START_FREQ_T (474000000) +#define START_FREQ_C (114000000) +static int adapt = 1; +static uint32_t start_freq = START_FREQ_C; +static int dvbt = 0; +static int writeNIT = 0; + +int timest = 1; + +typedef struct section_data_t { + uint8_t *section; + uint16_t sec_length; + uint16_t sec_pos; + uint8_t pack_num; +} section_data; + + +typedef struct transponder_{ + uint16_t tpid; + uint8_t delsys; + uint32_t freq; + uint8_t qam; + uint32_t symbolrate; + uint8_t bandwidth; + uint8_t guard; + uint8_t code_rate; + uint8_t trans_mode; +} transponder; + +typedef struct write_data_t { + int fd_in; + int *fd_out; + char *name; + int chans; + transponder tp[32]; +} write_data; + +#define ADAPT_FIELD 0x20 +#define MAX_PCR (2576980377599ULL) // max pcr 2^33*300-1 +#define PCR_FAC (2048ULL) +#define MAXPCR (MAX_PCR*PCR_FAC) +#define PCR_FLAG 0x10 +#define OPCR_FLAG 0x08 + +//++++++++++++++++++++++++++++++++CRC+++++++++++++++++++++++++++++++++++++ +static uint32_t dvb_crc_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + + +uint32_t dvb_crc32(uint8_t *data, int len) +{ + int i; + uint32_t crc=0xffffffff; + + for (i=0; i>24)^(data[i]))&0xff]; + return crc; +} + +void dvb_set_crc32(uint8_t *data, int len) +{ + uint32_t crc; + + crc=dvb_crc32(data, len); + data[len] = (uint8_t)(crc>>24); + data[len+1] = (uint8_t)(crc>>16); + data[len+2] = (uint8_t)(crc>>8); + data[len+3] = (uint8_t)(crc); +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//+++++++++++++++++++++++++++++++++++PCR++++++++++++++++++++++++++++++++++ + +uint16_t get_pid(uint8_t *pid) +{ + uint16_t pp; + + pp = (pid[0] & 0x1f) << 8; + pp |= pid[1] &0xff; + return pp; +} + +void set_pid(uint16_t pid, uint8_t *p) +{ + p[0] = (p[0] & 0xE0) | ((uint8_t) (pid >> 8 ) & 0x1F); + p[1] = (uint8_t) (pid); +} + +int pcr_in_msecs(uint64_t pcr) +{ + uint64_t PCR; + + PCR = pcr/PCR_FAC; + + return PCR/27000; +} + + +int check_pcr(uint8_t *buf) +{ + + if (buf[0] != 0x47) { + fprintf(stderr,"Not a TS packet header in check_pcr\n"); + return -1; + } + + if (buf[1] & 0x80) { + fprintf(stderr,"Corrupt packet in check_pcr\n"); + return -1; + } + + if (!(buf[3] & ADAPT_FIELD)){ +// fprintf(stderr,"no adaptation field\n"); + return 0; //no adapt. field + } + if (!buf[4]){ +// fprintf(stderr,"no adaptation field length\n"); + return 0; //No adaptation field length + } + if (! (buf[5] & PCR_FLAG) ){ +// fprintf(stderr,"no PCR flag\n"); + return 0; // no PCR FLAG + } +// fprintf(stderr,"Found pcr\n"); + return 1; +} + +uint8_t *get_pcr(uint8_t *buf) +{ + return buf+6; +} + +uint64_t convert_pcr(uint8_t *b){ + uint64_t pcr; + + pcr = ((uint64_t) b[0] << 25) | ((uint64_t) b[1] << 17) | + ((uint64_t) b[2] << 9) | ((uint64_t) b[3] << 1) | + ((uint64_t) b[4] >> 7); + pcr *= 300; + pcr += ((b[4] & 1) << 8) | b[5]; + return pcr*PCR_FAC; +} + +int find_pcr(uint8_t *buf, int l, int last) +{ + int c = 0; + while (buf[c] != 0x47) c++; + + if (last){ + c+=((l-c)/TS_SIZE-1)*TS_SIZE; + while (buf[c] != 0x47) c--; + while (c >=0 && !check_pcr(buf+c)) c-=TS_SIZE; + if (check_pcr(buf+c)) return c; + else return -1; + } else { + while (c < l && !check_pcr(buf+c)) c+=TS_SIZE; + if (check_pcr(buf+c)) return c; + else return -1; + } +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//+++++++++++++++++++++++++++++++++++TS PACKETS+++++++++++++++++++++++++++ + +void write_filler(uint8_t *buf) +{ + int j; + uint8_t *p; + + p = buf; + *p++ = 0x47; + *p++ = 0x1F; + *p++ = 0xFF; + *p++ = 0x00; + for(j = 0; j < TS_SIZE-4; j+=1 ) *p++ = 0x00; +} + +void setbcd(uint8_t *p, int freq, int b, int e) +{ + int i; + int f = freq/e; + int r = b%2; + + if (r){ + p[b/2] = ( (f % 10) >> 4) & 0xF0; + f = f / 10; + } + for ( i= b/2; i > 0; i--){ + p[i-1] = (f % 10) & 0x0F; + f = f / 10; + p[i-1] |= ( (f % 10) << 4) & 0xF0; + f = f / 10; + } +} + +static int write_cable_delsys_descriptor (uint8_t *buf, int blength, + transponder *tp) +{ + int length = 0; + uint8_t *p; + + p = buf; + length = 11; + + if (length > (int)(0xFF) || length+2 >blength){ + fprintf(stderr,"Not enough space for cable delsys descriptor %d, %d\n", + length, blength); + return -1; + } + p[0]= 0x44; // cable delivery system descriptor + p[1]= 0x0B; // length always 11 + setbcd(p+2, tp->freq, 8, 100); + p[6] = 0xFF; // reserved + p[7]= 0x02; // FEC_outer + p[8]= tp->qam; // QAM + setbcd(p+9,tp->symbolrate, 7, 100); + p[12]|= 0x0F; + + return length+2; +} + +static int write_terrestrial_delsys_descriptor (uint8_t *buf, int blength, + transponder *tp) +{ + int length = 0; + uint8_t *p; + uint32_t freq = tp->freq/10; + p = buf; + length = 11; + + if (length > (int)(0xFF) || length+2 >blength){ + fprintf(stderr,"Not enough space for cable delsys descriptor %d, %d\n", + length, blength); + return -1; + } + p[0]= 0x5a; // terretrial delivery system descriptor + p[1]= 0x0B; // length always 11 + p[2]= (freq >> 24) & 0xFF; + p[3]= (freq >> 16) & 0xFF; + p[4]= (freq >> 8) & 0xFF; + p[5]= freq & 0xFF; + p[6]= ((tp->bandwidth&0x7)<<5) |0x1f; + //bandwidth 8MHz 000,no priority 1, + //no time slice 1, no mpe-fce 1 + p[7]= 0x00 |((tp->qam & 0x03) << 6) | (tp->code_rate &0x07); + //64-QAM 10, no hierarchy 000, 7/8 100 + p[8]= 0x00 | ((tp->guard &0x03) <<3 )|((tp->trans_mode &0x03) << 1); + // no LP 000, 1/32 guard 00, 8k mode 01, no other freq 0 + + p[9]=0xff; + p[10]=0xff; + p[11]=0xff; + p[12]=0xff; + + return length+2; +} + +int CreateNIT(uint8_t *NIT, transponder *tp, int start, int stop, + char *nname, uint16_t nid, uint8_t secnum, uint8_t lastsec) +{ + uint8_t *p,*lp; + int c = 0; + int cc; + int i; + uint16_t TableLen = 0; + int slen; + + p = NIT; + + p[0] = 0x40; // Table Id NIT = 0x40 + + p[1] = 0x00; // Table length determined at end + p[2] = 0x00; + + p[3] = (nid >> 8) & 0xFF; // network id + p[4] = nid & 0xFF; + + p[5] = 0xC3; // seq number + p[6] = secnum; // section number + p[7] = lastsec; // last section number + + +// Network descriptor + p[8] = 0xF0; // network descriptor length + p[9] = 0x00; // set later + TableLen = 7; + c = 10; + p[c] = 0x40; // Network name descriptor + + slen = 0; + + slen = strlen(nname); + p[c+1] = slen; + memcpy(p+c+2,nname,slen); + + p[8] = 0xF0 | (((2+slen) >> 8) &0x0F); + p[9] = ((2+slen) & 0xFF); + TableLen += 2+slen; + c += 2+slen; + + +// Transport stream loop + p[c] = 0xF0; // transport stream loop length + p[c+1] = 0x00; // set later + cc = c+1; + + TableLen += 2; + c += 2; + slen = 0; + for (i= start; i < stop; i++){ + p[c] = (uint8_t)( tp[i].tpid >> 8 ); //transport stream id + p[c+1]= (uint8_t)( tp[i].tpid ); + p[c+2]= (nid >> 8) &0xFF; // orig network id + p[c+3]= nid & 0xFF; // id + + lp = p+c+4; + p[c+4]= 0xF0; // descriptor loop length + p[c+5]= 0x0D; // 13 for cable delivery descriptor + c += 6; + switch (tp->delsys){ + case SYS_DVBC_ANNEX_A: + slen = write_cable_delsys_descriptor (p+c, MAXNIT-c, &tp[i]); + break; + case SYS_DVBT: + slen = write_terrestrial_delsys_descriptor (p+c, MAXNIT-c, &tp[i]); + break; + default: + slen = 0; + } + c+=slen; + + lp[0] = 0xF0 | (((slen) >> 8) &0x0F); + lp[1] = ((slen) & 0xFF); + + + TableLen += 6+slen; + } + + p[cc-1]= 0xF0 | (((c-cc-1) >> 8) &0x0F); + p[cc] = ((c-cc-1) & 0xFF); + + TableLen += 4; + + p[1] = 0xF0 | ((TableLen >> 8) & 0x0F); + p[2] = (uint8_t)(TableLen) & 0xFF; + + + dvb_set_crc32(NIT, TableLen + 3 -4 ); + return TableLen+3; + +} + +void init_sec_data(section_data *s, uint8_t *sec, uint16_t seclength) +{ + uint16_t seclen = 0; + + s->section = sec; + seclen |= ((sec[1] & 0x0F) << 8); + seclen |= (sec[2] & 0xFF); + seclen += 3; + if (seclen > 3){ + s->sec_length = seclen; + if(seclength && seclen != seclength) + fprintf(stderr,"Setting seclength (%d) %d\n",seclength, seclen); + } else s->sec_length = seclength; + + s->sec_pos = 0; + s->pack_num = 0; +} + +void write_sec_pack(section_data *s, uint8_t *ts_pack, uint16_t pid) +{ + uint8_t *p = ts_pack; + uint16_t rest = 0; + uint16_t len = s->sec_length - s->sec_pos; + uint16_t pload = 0; + + *p++ = 0x47; + set_pid(pid,p); + if (!s->sec_pos){ + *p |= 0x40; + } else { + *p &= ~0x40; + } + p += 2; + *p++ = 0x10 | (s->pack_num & 0x0f); + s->pack_num += 1; + if (!s->sec_pos) *p++=0x00; + pload = TS_SIZE - (p - ts_pack); + if (pload > len) pload = len; + memcpy( p, s->section + s->sec_pos, pload); + p += pload; + rest = TS_SIZE - (p - ts_pack); + if (rest > 0) + memset(p, 0xff, rest); + s->sec_pos += pload; + if (s->sec_pos == s->sec_length){ + s->sec_pos = 0; + } +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//++++++++++++++++++++++++++++++++MAIN ROUTINE++++++++++++++++++++++++++++ + +#define OBSIZE (672*TS_SIZE) +#define BSIZE (7*OBSIZE) + +void write_mods(write_data *wd) +{ + uint8_t NIT[MAXNIT]; + section_data sec; + off64_t inflength,c=0; + uint8_t buffer[BSIZE]; + uint8_t obuffer[OBSIZE]; + int ci=0,w=0; + int isdvr=0; + int first = 0; + int last = 0; + uint64_t fpcr = 0; + uint64_t lpcr = 0; + uint64_t delta_pcr = 0; + uint64_t in_prate, out_prate; + int slen = 0; + + if (dvbt){ + out_prate = DEFAULT_BIT_RATE_T / (8*TS_SIZE); + } else { + out_prate = DEFAULT_BIT_RATE_C / (8*TS_SIZE); + } + slen = CreateNIT(NIT, wd->tp, 0, wd->chans, "DD", 1, 0, 0); + + init_sec_data(&sec, NIT, slen); + in_prate = 0; + + inflength = lseek64(wd->fd_in, 0, SEEK_END); + inflength = TS_SIZE*(inflength/TS_SIZE); + if (inflength <= 0){ + if (inflength == 0 || errno == ESPIPE){ + isdvr = 1; + fprintf(stderr,"Non seekable file, handling as dvr\n"); + } else { + fprintf(stderr,"Error in lseek, aborting\n"); + return; + } + } + + if (!isdvr){ + fprintf(stderr,"Input file length: %.2f MiB\n",inflength/1024./1024.); + lseek64(wd->fd_in,0,SEEK_SET); + } + + fprintf(stderr,"Starting %s\n",wd->name); + while (1) { + int wc=0; + first = 0; + last = 0; + + if ((ci=read(wd->fd_in,buffer,BSIZE))<0) return; + + first = find_pcr(buffer, ci, 0); + last = find_pcr(buffer, ci, 1); + if (first == last){ + fprintf(stderr,"Input buffer too small\n" + "first %d last %d length %d pidf 0x%x\n", first/TS_SIZE, + last/TS_SIZE, + ci/TS_SIZE, get_pid(buffer+first+1)); + } + fpcr = convert_pcr(get_pcr(buffer+first)); + lpcr = convert_pcr(get_pcr(buffer+last)); + if (lpcr > fpcr) delta_pcr = lpcr - fpcr; + else delta_pcr = (MAXPCR-fpcr)+lpcr; + int packs = (last-first)/TS_SIZE; + in_prate = ((packs*10000/pcr_in_msecs(delta_pcr))+5)/10; + +#if 0 + fprintf(stderr,"delta_pcr %d ms npack %d packet_rate in: %d out: %d\n", + pcr_in_msecs(delta_pcr), + packs, in_prate, out_prate); + fprintf(stderr,"Input bitrate %.2f MBit output bitrate %.2f MBit\n", + (in_prate*TS_SIZE*8)/1000000.0, + (out_prate*TS_SIZE*8)/1000000.0); +#endif + + if (in_prate > out_prate){ + fprintf(stderr,"Input bitrate (%.2f MBit) larger than " + "possible output bitrate (%.2f MBit)\n", + (in_prate*TS_SIZE*8)/1000000.0, + (out_prate*TS_SIZE*8)/1000000.0); + exit(1); + } + + int filler = (out_prate*10/in_prate+5)/10-1; + int d=0; + int rest=0; + for (int s = 0; s < ci; s+=TS_SIZE){ + memcpy(obuffer+d, buffer+s, TS_SIZE*sizeof(uint8_t)); + d+=TS_SIZE; + int fill = (OBSIZE - d)/TS_SIZE; + if (fill < filler) rest = filler - fill; + else fill = filler; +// fprintf(stderr,"d %d fill %d filler %d %d %d\n", d, fill,filler,s,ci); + int nit = 0; + for(int i=0;i < fill; i++){ + if (writeNIT && nit < 4 ) { + write_sec_pack(&sec, obuffer+d, NIT_PID); + nit++; + } else write_filler(obuffer+d); + d+=TS_SIZE; + } + if (d >= OBSIZE){ + for (int m = 0; m < wd->chans; m++){ + int lc = 0; + while (lc < OBSIZE){ + if ((w=write(wd->fd_out[m],obuffer+lc,OBSIZE-lc))<0){ + fprintf(stderr,"Problem writing to modulator from %s (%d %d) %s\n", + wd->name, w, ci,strerror(errno)); + return; + } + wc += w; + lc+=w; + d = 0; + } + } + } + + for(int i=0;i < rest; i++){ + write_filler(obuffer+d); + d+=TS_SIZE; + } + rest = 0; + } + if (inflength){ + c+=ci; + fprintf(stderr,"written %d %d%%\n",(int)c,(int)((100*c)/inflength)); + if (c >=inflength ){ + fprintf(stderr,"Restarting stream %s\n",wd->name); + lseek64(wd->fd_in,0,SEEK_SET); + c = 0; + } + } + } +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//+++++++++++++++++++++++++++++++++DVB DEVICES++++++++++++++++++++++++++++ + +#define DVB_TUNE 0 +#define DVB_MOD 1 +#define DVB_CA 2 +#define MAX_ADAPT 32 + +typedef struct dvb_devices_t { + int adapters; + int dtype[MAX_ADAPT]; + int ndevs[MAX_ADAPT]; + uint64_t snr[MAX_ADAPT]; + uint64_t hwid[MAX_ADAPT]; + uint64_t devid0[MAX_ADAPT]; +} dvb_devices; + + +static int check_tuner(int adapt) +{ + char device[35]; + int front = 0; + int done = 0; + + while (!done) { + snprintf(device,34,"/dev/dvb/adapter%d/frontend%d",adapt,front); +// fprintf(stderr,"Checking for %s\n", device); + if(access(device, F_OK) < 0) + done=1; + else { + front++; + } + } + return front; +} + +static int check_ca(int adapt) +{ + char device[25]; + int ca = 0; + int done = 0; + + while (!done) { + snprintf(device,24,"/dev/dvb/adapter%d/ci%d",adapt,ca); + if(access(device, F_OK) < 0) + done=1; + else { + ca++; + } + } + return ca; +} + +static int check_modulator(int adapt) +{ + char device[25]; + int mod = 0; + int done = 0; + + while (!done) { + snprintf(device,24,"/dev/dvb/adapter%d/mod%d",adapt,mod); + if(access(device, F_OK) < 0) + done=1; + else { + mod++; + } + } + return mod; +} + + +static int check_dvb(dvb_devices *ddevices){ + int done = 0; + int nadapt = 0; + int maxmod = 0; + int maxfront = 0; + int maxca = 0; + int i; + int adapter =0; + + while ( nadapt < MAX_ADAPT && !done){ + maxca = check_ca(nadapt); + maxmod = check_modulator(nadapt); + maxfront = check_tuner(nadapt); + + if (maxmod){ + ddevices->dtype[nadapt] = DVB_MOD; + ddevices->ndevs[nadapt] = maxmod; + maxmod = 0; + nadapt++; + } else { + if (maxfront) { + ddevices->dtype[nadapt] = DVB_TUNE; + ddevices->ndevs[nadapt] = maxfront; + maxfront = 0; + nadapt++; + } else { + if (maxca) { + ddevices->dtype[nadapt] = DVB_CA; + ddevices->ndevs[nadapt] = maxca; + maxca = 0; + nadapt++; + } else { + done = 1; + } + } + } + } + ddevices->adapters = nadapt; + if (!nadapt) return -1; + + fprintf(stderr,"Found %d dvb adapters\n",nadapt); + for (i=0; idtype[i]){ + case DVB_TUNE: + fprintf(stderr," Adapter %d is a TUNER CARD with %d FRONTENDS\n", i, ddevices->ndevs[i]); + break; + case DVB_MOD: + fprintf(stderr," Adapter %d is a MODULATOR CARD with %d MODULATORS\n", i, ddevices->ndevs[i]); + adapter = i; + break; + case DVB_CA: + fprintf(stderr," Adapter %d is a CI CARD with %d CA \n",i,ddevices->ndevs[i]); + break; + } + } + return adapter; +} + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//++++++++++++++++++++++++++++++++DVBT MOD++++++++++++++++++++++++++++++++ + +static int mci_cmd(int dev, struct mci_command *cmd) +{ + int ret; + struct ddb_mci_msg msg; + uint8_t status; + + memset(&msg, 0, sizeof(msg)); + msg.link = 0; + memcpy(&msg.cmd, cmd, sizeof(msg.cmd)); + //dump((uint8_t *) &msg.cmd, sizeof(msg.cmd)); + ret = ioctl(dev, IOCTL_DDB_MCI_CMD, &msg); + if (ret < 0) { + dprintf(2, "mci_cmd error %s\n",strerror(errno)); + return ret; + } + status = msg.res.status; + if (status == MCI_STATUS_OK) + return ret; + if (status == MCI_STATUS_UNSUPPORTED) { + dprintf(2, "Unsupported MCI command\n"); + return ret; + } + if (status == MCI_STATUS_INVALID_PARAMETER) { + dprintf(2, "Invalid MCI parameters\n"); + return ret; + } + return ret; +} + +int mci_set_output(int fd, uint8_t connector, uint8_t nchannels, uint8_t unit, + int16_t power) +{ + char con[14]; + char un[6]; + struct mci_command msg_output = { + .mod_command = MOD_SETUP_OUTPUT, + .mod_channel = 0, + .mod_stream = 0, + .mod_setup_output = { + .connector = MOD_CONNECTOR_F, + .num_channels = 14, + .unit = MOD_UNIT_DBUV, + .channel_power = 9000, + }, + }; + + msg_output.mod_setup_output.connector = connector; + msg_output.mod_setup_output.num_channels = nchannels; + msg_output.mod_setup_output.unit = unit; + msg_output.mod_setup_output.channel_power = power; + + + switch (connector){ + case MOD_CONNECTOR_F: + snprintf(con, 14, "F-Connector"); + break; + + case MOD_CONNECTOR_SMA: + snprintf(con, 14, "SMA-Connector"); + break; + + case MOD_CONNECTOR_OFF: + snprintf(con, 14, "off"); + break; + + default: + fprintf(stderr,"unknown connector in modulator setup\n"); + return -1; + break; + } + switch (unit){ + case MOD_UNIT_DBUV: + snprintf(un, 6, " dBuV"); + break; + + case MOD_UNIT_DBM: + snprintf(un, 6, " dBm"); + break; + + default: + fprintf(stderr,"unknow power unit in modulator setup\n"); + return -1; + break; + } + + fprintf(stderr,"Setting DVBT Modulator output to %s, %d channels, power %f%s\n", + con, nchannels, (double)power/100, un ); + + return mci_cmd(fd,&msg_output); +} + +int mci_set_output_simple(int adapt, uint8_t nchannels) +{ + char fn[128]; + int re = 0; + + snprintf(fn, 127, "/dev/dvb/adapter%u/mod0", adapt); + int fd = open(fn, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Could not open %s\n", fn); + return -1; + } + + re = mci_set_output(fd, MOD_CONNECTOR_F, nchannels, MOD_UNIT_DBUV, 9000); + close(fd); + return re; +} + +int mci_set_channels(int fd, uint32_t freq, uint8_t nchan, uint8_t standard, + uint32_t offset, uint32_t bandw) +{ + char stand[25]; + struct mci_command msg_channels = { + .mod_command = MOD_SETUP_CHANNELS, + .mod_channel = 0, + .mod_stream = 0, + .mod_setup_channels[0] = { + .flags = MOD_SETUP_FLAG_FIRST|MOD_SETUP_FLAG_LAST|MOD_SETUP_FLAG_VALID, + .standard = MOD_STANDARD_DVBT_8, + .num_channels = 25, + .frequency = 474000000, + }, + }; + + msg_channels.mod_setup_channels[0].frequency = freq; + msg_channels.mod_setup_channels[0].num_channels = nchan; + msg_channels.mod_setup_channels[0].standard = standard; + if (standard == MOD_STANDARD_GENERIC){ + msg_channels.mod_setup_channels[0].offset = offset; + msg_channels.mod_setup_channels[0].bandwidth = bandw; + } + + switch(standard){ + case MOD_STANDARD_GENERIC: + snprintf(stand, 24, "MOD_STANDARD_GENERIC"); + break; + + case MOD_STANDARD_DVBT_8: + snprintf(stand, 24, "MOD_STANDARD_DVBT_8"); + break; + + case MOD_STANDARD_DVBT_7: + snprintf(stand, 24, "MOD_STANDARD_DVBT_7"); + break; + + case MOD_STANDARD_DVBT_6: + snprintf(stand, 24, "MOD_STANDARD_DVBT_6"); + break; + + case MOD_STANDARD_DVBT_5: + snprintf(stand, 24, "MOD_STANDARD_DVBT_5"); + break; + + default: + fprintf(stderr,"unknown standard in channels setup\n"); + return -1; + break; + + } + fprintf(stderr,"Setting DVBT Modulator channels to %d HZ, %d channels, %s\n", + freq, nchan, stand); + + return mci_cmd(fd,&msg_channels); +} + +int mci_set_channels_simple(int adapt, uint32_t freq, uint8_t nchan) +{ + + char fn[128]; + int re = 0; + + snprintf(fn, 127, "/dev/dvb/adapter%u/mod0", adapt); + int fd = open(fn, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Could not open %s\n", fn); + return -1; + } + + re = mci_set_channels(fd, freq, nchan, MOD_STANDARD_DVBT_8, 0, 0); + close(fd); + return re; +} + +int mci_set_stream( int fd, uint8_t stream, uint8_t channel, uint8_t standard, + uint8_t stream_format, uint32_t symbol_rate, + uint8_t modulation, uint8_t rolloff, + uint8_t fft_size, uint8_t guard_interval, + uint8_t puncture_rate, uint8_t constellation, + uint16_t cell_identifier) +{ + struct mci_command msg_stream = { + .mod_command = MOD_SETUP_STREAM, + .mod_channel = 0, + .mod_stream = 0, + .mod_setup_stream = { + .standard = MOD_STANDARD_DVBC_8, + }, + }; + + msg_stream.mod_channel = channel; + msg_stream.mod_stream = stream; + msg_stream.mod_setup_stream.standard = standard; + msg_stream.mod_setup_stream.stream_format = stream_format; + if (symbol_rate) + msg_stream.mod_setup_stream.symbol_rate = symbol_rate; + if (modulation) + msg_stream.mod_setup_stream.qam.modulation = modulation; + if (rolloff) + msg_stream.mod_setup_stream.qam.rolloff = rolloff; + msg_stream.mod_setup_stream.ofdm.fft_size = fft_size; + msg_stream.mod_setup_stream.ofdm.guard_interval = guard_interval; + msg_stream.mod_setup_stream.ofdm.puncture_rate = puncture_rate; + msg_stream.mod_setup_stream.ofdm.constellation = constellation; + if (cell_identifier) + msg_stream.mod_setup_stream.ofdm.cell_identifier = cell_identifier; + + fprintf(stderr,"Setting DVBT Stream %d to channel %d\n",stream, channel); + + return mci_cmd(fd,&msg_stream); + +} + +void set_dvbt_mods(int adapt, int chans, uint32_t start_freq, write_data *wd) +{ + if ((mci_set_output_simple(adapt, chans) < 0)|| + (mci_set_channels_simple(adapt, start_freq, chans)< 0)) + { + fprintf(stderr,"Error setting up DVBT Modulator\n"); + exit(1); + } + wd->chans = chans; + wd->fd_out = (int *)malloc(chans*sizeof(int)); + memset(wd->fd_out,0,chans*sizeof(int)); + + for (int i = 0; i < chans; i++){ + char *device; + int fd; + + wd->tp[i].tpid = 1; // all the same transport stream id + wd->tp[i].delsys = SYS_DVBT; + wd->tp[i].freq = start_freq+8000000*i; + wd->tp[i].qam = 2; + wd->tp[i].symbolrate = 0; + wd->tp[i].bandwidth = 0; + wd->tp[i].guard = 0; + wd->tp[i].code_rate = 4; + wd->tp[i].trans_mode = MOD_STANDARD_DVBT_8; + + device = malloc(sizeof(char)*25); + snprintf(device,24,"/dev/dvb/adapter%d/mod%d",adapt,i); + fd = open(device, O_RDWR); + if( fd < 0 ) + { + fprintf(stderr,"Error opening %s : %s\n",device,strerror(errno)); + free(device); + exit(1); + } + + mci_set_stream( fd, i, i, MOD_STANDARD_DVBT_8, 4, 0, 0, 0, 1, 0, 4, 2, 0); + close(fd); + free(device); + } +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//++++++++++++++++++++++++++++++++DVBC MOD++++++++++++++++++++++++++++++++ + +static int set_property(int fd, uint32_t cmd, uint32_t data) +{ + struct dtv_property p; + struct dtv_properties c; + int ret; + + p.cmd = cmd; + c.num = 1; + c.props = &p; + p.u.data = data; + ret = ioctl(fd, FE_SET_PROPERTY, &c); + if (ret < 0) { + fprintf(stderr, "FE_SET_PROPERTY returned %d\n", errno); + return -1; + } + return 0; +} + +static int set_input_bitrate(int fd, uint64_t data) +{ + struct dtv_property p; + struct dtv_properties c; + int ret; + + p.cmd = MODULATOR_INPUT_BITRATE; + c.num = 1; + c.props = &p; + p.u.data64 = data; + ret = ioctl(fd, FE_SET_PROPERTY, &c); + if (ret < 0) { + fprintf(stderr, "FE_SET_PROPERTY returned %d\n", errno); + return -1; + } + return 0; +} + +void set_dvbc_mods(int adapt, int chans, uint32_t start_freq, write_data *wd) +{ + uint32_t freq = start_freq; + uint8_t qam = QAM_256; + uint32_t sym = 6900000; + + wd->chans = chans; + wd->fd_out = (int *)malloc(chans*sizeof(int)); + memset(wd->fd_out,0,chans*sizeof(int)); + + for (int i = 0; i < chans; i++){ + char *device; + int fd; + + wd->tp[i].tpid = i; + wd->tp[i].delsys = SYS_DVBC_ANNEX_A; + wd->tp[i].freq = freq; + wd->tp[i].qam = qam; + wd->tp[i].symbolrate = sym; + wd->tp[i].bandwidth = 0; + wd->tp[i].guard = 0; + wd->tp[i].code_rate = 0; + wd->tp[i].trans_mode = 0; + + device = malloc(sizeof(char)*25); + snprintf(device,24,"/dev/dvb/adapter%d/mod%d",adapt,i); + fd = open(device, O_RDWR); + if( fd < 0 ) + { + fprintf(stderr,"Error opening %s : %s\n",device,strerror(errno)); + free(device); + exit(1); + } + if (set_property(fd, MODULATOR_FREQUENCY, freq) < 0){ + fprintf(stderr,"setting freq %d failed\n",freq); + exit(1); + } + if (set_property(fd, MODULATOR_MODULATION, qam) < 0){ + fprintf(stderr,"setting qam %d failed\n",qam); + exit(1); + } + if (set_property(fd, MODULATOR_SYMBOL_RATE, sym) < 0){ + fprintf(stderr,"setting sym %d failed\n",sym); + exit(1); + } + + if (set_input_bitrate(fd, (DEFAULT_BIT_RATE_C << 32)) < 0){ + fprintf(stderr,"setting bitrate %d failed\n", + (int)DEFAULT_BIT_RATE_C); + exit(1); + } + + + if (set_property(fd, MODULATOR_PCR_MODE, 0) < 0){ + fprintf(stderr,"setting pcr mode 0 failed\n"); + exit(1); + } + freq += 8000000; + close(fd); + free(device); + } +} + +//+++++++++++++++++++++++++++++CLI++++++++++++++++++++++++++++++++++++++++ + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +static void usage(char *progname) +{ + printf ("usage: %s [options] \n\n",progname); + printf ("options:\n"); + printf (" --adapter, -a : adapter number of modulator card\n"); + printf (" --file, -i : input filename(default test.ts)\n"); + printf (" --frequency, -f : start frequency in MHz( default DVB_C 114MHz, DVB-T 474MHz)\n"); + printf (" --dvbt, -t : modulator is DVB-T\n"); + printf (" --NIT, -n : write a minimal NIT for fatser scan\n"); + printf (" --help, -h : print help message\n"); + printf ("\n"); + printf ("\n"); + exit(1); +} + + +static int parse_cl(int argc, char * const argv[], char **filename) +{ + int c; + int fset = 0; + + *filename = strdup("test.ts"); + + writeNIT = 0; + dvbt = 0; + while (1){ + int option_index = 0; + static struct option long_options[] = { + {"help", no_argument , NULL, 'h'}, + {"dvbt", no_argument , NULL, 't'}, + {"NIT", no_argument , NULL, 'n'}, + {"adapter", required_argument , NULL, 'a'}, + {"file", required_argument , NULL, 'i'}, + {"frequency", required_argument , NULL, 'f'}, + {NULL, 0, NULL, 0} + }; + + c = getopt_long (argc, argv, "ha:i:f:nt",long_options, + &option_index); + + if (c == -1) + break; + + switch (c){ + case 'a': + adapt = (int)strtol(optarg,(char **)NULL, 0); + break; + + case 'f': + fset = 1; + start_freq = strtoul(optarg, NULL, 0)*1000000; + break; + + case 'i': + if (*filename){ + free(*filename); + } + *filename = strdup(optarg); + break; + + case 'n': + writeNIT = 1; + break; + + case 't': + if (!fset) start_freq = START_FREQ_T; + dvbt = 1; + break; + + case 'h': + case '?': + default: + usage(argv[0]); + + } + } + + return adapt; + +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//++++++++++++++++++++++++++++++++++++MAIN++++++++++++++++++++++++++++++++ + +int main(int argc, char **argv) +{ + const char *progn = "modtest"; + + int maxout = 0; + int mods = 0; + char *filename; + char *device; + dvb_devices ddevices; + write_data wd; + int fd_out; + int fd_in; + + printf("%s \n\n", progn); + adapt = parse_cl(argc, argv, &filename); + if (check_dvb(&ddevices)<0){ + fprintf(stderr,"No DVB devices found\n"); + exit(1); + } + + for (int i=0; i