Source file: /~heha/basteln/PC/oszi/oszi.zip/src/quelle.h

// Alle Arten von Datenquellen
#pragma once

#ifdef _M_IX86
__forceinline __int64 rndint64(double f) {
 __int64 i;
 _asm	fld	f
 _asm	fistp	i
 return i;
}
#endif
#define rndint(x) (int)rndint64(x)

// zur Ersparnis von Schreibarbeit - und evtl. Umsetzung zur Registry
#define WriteString(s,k,v) WritePrivateProfileString(s,k,v,StdProfile)
#define GetString(s,k,d,b,l) GetPrivateProfileString(s,k,d,b,l,StdProfile)
#define GetInt(s,k,d) GetPrivateProfileInt(s,k,d,StdProfile)

// flags
#define Q_NONINTERLACED 0x01	// Sampledaten nicht verzahnt
#define Q_CONTINUOUS	0x02	// Sampledaten lückenlos (geringe Abtastfr.)
#define Q_SOFTTRIGGER	0x04	// Oszi-Software muss Trigger suchen
#define Q_ASYNC		0x08	// WaitMessage() unzulässig
//#define Q_FULL		0x10	// Puffer gefüllt
#define Q_ARMED		0x80	// "scharfgestellt": wird bei Triggerereignis
				// zurückgesetzt, wenn nicht lückenlos
typedef void(NEAR*GETPROC) (NPVOID,LPWAVEHDR NEAR*);
typedef UINT(NEAR*TRIGPROC)(LPSTR,UINT);
typedef void(NEAR*PRAEPROC)(LPSTR,UINT);
typedef int(_fastcall *GETSAMPLE)(LPSTR,BYTE);
typedef void(_fastcall *MAKESUB)(void);

#ifdef __cplusplus
// Zur softwaremäßigen Suche einer Pegeltriggerung
// für steigende und fallende Flanken
// als globale Variable im Oszi-Programm(!)
struct SOFT_TRIGGER {
 GETSAMPLE getsample;	// Funktionszeiger, liefert <int>-aufgeblasenen Wert
 TRIGPROC trigproc;	// Trigger-Funktion
 PRAEPROC praeproc;	// Vor-Trigger-Funktion (DC-Pegel anpassen usw.)
 MAKESUB makesub;	// Die Funktion, die "sub" nachführt
 BYTE mask;		// der Parameter für getsample
 BYTE blockadd;		// zur Korrektur des Zeigers für GetSample
 int xor;		// 0 für steigende, -1 für fallende Flanke
 long sub;		// Festkomma-Subtrahend, Null für DC, mitlaufend für AC
 int cmp1;		// Vergleichswerte für Hysterese beim Trigger
 int cmp2;		// Differenz muss mindestens 1 betragen!
 int y;			// Alter Wert für nächsten Vergleich
 UINT timestart;	// Sample-Zähler-Start für Tiefpass (LF)
 UINT timedown;		// Sample-Zählerstand für Tiefpass (LF)
 UINT blockalign;	// zur Inkrementierung des Zeigers
 long pre;		// Prätrigger IN BYTES, nicht in Samples!
 int min;		// FF80h oder 8000h
 int max;		// 7Fh (8 bit) oder 7FFFh (16 bit)
 inline void Reset() {y=((((unsigned)-1)>>1)-1)^xor;};
};

extern SOFT_TRIGGER st;
#define GET_SUB(x) ((int)((x)>>ACTIME))

bool FindTrigger();
// Der WAVEHDR ist eigentlich auch global!!
// GetProc geht an den Treiber, mit LPWAVEHDR FAR* als LPARAM

