Source file: /~heha/hs/Funkuhr.zip/src/DlgDemodulator.c

/****************************************
 * Projekt: Funkuhr DCF77		*
 * Eigenschaftsseite äDemodulatorô	*
 * Beobachtung der I/Q-Demodulation	*
 ****************************************/

#include "Funkuhr.h"

// Dialog-Daten, verschwinden mit dem Fokusverlust
#define HISTIQ 32
typedef struct {
 int iqidx;
 int lastr;		// letzter Radius (Vektordiagramm)
 int lastv;		// letztes Maximum der FFT
 POINT lastiq[HISTIQ];	// Vektoren, i = x, q = y (positiv nach oben)
 int lasta[HISTIQ];	// Amplituden (erspart hier das Wurzelziehen)
 struct {
  HFONT fText;		// kleine Schrift fⁿr Skalenbeschriftung (?) und Frequenzanzeige
  HPEN pVektor[HISTIQ],pKreis,pAchse;
 }gdi;
}dlg;

INT_PTR CALLBACK DemodulatorDlgProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
 dlg*D=(dlg*)GetWindowLongPtr(Wnd,DWLP_USER);
 DefHelpProc(Wnd,Msg,lParam,109);
 switch (Msg) {
  case WM_INITDIALOG: {
   HWND w;
   CheckDlgButton(Wnd,18,!DemodIQ.NoAGC);	// AGC
   ShowDlgItem(Wnd,18,Config.iDemodTrig&2?SW_HIDE:SW_SHOW);
   CheckDlgButton(Wnd,16,Config.iDemodTrig&1);	// AFC
   CheckDlgButton(Wnd,17,Config.iDemodTrig>>1&1);// Phasendemodulation
   w=GetDlgItem(Wnd,12);			// AGC-Pegel
   SendMessage(w,TBM_SETRANGE,FALSE,MAKELONG(0,63));
   SendMessage(w,TBM_SETTICFREQ,9,0);
   SendMessage(w,TBM_SETPOS,TRUE,Config.iDemodTrig>>2);
   EnableWindow(w,DemodIQ.NoAGC);
   ShowWindow(w,Config.iDemodTrig&2?SW_HIDE:SW_SHOW);
   AttachUpDown(Wnd,32,33,0,0,0);	// Frequenz grob
   w=GetDlgItem(Wnd,34);		// Frequenz fein (AFC)
   SendMessage(w,TBM_SETRANGE,FALSE,MAKELONG(-126,126));
   SendMessage(w,TBM_SETTICFREQ,18,0);
   //SendMessage(w,TBM_SETPOS,TRUE,fein);
   EnableWindow(w,!(Config.iDemodTrig&1));
// gelb und ohne Animationseffekte bei Stillstand
   SendDlgItemMessage(Wnd,13,WM_USER+16/*PBM_SETSTATE*/,3/*PBST_PAUSED*/,0);
   SendDlgItemMessage(Wnd,14,WM_USER+16/*PBM_SETSTATE*/,3/*PBST_PAUSED*/,0);
  }return TRUE;

  case WM_COMMAND: switch ((DWORD)wParam) {
   case MAKELONG(16,BN_CLICKED): {
    EnableDlgItem(Wnd,34,Config.iDemodTrig&1);
    Config.iDemodTrig^=1;
   }break;

   case MAKELONG(17,BN_CLICKED): {
    int i=Config.iDemodTrig&2?SW_SHOW:SW_HIDE;
    Config.iDemodTrig^=2;
    ShowDlgItem(Wnd,12,i);
    ShowDlgItem(Wnd,18,i);
   }break;
   
   case MAKELONG(18,BN_CLICKED): {
    DemodIQ.NoAGC=!DemodIQ.NoAGC;
    EnableDlgItem(Wnd,12,DemodIQ.NoAGC);
   }break;

   case MAKELONG(32,EN_CHANGE): {
    SetTimer(Wnd,32,100,NULL);
   }break;
   
   case MAKELONG(36,BN_CLICKED): {
    DemodIQ.DoFFT = DemodIQ.DoFFT ? 0 : 255;
   }break;

  }break;

  case WM_TIMER: switch ((BYTE)wParam) {
   case 32: {
    int f;
    if (GetUpDownInt(Wnd,33,&f)) {
     SetFiltFreq(f*100+(int)SendDlgItemMessage(Wnd,34,TBM_GETPOS,0,0));
    }
   }break;
  }break;

  case WM_VSCROLL: {
   Config.iDemodTrig=(BYTE)(Config.iDemodTrig&3|(63-(int)SendMessage((HWND)lParam,TBM_GETPOS,0,0))<<2);
   if (DemodIQ.NoAGC) SetTrig();
  }break;

  case WM_HSCROLL: {
   if ((BYTE)wParam==SB_ENDSCROLL) {	// Schieber wieder Richtung Mitte ziehen
    int fein=(int)SendMessage((HWND)lParam,TBM_GETPOS,0,0);
    int ganz;
    if (GetUpDownInt(Wnd,33,&ganz)) {
     if (fein<=-100) {fein+=100; ganz--;}
     else if (fein>=100) {fein-=100; ganz++;}
     else goto skip;
     SendMessage((HWND)lParam,TBM_SETPOS,TRUE,fein);
     SendDlgItemMessage(Wnd,33,UDM_SETPOS32,0,ganz);
     KillTimer(Wnd,32);
skip:;
    }
   }else SendMessage(Wnd,WM_COMMAND,MAKELONG(32,EN_CHANGE),0);
  }break;

  case WM_DRAWITEM: {
   DRAWITEMSTRUCT *dis=(void*)lParam;
   HDC dc=CreateCompatibleDC(dis->hDC);
   SIZE sz={dis->rcItem.right-dis->rcItem.left,dis->rcItem.bottom-dis->rcItem.top};
   HBITMAP bm=CreateCompatibleBitmap(dis->hDC,sz.cx,sz.cy);
   HBITMAP obm=SelectBitmap(dc,bm);
   SelectBrush(dc,(HBRUSH)SendMessage(Wnd,WM_CTLCOLORDLG,(WPARAM)dc,(LPARAM)Wnd));
   PatBlt(dc,0,0,sz.cx,sz.cy,PATCOPY);
   if (D) {
    WCHAR s[32];
    int a,ph;
    DemodIQ.lock|=1;	// FFT_Data nicht verΣndern oder gar freigeben
    if (DemodIQ.FFT_Data) {
     int k=2<<DemodIQ.FFT_LenShift;
     int i;
     int xlast=-1;
     int ylast;
     int vmax=MulDiv(D->lastv,15,16);
     if (vmax<16) vmax=16;
     for (i=0; i<k; i+=2) {
      int v=hypot_lazy(DemodIQ.FFT_Data[i],DemodIQ.FFT_Data[i+1]);
      DemodIQ.FFT_Data[i]=v;
      DemodIQ.FFT_Data[i+1]=0;
      if (vmax<v) vmax=v;
     }
     D->lastv=vmax;
     // Nicht bspw. 22050 Linien zeichnen, sondern je eine pro Y-Pixel, jeweils das Maximum
     for(i=0; i<k; i+=2) {
      int x=MulDiv(i,sz.cx,k);
      int y=DemodIQ.FFT_Data[i];
      if (xlast!=x) {
       if (xlast>=0) Line(dc,xlast,sz.cy-ylast,xlast,sz.cy);
       xlast=x;
       ylast=0;
      }
      if (ylast<y) ylast=y;
     }
     Line(dc,xlast,sz.cy-ylast,xlast,sz.cy);
    }else{
     int i,rad=MulDiv(D->lastr,15,16);	// Gleitende Skalierung (beim Vergr÷▀ern)
     if (rad<16) rad=16;	// Minimalradius!
     for(i=0; i<HISTIQ; i++) {
      int r=D->lasta[i];
      if (rad<r) rad=r;
     }
     D->lastr=rad;
     SaveDC(dc);
     SetGraphicsMode(dc,GM_ADVANCED);
     SetViewportOrgEx(dc,sz.cx>>1,sz.cy>>1,NULL);	// in die Bitmap-Mitte
     SetMapMode(dc,MM_ISOTROPIC);			// Kreise als Kreise erscheinen lassen
     SetWindowExtEx(dc,(rad<<1),(rad<<1),NULL);		// 2x Radius als Skalierung
     SetViewportExtEx(dc,dis->rcItem.right-dis->rcItem.left-1,dis->rcItem.top-dis->rcItem.bottom+1,NULL);
     SelectBrush(dc,GetStockBrush(BLACK_BRUSH));
     Ellipse(dc,-rad,-rad,rad,rad);
     SelectPen(dc,D->gdi.pAchse);
     Line(dc,rad,0,-rad,0);
     Line(dc,0,-rad,0,rad);
     if (!(Config.iDemodTrig&2)) {	// nur bei AM
      SelectBrush(dc,GetStockBrush(HOLLOW_BRUSH));
      SelectPen(dc,D->gdi.pKreis);
      rad=DemodIQ.at;
      Ellipse(dc,-rad,-rad,rad,rad);
     }
     for(i=0; i<HISTIQ; i++) {	// mit der dunkelsten Linie beginnen
      int k=(i+D->iqidx)&(HISTIQ-1);
      SelectPen(dc,D->gdi.pVektor[i]);
      Line(dc,D->lastiq[k].x,D->lastiq[k].y,0,0);
      if (i==HISTIQ-1) {
       ph=lrint(atan2(D->lastiq[k].y,D->lastiq[k].x)*180/PI);
       a=D->lasta[k];
      }
     }
     RestoreDC(dc,-1);
    }
    DemodIQ.lock&=~1;
    if (!DemodIQ.FFT_Data) {
     int f,r;
     SIZE fs;	// Schriftgr÷▀e (H÷he)
     SaveDC(dc);
     SelectFont(dc,D->gdi.fText);
     SetBkMode(dc,TRANSPARENT);
     SetTextColor(dc,RGB(128,64,0));		// DDS-Frequenz links oben
     f=Config.iFiltFreq*200+Config.iAFC;
     TextOutW(dc,0,0,s,wnsprintfW(s,elemof(s),L"%d%c%02d",f/100,sDecimal[0],f%100));
     GetTextExtentPoint32W(dc,L"Q",1,&fs);
     TextOutW(dc,0,fs.cy,L"Hz",2);
     SetTextAlign(dc,TA_RIGHT|TA_TOP);		// Kreisdurchmesser-Amplitude rechts oben
     r=D->lastr;
     f=MulDiv(r,100,1<<IQSHIFT);
     TextOutW(dc,sz.cx,0,s,wnsprintfW(s,elemof(s),L"%d%c%02d",f/100,sDecimal[0],f%100));
     SetTextAlign(dc,TA_LEFT|TA_BOTTOM);	// Amplitude (prozentual) links unten
     TextOutW(dc,0,sz.cy,s,wnsprintfW(s,elemof(s),L"%d %%",MulDiv(a,100,r)));
     SetTextAlign(dc,TA_RIGHT|TA_BOTTOM);	// Phasenwinkel rechts unten
     TextOutW(dc,sz.cx,sz.cy,s,wnsprintfW(s,elemof(s),L"%d░",ph));
     SetTextAlign(dc,TA_RIGHT|TA_TOP);
     SetTextColor(dc,RGB(192,192,192));
     TextOutW(dc,(sz.cx>>1)-2,fs.cy>>1,L"Q",1);
     TextOutW(dc,sz.cx-fs.cx,(sz.cy>>1)+2,L"I",1);
     RestoreDC(dc,-1);
    }
   }
   BitBlt(dis->hDC,dis->rcItem.left,dis->rcItem.top,sz.cx,sz.cy,dc,0,0,SRCCOPY);
   SelectBitmap(dc,obm);
   DeleteBitmap(bm);
   DeleteDC(dc);
  }break;

  case WM_NOTIFY: {
   LPPSHNOTIFY psn=(LPPSHNOTIFY)lParam;
   switch (psn->hdr.code) {
    case PSN_SETACTIVE: {
     int fein;
     D=LocalAlloc(LPTR,sizeof(dlg));
     SetWindowLongPtr(Wnd,DWLP_USER,(LONG_PTR)D);
     if (D) {
      int i,lpy;
      HDC dc=GetDC(0);
      lpy=GetDeviceCaps(dc,LOGPIXELSY);
      ReleaseDC(0,dc);
      D->gdi.fText=CreateFont(-MulDiv(7,lpy,72),0,0,0,700,0,0,0,0,0,
        OUT_TT_ONLY_PRECIS,PROOF_QUALITY,VARIABLE_PITCH|FF_SWISS,NULL);
      for (i=0; i<HISTIQ; i++) {
       BYTE grau=240-(HISTIQ-1)*7+i*7;	// i=0: grau=23
       COLORREF c=RGB(grau>>1,grau,grau>>1);
       if (i==HISTIQ-1) c=RGB(224,255,224);	// aktuell: extra hell
       D->gdi.pVektor[i]=CreatePen(PS_SOLID,0,c);
      }
      D->gdi.pKreis=CreatePen(PS_SOLID,0,RGB(192,192,0));
      D->gdi.pAchse=CreatePen(PS_SOLID,0,RGB(64,64,64));
     }
     SendDlgItemMessage(Wnd,33,UDM_SETRANGE32,10,Config.iSampleRate*500);
     SendDlgItemMessage(Wnd,33,UDM_SETPOS32,0,FiltFreqHz(&fein));
     KillTimer(Wnd,32);
     SendDlgItemMessage(Wnd,34,TBM_SETPOS,TRUE,fein);
     DemodIQ.hShowDemod=Wnd;
    }break;
    case PSN_KILLACTIVE: {
     if (D) {
      int i;
      for (i=0; i<sizeof(D->gdi)/sizeof(HGDIOBJ); i++) DeleteObject(((HGDIOBJ*)&D->gdi)[i]);
      SetWindowLongPtr(Wnd,DWLP_USER,(LONG_PTR)LocalFree(D));
     }
     DemodIQ.hShowDemod=0;
    }break;
   }
  }break;

  case WM_RECV_IQ: {
   if (!DemodIQ.NoAGC) {
    UpdateTrig();
    SendDlgItemMessage(Wnd,12,TBM_SETPOS,TRUE,63-(Config.iDemodTrig>>2));
   }
   SendDlgItemMessage(Wnd,13,PBM_SETPOS,Logarithmize(DemodIQ.a>>IQSHIFT,100),0);
   SendDlgItemMessage(Wnd,14,PBM_SETPOS,Logarithmize(DemodIQ.n,100),0);
   AllocFFTSpace();
   if (D) {
    int i=D->iqidx;
    D->lastiq[i].x=(int)wParam;
    D->lastiq[i].y=(int)lParam;
    D->lasta[i]=hypoti((int)wParam,(int)lParam);
    D->iqidx=(i+1)&(HISTIQ-1);
    InvalidateRect(GetDlgItem(Wnd,11),NULL,FALSE);
   }
  }break;
 }
 return FALSE;
}
Detected encoding: OEM (CP437)1
Wrong umlauts? - Assume file is ANSI (CP1252) encoded