/* scan.cpp for PLL2002 scanner like RS/PRO-2033, PRO-2030 etc.

  This program will allow you to connect your DUMB scanner to your computer.
  Yes, it works with even the dumbest scanners like PRO-2033 10 channel $50 scanner.
  And will allow you to get complete control of the scanners frequency ranges and bands.
  For now, it only works with the scanners which use PLL2002 PLL chip,  and, this includes
  about 90% of all scanners made by radioshack.

  This interface can be used like a no parts, no restrictions very low cost
  all scanner interface which will allow you q00% control of your scanner.

  You will need atleast the following interface between the computers parallel port 
  and the scanners internal circuitry: -
  
  DB25 pins      direction                       PLL2002/scanner 
  Pin2 (D0)   IN       -------   pin9   on PLL2002 (disconnect the PLL2002 pins from the scanners UP)
  Pin3 (D1)   IN       -------   pin10  on PLL2002 (disconnect the PLL2002 pins from the scanners UP)
  Pin4 (D2)   IN       -------   pin11  on PLL2002 (disconnect the PLL2002 pins from the scanners UP)
  pin6 (D4)   IN       -------   pullup power, connect a 10 K resistor between pin 6 and pin 13 of the db25
  pin7 (d5)   IN       -------   VHF frontend-select (disconnect this control pins from the scanners UP)
  pin8 (d6)   IN       -------   UHF frontend select (disconnect this control pins from the scanners UP)
  pin9 (d7)   IN       -------   800 Mhz frontend select (disconnect this control pins from the scanners UP)

  pin13 (s4)  OUT      -------   squelch output, connect a 10 K resistor between pin 6 and pin 13 of the db25

  pins 18-25 ground    -------   connect 1or two of these to scanner ground

  It is very highly recommended that all these signals into the scanner be buffered by 74LS244
  and those signals out of the scanner be buffered by a NPN transistor like 2N3904
  
  As alaways, be very-very careful to not fry your scanners circuitry and LSI chips like PLL2002 etc.

  PLL2002 control protocol ( reverse engineered from CRO waveforms and logic analyzer dumps )
  The PLL 2002 has two registers:-

  R register= This divides the RefOsc to generate the Ref freq which is 4.1666/5 /10 /15 Khz typically
              This value sets the basic PLL step size

  N register= This divides the VCO    to generate the signal to be compared against the Ref Freq. 
              The value, in conjuction with R sets the frequency the scanner is tuned to.

  The PLL2002 uses the 3 wire protocol to load values into these registers:-

  Pin 9 = clock, this is toggled from low - hi-lo to shift one bit into internal registers
  pin 10= data, this holds the bit value to be toggled into internal registers with the clock as above
  pin11= Enable or LE, this toggled lo-hi-lo to COMPLETE the transfer of the data packet.
         Toggling this signals the PLL2002 to load the R or N register


  R packet
  16 bits MSB first hold the R Value,  1 Control Bit always signal that it is a R packet, then LE is toggled to complete this pkt

  N packet
  24 bits, MSB first, the first 8 (dummy)bits are always low, the next 8 bits hold the N value, the a 0 control bit signals pkt completion

  In fact writing the following string to the LPT1 by copy <file-containing-string> lpt1 programs the PLL2002 to 162550 (Q:WHY ?)
  

00100100100100102320100100100100102320100100100100102320400010010232010010232010010010232232232010232232010232010010010010232010010010040


  usage  
   
    scan   > scan.001      // scans VHF band from 137 to 174 MHZ and logs frequencies found into scan.001
    scan   162550          // loads frequency into scanner
    scan   R  N            // raw mode, allows U to experiment by setting different step sizes etc etc etc.

   Use borland C++ 3.0 etc  to compile this program by  Bcc scan.cpp.

   This program is intended ONLY for educational purposes. blah blah blah.....
*/




#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <bios.h>
#include <dos.h>
#include <conio.h>



void _CType delay(unsigned __Millisecond);

#define VHF_STUFF_DATA  0x24
#define UHF_STUFF_DATA  0x25

#define NUM_N_BITS        24
#define NUM_R_BITS        17
#define NUM_CONTROL_BITS  1
#define NUM_STOP_BITS     1


#define VHF_MULTIPLIER    2
#define UHF_MULTIPLIER    3

#define VHF_R_VALUE       0x820
#define UHF_R_VALUE       0x9C0


#define REF_FREQ   10400
#define FIRST_IF   10850
#define SECOND_IF   450


#define LPT1       0x378
#define LPT2       0x278

#define LPT1_INT   0x07
#define LPT2_INT   0x05

#define VHF_LO_EDGE     0x628E  // 137.000 MHz
#define VHF_HI_EDGE     0x7F76  // 174.000 MHz

#define UHF_LO_EDGE     0x7B7C  // 406 MHz
#define UHF_HI_EDGE     0x9c9c  // 512 MHz

#define HAM_LO_EDGE     0x6806  // 144 Mhz
#define HAM_HI_EDGE     0x6B26  // 148 Mhz