/* Liest nacheinander Wave-Datenblöcke via <getproc> ein und sucht
   die Triggerbedingung darin:  Schlüsseltechnologie!
				^^^^^^^^^^^^^^^^^^^^
   Nebenbei wird - bei simulierter AC-Kopplung - der Triggerpegel
   dem Signal nachgeführt (aber wie???),
   und schnell soll die Routine ja ganz nebenbei auch noch sein...

PARAMETER:
Der eingegebene LPWAVEHDR hat folgende Interpretation:
.lpData		= Puffer (vom Anwender zur Verfügung gestellt, darf huge sein)
.dwBufferLength	= Größe des Puffers (auch huge!)
.dwBytesRecorded= Füllstand des Puffers mit NEUEN Daten
		  » Wird von FindTrigger inkrementiert «
.dwUser		= ab da beginnen die NOCH ALTEN Daten (Start mit dwBufferLength)
		  » Wird von FindTrigger inkrementiert «
.dwFlags	= Flags (aber auf keinen Fall eins für Rollbetrieb!)
	WHDR_DONE	= Keine weiteren ZUSAMMENHÄNGENDEN Daten mehr!
			  (bspw. 32-Kilobyte-Puffer leergesaugt)
			  » wird von FindTrigger gesetzt «
	WHDR_BEGINLOOP	= Datenbereich zwischen Anfang und .dwLoops
			  ist mit einer Runde Prä-Trigger-Daten gefüllt.
			  » wird von FindTrigger gesetzt «
	WHDR_ENDLOOP	= Keine Triggersuche. » Wird von FindTrigger gesetzt,
			  sobald ein Trigger gefunden wurde «
	WHDR_PREPARED	= Interruptgesteuerter "Paketdienst",
			  » wird von FindTrigger gesetzt «
.dwLoops	= Wenn WHDR_BEGINLOOP gesetzt, ist hier die Grenze zwischen
		  neuen und alten Prä-Trigger-Daten festgehalten.
		  Ist dann stets kleiner/gleich .reserved.
		  » wird von FindTrigger gesetzt «
		  Sollte zwecks einfacher Auswertung auf 0 gesetzt werden.
.lpNext		= interner Zeiger auf "gerade auszusaugenden" Wave-Block
.reserved	= Triggerposition (vorzeichenbehaftet!) » wird von FindTrigger
		  inkrementiert, sofern negativ, und WHDR_ENDLOOP «
Alle Angaben in Bytes, nicht Samples.

Beim erstmaligen Aufruf muss .lpNext Null sein! Ansonsten muss der
Aufrufer .lpNext in Ruhe lassen

FindTrigger führt niemals von selbst ein Wrap-Around aus!
Der Aufrufer muss bei .dwBytesRecorded==.dwBufferLength==.dwUser
.dwBytesRecorded und, wenn gewünscht, .dwUser auf Null setzen,
bspw. beim Rollbetrieb.
Ausnahme: .reserved==.dwBufferLength (aber das sieht nur so aus, s.u.)

Der Rollbetrieb wird vom Aufrufer realisiert, indem ausgehend von
.dwUser via .dwBufferLength bis hin zu .dwBytesRecorded ein Trace
rechtsbündig ausgegeben wird. WHDR_ENDLOOP ist zu setzen.
Ebenso muss sich der Aufrufer um das effektive Anzeigen monströser
Datenmengen kümmern! .lpData darf auch auf eine speicher-eingeblendete
Datei zeigen. Auch an ein "mitwachsendes" .lpData wurde gedacht;
deshalb der Verbot von automatischen Wrap-Arounds.
FindTrigger schaufelt Daten nur von den Quell-Waveheadern zum
Ziel-Waveheader, das Ziel ist write-only.

Eine Reduktion der Datenmenge beim Kopieren ist vorerst nicht vorgesehen.
[Wäre ein Fall für einen 3. Funktionszeiger mit Refrenzdaten:
typedef UINT(NEAR*SampleCopy)(NPVOID,char huge*,LPSTR,UINT); ]

Der Bereich von Anfang bis .reserved wird - auch ohne Trigger -
zum "Auffangen" möglicher Prä-Trigger-Daten gebraucht,
hierzu _simuliert_ die Routine hier das Schieben der Daten;
d.h. sobald WHDR_BEGINLOOP gesetzt ist, gilt die Regel
"neue Daten bis .dwBytesRecorded" nicht mehr; dann sind die neuen
Daten 1. zwischen .dwLoops und .reserved und 2. zwischen
Anfang und .dwLoops
Der visuelle Effekt bei langsamer Abtastung ist, dass
* rechts vom Triggerkreuz die alten Sampledaten zunächst stehen bleiben,
* links vom Trigger die Samples von links nach rechts gezeichnet werden,
* beim Erreichen des Triggers NUR LINKS VOM TRIGGER ein Rollbetrieb
  einsetzt
* bei Triggerbedingung das Zeichnen nach rechts unter Überschreibung
  der alten Daten fortgesetzt wird
* Ein angemessenes Hold-Off muss dafür sorgen, dass das Bild etwas
  stehen bleibt...
Bei schneller Abtastung sollte es ein Bildschirm-Update nur geben,
wenn der Puffer wirklich voll ist
(WHDR_ENDLOOP und .dwBytesRecorded==.dwBufferLength)
Nach dem Triggern sind die 3. Daten zwischen .reserved und .dwBytesRecorded

Mit dem Setzen von WHDR_ENDLOOP kann der _Aufrufer_ eine erfolglose
Triggersuche beenden (= "automatischer Trigger").

Ein negatives .reserved sorgt für Triggerverzögerung. Wenn der Trigger
gefunden wurde (WHDR_ENDLOOP), inkrementiert FindTrigger .reserved
bis zur Null; ab dann werden Samples in den Puffer kopiert.
Bei Verwendung von Auto-Trigger sollte der Aufrufer .reserved ebenso
von negativ auf Null stellen, um unnötige Verzögerung zu vermeiden.
Mehr als 100 % Prätrigger ist dagegen systembedingt nicht möglich,
simuliert höchstens über eine 2. Zeitbasis.

Beachte: GETPROC bekommt eine "Referenz" auf einen FAR-Zeiger auf eine
WAVEHDR-Struktur! Die "Referenz" ist niemals NULL, der FAR-Zeiger schon.
Ist dieser Zeiger ungleich NULL, kann der hiermit geleerte Wave-Block
umgehend wieder verwendet werden, bspw. mittels waveInAddBuffer().
Die Funktion sollte einen neuen (teilweise oder voll gefüllten)
Wave-Block-Zeiger einsetzen; ist keiner da, kann NULL eingesetzt werden,
um anzuzeigen, dass NOCH nichts da ist.
Ein gültiger WAVEHDR, der _nicht gefüllt_ (.dwBytesRecorded=0)
ist, zeigt an, dass die Sampledaten zu Ende sind; FindTrigger setzt
daraufhin WHDR_DONE.
Das Bit WHDR_PREPARED wird zum Aufrufer durchgereicht.
Win16: Die Puffer des WAVEHDR der GETPROC-Routine dürfen nicht huge sein.

TRIGPROC muss in den (FAR-, nicht HUGE-) Speicherblöcken nach einer
Triggerbedingung suchen, als Hilfsdaten bekommt es das NPVOID
übergeben (für die Vorgeschichte).

Win16: Beide Routinen müssen dem SMALL-Speichermodell entsprechen!
Beim Umzug in die .EXE (von DLL aufrufbar) wird's dann irgendwann MEDUIM?

Das Oszi-Programm ruft FindTrigger via Q_POLL auf.

RETURN:
Die Funktion liefert <true>, wenn neue Daten hinzugekommen sind.
Der Puffer muss aber deswegen nicht voll sein!
Bei Rückgabe von <false> darf sich der Aufrufer per WaitMessage
schlafen legen, wenn der Datenlieferant mit Interrupt arbeitet,
angezeigt mit WHDR_PREPARED. Ansonsten gibt es außer Timer keinen
Weckdienst; dann muss wieder nachgefragt werden.
<false> wird (auch) geliefert, wenn:
* der LPWAVEHDR==NULL ist
* WHDR_DONE gesetzt ist,
* .dwBytesRecorded==.dwBufferLength ist, AUSSER wenn
  .reserved==.dwBufferLength UND .dwFlags&WHDR_ENDLOOP==0 ist

Aufwand?
Die Funktion arbeitet single-threaded wegen Lauffähigkeit unter Win32s
und Win16 - und um sich Synchronisationsprobleme vom Hals zu halten.
 */

