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