#define VHFHI_LO_EDGE   0x6B26
#define VHFHI_HI_EDGE   0x7f76

#define WTHR_LO_EDGE    0x7666
#define WTHR_HI_EDGE    0x7684


#define DATA_PORT(p)    (p+0)
#define STATUS_PORT(p)  (p+1)
#define CONTROL_PORT(p) (p+2)


#define IS_SQUELCH_OPEN(p)  ( (inport(STATUS_PORT(p)) ^ 0x10 ) & 0x10)

#define VHF_TUNER_ENABLE   0x20
#define UHF_TUNER_ENABLE   0x40

char zero_byte = 0x30;
char zero_byte_clock = 0x31;
char one_byte = 0x32;
char one_byte_clock = 0x33;
char control_byte = 0x40;
char one_string = { 232 };
char zero_string = { 010 };
char control_string = { 040 };

/*
struct  ref_data_t{
        short dummy_bit:1=0;
        short r_val_msd:8;
        short r_val_lsd:8;
        short ctl_bit:1=1;
};

struct div_data_t{
    short stuff_data:8;
        short  n_val_msd:8;
        short  n_val_lsd:8;
        short  ctl_bit:1=0;
};

struct  pp_data_t {
        short clock:1;     //serial clock out
        short data:1;      //serial data out
        short enable:1;    // serial latch enable
        short mute:1=0;    //maybe used to muto audio to speaker, no effect
on 
LOut
        short pullup:1=1;  //supplies pullup power
        short vhf:1;
        short uhf:1;
        short dummy2:1=0;
};

struct  pp_status_t{
        short dummy:4;
        short squelch:1;    // squelch output from scanner
        short dummy1:1;
        short discr:1;      // discriminator op , also interrupts the PP
        short dummy2:1;
};
*/

struct scan_data_t {
  char data_0;
  char data_1;
  char data_2;
};

struct scan_data_t r_data[19];
struct scan_data_t n_data[26];

int write_scan_data_byte(struct scan_data_t *p_r_data, char scan_data,
                         char band)
{


  if (scan_data == 0) {
    p_r_data->data_0 = (0x30 | (band & 0xf0));
    p_r_data->data_1 = (0x31 | (band & 0xf0));
    p_r_data->data_2 = (0x30 | (band & 0xf0));
  } else if (scan_data == 1) {
    p_r_data->data_0 = (0x32 | (band & 0xf0));
    p_r_data->data_1 = (0x33 | (band & 0xf0));
    p_r_data->data_2 = (0x32 | (band & 0xf0));
  } else if (scan_data == 4) {
    p_r_data->data_0 = (0x30 | (band & 0xf0));
    p_r_data->data_1 = (0x34 | (band & 0xf0));
    p_r_data->data_2 = (0x30 | (band & 0xf0));
  }

  return 0;
}

int write_r_data(unsigned short pport, unsigned short UHF_VHF,
                 unsigned short r_latch_data)
{
  int i, current_r_data_byte;
  char band;

  if (UHF_VHF == 1)
    band = 0x40;
  else
    band = 0x20;

  current_r_data_byte = 0;

  write_scan_data_byte(&(r_data[current_r_data_byte]), 0, band);
// dummy
  current_r_data_byte++;


  for (i = 0; i < 16; i++) {
    if ((r_latch_data << i) & 0x8000) {
      write_scan_data_byte(&(r_data[current_r_data_byte]), 1, band);
      current_r_data_byte++;
    } else {
      write_scan_data_byte(&(r_data[current_r_data_byte]), 0, band);
      current_r_data_byte++;
    }
  }

  write_scan_data_byte(&(r_data[current_r_data_byte]), 1, band);
// control=1
  current_r_data_byte++;

  write_scan_data_byte(&(r_data[current_r_data_byte]), 4, band);
// LATCH
  current_r_data_byte++;

  char *p_char;
  for (i = 0, p_char = (char *) &r_data; i < sizeof(r_data); i++) {
//        fprintf( stderr, "%c", p_char[i]);
    outport(pport, (unsigned short) p_char[i]);
  }
  return 0;
}




int write_n_data(unsigned short pport, unsigned short UHF_VHF,
                 unsigned short n_latch_data)
{
  int i, current_n_data_byte;
  char band;

  if (UHF_VHF == 1)
    band = 0x40;
  else
    band = 0x20;

  current_n_data_byte = 0;

  for (i = 0; i < 8; i++) {
    write_scan_data_byte(&(n_data[current_n_data_byte]), 0, band);
    current_n_data_byte++;
  }

  for (i = 0; i < 16; i++) {

    if ((n_latch_data << i) & 0x8000) {
      write_scan_data_byte(&(n_data[current_n_data_byte]), 1, band);
      current_n_data_byte++;
    } else {
      write_scan_data_byte(&(n_data[current_n_data_byte]), 0, band);
      current_n_data_byte++;
    }
  }

  write_scan_data_byte(&(n_data[current_n_data_byte]), 0, band);
// CONTROL=0
  current_n_data_byte++;

  write_scan_data_byte(&(n_data[current_n_data_byte]), 4, band);
// LATCH
  current_n_data_byte++;

  char *p_char;
  for (i = 0, p_char = (char *) &n_data; i < sizeof(n_data); i++) {

//       fprintf( stderr, "%c", p_char[i]);
    outport(pport, (unsigned short) p_char[i]);
  }


  return 0;
}