//void _fastcall DcMakeSub(long&,int);
//void _fastcall AcMakeSub(long&,int);
#define ACTIME 8	// dualer Logarithmus der Zeitkonstante in Samples?
#endif //__cplusplus
// BASISKLASSE

typedef struct{
 BYTE flags;		// Q-Flags (s. oben)
 BYTE bits;		// Gültige Bits; |maximaler Wert|=1<<(bits-1)
 BYTE numchan;		// Anzahl der Kanäle
 BYTE numtrig;		// Anzahl der Trigger-Kanäle (>=numchan)
 float rateminmax[2];	// Abtastrate minimal/maximal
 float ratecont;	// max. Abtastrate bei kontinulierlichem Betrieb, 0=kein
 DWORD depth;		// Speichertiefe für Sampledaten, in Samples, 0=INF
 GETSAMPLE getsample;	// Zeiger auf GetSample-Prozedur
 UINT blockalign;	// Block-Ausrichtung in Bytes
}SYSINFO, *PSYSINFO, NEAR*NPSYSINFO, FAR*LPSYSINFO;

typedef struct{
 BYTE ch;		// Welchen Kanal betreffend? (INPUT!)
 BYTE couplings;	// DC,AC,GND;INV;R50
 BYTE byteoffset;	// zum Zugriff via GetSample: Adress-Offset
 BYTE mask;		// Bitmaske (für Logikanalysator) für GetSample
 float voltminmax[2];	// Minimale und maximale (absolute!) VoltProLsb
}CHANINFO, *PCHANINFO, NEAR*NPCHANINFO, FAR*LPCHANINFO;
#define CHANINFO_DC	1	// Bitmaske für couplings
#define CHANINFO_AC	2	// (DC-Kopplung kann nicht simuliert werden!)
#define CHANINFO_GND	4
#define CHANINFO_INV	8	// nur bei _echter_ Inversion
#define CHANINFO_R50	16
#define CHANINFO_CAL	128	// Wirklich kalibriert

