/* twpsk - A gui application for PSK * Copyright (C) 1999 Ted Williams WA0EIR * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, * USA. * * Version: 2.1 - Aug 2002 * * Disp Class - methods to draw the waterfall and spectrum analyser */ #include #include "twpskDisp.h" #include "twpskWids.h" #include "wfCursorShape.xbm" #include "wfCursorMask.xbm" #include "../server/server.h" extern Wids pskwids; extern AppRes appRes; void Disp::find_visual_info(int *truecolor, XVisualInfo *vinfo) { int scr = DefaultScreen(display); if( XMatchVisualInfo(display, scr, 8, PseudoColor, vinfo) ) { fprintf(stderr,"Using 8 bpp Pseudo Color\n"); *truecolor = 0; return; } if( XMatchVisualInfo(display, scr, 15, TrueColor, vinfo) ) { fprintf(stderr,"Using 15 bpp True Color\n"); *truecolor = 1; return; } if( XMatchVisualInfo(display, scr, 16, TrueColor, vinfo) ) { fprintf(stderr,"Using 16 bpp True Color\n"); *truecolor = 1; return; } if( XMatchVisualInfo(display, scr, 24, TrueColor, vinfo) ) { fprintf(stderr,"Using 24 bpp True Color\n"); *truecolor = 1; return; } if( XMatchVisualInfo(display, scr, 32, TrueColor, vinfo) ) { fprintf(stderr,"Using 32 bpp True Color\n"); *truecolor = 1; return; } } // color: 16 bit values! 0x0000..0xFFFF unsigned long Disp::get_mask_color(int color, unsigned long mask) { int shift = 0; int notused = 16; while (!(mask&0x01)) { shift++; mask >>=1; } while(mask&0x01) { notused--; mask >>=1; } unsigned long c = (color>>notused) << shift; return c; } /* * setup for wf/sa drawing area */ void Disp::setup (Widget wfDA) { /* constants */ int i; int bad_grays = 0; int rtn = 0; /* window values */ display = XtDisplay (wfDA); window = XtWindow (wfDA); screen = DefaultScreen (display); gc = XDefaultGC (display, screen); cmap = DefaultColormap (display, screen); int wfCursorHotX = 7; int wfCursorHotY = 15; XColor cursorShapePix, cursorMaskPix; Pixmap shape, mask; XSetLineAttributes (display, gc, 0, LineSolid, CapRound, JoinRound); /* waterfall cursor */ shape = XCreatePixmapFromBitmapData (display, window, wfCursorShape_bits, wfCursorShape_width, wfCursorShape_height, 1, 0, 1); mask = XCreatePixmapFromBitmapData (display, window, wfCursorMask_bits, wfCursorMask_width, wfCursorMask_height, 1, 0, 1); XParseColor (display, cmap, "red", &cursorShapePix); XParseColor (display, cmap, "black", &cursorMaskPix); /*not the best color*/ wfCursor = XCreatePixmapCursor (display, shape, mask, &cursorShapePix, &cursorMaskPix, wfCursorHotX, wfCursorHotY); XDefineCursor (display, XtWindow (wfDA), wfCursor); cnt = 0; row = 0; /* * create 100 Gray pixels */ XVisualInfo vinfo; int truecolor; find_visual_info(&truecolor, &vinfo); if(!truecolor) { /* 8bit pseudo color (with color map) */ for (i=0; i overlap=2048..256 * 0 -> 2^11 100-> 2^8 */ void Disp::fft_setup(int samps, int speed) { static int fftspeed=0; if(samps > 0) { samples = samps; /* Size of the sample - 512, 1K, 2K */ // fprintf(stderr,"sample size: %d\n",samps); } if (speed >= 0) { if(speed > 100) { fprintf(stderr,"error: speed=%d\n",speed); } else { float expo = 8+(100-speed)/99.0*3; // 8..11 fftspeed = (int)pow(2.0, expo); // fprintf(stderr,"setting fft speed to %d\n",fftspeed); } } ffts = samples/2; /* Number of filters */ deltaf = (float)8000 / samples; /* Hz per ffts sample */ delta = (int)(fc / deltaf - DISPLAY_WIDTH/2); /* Offset in ffts */ cnt = 0; // IMD Calculations upperSig = DISPLAY_WIDTH/2 + (samples / 512); lowerSig = DISPLAY_WIDTH/2 - (samples / 512); upperIMD = DISPLAY_WIDTH/2 + (samples / 512) * 3; lowerIMD = DISPLAY_WIDTH/2 - (samples / 512) * 3; commControl(COMM_FFTCH, COMM_FFTN, samples); commControl(COMM_FFTCH, COMM_FFTOVERLAP, fftspeed); } /* * getFFT handles fft data... * when buffer full, this funciton will calculate IMD, scale the * data and display it. */ void Disp::getFFT(float *val, int len) { //if(len != samples) //{ //fprintf(stderr,"FFT length mismatch\n"); // TODO: remove this error message. it may occure // when changing FFT parameters (really?) // Really! I've never seen it happen //} IMDcalc(val); /* calculate the IMD */ mkPoints(val); /* scale the fft data for display */ drawDisplay(); /* draw the wf or sa */ } /* * IMDcalc - calculates the IMD of the signal * If DCD is on then calculate the IMD of each main sig, and add to * IMDtotal. After IMD_CNT pairs are collected, compute the average * and display. If there are BLOCK_IMD_SIZE blocks in a row without DCD, * put "---" in the widget, so we don't display trash when there is * no signal */ #define IMD_CNT 20 #define BLOCK_IMD_SIZE 10 void Disp::IMDcalc(float *val) { static int cnt; static int block; static float IMDtotal = 0.0; static Widget imdTF = 0; char imdVal[8]; float ratio; if (imdTF == 0) imdTF = pskwids.getImdTF(); /* get the IMD widget id */ //if (pskwids.getDCD() == 1 && val[delta + lowerSig] > 5) // squelch ??? if (pskwids.getDCD() == 1) /* stop IMD if no signal */ { cnt++; block = 0; ratio = val[delta + lowerIMD] / val[delta + lowerSig]; IMDtotal = IMDtotal + 20.0 * log10 (ratio); ratio = val[delta + upperIMD] / val[delta + upperSig]; IMDtotal = IMDtotal + 20.0 * log10 (ratio); if (cnt == IMD_CNT) { sprintf (imdVal, "%2.0f ", IMDtotal/(IMD_CNT * 2)); XmTextFieldSetString (imdTF, imdVal); cnt = 0; IMDtotal = 0.0; } } else { block++; if (block == BLOCK_IMD_SIZE) { XmTextFieldSetString (imdTF, "---"); block = 0; } } } /* * mkPoints method - gets DISPLAY_WIDTH floats from fft buffer * fft data values seems to vary with sample size so, * samples/1024.0 -> 1,2,4 - keeps the fft data values constant * For negative freqs and freqs > 4000, set values to -1. */ void Disp::mkPoints(float *val) { int x, y; float scale = samples / 1024.0; for (y=row, x=0; x samples/2) /* for -freq or freq < 4000 */ values[x][y] = -1; /* mark it for gray */ else { values[x][y] = (short) (val[x+delta] / scale); } } } /* * drawDisplay method - draws display as spectrum or waterfall */ void Disp::drawDisplay() { int x, y; int pixIndex; int r = row + 1; /* For WF - start draw from oldest info */ if (r == DISPLAY_HEIGHT) r = 0; switch (displayFlag) { /* * draw waterfall */ case WF: for (y=0; y < DISPLAY_HEIGHT; y++) { for (x=0; x < DISPLAY_WIDTH; x++) { /* but don't draw over the red line */ if (( x < (DISPLAY_WIDTH - LINE_WIDTH) / 2) || ( x > (DISPLAY_WIDTH + LINE_WIDTH) / 2)) { if (values[x][r] == -1) /* if gray flag */ { pixIndex = 50; } else { pixIndex = (int)(brightness * log10(10 * values[x][r])); if (pixIndex > 99) { pixIndex = 99; } } XSetForeground (display, gc, dispPix[pixIndex].pixel); XDrawPoint (display, window, gc, x, y); } else /* draw the vertical red line */ { XSetForeground (display, gc, redPix.pixel); XDrawLine (display, window, gc, x, 0, x, DISPLAY_HEIGHT); XSetForeground (display, gc, WhitePixel(display, screen)); } } r++; if (r == DISPLAY_HEIGHT) /* recirculate? */ r = 0; } break; /* * draw spectrum analyser */ case SA: XClearArea (display, window, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0); XSetLineAttributes (display, gc, 0, LineSolid, CapRound, JoinRound); XSetForeground (display, gc, WhitePixel(display, screen)); for (x=0; x (DISPLAY_WIDTH + LINE_WIDTH) / 2)) { if (x+delta <= 1 || x+delta > samples/2) /* for -freq or freq < 4000 */ { XSetForeground (display, gc, dispPix[50].pixel); XDrawLine (display, window, gc, x, 0, x, DISPLAY_HEIGHT-1); XSetForeground (display, gc, WhitePixel(display, screen)); } else { XDrawLine (display, window, gc, x, DISPLAY_HEIGHT, x, DISPLAY_HEIGHT - pixIndex); } } else /* Draw the center line */ { XSetForeground (display, gc, redPix.pixel); XDrawLine (display, window, gc, x, 0, x, DISPLAY_HEIGHT); XSetForeground (display, gc, WhitePixel(display, screen)); } } break; default: fprintf (stderr,"twpskDisplay.drawDisplay - Broken WF/SA value\n"); break; } /* Increment row and make it circulate */ row++; if (row == DISPLAY_HEIGHT) row = 0; } /* * offset method - sets delta to starting index value of values array */ void Disp::offset(float freq) { fc = freq; delta = (int)(fc / deltaf - DISPLAY_WIDTH/2); } /* * new_fc method - given the cursor's x position in the display, * returns the frequency */ float Disp::new_fc (int x) { int newx; float newfreq; if ((newx = find_center(x)) != -1) /* find the center */ x = newx; newfreq = fc + deltaf * (x-DISPLAY_WIDTH/2); if(newfreq < 0.0 ) return 0.0; else if (newfreq > 4000.0) return 4000.0; else return newfreq; } /* * find_center method - trys to find the center of the signal that * was clicked. Returns -1 if it can't figure it out. * Time for the squelch again???? */ #define SENSITIVITY 0.50 int Disp::find_center (int x) { int r, i, start, stop; int high = 0, low = 0; int width = (int)(31.25 / deltaf); int center, sigWidth; int max = 0; r = row - 1; if (r < 0) /* work with the latest row */ row = DISPLAY_HEIGHT - 1; start = (int)((float)x-1.5*width); stop = (int)((float)x+1.5*width); /* find maximum */ for (i=start; i!=stop; i++) { if (i<0) /* it could be neg if near left edge */ continue; if (values[i][r] > max) max = values[i][r]; /* find the max value in the range */ } /* find the edges */ /* first the low edge */ for (i=start; istart; i--) { if (values[i][r] < SENSITIVITY * max) continue; else { /* found the high edge */ high = i; break; } } sigWidth = high - low; center = (high+low) / 2; //printf ("%d\n ", max); if (sigWidth > 0.5 * width && sigWidth < 1.5 * width) // && max >= 3) { //printf ("calc\n"); return center; } else { //printf ("pointer\n"); return -1; } }