/*
 *   satfind (signal monitoring tool) (dbox-II-project)
 *
 *   Homepage: http://dbox2.elxsi.de
 *
 *   Copyright (C) 2002 Hunz and the dbox2-team
 *
 *   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.
 *
 *   - modified to not use dbox2 specific stuff
 *   - loop ...
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <linux/dvb/dmx.h> 
#include <linux/dvb/frontend.h> 
#define DMX "/dev/dvb/adapter0/demux0"
#define FE "/dev/dvb/adapter0/frontend0"

#define FILLED 4

struct signal {
  uint32_t ber;
  uint16_t snr;
  uint16_t strength;
  fe_status_t status;
};

int max_values[3]={0,0xFFFF,0xFFFF};

int get_signal(struct signal *signal_data, int fe_fd) {
  if(ioctl(fe_fd,FE_READ_BER,&signal_data->ber)<0) {
    fprintf(stderr,"frontend ioctl - Can't read BER (set to 23): %d\n",errno);
    signal_data->ber=23;
    //return -1;
  }
  if(ioctl(fe_fd,FE_READ_SNR,&signal_data->snr)<0) {
    fprintf(stderr,"frontend ioctl - Can't read SNR (set to 23): %d\n",errno);
    signal_data->snr=23;
    //return -1;
  }
  if(ioctl(fe_fd,FE_READ_SIGNAL_STRENGTH,&signal_data->strength)<0) {
    fprintf(stderr,"frontend ioctl - Can't read Signal Strength (set to 23): %d\n",errno);
    signal_data->strength=23;
    //return -1;
  }
  if(ioctl(fe_fd,FE_READ_STATUS,&signal_data->status)<0) {
    fprintf(stderr,"frontend ioctl - Can't read Status: %d\n",errno);
    //return -1;
  }
  return 0;
}

int signal_changed(struct signal *a, struct signal *b)
{
	return ((a->ber != b->ber) ||
		(a->snr != b->snr) ||
	        (a->strength != b->strength) ||
		(a->status != b->status));
}

void get_network_name_from_nit(char *network_name, unsigned char *nit, int len) {
  unsigned char *ptr=nit+10;
  
  if (len<=24) {
    network_name[0]=0;
    return;
  }
  while((ptr-(nit+10)<(((nit[8]&0x0F)<<8)|nit[9]))&&(ptr<nit+len)) {
    if(ptr[0]==0x40) {
      if(ptr[1]>30)
	ptr[1]=30;
      memcpy(network_name,ptr+2,ptr[1]);
      network_name[ptr[1]]=0;
      return;
    }
    else
      ptr+=ptr[1]+2;
  }
  network_name[0]=0;
}

int main(int argc, char **argv) {
  struct dvb_frontend_parameters feparams;
  int tune = 0;
  int fe_fd,dmx_fd;
  fd_set rfds;
  int result;
  struct timeval tv;
  struct signal signal_quality,old_signal;
  struct dmx_sct_filter_params flt;
  unsigned char buf[1024];
  char network_name[31],old_name[31];

   if (argc == 2) {
   	if (!strncmp(argv[1], "--tune", 6)) { 
		tune  = 1; 
	}
	else {
		printf("Usage: satfind [--tune]\n");
		return 0;
	} 
  }

  /* open devices (demux, tuner) */
  if((dmx_fd=open(DMX,O_RDWR))<0) {
    perror("Can't open Demux");
    return 1;
  }
  
  if((fe_fd=open(FE,O_RDONLY))<0) {
    perror("[satfind.c] Can't open Tuner");
    return 1;
  }

  /* initialize demux to get the NIT */
 
  memset(&flt, 0, sizeof(flt));
  
  flt.pid=0x10;
  flt.filter.filter[0]=0x40;
  flt.filter.mask[0]=0xFF;
  flt.timeout=10000;
  flt.flags=DMX_IMMEDIATE_START;
  
  if (ioctl(dmx_fd, DMX_SET_FILTER, &flt)<0)  {
    perror("DMX_SET_FILTER");
    return -1;
  }
  
  /* main stuff here */
  network_name[0]=0;
  old_name[0]=0;
  FD_ZERO(&rfds);
  FD_SET(dmx_fd,&rfds);
  tv.tv_sec=0;
  tv.tv_usec=10000;
 
  if (tune) {
  	/* TP 82 (ProSiebenSat.1 Media AG) on ASTRA 1H */
  	feparams.frequency = 1880000;
  	feparams.inversion = 0;
  	feparams.u.qpsk.symbol_rate = 27500000; 
  	feparams.u.qpsk.fec_inner = FEC_3_4; 
  }

  while(1) {
    if((result=select(dmx_fd+1,&rfds,NULL,NULL,&tv))>0) {
      if(FD_ISSET(dmx_fd,&rfds)) {
	/* got data */
	if((result=read(dmx_fd,buf,sizeof(buf)))>0)
	  get_network_name_from_nit(network_name,buf,result);
	/* zero or less read - we have to restart the DMX afaik*/
	else {
	  printf("result: %d\n",result);
	  ioctl(dmx_fd,DMX_STOP,0);
	  ioctl(dmx_fd,DMX_START,0);
	  network_name[0]=0;
	}
	/* new networkname != "" */
	if((memcmp(network_name,old_name,sizeof(network_name))!=0)&&(network_name[0]!=0)) {
	  int count;
	  for(count=strlen(network_name);count<=10;count++)
	    network_name[count]=0x20;
	  network_name[count]=0;
	  memcpy(old_name,network_name,sizeof(old_name));
	}
      }
      else
	printf("that should never happen...\n");
    }

    FD_ZERO(&rfds);
    FD_SET(dmx_fd,&rfds);
    tv.tv_sec=0;
    tv.tv_usec=10000;

  if(tune) {
	/* TUNE and wait till a possibly LOCK is done */
	printf("frontend: tune to pro7 ...");
  	ioctl(fe_fd,FE_SET_VOLTAGE,SEC_VOLTAGE_13);
	ioctl(fe_fd,FE_SET_FRONTEND, &feparams);
	ioctl(fe_fd,FE_SET_TONE, SEC_TONE_ON);
	usleep(250);
	printf(" done\n");
  }

    get_signal(&signal_quality,fe_fd);
    if (!signal_changed(&signal_quality, &old_signal))
	    continue;
    printf("%s %u %u %u [%c%c]\n",network_name,signal_quality.ber,signal_quality.snr,signal_quality.strength,signal_quality.status&FE_HAS_SIGNAL? 'S':' ',signal_quality.status&FE_HAS_LOCK? 'L':' ');
  }

  /* close devices */
  close(dmx_fd);
  close(fe_fd);
  
  return 0;
}