typedef struct{
 BYTE ch;		// Welchen Kanal betreffend?
 BYTE what;		// Was setzen? (volt,coupling,inv siehe unten)
 BYTE coupling;		// Kopplung (DC,AC,GND;INV)
 BYTE resistance;	// Eingangswiderstand (Zuschaltbarer 50-Ohm-Widerstand)
 float volt;		// Wenn negativ gegebem, dann wird "inv" gedreht
 float dcoffset;	// Üblicherweise vom Gerät nicht unterstützt: Null
}CHAN, *PCHAN, NEAR*NPCHAN, FAR*LPCHAN;
#define CHAN_COUPLING	1	// Masken für <what>
#define CHAN_INV	2
#define CHAN_VOLT	4
#define CHAN_DCOFFSET	8
#define CHAN_DC		0	// Werte für <coupling>
#define CHAN_AC		1
#define CHAN_GND	2
#define CHAN_R1M	0	// Werte für <resistance>
#define CHAN_R50	1

typedef struct{		// NUR LESEN, je nach aktueller Triggerquelle
 BYTE couplings;	// Kopplungsarten (Bitmaske DC,AC,HF,LF,TVH,TVL;PAT),
 BYTE unused;		// Richtige Kopplungen (unsimuliert)
 BYTE edges;		// Flanken (Bitmaske)
 BYTE type;		// Name, 0=normaler Kanal, 1=extern, 2=Netz
}TRIGINFO, *PTRIGINFO, NEAR*NPTRIGINFO, FAR*LPTRIGINFO;
#define TRIGINFO_DC	1	// Bitmasken für <couplings>
#define TRIGINFO_AC	2
#define TRIGINFO_HF	4
#define TRIGINFO_LF	8
#define TRIGINFO_TVH	16
#define TRIGINFO_TVL	32
#define TRIGINFO_PAT	128
#define TRIGINFO_RISE	1	// Bitmasken für <edges>
#define TRIGINFO_FALL	2
#define TRIGINFO_CHAN	0	// Enum für <type>
#define TRIGINFO_EXT	1
#define TRIGINFO_LINE	2

typedef struct{
 BYTE what;		// Was setzen (source,coupling,edge,post,level,pattern)
 BYTE source;		// Triggerquelle
 BYTE coupling;		// Kopplung
 BYTE edge;		// Flankenrichtung (0=+ oder 1=-)
 long pre;		// Prätrigger in Samples, negativ=Triggerverzögerung
 union{
  int level;		// Triggerpegel (nur bei DC?)
  BYTE patternXorAnd[16];// oder Triggermuster (als Logikanalysator) 2*64 bit
 };
}TRIG, *PTRIG, NEAR*NPTRIG, FAR*LPTRIG;
#define TRIG_SOURCE	1	// Bitmasken für <what>
#define TRIG_COUPLING	2
#define TRIG_EDGE	4
#define TRIG_PRE	8
#define TRIG_LEVEL	16
#define TRIG_PATTERN	32
#define TRIG_DC		0	// Werte für <coupling>
#define	TRIG_AC		1
#define TRIG_HF		2
#define TRIG_LF		3
#define TRIG_TVH	4
#define TRIG_TVL	5
#define TRIG_PAT	7
#define TRIG_RISE	0	// Werte für <edge>
#define TRIG_FALL	1

typedef float FLOAT, *PFLOAT, NEAR*NPFLOAT, FAR*LPFLOAT;

typedef struct {	// für Setup-Dialoge
 HWND parent;		// Elternfenster
 HWND FAR*kbHand;	// entscheidend für moduslosen Dialog
 UINT idreserved;	// wird von Datenquelle mit ID überschrieben
}DLGINFO, *PDLGINFO, NEAR*NPDLGINFO, FAR*LPDLGINFO;

typedef enum {
 Q_INIT=1,	// Initialisierung, kann Auswahldialog zeigen
 Q_DONE,
 Q_SETUPDLG,	// Einrichtungs-Dialog (allgemein)
 Q_ARM,		// scharfstellen (bspw. WaveInStart)
 Q_UNARM,	// "sichern" (bspw. WaveInReset)
// Q_TRIGGER,	// Automatischer Trigger
// Q_DATACHANGED,
 Q_POLL,	// lParam=LPWAVEHDR	(nächsten) Datenblock abholen

 Q_GETSYSINFO,	// lParam=LPSYSINFO	allgemeine Information
 Q_GETCHANINFO,	// lParam=LPCHANINFO für angegebenen Kanal
 Q_SETCHAN,	// lParam=LPCHAN	-"-
 Q_GETTRIGINFO,	// lParam=LPTRIGINFO für aktuellen Trigger-Kanal
 Q_SETTRIG,	// lParam=LPTRIG	Trigger setzen/ändern/abfragen
 Q_SETRATE,	// lParam=LPFLOAT	Samplerate setzen/abfragen (0 zum Lesen)
// Q_ALLOC	// GlobalAlloc() mit lParam=Samples für <buffer>
		// Ggf. ReAlloc() - bzw. Free() für wParam=0
}Q_MSG;

/*********************************************
 ** Ab hier wird es eigentlich Privatsache! **
 **   Künftig wird nur noch je ein	    **
 **   Funktionszeiger "herausgucken"!	    **
 *********************************************/
#include "dso.h"
#include "inpout32.h"
#define outb DlPortWritePortUchar
#define outw DlPortWritePortUshort
#define inb DlPortReadPortUchar
#define inw DlPortReadPortUshort

typedef struct{
 HWND hDlg;		// Setup-Dialog-Handle, solange er läuft
 HWND FAR*kbHand;	// Tastatur-Handler-Zentrale für IsDlgMessage()
 DLGPROC dlgProc;	// Dialogprozedur (für DialogBox/CreateDialog)
 UINT helpId;		// sowohl Dialog- als auch Hilfe-ID
}SETUPDLG;

#ifdef __cplusplus
class QUELLE{
public:
 SETUPDLG sd;
 virtual bool RelayMsg(Q_MSG,LPVOID);
protected:
 BYTE trcoupling;	// (simulierte) Triggerkopplung
 BYTE state;		// Q_FULL u.ä.
};


// ABGELEITETE KLASSEN
class Q_ZUFALL:public QUELLE {
 virtual bool RelayMsg(Q_MSG,LPVOID);
};

class Q_DSO220:public QUELLE {
 void SetDSO();
 virtual bool RelayMsg(Q_MSG,LPVOID);
 INITPARAMS ip;
public:
 LPVOID p;
 HANDLE hPortTalk;
};

class Q_SOUND:public QUELLE {
 MMRESULT WaveOpen();
 MMRESULT NearestRate(DWORD);
 void MaxChannels();
 void MaxBits();
 virtual bool RelayMsg(Q_MSG,LPVOID);
public:
 void NextWaveHdr(LPWAVEHDR FAR&whp);
 HWAVEIN handle;	// Wave-Eingabe-Handle
#ifdef WIN32
 HMIXER hMixer;
 DWORD dwLineID,dwControlID;	// Gefundener Regler
#endif
 UINT DevID;		// Wave-Eingabe-Nummer (zwecks Sampleraten-Änderung)
 WAVEFORMATEX wf;	// Momentan aktive Wave-Information
 WAVEHDR wh[16];	// zugehörige Köpfe
 int currentblock;	// Momentaner Wave-Header
 DWORD MinRate,MaxRate;	// Beim Initialisieren abgefragte Grenzwerte
 float lsb_volt;	// Setup-Einstellung; ohne Balance für beide Kanäle
 BYTE coupling;		// Setup-Einstellung
 BYTE byteshift;	// 0 (8 bit) oder 1 (16 bit), zweckmäßiger!
 bool bekloppt;		// TRUE wenn der Treiber ohne zu murren 1MSa/s schluckt
};
#endif //__cplusplus
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded