/* 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
|
|