int write_rn_data(unsigned short pport, unsigned short UHF_VHF,
                  unsigned short r_latch_data, unsigned short n_latch_data)
{

  write_r_data(pport, UHF_VHF, r_latch_data);

  write_n_data(pport, UHF_VHF, n_latch_data);

  return 0;
}

int check_squelch(unsigned short port)
{
  unsigned long start_time;
  unsigned long cur_time;
  int i;

  start_time = cur_time = biostime(0, 0L);

//      while( ((unsigned long)cur_time - (unsigned long)start_time)  < 2)
  for (i = 0; i < 18; i++) {
    delay(1);
    cur_time = biostime(0, 0L);
    if (IS_SQUELCH_OPEN(port))
      return 1;
  }
  return 0;
}

int my_delay(unsigned short delay)
{

  unsigned long start_time;
  unsigned long cur_time;

  start_time = cur_time = biostime(0, 0L);

//      fprintf( stderr, "0cur_time=0x%lx, start_time=0x%lx, delay=%x\n", cur_time, start_time, delay);

  while ((unsigned short) ((unsigned long) cur_time -
                           (unsigned long) start_time) < delay) {
    cur_time = biostime(0, 0L);
//            fprintf( stderr, "1cur_time=0x%lx, start_time=0x%lx, delay=0x%x\n", cur_time, start_time, delay);
    if (kbhit())
      return 1;
  }

  return 0;
}

int print_frequency(int UHF_VHF, unsigned short n_data, int prompt)
{

  unsigned long freq, l_data;

  l_data = n_data;

  if (UHF_VHF)
    freq = ((l_data * 25) / 2) + 10850;
  else
    freq = (l_data * 5) + 10850;

  if (prompt)
    fprintf(stderr, "scanning freq=%-.6ld, nfound= %-.4d \r", freq, prompt - 1);
  else
    fprintf(stdout, "%-.6ld\n", freq);


  return 0;
}

void main(int argc, char **argv)
{
  int pport = 0x378;
  unsigned int R_data, N_data = VHF_LO_EDGE;
  int i, found_this_pass;
  int band;
  unsigned long l_freq, l_ref;

  if (argc == 1) {
    fprintf(stderr, "SCANNING VHF BAND , press any key to exit\n");

    for (i = 0; i < 100; i++) {
      found_this_pass = 0;

      l_freq = VHF_LO_EDGE;

      if (l_freq > 406000) {
        band = 1;
        N_data = ((l_freq - 10850) * 2) / 25;
        R_data = UHF_R_VALUE;
      } else {

        band = 0;
        N_data = (l_freq - 10850) / 5;
        R_data = VHF_R_VALUE;
      }
      fprintf(stdout, "scan no %d\n", i);

      for (N_data = VHF_LO_EDGE; N_data < VHF_HI_EDGE; N_data++) {
        print_frequency(0, N_data, found_this_pass + 1);
//prompt
        if (kbhit()) {
          fprintf(stderr, "\nclosing\n");
          return;
        }

        write_rn_data(pport, 0, R_data, N_data);
        if (check_squelch(pport)) {
          print_frequency(0, N_data, 0);
          found_this_pass++;
          my_delay(100);
          //           my_delay( 5000);
        }
      }
    }                           // scan the VHF band 10 times
  }

  else if (argc == 2) {         // tune to that frequency
    sscanf(argv[1], "%ld", &l_freq);
    fprintf(stderr, "\nl_freq=%ld\n", l_freq);

    if (l_freq > 406000) {
      band = 1;
      N_data = ((l_freq - 10850) * 2) / 25;
      R_data = UHF_R_VALUE;
    } else {
      band = 0;
      N_data = (l_freq - 10850) / 5;
      R_data = VHF_R_VALUE;
    }

    write_rn_data(pport, band, R_data, N_data);
  }

  else if (argc == 3) {         //raw mode
    sscanf(argv[1], "%ld", &l_freq);
    sscanf(argv[2], "%ld", &l_ref);
    fprintf(stderr, "\nl_freq=%ld, l_ref=%ld\n", l_freq, l_ref);

    if (l_freq > 406000) {
      band = 1;
      N_data = l_freq;
      R_data = l_ref;
    } else {
      band = 0;
      N_data = l_freq;
      R_data = l_ref;
    }

    N_data = (l_freq - 10850) / 5;

    write_rn_data(pport, band, R_data, N_data);
  }

}


syntax highlighted by Code2HTML, v. 0.9.1