Page 1 of 2 12 LastLast
Results 1 to 20 of 24

Thread: Creating a waterfall plot with the spectrogram

  1. #1
    Join Date
    Jul 2010
    Posts
    15
    Thanks
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Creating a waterfall plot with the spectrogram

    Hi,

    I need to create a waterfall plot where data is represented incrementally by horizontal 'bars' moving down the plot and the intensity represented by varying colors across each horizontal bar. It looks like the sprectrogram would be perfect for this application, and I am able to create and run the example without any problems, but I do not understand how I would modify it to get the horizontal bars moving down the plot over time that I need to show. I am totally new to using raster plots and do not have a good enough understanding of how the data is handled to get my data displaying properly. If anybody has created an app with similiar requirements using the spectrogram or any other widget I would appreciate any advice and/or a piece of sample code to get me started. Thank you in advance...

    tim

  2. #2
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    I recommend to start with displaying your data at a specific moment. Then you will have understood how the spectrogram item works and how you can connect your raster data to it. Then have a look at the cpuplot example, that shows how to implement a moving time axis.

    Rendering raster data is much more expensive than vector data ( like curves ). So don't expect a refresh rate of 25Hz. Consider using Qwt from SVN trunk, because in this version the spectrogram is able to render the image multithreaded with a linear effect on the performance on multicore systems.

    Uwe

  3. #3
    Join Date
    Jul 2010
    Location
    Taipei, Taiwan
    Posts
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    Sounds like something I did recently. In my case re-rendering the entire spectrogram was too slow. So I added a caching layer to get the speed I needed.

    Qt Code:
    1. class Waterfall : public QwtPlotSpectrogram
    2. {
    3. public:
    4. Waterfall(const QString &title = QString::null) :
    5. {
    6. }
    7.  
    8. virtual ~Waterfall()
    9. {
    10. }
    11.  
    12. protected:
    13. virtual QImage renderImage(const QwtScaleMap &xMap,
    14. const QwtScaleMap &yMap,
    15. const QRectF &rect) const
    16. {
    17. QRect rect = transform(xMap, yMap, area);
    18.  
    19. // I don't know why this adjustment is necessary, but
    20. // without it qwt plots a different area than we cache
    21. rect.adjust(-2, -2, -3, -3);
    22. QwtScaleMap xxMap = xMap;
    23. QwtScaleMap yyMap = yMap;
    24.  
    25. const QSize res(QwtPlotSpectrogram::rasterHint(area));
    26. if ( res.isValid() )
    27. {
    28. rect.setSize(rect.size().boundedTo(res));
    29. }
    30.  
    31. int px1 = rect.x();
    32. int px2 = rect.x() + rect.width();
    33. if ( xMap.p1() > xMap.p2() )
    34. qSwap(px1, px2);
    35.  
    36. double sx1 = area.x();
    37. double sx2 = area.x() + area.width();
    38. if ( xMap.s1() > xMap.s2() )
    39. qSwap(sx1, sx2);
    40.  
    41. int py1 = rect.y();
    42. int py2 = rect.y() + rect.height();
    43. if ( yMap.p1() > yMap.p2() )
    44. qSwap(py1, py2);
    45.  
    46. double sy1 = area.y();
    47. double sy2 = area.y() + area.height();
    48. if ( yMap.s1() > yMap.s2() )
    49. qSwap(sy1, sy2);
    50.  
    51. xxMap.setPaintInterval(px1, px2);
    52. xxMap.setScaleInterval(sx1, sx2);
    53. yyMap.setPaintInterval(py1, py2);
    54. yyMap.setScaleInterval(sy1, sy2);
    55.  
    56. // Redraw everything if the scale or size changed
    57. if (cachedImage_.isNull()
    58. || xMap.pDist() != cachedXMap_.pDist()
    59. || xMap.sDist() != cachedXMap_.sDist()
    60. || yMap.pDist() != cachedYMap_.pDist()
    61. || yMap.sDist() != cachedYMap_.sDist()
    62. || area.width() != cachedArea_.width()
    63. || area.height() != cachedArea_.height())
    64. {
    65. cachedImage_ = QwtPlotSpectrogram::renderImage(xMap, yMap, area);
    66. cachedXShift_ = 0.0;
    67. cachedYShift_ = 0.0;
    68. }
    69. else
    70. {
    71. QRect xRect, yRect;
    72.  
    73. // Shift the cached image to compensate for the area change
    74. // and calculate the x and y boxes to redraw
    75. //
    76. // 0
    77. // +------------------------+ ---
    78. // | -yRect | | yShift (negative)
    79. // +---+----------------+---+ ---
    80. // | - | | + | |
    81. // | x | | x | |
    82. // | R | Cached/shifted | R | | height - abs(yShift)
    83. // | e | image | e | |
    84. // | c | | c | |
    85. // | t | | t | |
    86. // | | | | |
    87. // +---+----------------+---+ ---
    88. // | +yRect | | yShift (positive)
    89. // +------------------------+ ---
    90. //
    91. // |---| |---|
    92. // xShift (negative) xShift (positive)
    93.  
    94. double xScale = xxMap.pDist() / xxMap.sDist();
    95. double yScale = yyMap.pDist() / yyMap.sDist();
    96. double xShift = -(cachedArea_.left() - area.left()) * xScale + cachedXShift_;
    97. double yShift = (cachedArea_.top() - area.top()) * yScale + cachedYShift_;
    98. cachedXShift_ = xShift - ceil(xShift);
    99. cachedYShift_ = yShift - ceil(yShift);
    100. xShift = ceil(xShift);
    101. yShift = ceil(yShift);
    102.  
    103. // Just redraw everything if the shift size is greater than the image
    104. if (abs(xShift) > rect.width() || abs(yShift) > rect.height())
    105. {
    106. cachedImage_ = QwtPlotSpectrogram::renderImage(xMap, yMap, area);
    107. cachedXShift_ = 0.0;
    108. cachedYShift_ = 0.0;
    109. }
    110. else
    111. {
    112. if (yShift < 0)
    113. {
    114. yRect.setCoords(rect.left(), rect.top(), rect.right(), rect.top() - yShift);
    115.  
    116. if (xShift < 0)
    117. {
    118. xRect.setCoords(rect.left(), yRect.bottom(), rect.left() - xShift, rect.bottom());
    119. }
    120. else if (xShift > 0)
    121. {
    122. xRect.setCoords(rect.right() - xShift, yRect.bottom(), rect.right(), rect.bottom());
    123. }
    124. }
    125. else if (yShift > 0)
    126. {
    127. yRect.setCoords(rect.left(), rect.bottom() - yShift, rect.right(), rect.bottom());
    128. if (xShift < 0)
    129. {
    130. xRect.setCoords(rect.left(), rect.top(), rect.left() - xShift, yRect.top());
    131. }
    132. else if (xShift > 0)
    133. {
    134. xRect.setCoords(rect.right() - xShift, rect.top(), rect.right(), yRect.top());
    135. }
    136. }
    137. else
    138. {
    139. if (xShift < 0)
    140. {
    141. xRect.setCoords(rect.left(), rect.top(), rect.left() - xShift, rect.bottom());
    142. }
    143. else if (xShift > 0)
    144. {
    145. xRect.setCoords(rect.right() - xShift, rect.top(), rect.right(), rect.bottom());
    146. }
    147. }
    148.  
    149. // Without this re-adjustment the scale is off by 1 pixel in each direction
    150. rect.adjust(0, 0, 1, 1);
    151. if (!yRect.isNull() || !xRect.isNull())
    152. {
    153. QImage newImage(rect.size(), cachedImage_.format());
    154. QPainter painter(&newImage);
    155. newImage.fill(0);
    156.  
    157. if (!yRect.isNull())
    158. {
    159. QImage Image;
    160. QRectF sArea = invTransform(xxMap, yyMap, yRect);
    161. Image = QwtPlotSpectrogram::renderImage(xxMap, yyMap, sArea);
    162. painter.drawImage(yRect, Image);
    163. }
    164.  
    165. if (!xRect.isNull())
    166. {
    167. QImage image;
    168. QRectF sArea = invTransform(xxMap, yyMap, xRect);
    169. image = QwtPlotSpectrogram::renderImage(xxMap, yyMap, sArea);
    170. painter.drawImage(xRect, image);
    171. }
    172.  
    173. painter.drawImage(rect.translated(-xShift, -yShift), cachedImage_, rect);
    174. cachedImage_ = newImage;
    175. }
    176. }
    177. }
    178.  
    179. cachedXMap_ = xMap;
    180. cachedYMap_ = yMap;
    181. cachedArea_ = area;
    182. cachedRect_ = rect;
    183.  
    184. return cachedImage_;
    185. }
    186.  
    187. private:
    188. mutable QImage cachedImage_;
    189. mutable QwtScaleMap cachedXMap_;
    190. mutable QwtScaleMap cachedYMap_;
    191. mutable QRectF cachedArea_;
    192. mutable QRect cachedRect_;
    193. mutable double cachedXShift_;
    194. mutable double cachedYShift_;
    195. };
    To copy to clipboard, switch view to plain text mode 

    If you need it pixel perfect you might have to fiddle with it a bit. I think it still had an off by one problem in the cached image.

  4. #4
    Join Date
    Jul 2010
    Posts
    15
    Thanks
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Creating a waterfall plot with the spectrogram

    Hi Uwe,

    Thank you for such a quick reply. Trying to work with data at one instance in time is what I have been trying to do using the qwt example and some of the other examples I have found here but I think.....no, I'm sure of it....that I just don't understand how the raster is used at all. I have set up a spectrogram plot with an overall area of for example, 500 X 500 and then try to put a horizontal 'band' of say 50 pixels high by 500 pixels wide in the plot with the color intensity based on my data (contained in an array of doubles which for simplicity right now all have the same value...) and using the next neighbor sampling method described in some of the other posts. So what I'm expecting/looking for is a 500 x 50 area of some solid color based on the value of my data, and the other 500x450 area of the plot remaining the same original background color since only one rectangular subset region has been created. The actual size of the 'bands' will be some constant area per data sample received. But what I continually get is a 'rainbow' of the colors displayed in the color bar filling the entire plot area of 500x500. Everything else looks correct in the plot...scale values, colorbar legend etc...I just don't know how to set up and control how my data is plotted using the raster object. Fortunately the data rate is very slow so I don't think the refresh rate will be an issue, at least not right now. I downloaded the latest from the svn trunk per your suggestion and will definately need the help provided in those examples when I get to that point. Thanks again.

    tim
    tim

  5. #5
    Join Date
    Jul 2010
    Posts
    15
    Thanks
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Creating a waterfall plot with the spectrogram

    Thank you xicoy, I'll give this a try...

    It looks like you created this without using the raster item at all on your spectrogram? Just created the image and rendered it...is that correct or is this just a small snippet of the code? What does the transform method used on line 18 do....I didn't see it in the Qt or Qwt docs so I'm guessing its homegrown?

    tim

  6. #6
    Join Date
    Jul 2010
    Location
    Taipei, Taiwan
    Posts
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    transform is part of QwtPlotItem. It transforms the data coordinates to paint coordinates:

    I still had to create and attach my custom raster data class of course. I made this class so I didn't have recalculate every point just because everything shifted down by one or two pixels.
    You may have no problems replotting everything on each update depending on how dense you pack your data. In may case I would have had to reprocess nearly 1GB of data each second after my buffer had filled. I probably should have built my raster data class to create ripmaps, but this image caching worked well enough.
    The job of this class was to shift the position of the cached image if needed, and call the default spectrogram draw functions but only for the slivers of data on the sides that really needed to be calculated. So it could potentially help improve your refresh rates later down the line.

    As Uwe said, you really do need to get it working for a particular point in time first. So just forget about this class for now
    Maybe you don't have your value() function in your raster data class set up right. You can write some quick tests for it.
    Is the data getting stretched across the entire plot? Then maybe you need to add a call to setBoundingRect.

  7. #7
    Join Date
    Jul 2010
    Posts
    15
    Thanks
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Creating a waterfall plot with the spectrogram

    xilcoy,

    Thank you again for your replies. I'm trying to get just a particuliar point in time done to start as you and Uwe suggest, but it seems no matter what I do with or without using a bounding rect I get the entire plot filled with bars spanning the spectrum of my colormap, instead of just the one horizontal 'bar' filling the coordinates of the bounding rect I am trying to fill in. The value function I am using is from one of the examples here on this forum (I think one of Uwe's) and uses uses a simple rounding for the re-sampling. The data fills the entire plot....seems to ignore the bounding rect I define. Maybe I'm defining it too late in the code after the raster area is already created...I'm not sure...

    tim

  8. #8
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    The spectrogram item requests the bounding rectangle from the raster data object each time it is repainted. Did you forget to copy the assigned bounding rectangle in your implementation of YourRasterData::copy() ?

    Uwe

    PS: All these nasty copy/clone methods are already removed in the development branch.

  9. #9
    Join Date
    Jul 2010
    Posts
    15
    Thanks
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Creating a waterfall plot with the spectrogram

    Hi Uwe,Xilcoy,

    I managed to get just a solid horizontal bar displayed on the spectrogram and have control over where it is drawn in the vertical direction (for step 1), but I still don't think I understand how the raster/ spectrogram plot works, and the way I'm drawing the bar is extremely slow....it takes in the order of 23 seconds to draw a screen with only one bar drawn (no animation even attempted yet).

    I am not doing anything with the copy method...should I be? Right now it is left as it was in the example I started with and just returns a pointer to the Spectrogram data object. I'm setting the bounding rect in the QwtRasterData method. To draw the horizontal line I have just been working with how the points are written to the value method. Admittedly this spectrogram plotting has me confused....Uwe, have you ever considered writing a tutorial book on Qwt? I'd buy it.

    tim

  10. #10
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    I managed to get just a solid horizontal bar displayed on the spectrogram and have control over where it is drawn in the vertical direction (for step 1), but I still don't think I understand how the raster/ spectrogram plot works, and the way I'm drawing the bar is extremely slow....it takes in the order of 23 seconds to draw a screen with only one bar drawn (no animation even attempted yet).
    Well, when the spectrogram item is requested ( by the plot widget ) to draw itself, it iterates over all pixels of the target device ( the widget ). For each pixel it calculates the corresponding scale coordinates (x/y) and requests a value ( QwtRasterData::value() ) for this position. Then it maps the value into a color ( using a QwtColorMap ) and puts it to a QImage. When all pixels are collected the image is painted to the target device.

    So your implementation of YourRasterData::value() needs to be pretty fast as it is called often ( width * height ! ). If you needs 23 seconds you are doing something very, very expensive in YourRasterData::value() ( or in your color map, when you have implemented your own ).

    Uwe, have you ever considered writing a tutorial book on Qwt? I'd buy it.
    Writing a tutorial is one of my priorities, when Qwt 6.0.0 has been done.

    Uwe

  11. #11
    Join Date
    Jan 2010
    Location
    Poland Warsaw
    Posts
    25
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    Hello!

    I have wrote simple program which captures sound samples from sound card in one thread and writes them to circular buffer. Another thread takes samples from buffer and process them (calculate FFT). The output (double*) is added in beggining of QList<double* > (last item is removed) and than signal is emmited to main thread that new data is ready to display.
    In main thread i have two plots: simple qwtplot which plots current spectrum and second qwtspectrogram which plots spectrogram from QList.
    The problem is that it works very slow - about 2 FPS. When I remove spectrogram from GUI and leave only qwtplot I have about 22 FPS.
    So my question is: how to speed up generating/drawing spectrogram ?

    Misza

  12. #12
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    Quote Originally Posted by m15ch4 View Post
    So my question is: how to speed up generating/drawing spectrogram ?
    Implement YourRasterData::rasterHint().

    Uwe

  13. #13
    Join Date
    Jul 2010
    Posts
    15
    Thanks
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Creating a waterfall plot with the spectrogram

    I have gotten a waterfall plot working using the spectrogram widget thanks to a lot of help from Uwe and Xicoy above.... However, until now I have been using all positive values.....fe...x axis: 0-4096 samples, y axis: 0-250 ms, sample values between 0-40, colorbar scale 0-40. Now I need to modify it so that the x axis has +/- values (-2048 to 2048 for example), and data values from -200 to 200. Can that be done with this spectrogram widget? My attempts have yielded a colorbar which is still fine for positive values, but all negative values just remain at the color used for 0. And any negative data values default to the color for zero on the colorbar. Also values only plot on the positive side of the x axis, negative data values seem to be ignored and not plotted at all. I've tried adjusting range and axis values etc...but so far have had no success. Any thoughts or advice would be appreciated.
    I am using qwt 6.0.

    Thank you,
    tim

  14. #14
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    There is no limitation for ranges - the problem is on your side. Maybe the bounding rectangle of your data is wrong.

    Uwe

  15. The following user says thank you to Uwe for this useful post:

    twells5 (17th December 2010)

  16. #15
    Join Date
    Jul 2010
    Posts
    15
    Thanks
    2
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11

    Default Re: Creating a waterfall plot with the spectrogram

    Can the labels on the x-axis scale major ticks be changed to a different string other than the bounding rect interval values it defaults to? My data is displayed fine now thanks to your tip above, but I would like the major tick labels to show something like ...50Mhz 100Mhz 150Mhz....? I don't want to change anything about how the spectrogram data is plotted, just the label strings displayed.

    tim

  17. #16
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    Can the labels on the x-axis scale major ticks be changed to a different string other than the bounding rect interval values it defaults to?
    Derive from QwtScaleDraw and implement "QwtText YourScaleDraw::label((double) const".
    F.e. see the cpuplot example.

    Uwe

  18. The following user says thank you to Uwe for this useful post:

    twells5 (20th December 2010)

  19. #17
    Join Date
    Apr 2010
    Posts
    8
    Thanks
    2
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    Quote Originally Posted by xilcoy View Post
    Sounds like something I did recently. In my case re-rendering the entire spectrogram was too slow. So I added a caching layer to get the speed I needed.

    [..]

    If you need it pixel perfect you might have to fiddle with it a bit. I think it still had an off by one problem in the cached image.
    Hi xilcoy,
    I've a couple of questions about your class Waterfall and in particular about renderImage method.
    First of all i d'dn't understand why the third parameter is const QRectF; shouldn't it be const QwtDoubleRect instead?
    Then, the other thing that is not clear to me is if the area must be different each time or if it's always the same. In this case, in fact, xShift and yShift are 0. If area should change, how can you change it?

    Thx

  20. #18
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Creating a waterfall plot with the spectrogram

    Quote Originally Posted by stilgar View Post
    Hi xilcoy,
    First of all i d'dn't understand why the third parameter is const QRectF; shouldn't it be const QwtDoubleRect instead?
    A QwtDoubleRect is a QRectF. From the Qwt docs:

    Qt Code:
    1. typedef QPointF QwtDoublePoint
    2. typedef QSizeF QwtDoubleSize
    3. typedef QRectF QwtDoubleRect
    To copy to clipboard, switch view to plain text mode 

  21. #19
    Join Date
    Apr 2010
    Posts
    8
    Thanks
    2
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Creating a waterfall plot with the spectrogram

    Quote Originally Posted by d_stranz View Post
    A QwtDoubleRect is a QRectF. From the Qwt docs:

    Qt Code:
    1. typedef QPointF QwtDoublePoint
    2. typedef QSizeF QwtDoubleSize
    3. typedef QRectF QwtDoubleRect
    To copy to clipboard, switch view to plain text mode 

    Yes, it is true, but my question was not exactly what I meant. The real question was: the third parameter shouldn't be area instead of rect?

    Thx

  22. #20
    Join Date
    Aug 2011
    Posts
    1
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Creating a waterfall plot with the spectrogram

    I'm using Qt-4.7.2 and qwt-6.0.0 and I need a waterfall plot.
    If you have a implementation and you want share it, I would be pleased and it would save me much time.

    Many thanks.

Similar Threads

  1. Replies: 0
    Last Post: 2nd May 2010, 03:55
  2. Replies: 2
    Last Post: 12th October 2009, 22:17
  3. x-axis for waterfall plot
    By janK in forum Qwt
    Replies: 8
    Last Post: 29th July 2009, 16:00
  4. Spectrogram Plot Hints
    By shargath in forum Qwt
    Replies: 2
    Last Post: 25th February 2009, 12:11
  5. waterfall display
    By jmsbc in forum Qt Programming
    Replies: 7
    Last Post: 15th November 2008, 09:29

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.