#include "1210.h"
#include <math.h>
#include <limits>
#include <uxtheme.h>
#define __field_ecount_opt(x)
#include <gdiplus.h>
const int FINFI=0x7F800000;
#define FINF *(float*)&FINFI
const int MAX_SAMPLE_COUNT=64*1024;
I16 m_hCard;
unsigned m_wSelectedChans=0x0F; // Bitmaske
U16 m_wRange;
double fSampleRate=20000; // Hz
double fBlockTime=0.1; // s
//#undef max
//#undef min
template<class T> void setMax(T&a,T b) {if (!(a>=b)) a=b;}
template<class T> void setMin(T&a,T b) {if (!(a<=b)) a=b;}
template<class T> struct Range{
T a,e;
T delta() const {return e-a;}
// void reset() {init(std::numeric_limits<T>::max(),std::numeric_limits<T>::min());}
bool init(T b, T c) {bool r=false;if(a!=b){a=b;r=true;}if(e!=c){e=c;r=true;}return r;}
bool init() {return init(0,0);}
bool expand(T b) {return expand(b,b);}
bool expand(T b, T c) {bool r=false;if(!(a<=b)){a=b;r=true;}if(!(e>=c)){e=c;r=true;}return r;}
};
//Range<float> range={-10,10};
static struct Samp{
short*buf;
int buflen; // in int16_t
F64 rate; // Tatsächliche Samplerate
U16 wChan[4];
U16 wGain[4];
U16 nChan;
int polylen,mux;
// int getInt(int i) const{return buf[i];}
// int get
float getFloat(int i,int j) const{
return toFloat(buf[i*nChan+j],j);
}
Range<short>ranges[4];
float fak[4];
void detectRanges();
float toFloat(int adcval, int chan) const;
}samp;
float Samp::toFloat(int adcval, int chan) const{
// if (adcval<=-32767) return -FINF;
// if (adcval>=+32767) return +FINF;
return fak[chan]*adcval;
}
void Samp::detectRanges() {
int k;
for (k=0; k<nChan; k++) ranges[k].init();
short*samples=samp.buf;
for (int i=0; i<samp.buflen; i++) for (k=0; k<nChan; k++) ranges[k].expand(*samples++);
}
class Scale{
public:
virtual void make(float,float,float,float)=0; // Zweipunktkalibrierung
virtual float scale(float) const=0;
virtual float unscale(float) const=0;
};
class LinScale:public Scale{
float f,O;
virtual void make(float a,float e,float A,float E);
virtual float scale(float x) const {return x*f+O;}
virtual float unscale(float X) const {return (X-O)/f;}
public:
LinScale(float s=1,float o=0):f(s),O(o) {}
};
void LinScale::make(float a,float e,float A,float E) {
e-=a; if (!e) return; // illegal
E-=A; if (!E) return; // illegal
f=E/e;
O=A-a*f;
}
class Trace;
RECT rcPlot;
HWND hPlot;
HBITMAP bufbm;
static void plotCalcRect(const RECT&rc) {
SetRect(&rcPlot,rc.left+44,rc.top+8,rc.right-10,rc.bottom-24);
}
static void updatePlot(bool all=false) {
// plotFindMinMax(r); // Für alle Plots (Quatsch bei mehreren Skalen)
// int vzlmax=r.e;
// setMax(vzlmax,-r.a);
// setMax(vzlmax,50);
// Range<float>rf;
// rf.e=(m_wRange==AD_B_10_V?10.0f:2.0f)*vzlmax/0x8000;
// rf.a=-rf.e;
// if (range.e==rf.e)
// else range=rf; // alles updaten
// }
InvalidateRect(hPlot,all?0:&rcPlot,FALSE);
}
class Axis{
public:
Range<float>range,extent; // range = Datenbereich, extent = in Pixel
Scale*scale;
float scaleInf(float x) {
/*if (isfinite(x))*/ return scale->scale(x);
}
void make() {scale->make(range.a,range.e,extent.a,extent.e);}
static double goodDivider(double x,int*nk=0);
};
// a divider of 1, 2 or 5:
// if 0.5<x<=1 return 1, x<=2 return 2, x<=5 return 5, x<10 return 10 etc.
double Axis::goodDivider(double x,int*nk) {
if (x<=0) return 1; // never return <=0
double b=ceil(log10(x)); // decimal base of x
if (nk) *nk=b<0?-lrint(b):0;
double e=pow(10.0,b);
x/=e; // now in range <0.1 1]
if (x>0.5) x=1;
else if (x>0.2) x=0.5;
else x=0.2;
return x*e;
}
static class XAxis:public Axis{
public:
bool updateRange();
void paint(HDC dc);
}xaxis;
bool XAxis::updateRange() {
bool r=extent.init(rcPlot.left,rcPlot.right-1); // -1 for plot line width
r|=range.init(0,samp.polylen-1);
if (r) make();
return r;
}
void XAxis::paint(HDC dc) {
SetTextAlign(dc,TA_CENTER|TA_TOP|TA_UPDATECP);
double fulltime=samp.buflen/samp.rate;
TCHAR prefix[2]={0};
if (fulltime<10) {fulltime*=1000; prefix[0]=TEXT('m');}
if (fulltime<10) {fulltime*=1000; prefix[0]=TEXT('µ');}
int nk;
double divi=goodDivider(fulltime/10,&nk);
int width=extent.delta();
for (double x=0; x<=fulltime; x+=divi) {
int X=lrint(width*x/fulltime);
TCHAR s[10];
int l=_sntprintf(s,elemof(s),TEXT("%.*f%s"),nk,x,prefix);
MoveToEx(dc,X+extent.a,rcPlot.bottom+2,0);
LineTo(dc,X+extent.a,rcPlot.bottom+8);
TextOut(dc,0,0,s,l);// draw the text on X-Axis
}
}
static class YAxis:public Axis{
public:
bool updateRange();
void paint(HDC dc);
HPEN peMinorTick;
void allocGdi();
void deallocGdi();
}yaxis;
bool YAxis::updateRange() {
bool r=extent.init(rcPlot.bottom-1,rcPlot.top);
Range<float>all;
// all.reset();
all.a=+FINF;
all.e=-FINF;
for (int k=0; k<samp.nChan; k++) all.expand(samp.toFloat(samp.ranges[k].a,k),samp.toFloat(samp.ranges[k].e,k));
float vzlmax=all.e;
setMax(vzlmax,-all.a);
setMax(vzlmax,0.001F); // Mit der Auflösung nicht übertreiben!! TODO: Programmatisch lösen!
vzlmax=goodDivider(vzlmax);
r|=range.init(-vzlmax,vzlmax);
if (r) make();
return r;
}
void YAxis::allocGdi() {
peMinorTick=CreatePen(PS_SOLID,1,RGB(132,130,132));
}
void YAxis::deallocGdi() {
DeletePen(peMinorTick);
}
void YAxis::paint(HDC dc) {
const int y_divider=4;
// calculate the interval for tick/grid
int y_intv = -extent.delta() / y_divider;
double delta=range.delta()/y_divider;
int nk;
goodDivider(delta,&nk); // TODO: Diesen Step verwenden!!
SetTextAlign(dc,TA_RIGHT|TA_BASELINE);
int i;
for (i=0; i<= y_divider; i++ ) {
int y=extent.a-5-(i*y_intv);
MoveToEx(dc,rcPlot.left-12,y,0);
LineTo(dc,rcPlot.left-3,y);
TCHAR s[10];
int l=_sntprintf(s,elemof(s),TEXT("%.*f"),nk,delta*i+range.a);
TextOut(dc,rcPlot.left-14,y+4,s,l);
}
SelectPen(dc,peMinorTick);
for (i=0; i< y_divider; i++) {
int y=extent.a -5 - (i*y_intv) - y_intv/2;
MoveToEx(dc,rcPlot.left-9, y, 0);
LineTo(dc,rcPlot.left-4, y);
}
}
class Data{
public:
virtual float get(int) const=0;
};
class PhysData:public Data{
int ch;
public:
PhysData(int c):ch(c) {}
virtual float get(int i) const {return samp.getFloat(i,ch);}
};
class PowerData:public Data{
int ch_u,ch_i;
public:
PowerData(int chu,int chi):ch_u(chu),ch_i(chi) {}
virtual float get(int i) const {return samp.getFloat(i,ch_u)*samp.getFloat(i,ch_i);}
};
class Trace{
// static bool gdiplus=true;
Gdiplus::Pen *pnLine;
Gdiplus::Brush *brLine,*brMinMax,*brStdDev,*brPoly;
Gdiplus::PointF*ptLine,*ptMinMax,*ptStdDev,*ptPoly;
public:
static const COLORREF colors[4];
void deallocate();
void allocGdi(int index);
void deallocGdi();
int polylen;
int nLines; // bei Mehrfachtrigger und <=4 Ergebnis-Linien, sonst 1
bool lineAsPoly;
Axis*xaxis,*yaxis;
Data*ydata;
void make(); // Generiere Punktkoordinaten für Hauptfenster, noch nicht für Miniatur
void paintPoly(Gdiplus::Graphics&)const; // Polygon zwischen Kurvenzügen
void paintMinMax(Gdiplus::Graphics&)const; // Unterste Polygonlage
void paintStdDev(Gdiplus::Graphics&)const; // Darüberliegende Polygonlage
void paintLine(Gdiplus::Graphics&)const; // Kurvenzug
void paintMarkers(Gdiplus::Graphics&)const; // Punkt-Marker
}traces[4];
const COLORREF Trace::colors[4]={RGB(255,0,0),RGB(255,255,0),RGB(128,128,255),RGB(64,128,64)};
void Trace::deallocate() {
if (ptLine) {delete[] ptLine; ptLine=0;}
if (ptMinMax) {delete[] ptMinMax; ptMinMax=0;}
if (ptStdDev) {delete[] ptStdDev; ptStdDev=0;}
}
void Trace::allocGdi(int index) {
COLORREF cr=colors[index];
Gdiplus::Color c(GetRValue(cr),GetGValue(cr),GetBValue(cr));
pnLine=new Gdiplus::Pen(c);
brLine=new Gdiplus::SolidBrush(c);
c.SetValue(c.GetValue()&0xC0FFFFFF);
brStdDev=new Gdiplus::SolidBrush(c);
c.SetValue(c.GetValue()&0x80FFFFFF);
brMinMax=new Gdiplus::SolidBrush(c);
// Erst mal kein Polygon
}
void Trace::deallocGdi() {
if (brPoly) {delete brPoly; brPoly=0;}
if (brMinMax) {delete brMinMax; brMinMax=0;}
if (brStdDev) {delete brStdDev; brStdDev=0;}
if (brLine) {delete brLine; brLine=0;}
if (pnLine) {delete pnLine; pnLine=0;}
}
void Trace::make() {
if (polylen!=samp.polylen) deallocate();
polylen=samp.polylen;
if (!nLines) nLines=1;
if (!ptLine) ptLine=new Gdiplus::PointF[polylen*nLines];
if (samp.mux>1 && !ptMinMax) ptMinMax=new Gdiplus::PointF[polylen<<1];
if (samp.mux>1 && !ptStdDev) ptStdDev=new Gdiplus::PointF[polylen<<1];
for (int i=0; i<polylen; i++) {
float sum=0;
double sq=0;
Range<float>minmax;
minmax.a=+FINF; minmax.e=-FINF;
for (int j=0; j<samp.mux; j++) {
float y=ydata->get(i*samp.mux+j);
minmax.expand(y);
sum+=y;
sq+=(double)y*y;
}
sum/=samp.mux;
float dev=float((double)sum*sum-sq/samp.mux); // TODO: Stimmt das??
ptLine[i].X=xaxis->scaleInf(i);
ptLine[i].Y=yaxis->scaleInf(sum);
if (samp.mux>1) {
int j=polylen*2-1-i;
ptMinMax[j].X=ptMinMax[i].X=ptLine[i].X;
ptMinMax[i].Y=yaxis->scaleInf(minmax.e);
ptMinMax[j].Y=yaxis->scaleInf(minmax.a);
ptStdDev[j].X=ptMinMax[i].X=ptLine[i].X;
ptStdDev[i].Y=yaxis->scaleInf(sum+dev);
ptStdDev[j].Y=yaxis->scaleInf(sum-dev);
}
}
}
void Trace::paintPoly(Gdiplus::Graphics&g) const{
if (brPoly && ptPoly) g.FillPolygon(brPoly,ptPoly,polylen<<1);
}
void Trace::paintMinMax(Gdiplus::Graphics&g) const{
if (brMinMax && ptMinMax) g.FillPolygon(brMinMax,ptMinMax,polylen<<1);
}
void Trace::paintStdDev(Gdiplus::Graphics&g) const{
if (brStdDev && ptStdDev) g.FillPolygon(brStdDev,ptStdDev,polylen<<1);
}
void Trace::paintLine(Gdiplus::Graphics&g) const{
g.DrawLines(pnLine,ptLine,polylen);
}
static void plotPaintTraces(HDC dc) {
int k;
Gdiplus::Graphics g(dc);
for (k=0; k<samp.nChan; k++) traces[k].paintPoly(g);
if (samp.mux>1) {
for (k=0; k<samp.nChan; k++) traces[k].paintMinMax(g);
for (k=0; k<samp.nChan; k++) traces[k].paintStdDev(g);
}
for (k=0; k<samp.nChan; k++) traces[k].paintLine(g);
}
static void plotPaint(HDC dc) {
RECT rc;
GetClientRect(hPlot,&rc);
if (!bufbm) bufbm=CreateCompatibleBitmap(dc,rc.right,rc.bottom);
HDC memdc=CreateCompatibleDC(dc);
SelectFont(memdc,GetCurrentObject(dc,OBJ_FONT));
SetBkColor(memdc,GetBkColor(dc));
HBITMAP obm=SelectBitmap(memdc,bufbm);
SaveDC(memdc);
ExcludeClipRect(memdc,rcPlot.left,rcPlot.top,rcPlot.right,rcPlot.bottom);
FillRect(memdc,&rc,GetSysColorBrush(COLOR_3DFACE));// fill axes background
RestoreDC(memdc,-1);
FillRect(memdc,&rcPlot,GetStockBrush(BLACK_BRUSH)); // fill plotarea background
if (samp.nChan) {
yaxis.paint(memdc);
xaxis.paint(memdc);
plotPaintTraces(memdc);
}
BitBlt(dc,0,0,rc.right,rc.bottom,memdc,0,0,SRCCOPY);
SelectBitmap(memdc,obm);
DeleteDC(memdc);
}
static void cb() {
// OutputDebugString(TEXT("Hier\n"));
UD_AI_AsyncDblBufferTransfer(m_hCard,samp.buf);
samp.detectRanges();
bool all=xaxis.updateRange()|yaxis.updateRange();
for (int k=0; k<samp.nChan; k++) traces[k].make();
updatePlot(all);
}
static bool onStart(HWND wnd) {
int err = UD_AI_Channel_Config( m_hCard, UD_AI_Differential, UD_AI_Differential, UD_AI_Differential, UD_AI_Differential );
if (err) {MBox(wnd,4,MB_OK,err); return false;}
err = UD_AI_Trigger_Config( m_hCard, UD_AI_CONVSRC_INT, UD_AI_TRGMOD_POST, UD_AI_TRGSRC_SOFT, 0, 0, 0, 0 );
if (err) {MBox(wnd,5,MB_OK,err); return false;}
err = UD_AI_AsyncDblBufferMode(m_hCard, true); // single-buffer mode
if (err) {MBox(wnd,6,MB_OK,err); return false;}
for (int k=0; k<4; k++) if (m_wSelectedChans&1<<k) {
samp.wChan[samp.nChan] = k;
samp.wGain[samp.nChan] = m_wRange; // TODO: Per channel
samp.nChan++; // popcount(m_wSelectedChans)
}
if (!samp.nChan) return false;
err = UD_GetActualRate(m_hCard,fSampleRate,&samp.rate);
SendDlgItemMessage(wnd,21,UDM_SETPOS32,0,lrint(samp.rate));
samp.buflen=lrint(samp.rate*fBlockTime)&~255; // ganze 256 Byte
setMax(samp.buflen,256);
setMin(samp.buflen,MAX_SAMPLE_COUNT);
samp.mux=samp.buflen>>10; // 513..1024 Punkte anstreben
if (!samp.mux) samp.mux=1;
samp.polylen=samp.buflen/samp.mux;
// allocate a memory for user DMA buffer
DWORD mem_size = samp.buflen * samp.nChan * 2; // 2 = Doppel-Puffer
samp.buf = new short[mem_size];
if (!samp.buf) {MBox(wnd,3,MB_OK); samp.nChan=0; return false;}
for (k=0; k<samp.nChan; k++) {
Trace&tr=traces[k];
tr.xaxis=&xaxis;
tr.yaxis=&yaxis;
float gain=samp.wGain[k]==1?10.0F:2.0F;
gain/=32768;
samp.fak[k]=gain;
//TODO: Hier kommt der Shunt ins Spiel! Und dann noch die Extra-Traces für die Leistungsberechnung!
tr.ydata=new PhysData(k); // Callback-Zeiger allesamt bereit machen
tr.allocGdi(samp.wChan[k]);
}
UD_AI_EventCallBack(m_hCard,1,DBEvent,cb);
err = UD_AI_ContReadMultiChannels(m_hCard,samp.nChan,samp.wChan,samp.wGain,
0,mem_size,samp.rate,ASYNCH_OP);
if (err) {MBox(wnd,8,MB_OK,err); delete[] samp.buf; samp.nChan=0; return false;}
// SetTimer(wnd,1,200,0);
return true;
}
INT_PTR CALLBACK MainDlgProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static HFONT bold;
static ULONG_PTR token;
switch (msg) {
case WM_INITDIALOG: {
HMENU sysm = GetSystemMenu(wnd,FALSE);
TCHAR s[64];
LoadString(hInstance,101,s,elemof(s));
AppendMenu(sysm,MF_SEPARATOR,0,0);
AppendMenu(sysm,MF_STRING,0x70,s);
HICON icon=LoadIcon(hInstance,MAKEINTRESOURCE(100));
SetClassLongPtr(wnd,GCL_HICON,(LONG_PTR)icon); // Set big icon
SetClassLongPtr(wnd,GCL_HICONSM,(LONG_PTR)icon); // Set small icon
Gdiplus::GdiplusStartupInput si; // Defaultkonstruktor
Gdiplus::GdiplusStartup(&token,&si,0);
hPlot=GetDlgItem(wnd,42);
RECT rc;
GetClientRect(hPlot,&rc);
plotCalcRect(rc);
HWND hCombo=GetDlgItem(wnd,103);
ComboBox_AddString(hCombo,TEXT("± 10 V"));
ComboBox_AddString(hCombo,TEXT("± 2 V"));
ComboBox_SetCurSel(hCombo,0);
PostMessage(wnd,WM_COMMAND,MAKELONG(103,CBN_SELCHANGE),(LPARAM)hCombo);
// set the default SampleRate
SetDlgItemInt(wnd,101,lrint(fSampleRate),false);
HWND ud=CreateUpDownControl(WS_CHILD|WS_BORDER|WS_VISIBLE|UDS_ALIGNRIGHT|UDS_ARROWKEYS|UDS_HOTTRACK|UDS_SETBUDDYINT,
0,0,0,0,wnd,21,0,GetDlgItem(wnd,101),100,0,0);
SendMessage(ud,UDM_SETRANGE32,1000,2000000);
SendMessage(ud,UDM_SETPOS32,0,lrint(fSampleRate));
SendDlgItemMessage(wnd,101,EM_SETMODIFY,TRUE,0); // Startschuss für EN_CHANGE geben
HFONT f=(HFONT)SendMessage(wnd,WM_GETFONT,0,0);
LOGFONT lf;
GetObject(f,sizeof lf,&lf);
lf.lfWeight=700;
bold=CreateFontIndirect(&lf);
for (int k=0; k<4; k++) {
HWND w=GetDlgItem(wnd,64+k);
// if (k<2) SetWindowTheme(w,L"x",L"x");
SetWindowFont(w,bold,FALSE);
if (m_wSelectedChans&1<<k) SendMessage(w,BM_SETCHECK,BST_CHECKED,0);
}
xaxis.scale=new LinScale();
yaxis.scale=new LinScale();
}return TRUE;
case WM_SYSCOMMAND: switch (wParam&0xFFFF0) {
case 0x70: MBox(wnd,102,MB_OK); break;
}break;
case WM_COMMAND: switch (wParam) {
case 64:
case 65:
case 66:
case 67: m_wSelectedChans^=1<<(wParam-64); break;
case MAKELONG(101,EN_CHANGE): /*if (Edit_GetModify((HWND)lParam))*/ {
BOOL err=TRUE;
unsigned u=SendDlgItemMessage(wnd,21,UDM_GETPOS32,0,(LPARAM)&err);
if (!err && u) {
fSampleRate=u;
// updatePlot(true);
}
}break;
case MAKELONG(103,CBN_SELCHANGE): {
int nIndex = ComboBox_GetCurSel((HWND)lParam);
switch (nIndex) {
case 0: // +/-10V
m_wRange = AD_B_10_V;
// range.e = 10.0; range.a = -10.0;
break;
case 1: // +/-2V
m_wRange = AD_B_2_V;
// range.e = 2.0; range.a = -2.0;
break;
}
updatePlot(true);
}break;
case 11: onStart(wnd); break;
case 12: {
ULONG dwAccessCnt;
I16 nErr = UD_AI_AsyncClear(m_hCard,&dwAccessCnt);
delete[] samp.buf;
for (int k=0; k<samp.nChan; k++) {
Trace&tr=traces[k];
tr.deallocate();
tr.deallocGdi();
delete tr.ydata;
}
samp.nChan=0;
// KillTimer(wnd,1);
}break;
case IDCANCEL: EndDialog(wnd,wParam); break;
}break;
case WM_NOTIFY: {
NMHDR&hdr=*(NMHDR*)lParam;
switch (hdr.idFrom) {
case 21: if (hdr.code==UDN_DELTAPOS) {
NMUPDOWN&ud=*(NMUPDOWN*)lParam;
ud.iDelta*=ud.iPos>=100000?10000:1000;
}break;
case 64:
case 65:
case 66:
case 67: if (hdr.code==NM_CUSTOMDRAW) {
NMCUSTOMDRAW&cd=*(NMCUSTOMDRAW*)lParam;
if (cd.dwDrawStage==CDDS_PREPAINT) {
SetTextColor(cd.hdc,Trace::colors[hdr.idFrom-64]);
SetBkColor(cd.hdc,0);
RECT rc={0,0,11,8};
MapDialogRect(wnd,&rc);
int w=rc.right-rc.left;
CopyRect(&rc,&cd.rc);
rc.left+=w;
TCHAR s[64];
int l=GetWindowText(hdr.hwndFrom,s,elemof(s));
DrawText(cd.hdc,s,l,&rc,DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX);
if (cd.uItemState&CDIS_FOCUS) {
DrawText(cd.hdc,s,l,&rc,DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
InflateRect(&rc,1,1);
DrawFocusRect(cd.hdc,&rc);
}
return SetDlgMsgResult(wnd,WM_NOTIFY,CDRF_SKIPDEFAULT);
}
}break;
}
}break;
case WM_CTLCOLORSTATIC: {
UINT id=GetDlgCtrlID((HWND)lParam);
if (64<=id && id<68) {
SetTextColor((HDC)wParam,Trace::colors[id-64]);
SetBkColor((HDC)wParam,0);
return (BOOL)GetSysColorBrush(COLOR_3DFACE);
}
}break;
case WM_TIMER: switch (wParam) {
case 1: {
bool bStopped;
ULONG dwAccessCnt;
I16 nErr = UD_AI_AsyncCheck( m_hCard, &bStopped, &dwAccessCnt);
if (!nErr && bStopped) {
KillTimer(wnd,1);
nErr = UD_AI_AsyncClear(m_hCard,&dwAccessCnt);
updatePlot();
}else if (nErr==ErrorAdFifoFull){
KillTimer(wnd,1);
nErr = UD_AI_AsyncClear(m_hCard,&dwAccessCnt);
MBox(wnd,7,MB_OK);
}
}break;
}break;
case WM_DRAWITEM: {
DRAWITEMSTRUCT&dis=*(DRAWITEMSTRUCT*)lParam;
plotPaint(dis.hDC);
}break;
case WM_DESTROY: {
KillTimer(wnd,1);
DeleteFont(bold);
Gdiplus::GdiplusShutdown(token);
}break;
}
return FALSE;
}
| Detected encoding: ANSI (CP1252) | 4
|
|
|