#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include <stdlib.h>
#include <math.h>
/* EDACS control channel monitoring program... */
/* Scanner / system specific stuff needed for "trunk tracking" mode */
static double baudrate = 1200.0; /* baud rate of computer to scanner link */
/* This is the table equating logical channel numbers with actual */
/* frequencies. The first entry, chan[0], gives the frequency for */
/* logical channel number 1; chan[1] give the frequency for LCN 2, */
/* et cetera. You will need to change this table to match those */
/* systems in your own area unless you happen to live in Huntsville */
/* Alabama and want to monitor this 5 channel commercial SMR trunk */
static double chan[30] = {
856.0375, 857.0375, 858.0375, 859.0375, 860.0375
};
/* this identifies the control channel in the above table */
int control = 1; /* this number is the lcn of the control channel - 1 */
int nucha = 20; /* number of channels in system; for the above */
/* system it should be set to five, but it was put to */
/* 20 for people who don't want to change the default */
/* global variables */
int lc = 0;
int fobp = 0; /* pointer to current position in array fob */
char ob[1000]; /* buffer for raw data frames */
int obp = 0; /* pointer to current position in array ob */
int invfla = 0; /* bit inversion flag */
static int comport = 0x3f8; /* serial port base address; set in main */
static int sport = 1; /* serial port number to use */
static int dotting = 0; /* dotting sequence detection flag */
static int mode = 0; /* mode flag; 0 means we're on control */
/* channel; 1 means active frequency */
/* array for raw data coming off the serial port */
static unsigned int buflen = 15000; /* length of data buffer */
static volatile unsigned int cpstn = 0; /* current position in buffer */
static volatile unsigned int fdata[15001]; /* timing data array */
void interrupt(*oldfuncc) (); /* vector to old com port interrupt */
/*--------------------------------------------------------------------*/
/* A BUNCH OF LOW LEVEL STUFF FOLLOWS */
/*--------------------------------------------------------------------*/
/**********************************************************************/
/* comint */
/* */
/* this is serial com port interrupt */
/* we assume here that it only gets called when one of the status */
/* lines on the serial port changes (that's all you have hooked up). */
/* All this handler does is find the number of system timer ticks */
/* since the last call and stores it in the fdata array. The MSB */
/* is set to indicate whether the status line is zero. In this way */
/* the fdata array is continuously updated with the appropriate */
/* length and polarity of each data pulse for further processing by */
/* the main program. */
void interrupt comint()
{
static unsigned int d1, d2, ltick, tick, dtick;
/* the system timer is a 16 bit counter whose value counts down */
/* from 65535 to zero and repeats ad nauseum. For those who really */
/* care, every time the count reaches zero the system timer */
/* interrupt is called (remember that thing that gets called every */
/* 55 milliseconds and does housekeeping such as checking the */
/* keyboard. */
outportb(0x43, 0x00); /* latch counter until we read it */
d1 = inportb(0x40); /* get low count */
d2 = inportb(0x40); /* get high count */
/* get difference between current, last counter reading */
tick = (d2 << 8) + d1;
dtick = ltick - tick;
ltick = tick;
/* set MSB to reflect state of input line */
if ((inportb(comport + 6) & 0xF0) > 0)
dtick = dtick | 0x8000;
else
dtick = dtick & 0x3fff;
fdata[cpstn] = dtick; /* put freq in fdata array */
cpstn++; /* increment data buffer pointer */
if (cpstn > buflen)
cpstn = 0; /* make sure cpstn doesnt leave array */
d1 = inportb(comport + 2); /* clear IIR */
d1 = inportb(comport + 5); /* clear LSR */
d1 = inportb(comport + 6); /* clear MSR */
d1 = inportb(comport); /* clear RX */
outportb(0x20, 0x20); /* this is the END OF INTERRUPT SIGNAL */
}
/************************************************************************/
/* SERIAL PORT INITIALIZATION */
/************************************************************************/
/* basic purpose: enable modem status interrupt and set serial port */
/* output lines to supply power to interface */
void set8250(double bps)
{ /* sets up the 8250 UART */
static unsigned int t, dv, tp;
dv = (int) 1843200.0 / (16.0 * bps);
/* how to configure your serial port setup: */
/* do you want two stop bits? then make sure you set bit 2 in tp */
/* do you want a parity bit generated? then set bit bit 3 in tp */
/* do you want even parity? then set bit 4 in tp */
/* do you want an 8 bit word length? set bits 0 and 1 in tp */
/* do you want an 7 bit word length? set bit 1 in tp */
tp = 0x80 + 0x02 + 0x01;
outportb(comport + 3, tp); /* set line control register */
outport(comport, dv); /* output brg divisor latch */
tp = tp & 0x7f; /* switch out brg divisor reg */
outportb(comport + 3, tp);
outportb(comport + 1, 0x08); /* enable MODEM STATUS INTERRUPT */
outportb(comport + 4, 0x0a); /* push up RTS, DOWN DTR */
t = inportb(comport + 5); /* clear LSR */
t = inportb(comport); /* clear RX */
t = inportb(comport + 6); /* clear MSR */
t = inportb(comport + 2); /* clear IID */
t = inportb(comport + 2); /* clear IID - again to make sure */
}
/* this routine allows the RTS output line to be set either high or */
/* low depending on whether sta = 0. */
void rts_state(int sta)
{
static int rv;
rv = inportb(comport + 4);
rv = rv & 0x01; /* save DTR state */
rv = rv | 0x08; /* enable interrupt to reach PIC */
if (sta != 0)
rv = rv | 0x02;
outportb(comport + 4, rv);
}
/* this routine allows the DTR output line to be set either high or */
/* low depending on whether sta = 0. */
void dtr_state(int sta)
{
static int rv;
rv = inportb(comport + 4);
rv = rv & 0x02; /* save RTS state */
rv = rv | 0x08; /* make sure interrupts can reach PIC */
if (sta != 0)
rv = rv | 0x01;
outportb(comport + 4, rv);
}
/************************************************************************/
/* send_char */
/* */
/* This routine sends a character out on the serial port TxD line. */
/* It makes sure the previous character has been completely sent and */
/* then sends puts the current character into the transmit register. */
/************************************************************************/
void send_char(int c)
{
/* wait for transmit reg to empty */
while ((inportb(comport + 0x05) & 0x20) == 0x00);
/* send character */
outportb(comport, c);
}
/************************************************************************/
/* set_freak */
/* */
/* This routine must be written by the user for his/her specific radio. */
/* The input is the desired frequency the scanner should go to. */
/* Characters are sent to the scanner using the send_char routine. */
/************************************************************************/
void set_freak(double freq)
{
/* put your code here */
/* call send_char() to send a byte to your scanner */
}
/************************************************************************/
/* TIMER CHIP INITIALIZATION */
/************************************************************************/
/* purpose: make sure computer timekeeper is set up properly. This */
/* routine probably isn't necessary - it's just an insurance */
/* policy. */
void set8253()
{ /* set up the 8253 timer chip */
/* NOTE: ctr zero, the one we are using */
/* is incremented every 840nSec, is */
/* main system time keeper for dos */ outportb(0x43, 0x34);
/* set ctr 0 to mode 2, binary */
outportb(0x40, 0x00); /* this gives us the max count */
outportb(0x40, 0x00);
}
/**********************************************************************/
/* higher level stuff follows */
/**********************************************************************/
/* structure for storing the control channel data frame information */
struct ccdat {
int cmd; /* command */
int lcn; /* logical channel number */
int stat; /* status bits */
int id; /* agency/fleet/subfleet id */
int bad; /* CRC check flag - when set to 1 it means data is corrupt */
};
/* array acn[] indicates which channels are currently active; aid[] */
/* indicates which id is using the active channel */
/* acn[] is actually a countdown timer decremented each time a */
/* control channel command is received and is reset whenever that */
/* channel appears in control channel data stream. When this reaches */
/* zero the channel is assumed to have become inactive. */
/* aid[] is used to make sure a new group appearing on an active */
/* channel is recognized even if acn[] had not yet reached zero */
int acn[33], aid[33], nacn = 0;
/************************************************************************/
/* show_setup */
/* */
/* Purpose: setup things for screen display */
/************************************************************************/
void show_setup()
{
static int i;
clrscr();
for (i = 0; i < 33; i++) {
acn[i] = 0;
aid[i] = 0xffff;
}
gotoxy(1, 1);
textcolor(YELLOW);
cprintf("LCN ID ST COMMAND");
gotoxy(1, 2);
cprintf("--- ---- --- ---------");
textcolor(LIGHTGRAY);
gotoxy(65, 2);
cprintf("Using COM%1i", sport);
gotoxy(63, 3);
cprintf("# channels: %2i", nucha);
gotoxy(60, 4);
cprintf("Exit: Space or Esc ");
gotoxy(60, 6);
cprintf("Misc Control data");
gotoxy(60, 7);
cprintf("LCN ID ST CMD");
gotoxy(75, 1);
textcolor(WHITE);
cprintf("-");
nacn = 0;
}
/************************************************************************/
/* show_active */
/* */
/* this routine gets called ONCE whenever a new ID becomes active on */
/* a given channel number */
/************************************************************************/
void show_active(struct ccdat info)
{
static int linx, liny, cm;
/* this is where you will want to insert some code that decides if */
/* you want to switch to this particular active frequency. Right */
/* now this commented out code fragment would have sent your scanner */
/* off to any active frequency. */
/* you'll probably also want to filter out data channel assignemnts */
/* set_freak(chan[info.lcn-1]); */
/* mode = 1; */
/* dotting = 0; */
linx = 1;
liny = 2 + info.lcn;
if (liny > 22) {
linx = 31;
liny -= 20;
}
gotoxy(linx, liny);
cm = info.cmd;
cprintf("%2i: %03X %01X %02X ", info.lcn, info.id, info.stat, cm);
textcolor(LIGHTGRAY);
if (cm == 0xEE)
cprintf("VOICE");
else if (cm == 0xEC)
cprintf("PHONE");
else if (cm == 0xF6)
cprintf("VOICE");
else if (cm == 0xA0)
cprintf("DATA");
else if (cm == 0xA1)
cprintf("DATA");
textcolor(WHITE);
}
/************************************************************************/
/* show_inactive gets called when a channel is no longer active */
/* it simply writes out a bunch of spaces to erase the old */
/* information */
/************************************************************************/
void show_inactive(int lcn)
{
static int linx, liny;
linx = 4;
liny = 2 + lcn;
if (liny > 22) {
linx = 34;
liny -= 20;
}
gotoxy(linx, liny);
aid[lcn] = 0xffff;
cprintf(" ");
}
/* scroll raw commands on window in right hand side of display */
void show_raw(struct ccdat inf)
{
static int lc = 8;
if (lc == 24)
movetext(60, 9, 79, 24, 60, 8);
else
lc++;
gotoxy(61, lc);
cprintf("%02X %03X %01X %02X", inf.lcn, inf.id, inf.stat, inf.cmd);
}
/************************************************************************/
/* proc_cmd */
/* */
/* This routine figures out when a group becomes active on a new */
/* channel and when a channel is has become inactive. This info is */
/* passed to the two routines above which show the changes on the */
/* screen */
/************************************************************************/
void show(struct ccdat info)
{
static int idup = 0, cdup = 0, i, aid[8], sysid = 0x0000;
static char dup[4] = { 45, 47, 124, 92 };
/* update the "I'm still receiving data" spinwheel character on screen
*/ cdup++;
if (cdup > 9) {
gotoxy(75, 1);
idup = (idup + 1) & 0x03;
putch(dup[idup]);
cdup = 0;
}
/* process only good information blocks */
if (info.bad == 0) {
/* try to display all non idle information blocks */
if (info.cmd < 0x80) {
show_raw(info);
} else if (((info.cmd >> 1) != 0x7E)) {
if ((info.lcn > 0) & (info.lcn <= nucha)) {
/* check to see if ID is in active list... if not it must be new */
if (aid[info.lcn] != info.id) {
/* if (aid[info.lcn] != 0xffff) show_inactive(info.lcn); */
/* a new ID has become active */
show_active(info);
/* add to activity list */
aid[info.lcn] = info.id;
}
/* update activity timer for this active channel */
acn[info.lcn] = 8 + (nacn << 2);
}
} else if (info.cmd == 0xFD) {
/* look at background / idle stuff to find system id */
if (sysid != info.id) {
sysid = info.id;
textcolor(YELLOW);
gotoxy(35, 1);
cprintf("SYS ID: %03X", sysid);
textcolor(WHITE);
}
}
}
/* see if any channel numbers have dropped off */
/* this is done by waiting for a certain number of control channel */
/* commands to go by before assuming the channel is no longer */
/* active. */
nacn = 0; /* nacn holds the number of active channels */
for (i = 1; i <= nucha; i++) {
if (acn[i] != 0) {
nacn++;
acn[i]--;
if (acn[i] == 0) {
/* LCN has become inactive */
show_inactive(i);
aid[i] = 0xffff;
}
}
}
}
/************************************************************************/
/* proc_cmd */
/* */
/* This routine processes a data frame by checking the CRC and nicely */
/* formatting the resulting data into structure info */
/************************************************************************/
void proc_cmd(int c)
{
static int ecc = 0, sre = 0, cc = 0, ud = 0xA9C, nbb = 0, tb, orf, i;
static char oub[50];
static struct ccdat info;
if (c < 0) {
cc = 0;
sre = 0x7D7;
ecc = 0x000;
oub[28] = 0;
/* pick off, store command (eight bits) */
for (i = 0; i <= 7; i++) {
orf = orf << 1;
orf += oub[i];
}
orf = orf & 0xff;
info.cmd = orf;
/* pick off LCN (five bits) */
for (i = 8; i <= 12; i++) {
orf = orf << 1;
orf += oub[i];
}
orf = orf & 0x1f;
info.lcn = orf;
/* pick off four status bits */
for (i = 13; i <= 16; i++) {
orf = orf << 1;
orf += oub[i];
}
orf = orf & 0x0f;
info.stat = orf;
/* pick off 11 ID bits */
for (i = 17; i <= 27; i++) {
orf = orf << 1;
orf += oub[i];
}
orf = orf & 0x07ff;
info.id = orf;
if (nbb == 0)
info.bad = 0;
else
info.bad = 1;
if (nbb == 0) {
show(info);
}
nbb = 0;
} else {
cc++;
/* bits 1 through 28 will be run through crc routine */
if (cc < 29) {
oub[cc - 1] = c;
if (c == 1)
ecc = ecc ^ sre;
if ((sre & 0x01) > 0)
sre = (sre >> 1) ^ ud;
else
sre = sre >> 1;
} else {
/* for the rest of the bits - check if they match calculated crc */
/* and keep track of the number of wrong bits in variable nbb */
if ((ecc & 0x800) > 0)
tb = 1;
else
tb = 0;
ecc = (ecc << 1) & 0xfff;
if (tb != c)
nbb++;
}
}
}
/************************************************************************/
/* proc_frame */
/* */
/* This routine processes the two raw data frames stored in array ob[]. */
/* Each data frame is repeated three times with the middle repetition */
/* inverted. So bits at offsets of 0, 40, and 80 should all be carrying */
/* the same information - either 010 or 101 if no errors have occured). */
/* Error correction is done by ignoring any single wayward bit in each */
/* triplet. For example a 000 triplet is assumed to have actually been */
/* a 010; a 001 -> 101; 011 -> 010; et cetera. Array tal[] holds a table*/
/* giving the "corrected" bit value for every possible triplet. Two */
/* or three wrong bits in a triplet cannot be corrected. The resulting */
/* data bits are send on the proc_cmd routine for the remaining */
/* processing. */
/************************************************************************/
void proc_frame()
{
static int i, l;
static int tal[9] = { 0, 1, 0, 0, 1, 1, 0, 1 };
/* do the first data frame */ proc_cmd(-1);
/* reset proc_cmd routine
*/
for (i = 0; i < 40; i++) {
l = (int) ((ob[i] << 2) + (ob[i + 40] << 1) + ob[i + 80]); /* form triplet */
l = tal[l]; /* look up the correct
bit value in the table */
proc_cmd(l); /* send out bit */
}
/* do the second data frame */
proc_cmd(-2);
for (i = 0; i < 40; i++) {
l = (int) ((ob[i + 120] << 2) + (ob[i + 160] << 1) + ob[i + 200]);
l = tal[l];
proc_cmd(l);
}
}
/************************************************************************/
/* frame_sync */
/* */
/* This routine takes the raw bit stream and tries to find the 48 bit */
/* frame sync sequence. When found it stores the next 240 raw data bits */
/* that make up a data frame and stores them in global array ob[]. */
/* Routine proc_frame is then called to process the data frame and the */
/* cycle starts again. When mode = 1 it will only look for the dotting */
/* sequence and updat the dotting flag. */
/************************************************************************/
void frame_sync(char gin)
{
static int sr0 = 0, sr1 = 0, sr2 = 0, hof = 300, xr0, xr1, xr2, i, nh =
0, ninv = 0;
static int fsy[3][3] = {
0x1555, 0x5712, 0x5555, /* control channel frame sync */
0x5555, 0x5555, 0x5555, /* dotting sequence */
0xAAAA, 0xAAAA, 0x85D3 /* data channel frame sync (not used) */
};
/* update registers holding 48 bits */
sr0 = sr0 << 1;
if ((sr1 & 0x8000) != 0)
sr0 = sr0 ^ 0x01;
sr1 = sr1 << 1;
if ((sr2 & 0x8000) != 0)
sr1 = sr1 ^ 0x01;
sr2 = sr2 << 1;
sr2 = sr2 ^ (int) gin;
/* update ob array */
if (obp < 600) {
ob[obp] = gin;
obp++;
}
/* find number of bits not matching sync pattern */
xr0 = sr0 ^ fsy[mode][0];
xr1 = sr1 ^ fsy[mode][1];
xr2 = sr2 ^ fsy[mode][2];
nh = 0;
for (i = 0; i < 16; i++) {
if ((xr0 & 0x01) > 0)
nh++;
xr0 = xr0 >> 1;
if ((xr1 & 0x01) > 0)
nh++;
xr1 = xr1 >> 1;
if ((xr2 & 0x01) > 0)
nh++;
xr2 = xr2 >> 1;
}
/* if there are less than 3 mismatches with sync pattern and we aren't
*//* inside a data frame (hold-off counter less than 288) sync up */
if (nh < 4) { /* mode zero means we're monitoring control channel */
if ((hof > 287) && (mode == 0)) {
proc_frame();
obp = 0;
hof = 0;
ninv = 0;
} /* mode 1 means we're on voice channel and so we just found
dotting */
else if ((mode == 1) && (nh < 2)) {
dotting++;
}
}
/* check for polarity inversion - if all frame sync bits mismatch 12 */
/* times in a row without ever getting a good match assume that one */
/* must invert the bits */
if ((nh == 48) && (mode == 0)) {
ninv++;
if (ninv > 12) {
invfla ^= 0x01;
gotoxy(65, 1);
if (invfla == 1)
cprintf("INVERT");
else
cprintf(" ");
ninv = 0;
}
}
if (hof < 1000)
hof++;
}
/************************************************************************/
/* DISPLAY HELP SCREEN */
/************************************************************************/
void help()
{
printf("\n EDACS control channel monitoring program\n");
printf(" Command line arguement summary\n\n");
printf(" /NC:x - set x to number of channels used in system you \n");
printf(" are monitoring. Minimum value = 3; max = 31 \n");
printf(" /COM:y - set y = 1,2,3,4 to set com port you want to use.\n");
printf("\nExample: if your program is called edacs.exe and you wish to \n");
printf(" monitor a 10 channel system using COM2 you should type \n");
printf(" the following in at the DOS prompt:\n\n");
printf(" EDACS /nc:10 /com:2\n");
printf("\n Interface requirements: Hamcomm type data slicer circuit.\n");
printf(" The program automatically determines the property polarity of \n");
printf(" the incoming data. When everything is working properly you will\n");
printf(" see the the character on the upper right hand corner of your \n");
printf(" screen spinning around indicating that EDACS control channel \n");
printf(" information is being successfully processed.\n\n");
printf(" See text file for further details...\n\n");
printf(" press any key to continue...\n");
getch();
}
void main(int argc, char *argv[], char *env[])
{
static unsigned int n, i = 0, j;
static int irqv = 0x0c, ef = 0, ch;
FILE *out;
static char s = 48, temp[20];
static double dt, exc = 0.0, clk = 0.0, xct, dto2;
for (n = 1; n < argc; n++) {
strcpy(temp, argv[n]);
strupr(temp);
j += sscanf(temp, "/NC:%i", &nucha);
j += sscanf(temp, "/COM:%i", &sport);
if (temp[1] == 'H')
j = 30;
}
if (nucha > 31) {
nucha = 31;
j = 30;
}
if (nucha < 3) {
nucha = 3;
j = 30;
}
if (sport > 4) {
sport = 4;
j = 30;
}
if (sport < 1) {
sport = 1;
j = 30;
}
if ((j + 1) != argc)
help();
clrscr();
/* dt is the number of expected clock ticks per bit */
dt = 1.0 / (9600.0 * 838.22e-9);
dto2 = 0.5 * dt;
/* set up items related to serial port */
n = inportb(0x21);
if ((sport == 1) | (sport == 3)) {
irqv = 0x0c;
oldfuncc = getvect(irqv); /* save COM Vector */
setvect(irqv, comint); /* Capture COM vector */
outportb(0x21, n & 0xef);
if (sport == 1)
comport = 0x3f8;
else
comport = 0x3e8;
} else {
irqv = 0x0b;
oldfuncc = getvect(irqv); /* save COM Vector */
setvect(irqv, comint); /* Capture COM vector */
outportb(0x21, n & 0xf7);
if (sport == 2)
comport = 0x2f8;
else
comport = 0x2e8;
}
set8253(); /* set up 8253 timer chip */
set8250(baudrate); /* set up 8250 UART */
printf("Checking for data coming in on COM%i... \n", sport);
printf("If program just sits here press any key to exit...\n");
while ((cpstn < 3) & (kbhit() == 0));
if (cpstn < 3) {
printf("HEY - no data seems to be coming in over your interface.\n\n");
outportb(0x21, n); /* disable IRQ interrupt */
setvect(irqv, oldfuncc); /* restore old COM Vector */
exit(1);
} else
printf("Interface seems to work properly...\n\n");
if (nucha > 31)
nucha = 31;
if (nucha < 3)
nucha = 3;
show_setup();
while (ef == 0) {
/* check if key has been hit */
if (kbhit() != 0) {
ch = getch();
/* a space or Esc key press sets the exit flag */
if ((ch == 32) | (ch == 27))
ef = 1;
else {
/* any other key press returns the scanner to the control */
/* channel if it had switched to an active channel */
set_freak(chan[control]);
mode = 0;
i = cpstn;
}
}
if (i != cpstn) {
s = (char) ((fdata[i] >> 15) ^ invfla);
/* add in new number of cycles to clock */ clk += (fdata[i] &
0x7fff);
xct = exc + dto2; /* exc is current boundary */
while (clk >= xct) {
frame_sync(s);
clk = clk - dt;
} /* clk now holds new
boundary position. update exc slowly... *//* 0.005 sucks; 0.02 better;
0.06 mayber even better; 0.05 seems pretty good */
exc = exc + 0.050 * (clk - exc);
i++;
if (i > buflen)
i = 0;
/* If we are sitting on an active channel and the dotting sequence */
/* is detected, we should switch back to the control channel */
if ((mode == 1) && (dotting > 0)) {
set_freak(chan[control]);
mode = 0;
dotting = 0;
i = cpstn;
}
}
}
outportb(0x21, n); /* disable IRQ interrupt */
setvect(irqv, oldfuncc); /* restore old COM Vector */
gotoxy(1, 23);
printf("\n");
}
syntax highlighted by Code2HTML, v. 0.9.1