/*
**	Projekt:			SCCD (Simple City-Call Decoder)
**  Modul:      		sccd.c (serial)
**  Abhaengige module:  none
**  Autor:      		Hacko
**  Copyright:  		gone
**  Version:    		0.000002zetatestblagammablubber
**  Lastmodification:   11.2.92 23:02
**  History:
**  Hardware:
**
**                                O+5V
**                                ³
**                  0.022æF  ÚÄÄÄÄÁÄÄÄÄ¿
**                    ³³     ³    7UB+ ³OP:TLC 271
**                OÄÄÄ´ÃÄÄÄÄÄ´2-       ³
**                    ³³     ³         ³
**                |          ³     Aus6ÃÄÄÄÄÄ*ÄÄÄÄÄÄÄÄÄÄO Pin 5 DB25 (CTS)
**                |          ³         ³     ³
**                |       ÚÄÄ´3+       ³     ³          |
**                |       ³  ³    4UB- ³     ³          |
**     OhrStecker |       ³  ÀÄÄÄÄÂÄÄÄÄÙ    ÚÁ¿/        | Computer
**     Scanner    |       ³       ³         ³ /         | COM1
**                |       ³       O-5V      ³/³         |
**                |       ³                 / ³100kOHM  |
**                V       ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ/ÀÂÙ         V
**                                           ³
**                OÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ*ÄÄÄÄÄÄÄÄÄÄO Pin 7 DB25 (GND)
**                                 GND
**
** f1 = 465.970 MHz 	512 Bits/sec
** f2 = 466.075 MHz     1200 Bits/sec
** f3 = 466.230 MHz     1200 Bits/sec
**
*/


/* compact memory model */
/* keine flotingpoint emu */

/*
**
** Includes fuer dieses Modul
**
*/
#include <stdio.h>
#include <io.h>
#include <dos.h>
#include <fcntl.h>
#include <alloc.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <time.h>


/*
**
** Defines die die arbeitsweise beeinflussen
**
*/
#define BITWRONG                /* fallback wenn paritaet nicht stimmt */
#define LOGFILE					/* Wir schreiben eine Logfile fuer spaetere
								   Datenbankauswertungen (grins) */
#define CHANNELSCAN				/* andere kanaele abtasten */


/*
**
** Defines fuer dieses Modul
**
*/
#define TIMER0CLOCK 1193182L	/* Grundfreqenz des Timerbausteins */
								/* divisor fr timer 0 : sample
								   time=30/CLOCK_FREQ = 25.1 uSec */
#define TIMER0B512  2330  		/* Teilerrate fuer Baudratenscannung
								   (in der Praeambel) */
#define TIMER0B1200 994			/* Baudrate auf der erkannt werden soll */

#define SYNCRON  0x7cd215d8L	/* Frame fuer das Syncronwort */
#define SYNCINFO 0x7cd21436L	/* Frame fuer das InforufSyncronwort */
#define IDLE     0x7a89c197L	/* Frame fuer das Idlewort */

#define COMPORT	0x3f8           /* COM1 */
#define TXBYTE  0
#define RXBYTE  0
#define DIFLLB  0
#define DIFLHB  1
#define IER     1
#define IIR		2
#define LCR		3
#define MCR		4
#define LSR		5
#define MSR		6

#define GETSYNCR 	1			/* Auf erstes Syncronwort warten */
#define GETFRAME	2			/* Frames einladen */


/*
**
** Variablen / Speicher der von diesem Modul benutzt wird
**
*/
#define MAXARRAY 60000		    /* maximale an Bytes im pufferfeld */
		 char *roh_daten_feld;	/* zeiger auf pufferfeld */
volatile unsigned int  roh_daten_anfang; 	/* momentaner Anfang des Ringpuffers */
volatile unsigned int  roh_daten_ende;		/* momentanes Ende des Ringpuffers */
volatile unsigned int  roh_daten_laenge; 	/* momentane Ringpufferlaenge (anzahl der Daten) */

unsigned int tics;				/* soviel Teilerschritte treten pro Bit auf */
unsigned int tics_2;			/* die Haelfte der Schritte */

void interrupt (*old_vec)();	/* Adresse der alten Timerseviceroutine */
void interrupt (*time_int)();	/* Adresse der alten Timer0 Routine */
void interrupt time_i();		/* prototyp der Timer0 Routine */
void interrupt lauf_er();		/* prototyp der bitholroutine */
volatile int abbruch = 0;		/* wir sollen das Programm verlassen */
volatile int eine_sek_weg = 0;	/* es ist eine Sekunde vergangen */
volatile char carrier = 0;		/* 0= wenn carrier weg, 1=wenn carrier da */

#ifdef LOGFILE
FILE *logfile = NULL;			/* logfile fuer die daten */
volatile int last_sync;			/* Sekunden zaehlen wann das letzte mal ein Sync emfangen wurde */
#endif

char asc_time[40] = "Keine Zeit angegeben";

static char numerik[] = {       /* Decodierrung von Numerikpagern */
'0',
'8',
'4',
' ',
'2',
'=',
'6',
']',
'1',
'9',
'5',
'_',
'3',
'U',
'7',
'['
};

#ifdef CHANNELSCAN
static struct chan_car {
	unsigned long chan_freq;
	unsigned long car_ges;
	unsigned long car_viertel_h[96];
};

#define CHANCARSIZE sizeof(struct chan_car)
#define FILECHANCAR "channel.bin"
#endif /* CHANNELSCAN */

/*
**
** Routinen dieses Moduls
**
*/
char *
strip_cr(char *cptr)					/* CR+LF rausscheissen */
{
	char *oldcptr;

	oldcptr = cptr;
	while (*cptr != '\0') {
		if (*cptr == '\r' ||  *cptr == '\n')   *cptr = '\0';
		cptr++;
	}
	return(oldcptr);
}

void
inst_ser()
{
	outportb(COMPORT+LCR, 0x83);
	outport(COMPORT+DIFLLB, 0x0018); /* 4800 Baud */
	outportb(COMPORT+LCR, 0x03);
}

void sendchar(c)
char c;
{
		outportb(COMPORT+TXBYTE, c);
		while ((inportb(COMPORT+LSR) & 0x20) != 0)   ;
		printf("0x%02x ",c);
		fflush(stdout);
}

#define MODLSB 0x10
#define MODUSB 0x11
#define MODAMN 0x14
#define MODAMW 0x15
#define MODFMN 0x16
#define MODFMW 0x17

void changefrequenz(modulation, frequenz)
int modulation;
long frequenz;
{
static int 	oldmodulation 	= 0;
static long oldfrequenz 	= 0L;
	char c1,c2,c3,c4,c;

	if (modulation != oldmodulation) {
		if 		(modulation == MODLSB)	sendchar(MODLSB);
		else if (modulation == MODUSB)  sendchar(MODUSB);
		else if (modulation == MODAMN)  sendchar(MODAMN);
		else if (modulation == MODAMW)  sendchar(MODAMW);
		else if (modulation == MODFMW)  sendchar(MODFMW);
		else   							sendchar(MODFMN);
		sendchar('A');
		sendchar('A');
		sendchar('A');
		sendchar('A');
		oldmodulation = modulation;
	}
	if (frequenz != oldfrequenz) {
		oldfrequenz = frequenz;
		frequenz /= 10L;
		c1 = frequenz % 10L;
		frequenz /= 10L;
		c1 = ((frequenz % 10L)*16) + c1;
		frequenz /= 10L;
		c2 = frequenz % 10L;
		frequenz /= 10L;
		c2 = ((frequenz % 10L)*16) + c2;
		frequenz /= 10L;
		c3 = frequenz % 10L;
		frequenz /= 10L;
		c3 = ((frequenz % 10L)*16) + c3;
		frequenz /= 10L;
		c4 = frequenz % 10L;
		frequenz /= 10L;
		c4 = ((frequenz % 10L)*16) + c4;
		sendchar(0x0A);
		sendchar(c4);
		sendchar(c3);
		sendchar(c2);
		sendchar(c1);
	}
}

void scanchannels(void)
{
	static int lastchannel = 0;
	static FILE *channelfile = NULL;
	static int anzchannel = 0;
	int i;
	unsigned long l;
	struct chan_car channelinfo;
	unsigned long zeit;
	struct tm *zeit2;

	if (channelfile != NULL) {
		fclose(channelfile);
		channelfile = NULL;
	}
	if (channelfile == NULL) {	/* noch nie geoeffnet */
		disable();
		channelfile = fopen(FILECHANCAR, "rb+");
		if (channelfile == NULL) {
			printf("openerror channelfile\n");
		} else {
			fseek(channelfile, 0L, 2);	/* ende des Files */
			l = ftell(channelfile);		/* anzahl der Bytes */
			anzchannel = l / CHANCARSIZE;
		}
		enable();
	}
	do {
        memset(&channelinfo, 0, CHANCARSIZE);
		fseek(channelfile,(long) ((long)lastchannel*(long)CHANCARSIZE), 0);
		fread(&channelinfo, CHANCARSIZE, 1, channelfile);
		changefrequenz(MODFMN, channelinfo.chan_freq);
		printf("Frequenz %ld\n", channelinfo.chan_freq);
        for (l = 0; l <= 10000L; l++)   ;
		carrier = 0;
		l = 0;
		do {
			if (carrier == 1)   break;
			l++;
		} while (l < 20000L);
		if (carrier == 1) {
			channelinfo.car_ges++;
			time(&zeit);
			zeit2 = localtime(zeit);
			if (zeit2->tm_min >= 0  &&  zeit->tm_min < 15)   i = 0;
			if (zeit2->tm_min >= 15  &&  zeit->tm_min < 30)   i = 1;
			if (zeit2->tm_min >= 30  &&  zeit->tm_min < 45)   i = 2;
			if (zeit2->tm_min >= 45  &&  zeit->tm_min < 60)   i = 3;
			channelinfo.car_viertel_h[(zeit2->tm_hour*4)+i]++;
			fseek(channelfile, (long)((long)lastchannel*(long)CHANCARSIZE), 0);
			fwrite(&channelinfo, CHANCARSIZE, 1, channelfile);
		}
		lastchannel++;
		if (lastchannel >= anzchannel)   lastchannel = 0;
		changefrequenz(MODFMN, 465970000L);
		for (l = 0; l <= 10000L; l++)   ;
		carrier = 0;
		l = 0;
		do {
			if (carrier == 1)   return;
			l++;
		} while (l < 20000L);
	} while (42);
}




#define ADRESS 0                /* Wir haben eine Adresse */
#define DATEN  1				/* Wir haben Daten */

void
decode_frame(int framepos, unsigned long frame)	/* Ein emfangenes Frame entschluesseln */
{
static unsigned long lastadress = 0L;	/* letzte emfaenger adresse */
static int lastadrfunktion = 0;			/* letzte funktion der adresse */
static char lastchar = '\0';
static int lastbitpos = 0;
static int lastget = ADRESS;

	int i;
	unsigned long l;


	if ((frame&0x80000000) != 0L)   i = 1;  	/* nachricht */
	else   i = 0;								/* adresse (nur Ton) */
	if (i == 0) {								/* es ist ein adress frame */

		if (lastget == ADRESS  &&  lastadrfunktion > 0) {	/* der letzte war eine adresse also war er nurton */

			printf("\nA:%07ld | %s | T | BEEP", lastadress, asc_time);
			printf("%d", lastadrfunktion);
#ifdef LOGFILE
			fprintf(logfile, "\nA:%07ld | %s | T | BEEP", lastadress, asc_time);
			fprintf(logfile, "%d", lastadrfunktion);
#endif
		}

		if (frame == SYNCRON) {         /* das ist nur ein eingeschobenes Syncronwort */
			lastget = DATEN;			/* wird nicht als adresse gewertet */
		} else if (frame == IDLE) {	    /* das ist nur ein Fuellwort */
			lastadress = 0L;
			lastadrfunktion = 0;		/* alles zurueckstellen */
			lastbitpos = 0;
			lastget = ADRESS;			/* ende einer Nachricht */
		} else {      					/* adressse + funktion merken */
			lastadress = ((frame>>10)&0x001FFFF8L)+(framepos/2);
			lastadrfunktion = ((frame >> 11)&0x00000003L)+1;
			lastbitpos = 0;
			lastget = ADRESS;			/* dies war eine Adresse */
		}
	} else {							/* es ist ein Daten frame */

		if (lastbitpos == 0) {			/* wir sind am anfang eines Datenwortes */
			if (lastadrfunktion == 1) {	/* numerikpager */
				printf("\nA:%07ld | %s | N | ", lastadress, asc_time);
#ifdef LOGFILE
				fprintf(logfile, "\nA:%07ld | %s | N | ", lastadress, asc_time);
#endif
			} else if (lastadrfunktion == 4) {	/* oh! ein kleiner alpha */
				printf("\nA:%07ld | %s | A | ", lastadress, asc_time);
#ifdef LOGFILE
				fprintf(logfile, "\nA:%07ld | %s | A | ", lastadress, asc_time);
#endif
				lastchar = '\0';
			}
/*			else   printf("nicht definierter Pager\n"); */
		}

		/* Daten analysiern und ausgeben */
		frame <<= 1;					/* das erste bit wurde schon interpretiert und wird jetzt ignoriert */
		i = 0;
		if (lastadrfunktion == 1) {		/* numerik Pager decodieren */
			for (i = 0; i <= 4; i++) {
				l = frame&0xf0000000L;	/* ich brauch nur die hoechsten vier bits */
				l >>= 28;				/* bitte als char */
				printf("%c", numerik[(char)l]);	/* so einer sind wir */
#ifdef LOGFILE
				fprintf(logfile, "%c", numerik[(char)l]);
#endif
				frame <<= 4;
				lastbitpos += 4;
			}
		}
		if (lastadrfunktion == 4) {		/* alpha Pager decodieren */
			for (i = 0; i <= 19; i++) {	/* alle 20 Bits nacheinader durchmachen */
				lastchar >>= 1;
				if ((frame&0x80000000L) != 0L)   lastchar |= 0x40; /* bit in ein Char schieben */
				frame <<= 1;
				lastbitpos++;
				if ((lastbitpos%7) == 0) {		/* ein neues Datenwort ist voll */
					printf("%c", lastchar);
#ifdef LOGFILE
					fprintf(logfile, "%c", lastchar);
#endif
					lastchar = '\0';
				}
			}
		}
		lastget = DATEN;
	}
}


void
mach_hin(int baud)				/* wir gehen auf emfang */
{
	int i, j, k, anz, paritaet, timercl, timertic;
	unsigned long l;
	int getmodus;
	char old_0x21, old_pio_b;
	char c;
	time_t zeit;
	struct tm *zeit2;

#ifdef LOGFILE
	logfile = fopen("logfile.txt", "a");
	if (logfile == NULL)   return;
#endif


	disable();					/* alle interrupts abschalten */

	roh_daten_anfang = roh_daten_ende = roh_daten_laenge = 0;
	getmodus = GETSYNCR;
	anz = k = 0;
	if (baud == 1200) {
		printf("1200 Baud\n");
		tics = TIMER0B1200;
	} else {
		printf("512 Baud\n");
		tics = TIMER0B512;
	}
	tics_2 = tics/2;

	old_vec = getvect(12);		/* alte Timerserviceroutine retten */
	setvect(12, lauf_er);		/* timerserviceroutine setzen */

	time_int = getvect(0x1c);	/* timer0 interrupt setzen */
	setvect(0x1c, time_i);

	outportb(COMPORT+MCR, 0x09);/* Leitungen setzen */
	outportb(COMPORT+IER, 0x08);/* MODEMzustandswechsel */
	inst_ser();

	old_0x21 = inportb(0x21);	/* interrupt maske holen */
	outportb(0x21, old_0x21&(char)(~((char)0x10)));		/* Timer 2 einschalten */

	outportb(0x43, 0xB0);		/* Zaehler auf hoechsten wert stellen */
	outportb(0x42, 0xff);
	outportb(0x42, 0xff);
	old_pio_b = inportb(0x61);			/* pio-b einlesen */
	outportb(0x61, old_pio_b|0x01);		/* Zaehler einschalten */

	enable();					/* jetzt gehts los  */

	changefrequenz(MODFMN, 465970100L);

	do {
		if (abbruch)   break;

		if (eine_sek_weg) {				/* uhr nachstellen */
			eine_sek_weg = 0;

			if (kbhit() != 0) {	/* ein Zeichen von der Tastatur liegt an also abbrechen */
				break;
			}

			time(&zeit);
			zeit2 = localtime(&zeit);
			strcpy(asc_time, strip_cr(asctime(zeit2)));	/* neue uhrzeit */

#ifdef LOGFILE
			if (last_sync >= 0)   last_sync++;
			if (last_sync > 3) {	/* 3 Sekunden lang kein Sync mehr gekommen */
				disable();
				if (logfile != NULL)   fclose(logfile);
				logfile = fopen("logfile.txt", "a");
				if (logfile == NULL) {
					printf("Logfile laesst sich nicht oeffnen\n");
					abbruch = 1;
				}
				enable();
				last_sync = -1;
#ifdef CHANNELSCAN
				/* andere frequenzen scannen */
				scanchannels();
				/* wieder zurueck auf die alte Frequenz und weitermachen */
				roh_daten_laenge = 0;
				roh_daten_ende = roh_daten_anfang;
#endif 	/* CHANNELSCAN */
			}
#endif	/* LOGFILE */
		}

		if (roh_daten_laenge > 0) {		/* timerinterrupt hat ein oder mehr neue Zeichen eingelesen */

			/* felddaten holen */
			i = roh_daten_feld[roh_daten_anfang++];
			if (roh_daten_anfang > MAXARRAY)   roh_daten_anfang = 0;
			roh_daten_laenge--;
/*
			printf("%c", i+'0');
			fflush(stdout);
*/
			/* felddaten auswerten */

			if (getmodus == GETSYNCR) {					/* Erstes Syncronframe erwarten */
				l <<= 1;
				if (i == 1)   l |= 0x00000001L;			/* bit setzen */
				if (l == SYNCRON  ||  l == SYNCINFO) {	/* wert stimmt mit Syncronwort ueberein */
					printf("\nSyncronwort");
					fflush(stdout);
					last_sync = 0;
					getmodus = GETFRAME;
					anz = k = paritaet = 0;
					l = 0L;
					decode_frame(0, IDLE);
				}
			} else if (getmodus == GETFRAME) {							/* GETFRAME Daten als Frame einlesen */
				anz++;
				l <<= 1;
				if (i == 1) {
					l |= 0x00000001L; 			/* bit setzen */
					if (paritaet == 0)   paritaet = 1;
					else   paritaet = 0;
				}
				if (anz >= 32) {				/* neues Frame ist vollstaendig */
					if (paritaet == 1) {
						printf("%c", 247);
						fflush(stdout);
#ifdef BITWRONG
						l = 1L;					/* fallback machen */
						getmodus = GETSYNCR;
						decode_frame(0, IDLE);
						anz = k = paritaet = 0;
#endif
					}
					if (k >= 16  ||  l == SYNCRON  ||  l == SYNCINFO) {	/* egal was dies MUSS ein Syncronwort seien */
						decode_frame(0, SYNCRON);
						last_sync = 0;
						k = 0;
					} else if (l == IDLE) {     /* nur idle keine relevanten daten */
						decode_frame(k, l);
/*						printf("IDLE\n"); */
						k++;
					} else if ((l&0x0000FFFFL) == 0x00000000L  ||  (l&0x0000FFFFL) == 0x0000FFFFL) {	/* ungueltige Daten (FALLBACK) */
						printf("Warten Sync\n");
						getmodus = GETSYNCR;
						anz = 0;
						l = 0L;
					} else {					/* endlich ein normales Frame */
						decode_frame(k, l);
						k++;
					}
					anz = paritaet = 0;
					l = 0L;
				}
			}
		}
	} while(42);				/* endlos */

	/* destall interrupts */
	disable();
	setvect(12, old_vec);		/* timerserviceroutine zuruecksetzen */
	setvect(0x1c, time_int);	/* alter Timer2 interrupt */
	outportb(0x21, old_0x21);	/* alte Timer einschalten */
	outportb(0x61, old_pio_b);	/* Zaehler Ursprung */
	enable();
#ifdef LOGFILE
	if (logfile != NULL)   fclose(logfile);
#endif
	return;
}


void
interrupt lauf_er()				/* interrupt wenn wechsel an CTS, verstrichene Zeit und Zustand feststellen, anzahl von 0/1 in puffer schreiben */
{
	char bytein, c;
	unsigned int i;
	unsigned int j;


	bytein = inportb(COMPORT+MSR);
	if ((bytein&0x01) == 0x01) {	/* CTS zustands wechselaufgetreten */
		c = ((bytein&0x10) != 0) ? (char)0 : (char)1; /* hab ich ne 0 oder ne 1 */

		outportb(0x43, 0x80);		/* Zaehlerstand auslesen */
		i = inportb(0x42);
		i += (256 * inportb(0x42));
		outportb(0x43, 0xB0);		/* Zaehler auf hoechsten wert neustellen */
		outportb(0x42, 0xff);
		outportb(0x42, 0xff);

		i = 0xffff-i;				/* wieviele Zaehlerschritte sind vergangen */
		j = i/tics;                 /* wieviele Bits waehren das ? */
		if (i%tics > tics_2)  j++;  /* ist vieleicht die Zeit unterlaufen worden? */
/*	if (j <= 0)  j = 1; */

		for (i = 1; i <= j; i++) {	/* anzahl der bits die decodiert wurden speichern */
			if (roh_daten_laenge < MAXARRAY) {
				roh_daten_feld[roh_daten_ende++] = c;
				if (roh_daten_ende > MAXARRAY)   roh_daten_ende = 0;
				roh_daten_laenge++;
			} else   printf("\nPufferueberlauf\n");
		}
	}
	if ((bytein&0x08) == 0x08) {	/* CD zustands wechselaufgetreten */
		c = ((bytein&0x80) != 0) ? (char)0 : (char)1; /* hab ich ne 0 oder ne 1 */
		if (c == 1)   carrier = 1;
	}

	outportb(0x20,0x20);            /* end of interrupt klarmachen */
}


void interrupt time_i()				/* wird 18.2/Sekunde aufgerufen */
{
static int t = 0;


	t++;
	if (t >= 19) {	/* genuegend zeit vergangen ? */
		t = 0;
		eine_sek_weg = 1;
	}
/*	time_int();	*/	/* alte zeitroutine weiter machen */
}




int
main()
{
/*
FILE *channelfile;
	struct chan_car channelinfo;
	unsigned long l;

	channelfile = fopen(FILECHANCAR, "wb");

	for (l = 167560000L; l <= 169380000; l+=20000L) {
	memset(&channelinfo, 0, CHANCARSIZE);
	channelinfo.chan_freq = l;
	fwrite(&channelinfo, CHANCARSIZE, 1, channelfile);
	}
	for (l = 172160000L; l <= 173980000; l+=20000L) {
	memset(&channelinfo, 0, CHANCARSIZE);
	channelinfo.chan_freq = l;
	fwrite(&channelinfo, CHANCARSIZE, 1, channelfile);
	}
	for (l = 165210000L; l <= 165690000; l+=20000L) {
	memset(&channelinfo, 0, CHANCARSIZE);
	channelinfo.chan_freq = l;
	fwrite(&channelinfo, CHANCARSIZE, 1, channelfile);
	}
	for (l = 169810000L; l <= 170290000; l+=20000L) {
	memset(&channelinfo, 0, CHANCARSIZE);
	channelinfo.chan_freq = l;
	fwrite(&channelinfo, CHANCARSIZE, 1, channelfile);
	}

	exit(0);
*/
	roh_daten_feld = (char *)malloc(MAXARRAY+300);
	if (roh_daten_feld == NULL)   exit(0);
	mach_hin(512);
	free(roh_daten_feld);
	exit(0);
}

