/*
 * Merlin query program v1.1.1
 * Copyright 1999 Nicholas Sayer
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <ncurses.h>
#include <time.h>
#include <unistd.h>

#define TARGET_IP	"10.0.0.1"
#define TARGET_PORT	4950

/* color pairs */
#define RED	1
#define YELLOW	2
#define GREEN	3
#define NORM	4	/* predefined as the "normal" color */
#define N_COLORS 5

char use_color=0;

/*
 * Recvfrom for only 1 seconds, bailing out otherwise
 */
int recvtime(int fd,unsigned char *buf,int bufsiz)
{
  fd_set set;
  struct timeval tv;
  int size;

  FD_ZERO(&set);
  FD_SET(fd,&set);
  tv.tv_sec=1;
  tv.tv_usec=0;
  if (select(fd+1,&set,NULL,&set,&tv)!=1)
  {
    return -1;
  }
  return recvfrom(fd,buf,bufsiz,0,NULL,0);
}

char *fancy_bytes(int n)
{
  static char buf[BUFSIZ];
  if (n>1024*1024)
  {
    sprintf(buf,"%3.2f M",((float)n)/(1024.0*1024.0));
    return buf;
  }
  if (n>1024)
  {
    sprintf(buf,"%3.2f K",((float)n)/1024.0);
    return buf;
  }
  sprintf(buf,"%d",n);
  return buf;
}

int fetchparm(int fd, struct sockaddr_in *addr, int block, int parm, unsigned char *bf,int bfsiz)
{

  int size;

  bf[0]=0x80;
  bf[1]=2;
  bf[2]=0;
  bf[3]=block;
  bf[4]=parm;
  sendto(fd,bf,5,0,(struct sockaddr *)addr,sizeof(*addr));
  size=recvtime(fd,bf,bfsiz);
  if (size<0)
  {
    return -1;
  }
  if (size!=bf[1]+(bf[2]<<8)+3)
  {
    return -1;
  }
  if (bf[5]!=0)
  {
    return -1;
  }
  return size;
}

error(msg)
char *msg;
{
  erase();
  move(LINES/2,(COLS-strlen(msg))/2);
  if (use_color)
      color_set(RED,NULL);
  printw(msg);
  if (use_color)
      color_set(NORM,NULL);
}

redgreen_yesno(char *msg,char n)
{
  if (n) {
    if (use_color)
      color_set(GREEN,NULL);
    printw("%s%s\n",msg,"YES");
    if (use_color)
      color_set(NORM,NULL);
  } else {
    if (use_color)
      color_set(RED,NULL);
    printw("%s%s\n",msg,"no");
    if (use_color)
      color_set(NORM,NULL);
  }
}

main()
{

  int fd,size,c;
  struct sockaddr_in addr;
  unsigned char buf[1024];
  WINDOW *win;
  time_t t;

  win=initscr();
  if (has_colors()) {
    start_color();
    if (COLOR_PAIRS>=N_COLORS)
      use_color++;
  }

  if (use_color) {
    init_pair(RED,COLOR_RED,COLOR_BLACK);
    init_pair(YELLOW,COLOR_YELLOW,COLOR_BLACK);
    init_pair(GREEN,COLOR_GREEN,COLOR_BLACK);
    init_pair(NORM,COLOR_WHITE,COLOR_BLACK);
  }
  cbreak();
  noecho();
  nodelay(win,TRUE);
  leaveok(win,TRUE);

  while(1) {
    int tos;
    if ((fd=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP))<0)
    {
      perror("socket()");
      exit(1);
    }
    tos = IPTOS_LOWDELAY;
    setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));


    if (inet_aton(TARGET_IP,&(addr.sin_addr))!=1)
    {
      perror("inet_aton()");
      exit(1);
    }

    addr.sin_port=htons(TARGET_PORT);
    addr.sin_family=AF_INET;

/*
 * Command 0 returns ID frame 1
 */
    buf[0]=0;
    sendto(fd,buf,1,0,(struct sockaddr *)&addr,sizeof(addr));
    size=recvtime(fd,buf,sizeof(buf));
    if (size<=0) {
      error("Timeout reading status frame 1");
      goto skip;
    }

    if (use_color)
      color_set(NORM,NULL);
    move(0,0);
    printw("EID: %02x:%02x:%02x:%02x:%02x:%02x\n",buf[1],buf[2],buf[3],
      buf[4],buf[5],buf[6]);
    printw("Firmware rev: %s\n",buf+7);
    printw("Equipment model: %s\n\n",buf+7+strlen(buf+7)+1);

/*
 * Command 2 returns status frame 3
 */
    buf[0]=2;
    sendto(fd,buf,1,0,(struct sockaddr *)&addr,sizeof(addr));
    size=recvtime(fd,buf,sizeof(buf));
    if (size<=0) {
      error("Timeout reading status frame 3");
      goto skip;
    }

    redgreen_yesno("Channel acquired: ", buf[1]&1);
    redgreen_yesno("Link established: ", buf[1]&2);
    redgreen_yesno("      Registered: ", buf[1]&4);
    if (use_color)
      color_set((buf[3]==0)?GREEN:RED,NULL);
    printw(" Last reg. error: %d\n",buf[3]);
    if (use_color)
      color_set(NORM,NULL);

    if (use_color)
      color_set((buf[4]!=4)?RED:GREEN,NULL);
    printw("       RRM state: ");
    switch(buf[4]) {
      case 0:printw("Inactive\n\n"); break;
      case 1:printw("Wide Area Scan\n\n"); break;
      case 2:printw("Wide Area Search\n\n"); break;
      case 3:printw("Quality Check\n\n"); break;
      case 4:printw("Channel Acquired\n\n"); break;
      case 5:printw("Reference Scan\n\n"); break;
      case 6:printw("Channel Search\n\n"); break;
      case 7:printw("Direted Hop\n\n"); break;
      case 8:printw("Sleep\n\n"); break;
      case 9:printw("RSSI Channel Scan\n\n"); break;
      default:printw("Unknown\n\n"); break;
    }
    if (use_color)
      color_set(NORM,NULL);

    printw("    SPI: %d\n",buf[12]<<8|buf[13]);
    printw("   SPNI: %d\n",buf[16]<<8|buf[17]);
    printw("   WASI: %d\n",buf[14]<<8|buf[15]);
    printw("cell ID: %d\n",buf[18]<<8|buf[19]);
    printw("channel: %d\n",buf[6]<<8|buf[7]);
    printw("  color: %d\n\n",buf[5]);
    if (use_color) {
      if (buf[8]<16)
	color_set(RED,NULL);
      else if (buf[8]<21)
	color_set(YELLOW,NULL);
      else
	color_set(GREEN,NULL);
    }
    printw("     RSSI: %d db\n",-115+buf[8]);  
    if (use_color)
      color_set(NORM,NULL);
    if (use_color) {
      if (buf[9]<4)
	color_set(GREEN,NULL);
      else
	color_set(RED,NULL);
    }
    printw("     BLER: %d\n",buf[9]);
    if (use_color)
      color_set(NORM,NULL);
  
    size=fetchparm(fd,&addr,0x40,0x21,buf,sizeof(buf));
    if (size<=0) {
      error("Timeout fetching parameter 0x40/0x21");
      goto skip;
    }
    if (use_color) {
      if (buf[7]<25)
	color_set(GREEN,NULL);
      else if (buf[7]<50)
	color_set(YELLOW,NULL);
      else
	color_set(RED,NULL);
    }
    printw("NDBC busy: %d %%\n\n",buf[7]);
    if (use_color)
      color_set(NORM,NULL);

    size=fetchparm(fd,&addr,0x40,0x20,buf,sizeof(buf));
    if (size<=0) {
      error("Timeout fetching parameter 0x40/0x20");
      goto skip;
    }

    printw("counts (packets / bytes) - RX:  %5d / %-10s   ",buf[7]|buf[8]<<8,
      fancy_bytes((((((buf[14]<<8) | buf[13])<<8) | buf[12])<<8) | buf[11]) );
    printw("TX:  %5d / %-10s\n",buf[9]|buf[10]<<8,
      fancy_bytes((((((buf[18]<<8) | buf[17])<<8) | buf[16])<<8) | buf[15]) );

skip:
    move(0,69);
    time(&t);
    strftime(buf,sizeof(buf),"%r",localtime(&t));
    printw("%s",buf);

    close(fd);

    refresh();
    c=getch();
    if (c==EOF || c==ERR) {
      sleep(1);
      continue;
    }
    if (c=='q' || c=='Q') {
      endwin();
      exit(0);
    }
    if (c=='\014') /* ^L */
      clearok(win,TRUE);
  }
}



syntax highlighted by Code2HTML, v. 0.9.1