Source file: /~heha/basteln/Haus/Telefon/CLIP-Anzeige/clipgen.zip/clipgen.cpp

/* clipgen, Win32-Konsolenprogramm, haftmann#software, 15. März 2013
 * Generiert CLIP-Signal (Caller Line Identification Presentation) = Rufnummernanzeige
 * zum Testen eines CLIP-Dekoders an einer Soundkarte statt einer Telefonleitung
 * Erstellt mit MSVC6 ohne Laufzeitbibliothek, siehe mein_msvc.htm. Lauffähig ab Windows 95.
 * Siehe ETSI-Standards für Definitionen, etwa „On-Hook data transmission“
 */
#define _DLL	// declspec(dllimport) erzeugen lassen (wichtig für stderr-Zugriff)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmsystem.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define elemof(x) (sizeof(x)/sizeof(*(x)))
#define PI 3.1415926535897932384626433832795


double baud=1200;	// Deutschland (gemessen)	Bell-Standard
double f[]={2750,2130};	// 1950	(SPACE)			2200
			// 1400	(MARK)			1200
double amp=10;		// Die Vorgabe sind vom ETSI-Standard (hm, weltfremd)
double ampring=100;
double fring=25;
double tring=0.3;	// Klingeln, Pause, klingeln
double tpause=0.5;	// Minimum, Maximum 2
int t1=300;	// Bitzeiten für 010101-Einlauf
int t2=180;	// Bitzeiten für 111111-Einlauf (MARK)
double extrastop;

WAVEFORMATEX wf={
 WAVE_FORMAT_PCM,
 1,
 44100,
 0,
 0,
 16,
 1};			// hier: Kanalzuordnungs-Bitmaske, Bit 0 = links, Bit 1 = rechts

HWAVEOUT hwo;
HANDLE hWakeGen;
HMMIO outfile;

#define NBUF 10		// Anzahl Waveout-Puffer
#define LBUF 2048	// Länge jedes Puffers, in 16-Bit-Einheiten (also 4 KByte)
WAVEHDR wh[NBUF];
union{
 BYTE *b;
 char *c;
 WORD *w;
 short *s;
 DWORD *d;
}samplebuf;
int wp;			// Schreibindex

__forceinline __int64 rndint64(double f) {
 __int64 i;
 _asm	fld	f
 _asm	fistp	i
 return i;
}
#define rndint(x) (int)rndint64(x)
EXTERN_C char _fltused;
char _fltused;

static void putsample(double v, bool otherchannel=false) {
 long max=(1<<(wf.wBitsPerSample-1))-1;	// typisch 32767 oder 127
 long sa=rndint(v/100*(max-1));		// typisch 32766 oder 126 = FullScale
 if (sa>max) sa=max;
 if (sa<-max) sa=-max;
 if (max<=127) sa+=128;			// 80h-Zentrierung bei 8-Bit-Daten
 if (!outfile) {
  if (!(wp%LBUF)) {	// Ist der Wave-Block verfügbar?
   WAVEHDR *pwh=wh+wp/LBUF;
   while (pwh->dwFlags&WHDR_INQUEUE) WaitForSingleObject(hWakeGen,INFINITE);
  }
 }
 switch (wf.nChannels) {
  case 1: switch (wf.nBlockAlign) {
   case 1: samplebuf.b[wp]=(BYTE)sa; break;
   case 2: samplebuf.s[wp]=(short)sa; break;
  }break;
  case 2: if (otherchannel) switch (wf.nBlockAlign) {
   case 2: samplebuf.w[wp]=MAKEWORD(wf.cbSize!=1?(BYTE)sa:0x80,wf.cbSize!=2?(BYTE)sa:0x80); break;
   case 4: samplebuf.d[wp]=MAKELONG(wf.cbSize!=1?(short)sa:0,wf.cbSize!=2?(short)sa:0); break;
  }else switch (wf.nBlockAlign) {
   case 2: samplebuf.w[wp]=MAKEWORD(wf.cbSize&1?(BYTE)sa:0x80,wf.cbSize&2?(BYTE)sa:0x80); break;
   case 4: samplebuf.d[wp]=MAKELONG(wf.cbSize&1?(short)sa:0,wf.cbSize&2?(short)sa:0); break;
  }break;
 }
 if (outfile) {
  mmioWrite(outfile,samplebuf.c,wf.nBlockAlign);
 }else{
  wp++;
  if (!(wp%LBUF)) {
   waveOutWrite(hwo,wh+(wp/LBUF-1),sizeof WAVEHDR);
  }
  if (wp==NBUF*LBUF) wp=0;
 }
}

static void sampleflush() {
 if (wp%LBUF) while (wp%LBUF) putsample(0);
};

static void ring(double f, double t) {
 double st=1.0/wf.nSamplesPerSec;
 double om=f*2*PI;
 double time;
 double a,b;
 for (time=0; time<t; time+=st) {
  a=sin(om*time)*ampring;
  putsample(a,true);
 }
// Anstehende Amplitude abbauen bis zum Nulldurchgang
 for (;;time+=st) {
  b=sin(om*time)*ampring;
  if (b*a<0) break;	// bei Nulldurchgang raus
  putsample(b,true);
 }
}

static void silence(double t) {
 double st=1.0/wf.nSamplesPerSec;
 for(double time=0; time<t; time+=st) putsample(0);
}

#undef putchar

struct DDS{
 double phase;		// 0..2*PI
 double wsample;	// >0..Sample-Zeit, Ende der letzten Frequenz-Generierung in Subsamples
 void gen(double f,double t);
 void putbit(bool b);
 void putchar(char c);
 void packet(const char*s, int n);
}dds;

// generiert Sinus-Frequenz fester Amplitude für Zeit t
// und kümmert sich um das nahtlose Anstückeln
void DDS::gen(double f, double t) {
 double st=1.0/wf.nSamplesPerSec;	// Sample-Zeit in s
 double om=f*2*PI;			// Omega, Kreisfrequenz, in Hz
 double time=wsample;			// Zeit für erstes Sample
 for (; time<t; time+=st) {
  putsample(sin(phase+om*time)*amp);
 }
 wsample=time-t;
 phase=fmod(phase+om*t,2*PI);
}

// 1 Bit (1 Bitzeit) ausgeben
void DDS::putbit(bool b) {
 gen(f[b],1/baud);
}

// 1 Byte (10 Bitzeiten; fest: 1 Stopbit) ausgeben
void DDS::putchar(char c) {
 putbit(0);
 for (int i=0; i<8; i++) {
  putbit(c&1);
  c>>=1;
 }
 putbit(1);
 if (extrastop) gen(f[1],rand()*extrastop/RAND_MAX/baud);
}

// Ganze Data-Link-Message ausgeben mit Präambelgenerierung (lückenlos)
void DDS::packet(const char*s, int n) {
 int i;
 for (i=0; i<t1; i+=2) {putbit(0); putbit(1);}		// 010101-Einlauf
 for (i=0; i<t2; i++) putbit(1);			// 111111-Einlauf
 for (i=0; i<n; i++) putchar(s[i]);	// Datenbytes + Prüfsumme
}

// Benutzerdefinierte (freie) Pakete
char*paket[5];	// unbearbeitete Kommandozeilen-Argumente: Hexbyte:String oder HexbyteHexbyteHexbyte
int npaket;	// Anzahl derer

// Interpretiert Argument und füllt s mit Sub-Paket
// Liefert Länge des Sub-Paketes (mindestens 2)
// 0 bei Syntaxfehler
// 1 bei Längenüberschreitung (> 160 Bytes / Zeichen)
int buildpaket(const char *arg, char*s) {
 char *s0=s;
 char *p=strchr(arg,':');
 char *q;
 if (p) {	// String-Modus
  *s=(BYTE)strtoul(arg,&q,16);
  if (q!=p) return 0;		// Fehler: Keine Hexzahl
  s+=2;
  arg=p+1;
  int l=strlen(arg);
  if (l>160) return 1;		// Fehler: Zu lang!
  strcpy(s,arg);
  s+=l;
 }else{		// Hex-Modus
  char buf[3];
  buf[2]=0;
  memcpy(buf,arg,2);
  *s=(BYTE)strtoul(buf,&q,16);
  if (q!=buf+2) return 0;	// Fehler: Keine zweistellige Hexzahl
  s+=2;
  for(;;) {
   arg+=2;
   if (!*arg) break;
   memcpy(buf,arg,2);
   *s++=(BYTE)strtoul(buf,&q,16);
   if (q!=buf+2) return 0;
   if (s-s0>162) return 1;	// Fehler: Zu lang!
  }
 }
 s0[1]=s-s0-2;
 return s-s0;
}
// Weitere ETSI-dokumentierte Werte für das 1. Byte:
// 03	<=20	Called Line Identity
// 07	<=50	Calling Party Name
// 08	1	Reason for absence of Calling Party Name
// 0B	1	Visual Indicator
// 0D	3	Message Identification
// 0E	<=20	Originating Identity
// 0F	8,10	Complementary Date and Time
// 10	<=20	Complementary Calling Line Identity
// 11	1	Call type
// 12	<=20	First Called Line Identity
// 13	1	Network Message System Status
// 15	1	Type of Forwarded call
// 16	1	Type of Calling user
// 1A	<=20	Redirecting Number
// 20	14	Charge
// 21	14	Additional Charge
// 22	14	Extra Charge
// 23	6	Duration of the Call
// 30	<=20	Network Provider Identity
// 31	<=20	Carrier Identity
// 40	<=21	Selection Of Terminal Function
// 50	<=253	Display Information (SMS)
// 55	1	Service Information
// E0	10	Extension for network operator use
// >=E1	?	Reserved for network operator use

// Komplette Data-Link-Message zusammenstellen, inklusive Prüfsumme
// Liefert Anzahl der Bytes der Message, minimal 3
// Der Puffer s muss genügend Platz haben, kein Überlauftest!
int BuildPacket(char*s, const char*Nummer, char privat, bool WithTime) {
 int nlen=strlen(Nummer);
 if (nlen>20) nlen=20;
 char*s0=s;	// Anfang merken
 *s=(char)0x80;	// Kennung "Anrufvorbereitung" nach ETSI (bspw. 0x89 == SMS)
 s+=2;
 if (WithTime) {
  *s++=1;	// Kennung "Uhrzeit" nach ETSI
  *s++=8;
  GetDateFormat(LOCALE_USER_DEFAULT,0,NULL,"MMdd",s,10); s+=4;
  GetTimeFormat(LOCALE_USER_DEFAULT,0,NULL,"HHmm",s,10); s+=4;
 }
 if (privat) {	// Es muss entweder "Nummer" oder "Keine Nummer" gesendet werden.
  *s++=4;
  *s++=1;
  *s++=privat;
 }else{
  *s++=2;	// Kennung "CLI" nach ETSI
  *s++=nlen;
  memcpy(s,Nummer,nlen); s+=nlen;
 }
 for (int i=0; i<npaket; i++) s+=buildpaket(paket[i],s);
 s0[1]=s-s0-2;
 char csum=0;
 for (const char*s1=s0; s1<s; s1++) csum-=*s1;
 *s++=csum;
 return s-s0;
}

// Einfaches Hex/ASCII-Dump zum Kontrollieren der Data-Link-Message
void hdump(int adr, const char*data, int len, bool withascii=true) {
 while (len) {
  int i,l=len;
  if (l>16) l=16;
  printf("%03X",adr);
  for (i=0; i<l; i++) printf(" %02X",(BYTE)data[i]);
  if (withascii) {
   for (   ;i<17; i++) printf("   ");
   for (i=0; i<l; i++) printf("%c",isprint(data[i]) ? data[i] : '.');
  }
  printf("\n");
  data+=l;
  adr+=l;
  len-=l;
 }
}

static char* parm(int &i, char**argv) {
 char *p=argv[i]+2;
 switch (*p) {
  case 0: return argv[++i];
  case '=':
  case ':': p++;
 }
 return p;
}

static void usage() {
 printf(
       "Generiert Waveform zum Testen eines CLIP-Dekoders\n"
       "haftmann#software, 03/13, Freeware\n"
       "Parameter: Telefonnummer (max. 20 Zeichen) [wahlfreie Pakete]\n"
       "-h, -?	Diesen Text ohne Gepiepse\n"
       "-c, -n	Keine Uhrzeit\201bermittlung\n"
       "-o; -p	Keine Nummer (Kode O); Nummernunterdr\201ckung (Kode P)\n"
       "-r rate	Festlegung der Abtastrate, sonst %d\n"
       "-b bits	Bit pro Sample, 8 oder 16, sonst %d\n"
       "-k kan	Kanalzuordnung (l)inks, (r)echts, (m)ono, (s)tereo, sonst mono\n"
       "-s	Keine Bildschirmausgaben (kein Hexdump)\n"
       "-d num	Nummer der Soundkarte, sonst WAVE_MAPPER\n"
       "-w name	Ausgabe in Wave-Datei\n"
       "-A amp	Amplitude, in Prozent vom Maximum, sonst %g\n"
       "-B baud	Baudrate, in Hz, sonst %g\n"
       "-E max	zuf\204llige Extra-Stoppbits (zum Test der Startbiterkennung)\n"
       "-M fh	Mark-Frequenz (1-Bits), in Hz, sonst %g\n"
       "-S fl	Space-Frequenz (0-Bits), in Hz, sonst %g\n"
       "-U t1	L\204nge der 010101-Pr\204ambel (\257U\256-Zeichen), in Bitzeiten, sonst %d\n"
       "-P t2	L\204nge der 111111-Pr\204ambel (Mark = Stopbits), in Bitzeiten, sonst %d\n"
       "-R	Vorher 2x kurz Rufwechselspannung generieren\n"
       "	V=Amplitude (%g), F=Frequenz (%g), T=Dauer (%g), W=Pause (%g)\n"
       "Paket-Syntax:	entweder  XX:string	XX = Pakettyp, hexadezimal\n"
       "		oder	  XXYYYYYYYY	YY = Daten, hexadezimal\n",
   wf.nSamplesPerSec,wf.wBitsPerSample,amp,baud,f[1],f[0],t1,t2,ampring,fring,tring,tpause);
}

static char*nummer;
static bool doring;
static bool verbose=true;
static bool withtime=true;
static char privat;

static void WriteWave() {
 if (doring) {
  if (verbose) printf("Es sollte jetzt klingeln");
  ring(fring,tring);
  silence(tring);
  ring(fring,tring);
  if (verbose) printf(".\n");
  silence(tpause);
 }
 char buf[32],buf2[32];
 buf[0]=0;
 if (npaket) sprintf(buf,"und %d Paket%s ",npaket,npaket>1?"e":"");
 if (privat) sprintf(buf2,"keine Nummer (Grund-Kode %c) ",privat);
 else sprintf(buf2,"Nummer \257%s\256 ",nummer);
 if (verbose) printf("Gibt %s%s%saus.\n",withtime?"Uhrzeit und ":"",buf2,buf);
 char s[260];
 int l=BuildPacket(s,nummer,privat,withtime);
 if (verbose) hdump(0,s,l);
 dds.packet(s,l);
}

int _cdecl main(int argc, char**argv) {
 char*wavename=NULL;
 int nsoundcard=WAVE_MAPPER;
 int i;
 for (i=1; i<argc; i++) {
  switch (argv[i][0]) {
   case '/':
   case '-': switch (argv[i][1]) {
    case '?':
    case 'h': usage(); return 0;
    case 'n':
    case 'c': withtime=false; break;
    case 'o': privat='O'; break;
    case 'p': privat='P'; break;
    case 'r': wf.nSamplesPerSec=atoi(parm(i,argv)); break;
    case 'b': wf.wBitsPerSample=atoi(parm(i,argv)); break;
    case 'k': switch (*parm(i,argv)) {
     case 'L':
     case 'l': wf.cbSize=1; wf.nChannels=2; break;
     case 'R':
     case 'r': wf.cbSize=2; wf.nChannels=2; break;
     case 'S':
     case 's': wf.cbSize=3; wf.nChannels=2; break;
    }break;
    case 's': verbose=false; break;
    case 'd': nsoundcard=atoi(parm(i,argv)); break;
    case 'w': wavename=parm(i,argv); break;
    case 'A': amp=atof(parm(i,argv)); break;
    case 'B': baud=atof(parm(i,argv)); break;
    case 'E': extrastop=atof(parm(i,argv)); srand(GetTickCount()); break;
    case 'S': f[0]=atof(parm(i,argv)); break;
    case 'M': f[1]=atof(parm(i,argv)); break;
    case 'U': t1=atoi(parm(i,argv)); break;
    case 'P': t2=atoi(parm(i,argv)); break;
    case 'R': doring=true; break;
    case 'F': fring=atof(parm(i,argv)); doring=true; break;
    case 'T': tring=atof(parm(i,argv)); doring=true; break;
    case 'V': ampring=atof(parm(i,argv)); doring=true; break;
    case 'W': tpause=atof(parm(i,argv)); doring=true; break;
    default: fprintf(stderr,"Unbekannte Option %s wird ignoriert.\n",argv[i]);
   }break;
   default: if (!nummer && !privat) nummer=argv[i]; else{
    if (npaket<elemof(paket)) {
     char test[162];
     switch (buildpaket(argv[i],test)) {
      case 0: fprintf(stderr,"Paket-Syntaxfehler in \257%s\256!\n",argv[i]); return 2;
      case 1: fprintf(stderr,"Paket %d zu lang!\n",npaket+1); return 2;
     }
     paket[npaket++]=argv[i];
    }else{
     fprintf(stderr,"Zu viele Pakete!\n");
     return 2;
    }
   }
  }
 }
 if (argc<2) usage();
 if (!nummer) nummer="1234567890";

 i=(wf.wBitsPerSample+7)>>3;	// Bytes pro Sample
 wf.nBlockAlign=wf.nChannels*i;
 wf.nAvgBytesPerSec=wf.nSamplesPerSec*wf.nBlockAlign;

 if (wavename) {
  outfile=mmioOpen(wavename,NULL,MMIO_ALLOCBUF|MMIO_CREATE|MMIO_DENYWRITE|MMIO_WRITE);
  if (!outfile) {
   fprintf(stderr,"Kann Wave-Datei %s nicht erzeugen oder \201berschreiben!\n",wavename);
   return 1;
  }
  samplebuf.c=new char[wf.nBlockAlign];
  MMCKINFO riff,wave;
  riff.fccType=mmioFOURCC('W','A','V','E');
  mmioCreateChunk(outfile,&riff,MMIO_CREATERIFF);
  wave.ckid=mmioFOURCC('f','m','t',' ');
  mmioCreateChunk(outfile,&wave,0);
  mmioWrite(outfile,(char*)&wf,sizeof(wf));
  mmioAscend(outfile,&wave,0);
  wave.ckid=mmioFOURCC('d','a','t','a');
  mmioCreateChunk(outfile,&wave,0);

  WriteWave();

  mmioAscend(outfile,&wave,0);
  mmioAscend(outfile,&riff,0);
  if (mmioClose(outfile,0)) {
   fprintf(stderr,"Fehler beim Schreiben der Wave-Datei %s!\n",wavename);
   return 1;
  }
 }else{
  hWakeGen=CreateEvent(NULL,FALSE,FALSE,NULL);
  if (waveOutOpen(&hwo,nsoundcard,&wf,(LPARAM)hWakeGen,0,CALLBACK_EVENT)) {
   fprintf(stderr,"Kann Soundkarte Nr. %d nicht \224ffnen!\n",nsoundcard);
   return 1;
  }
  samplebuf.c=new char[NBUF*LBUF*wf.nBlockAlign];
  for (i=0; i<NBUF; i++) {
   wh[i].lpData=samplebuf.c+i*LBUF*wf.nBlockAlign;
   wh[i].dwBufferLength=LBUF*wf.nBlockAlign;
   waveOutPrepareHeader(hwo,wh+i,sizeof WAVEHDR);
  }

  WriteWave();
  sampleflush();

  for (i=0; i<NBUF; i++) {
   while (wh[i].dwFlags&WHDR_INQUEUE) WaitForSingleObject(hWakeGen,INFINITE);
   waveOutUnprepareHeader(hwo,wh+i,sizeof WAVEHDR);
  }
  waveOutClose(hwo);
//  CloseHandle(hWakeGen);
 }
 delete[] samplebuf.c;
 return 0;
}

EXTERN_C void CALLBACK mainCRTStartup() {
 int argc;
 char **argv,**envp;
 _CRTIMP void _cdecl __getmainargs(int*,char***,char***,void*,void*);
 __getmainargs(&argc,&argv,&envp,NULL,&envp);
 ExitProcess(main(argc, argv));
} 
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded