Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Go Back  Xtreme Visual Basic Talk > > > Displaying data graphically (Ultrasonic radar)


Reply
 
Thread Tools Display Modes
  #1  
Old 03-19-2012, 09:35 AM
Alex83 Alex83 is offline
Newcomer
 
Join Date: Mar 2012
Posts: 5
Default Displaying data graphically (Ultrasonic radar)


Hi all, a beginner here
I'm working on an ultrasonic sonar and I would like to use the computer to display a standard green radar system detecting the objects.
I'm using a micro controller with the software written in C to send out the sensor's angle and position and it works fine.
On the computer side, I'm using Visual Basic 2010 to read the COM port which then contains the sensor's data in numbers (sensor's angle and distance between sensor and object) which is a constant stream. On the debug screen (Textbox) I can already see all the data coming through.
Now my question is: How do I convert those numbers into graphics?

More explanations about these numbers I want to convert:
I have a step motor to rotate the ultrasonic sensor, and it works that way:
The motor advances 1 step forward, then measure distance between sensor and object, then send these 2 values out.
So, if you see the attached file, those numbers on the text box mean:
000032 -> 000: step number & 032: 32cm between sensor and object
001031 -> 001: step number & 031: 31cm between sensor and object
002032 -> 002: step number & 032: 32cm between sensor and object
003007 -> 003: step number & 007: 7cm between sensor and object
and so on...

So those are the numbers I wish to convert into graphics. The scan line would need to rotate clockwise at same speed of the step motor. And then I would like to display dots when an object is detected.

Thanks in advance!

My O.S.: Windows Vista 32 bits.
Softwares: MPLAB IDE v8.53 (microcontroller part) and Microsoft Visual Basic 2010 Express (computer part)
Attached Images
File Type: jpg print.jpg (123.2 KB, 40 views)
Reply With Quote
  #2  
Old 03-22-2012, 03:14 PM
passel's Avatar
passelDisplaying data graphically (Ultrasonic radar) passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,026
Default

How do you decide when to erase the dot?
Would the angle always be relative to the "nose" of the platform?
Would the platform's heading always be "up", so the world spins around it when it turns, or would the world stay fixed, and the heading of the platform change (rotating around the center of your graph, with the sensor rotating in addition to the the platform heading change).
Or to do you plan to do a full sweep, then move, then do another full sweep, then move, etc.?
If you store the angles and distances in an array for a full sweep, then you could simply display one sweep, while collecting data for the next, once that sweep is complete, erase, and display the full new sweep, and collect data for the next one.

A lot of options, depending on what you want.
You could use Sin and Cos of the angle and multiply by the distance, to draw the dot where you want.

Another option, perhaps simpler, is simply to translate the 0,0 coordinate to the center of the grid, rotateTransform by the angle, and plot the distance along one axis (-y) for instance.

A lot of rotation and placing objects in this thread recently. Perhaps you can pull some ideas from it.
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.
Reply With Quote
  #3  
Old 03-26-2012, 04:15 AM
Alex83 Alex83 is offline
Newcomer
 
Join Date: Mar 2012
Posts: 5
Default

passel: Thanks for your reply!
I have done some progress since my first post.
I am using rotateTransform to rotate the scan line which is ''attached'' at the center of my background image from the picture box. See code below:
Code:
e.Graphics.RotateTransform(angle * 360 / 250, Drawing2D.MatrixOrder.Append)
e.Graphics.TranslateTransform(Me.PictureBox1.Width / 2, Me.PictureBox1.Height / 2, Drawing2D.MatrixOrder.Append)
e.Graphics.DrawLine(New Pen(Color.Green, 3), 0, 0, 200, 200) 'x1 y1 x2 y2
And I haven't decided when to erase the dots yet because at the moment I'm not displaying them.

But now I have another problem. The Visual Basic program is not fast enough to display the real-time data. My motor takes about 4.5 seconds to complete a full rotation (which is 250 steps) but in my Visual Basic display it takes about 10 seconds to complete the full rotation.
I have already changed the Baud rate from my microcontroller but it doesn't improve a lot.
I am planning to do the sweep and display the data at same time (is that possible?) Because I aiming to build a real-time data 'radar'.

And this is how I am receiving the data, splitting it into angle and distance, and plotting the angle (snapshots):
Code:
'from mySerialPort subroutine
inputData = mySerialPort.ReadChar

'from data handling subroutine
Dim data As String
        data = Chr(inputData)
        If data = " " Then
            Temp = ""

        Else
            Temp &= data
        End If

        If Temp.Length = 6 Then
            angle = Val(Mid(Temp, 1, 3))
            radius = Val(Mid(Temp, 4, 3))
            PictureBox1.Invalidate()

        End If

'from PictureBox1_Paint subroutine
 e.Graphics.RotateTransform(angle * 360 / 250, Drawing2D.MatrixOrder.Append)
Reply With Quote
  #4  
Old 03-26-2012, 04:19 PM
passel's Avatar
passelDisplaying data graphically (Ultrasonic radar) passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,026
Default

The PictureBox1.Invalidate will cause the control to "clear" the picturebox, and trigger the Paint Event so you can draw on this cleared area.
So, right now you're drawing the one line, but you will need to keep track of your blips, so you can draw them as well, since they will all be cleaned away for you when the paint event is triggered.
One thing you normally have to do in "real time" situations is separate your tasks so one doesn't impact the other.
Your code now looks to be a single thread, with a cycle of sitting on a read from the serial port, processing the data, triggering the drawing, and doing the drawing.
If the drawing is slow, then you can't be processing data while the drawing is going on, so serial data may be building up in the device buffer.
Eventually you may be dropping some serial data.
If you read and process the data in one thread, and have a timer in the GUI trigger the Paint event, you can disconnect the two processes so one doesn't impact the other.
When you do the paint, it will process all the points (in your 250 element array, lets say) and draw the sweep at the current angle.
If the drawing is as slow as you say, then you would be updating the display about every other Radar position, so will be a little coarser looking, but will keep up with the serial port since you're not trying to display every single sweep, and you would still display all the data for all 250 points accumlated, so you will still see everything important.
You can then focus on seeing if there are ways to speed up the rendering.

I haven't used the serial port in .Net too much at this point, but I have had to updated graphical instruments from external data coming in over Ethernet.
In that case, I am receiving data at 50hz on one thread listening on the Ethernet port, but have a timer in the GUI thread updating the instruments at about 20hz.
You don't have to draw every time you get data, as long as you accumlate and draw all the data received at a reasonable rate.

Is the green grid you attached in your first post loaded into the picturebox as an .Image or .BackgroundImage, or do you draw it on the fly?
If you have it, or anything loaded in the .BackgroundImage, remove it.
Loading something in the BackgroundImage will usually more than double the time it takes to refresh the picturebox, so don't use it unless you have a relatively static image you are using the picturebox for.

There is a BufferedGraphics Class that may improve on the speed of update, by doing your drawing in the memory buffer and then Rendering it to the picturebox. In that case, you may not actually want to use a picturebox, but perhaps a panel as you wouldn't need the defaulted doublebuffering or image buffers built into the picturebox.
But, you may also be able to keep the picturebox and set a clipping area in your Invalidate call, to limit the area being updated, which could speed things up without changing your current controls.
I guess I'll have to get back to you on any details.

Another thing, and it probably doesn't make a difference speed wise, but I don't see a need for overriding the default order of matrix operations by using .Append.
Using MatrixOrder.Append essentially says do this transform before all the transforms I've done up to this point.
So,
e.Graphics.RotateTransform(angle * 360 / 250, Drawing2D.MatrixOrder.Append)
e.Graphics.TranslateTransform(Me.PictureBox1.Width / 2, Me.PictureBox1.Height / 2, Drawing2D.MatrixOrder.Append)

says, do the TranslateTransform before the RotateTransform.
I think it is clearer to just do them in that order to begin with, and not have to use .Append

e.Graphics.TranslateTransform(Me.PictureBox1.Width / 2, Me.PictureBox1.Height / 2)
e.Graphics.RotateTransform(angle * 360 / 250)
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.

Last edited by passel; 03-26-2012 at 05:52 PM.
Reply With Quote
  #5  
Old 03-27-2012, 07:21 PM
passel's Avatar
passelDisplaying data graphically (Ultrasonic radar) passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,026
Default

Well, I assume you have to do at least 4800 baud as a minimum to meet the 250 sensor reports over a 4.5 second period, which works out to between 55 to 56 reports per second (that is about 18 ms between reports).
Assuming you send one report per step, a slower baud rate shouldn't help. It should actually make things worse as you have a serial process so the longer the com port read takes, the less time you have to draw between reads.
At 4800 baud, the serial port read will take almost the whole 18 ms to transfer the 7 characters over the connection, leaving almost no time for drawing.
However, if you used 115K baud, the serial transfer would take less than 1 ms leaving 17 milliseconds available for drawing (assuming that you don't transfer more often just because the baud rate is higher, i.e that you transfer the same 55 to 56 reports per second regardless of baud rate).

But, to examine speeding up the drawing process, I put together an example, which is kind of the final result of a series of testing different methods. It is not exhaustive at this point, I still need to do a more definitive testing of utilizing a user defined control, along the lines of what AtmaWeapon did in his "Bullets" post (ParticleDemo.zip).

The main speed up comes from not using the Picturebox .BackgroundImage or .Image properties. Using a BufferedGraphics object to hold the background image and rendering it first in the Paint event (this saves a few percent over having it in the .image).
Second major speed up comes from only invalidating the portion of the display (quadrant) the the sweep is currently in. Thus only about a quarter of the page is involved with the drawing process and this more than doubles the update rate.
Third speed up comes from also limiting the points drawn to just doing enough to cover the point in that quadrant.

The test uses DoEvents, for reasons explained in the code. You would not normally use DoEvents, but for the method of timing the graphics, and optimizations, was necessary.
The code does a tight, 250 step sweep loop, so isn't regulated by a timer or other things so should over several runs give an idea of the average minimum drawing time a given update takes. Depending on your baud rate, and how much time you have left for drawing, you can get an idea of whether you can expect to be able to draw every sweep position (manage to reliably do 56 updates per second with time to spare), or have to decouple and allow the drawing to update at whatever rate it can, just drawing all the collected data so nothing is missing per given frame.
So, if we could update and display the data in less than 10 milliseconds, raw speed, we should be in good shape, even if you ran as slow as 19200 baud.

On my machine, the attached example took about 500 milliseconds to complete a sweep, which, since we're doing 250 updates, works out to 2ms per update, or a frame rate of around 500hz.
So, if you used your existing code of reading from the port, then calling the update to refresh the drawing, it should be done drawing well in time for you to collect your next point.
The total time (in milliseconds) for the sweep is displayed in on the form's title bar.
Really put a lot of comments in the code, so will let that "speak for itself" for now.
Attached Images
File Type: jpg sweepDisplay2.JPG (48.3 KB, 28 views)
Attached Files
File Type: zip RadarSweepTimeTest.zip (425.7 KB, 70 views)
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.

Last edited by passel; 03-27-2012 at 07:30 PM.
Reply With Quote
  #6  
Old 03-28-2012, 10:50 AM
Alex83 Alex83 is offline
Newcomer
 
Join Date: Mar 2012
Posts: 5
Default

passel: Firstly let me start by saying a big thank you for taking your time to write two replies with so much valuable information!

I have done some research about the double buffering mode and how to improve the speed. I can't simply use the picturebox to generate the graphics object and draw directly to the picturebox as every draw command forced the picturebox to re-render itself, causing a terrible slowdown and image jitter. Therefore, using a bitmap to buffer the image greatly reduces the rendering time.

And you are also correct about if I reduce the baud rate the drawing will be even slower (I learned that during my trials)

Anyway, I have looked at your code and tried to modify mine but it is still slow. Do you mind having a look at my code and see if I am missing something? I'd really appreciate that!

Code:
Imports System.IO.Ports
Imports System.Text
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms

Public Class Form1
    Inherits System.Windows.Forms.Form
    Private WithEvents mySerialPort As New SerialPort
    Friend WithEvents btnMap As System.Windows.Forms.Button
    Dim inputData As String = ""
    Public Event DataReceived As IO.Ports.SerialDataReceivedEventHandler
    Dim Temp As String = ""
    Dim angle As Integer
    Dim radius As Integer
    Private Mypen As Pen = New Pen(Color.Green, 3)
    Private x As Integer
    Private y As Integer
    Private MyImage As Bitmap
    Private MyGraphic As Graphics
    Private WithEvents Timer1 As Windows.Forms.Timer

    Private Sub Form1_Load(ByVal sender As System.Object, _
                   ByVal e As System.EventArgs) Handles MyBase.Load

        x = PictureBox1.Width / 2
        y = PictureBox1.Height / 2

        Timer1 = New Windows.Forms.Timer
        MyImage = New Bitmap(PictureBox1.Width, PictureBox1.Height)
        MyGraphic = Graphics.FromImage(MyImage)
        Timer1.Interval = 10
        Timer1.Enabled = True

        'Set values for some properties
        With mySerialPort
            .PortName = "COM16"
            .BaudRate = 38400
            .Parity = IO.Ports.Parity.None
            .DataBits = 8
            .StopBits = IO.Ports.StopBits.One
            .Handshake = IO.Ports.Handshake.None
            .RtsEnable = True
        End With

        ' Open the Serial Port
        mySerialPort.Open()

    End Sub


    ' Receive data from the Serial Port
    Private Sub mySerialPort_DataReceived(ByVal sender As System.Object, _
                      ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
                      Handles mySerialPort.DataReceived
        inputData = mySerialPort.ReadChar 

        Me.Invoke(New EventHandler(AddressOf DoUpdate))
    End Sub

   
    Public Sub DoUpdate()
       
        Dim data As String
        data = Chr(inputData)
        If data = " " Then
            Temp = ""

        Else
            Temp &= data
        End If

        If Temp.Length = 6 Then
            angle = Val(Mid(Temp, 1, 3))
            radius = Val(Mid(Temp, 4, 3))

            'Display real-time data in textbox
            TextBox1.Text = Temp

        End If

    End Sub

 Private Sub Form1_FormClosed(ByVal sender As System.Object, _
                   ByVal e As System.Windows.Forms.FormClosedEventArgs) _
                   Handles MyBase.FormClosed
        ' Close the Serial Port
        mySerialPort.Close()
    End Sub

 Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint

        If angle = 0 Then e.Graphics.ResetTransform()

        e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
        e.Graphics.TranslateTransform(x, y)        
        e.Graphics.RotateTransform(angle * 360 / 250)
        e.Graphics.DrawLine(Mypen, 0, 0, 150, 150) 'x1 y1 x2 y2

    End Sub

    Private Sub Timer1_Tick1(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        MyGraphic.Clear(Color.White)
        PictureBox1.Image = MyImage

    End Sub

End Class
Thanks in advance!
Reply With Quote
  #7  
Old 03-28-2012, 06:34 PM
passel's Avatar
passelDisplaying data graphically (Ultrasonic radar) passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,026
Default

Quote:
Originally Posted by Alex83 View Post
...I have done some research about the double buffering mode and how to improve the speed. I can't simply use the picturebox to generate the graphics object and draw directly to the picturebox as every draw command forced the picturebox to re-render itself, causing a terrible slowdown and image jitter. Therefore, using a bitmap to buffer the image greatly reduces the rendering time.
I don't think that is true in this case (you're just not doing it right).
There are reasons to use a persistent backbuffer, but for "simple" drawing a memory buffer will always be slower than drawing directly to the picturebox.
The picturebox is doubled buffered by default, not to be confused with having a persistent "Autoredraw" backbuffer.
What the double buffer means in this case, is that in the paint event the graphics object is referencing a memory buffer and all your drawing during the paint event is happening there.
When you exit the paint event, the screen is updated in one shot from that memory buffer.
If you've drawn everything correctly in the paint event, there should be no jitter, flashing, etc. since the screen is updated as quickly as can be done.
Apparently, that transfer is quite efficient.
The only way I've been able to updated from a memory buffer to the screen as quickly is by using the BufferedGraphics class.
But the BufferedGraphics object is built for that purpose, and the focus on fast rendering I think limits the capabilities of the object for advanced drawing techniques.
I would have to do more extensive testing to be sure, but I had working code doing various transforms, such as your radar sweep, and I can use the code on a Graphics object for the form, a panel, a label, a picturebox, etc...
But if I try to pass a BufferedGraphics Graphics object to the code, it doesn't render properly.
Also, when you call the .Render, the "rendering" is not subject to the current transforms in effect. The buffer is just "blasted" to the screen 1 to 1.
So it is fast in cases where you can use it, but not suited here where we want to use a RotateTransform for drawing.

So, bottom line, just for investigation purposes, I took the existing code from my last post, and added a memory buffer, and changed the code to draw into that. The paint event would update from that memory buffer.
The result was that the drawing was more than twice as slow.
The code I posted on "this machine" would complete the 250 redraws of the window in about 1/2 second (500ms).
Using a memory buffer extended that time to over 1200ms (1.2 seconds).

Also, if you run the code, I assume even though it is drawing directly to the picturebox without a backbuffer, you are not seeing it drawing slow, or jittery (or flashy, etc...). And it is drawing both the sweep and representative sensor returns points.

To make that example more representative of what you're trying to do, you can modify that code to "simulate" the serial data updates.
Add a timer component to the form, and add the following line to the top of the declarations area
Dim SensorStep As Integer 'Steps 0 to 249 around a 360 degree circle

At the bottom of the Form_Load event, add the two lines to kick off the timer.
Timer1.Interval = 10 'actually get 15.625 ms most likely (64hz)
Timer1.Start()

Then add these two subs to the Form class code (I put it at the bottom)
Code:
  Private Sub IncrementSensorStep()
    SensorStep += 1
    If SensorStep > 249 Then
      pseudoDistance = 100     'We'll initialize the pseudo distance data to 100 each cycle. We'll add or subtract up to 1 from this for each step
      SensorStep -= 250
    End If
    pseudoDistance += pseudoRnd.Next(-1, 2)           '  Modify (possibly) a value representing the distance returned by the sensor
    pseudoDistanceSample(SensorStep) = pseudoDistance '  Save that value in our sensor sample data array (indexed by step)
    sweepAngle = SensorStep * deltaAngle
  End Sub

  Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    IncrementSensorStep()
    CodeInvalidated = True   'While we're active in the loop, indicate that we are invalidating areas (causing the paint events)

    Select Case sweepAngle                 'Depending on the angle of the sweep
      Case Is < 90                         '  Choose a "corner" of the display that needs to be redrawn
        PictureBox2.Invalidate(quad1)
      Case Is < 180
        PictureBox2.Invalidate(quad2)
      Case Is < 270
        PictureBox2.Invalidate(quad3)
      Case Is < 360
        PictureBox2.Invalidate(quad4)
      Case Else
        PictureBox2.Invalidate()             'do the whole window for any unexpected cases
    End Select
    CodeInvalidated = False
  End Sub
The timer will simulate the actions of your serial interface. Stepping the motor, saving the return data in the array, and updating the display.

If you let it cycle a number of times around, you should see it displaying the data as it is generated without glitches.

Another problem with using the memory buffer is coordinating the clearing of dots, and handling the paint event well.
Since we are only activating portions of the display to speed up the drawing, spurious paint events may only update part of the screen (and not necessarily the part we have enabled).
If we make things simple by just using the whole memory buffer all the time, and update the whole picturebox whenever the paint event happens, then the time to draw takes even a bigger hit.
It simplifies the logic, but on "this machine" a full sweep now takes a little over 2 seconds to complete updating the whole memory buffer and picturebox.

Quote:
Originally Posted by Alex83 View Post
...
Code:
Public Class Form1
'...

    ' Receive data from the Serial Port
    Private Sub mySerialPort_DataReceived(ByVal sender As System.Object, _
                      ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
                      Handles mySerialPort.DataReceived
        inputData = mySerialPort.ReadChar 

        Me.Invoke(New EventHandler(AddressOf DoUpdate))
    End Sub
Ok, the above is a definite slowdown.
I'm pretty sure the DataReceived is called from a non-GUI thread, which is why you have to Invoke to get to the GUI thread to update the textbox, but you're only reading one character at a time, invoking the GUI, coming back, reading another character, invoke the GUI thread, etc.
So you're taking a fair amount of time switching threads back and forth for every character.

You should process the serial data in the serial thread.
When you have your complete message (temp.Length = 6), then invoke the GUI thread to process that completed message.
The string concatenation, Temp &= data, should be done using the StringBuilder class, as it is more efficient for this operation, but that shouldn't be a big impact overall at the moment.

Quote:
Originally Posted by Alex83 View Post
'...
Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint

If angle = 0 Then e.Graphics.ResetTransform()

e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
e.Graphics.TranslateTransform(x, y)
e.Graphics.RotateTransform(angle * 360 / 250)
e.Graphics.DrawLine(Mypen, 0, 0, 150, 150) 'x1 y1 x2 y2

End Sub
The ResetTransform is doing nothing for you. The Transform is already reset at the beginning of every paint event. If it wasn't your angles would be accumlating, draw at 0, 1, 3 (1+2), 6 (3+3), 10 (6+4), 15 (10+5), etc...

You're drawing your line at a 135 degree angle (0,0, 150, 150) as measured with a clockwise rotation and 0 degrees being straight up.
So, if you want your reference heading (0 degrees) to be down and to the right (135 degrees clockwise rotation from the top center), then you're set.
If you want 0 degrees to be straight up, then you need to draw up the Y axis (in the negative Y direction). The length of your line is 1.414 * 150, so if you want that length, and straight up to be 0, the DrawLine call should be
e.Graphics.DrawLine(Mypen, 0, 0, -212, 0) 'x1 y1 x2 y2

Quote:
Originally Posted by Alex83 View Post
Private Sub Timer1_Tick1(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
MyGraphic.Clear(Color.White)
PictureBox1.Image = MyImage
End Sub
[/code]
This just clears your bitmap to white and assigns it (again and again) to the PictureBox1.
You could assign it once at the end of the form load, and since you're drawing directly to it, you don't have to assign it repeatedly to the picturebox.
Just invalidate the picturebox, and it should update its buffer from the image before handing the buffer to you to draw on in the paint event.

I have to watch out for exceeding 10,000 characters, so will stop here. I need to buy gas and head home anyway.
Later, perhaps I'll just slip a modified version of your serial code into my example, to see how it looks. It should be really simple to add a few bits of your serial interface into the example and see what it looks like.
Just insert the range data into the sample array at the step index, update the sweep angle (essentially replacing the code in the added IncrementSensorStep) and put the code from the Timer_Tick, minus the IncrementSensorStep call, in a routine and call (or invoke) it.
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.

Last edited by passel; 03-28-2012 at 06:45 PM.
Reply With Quote
  #8  
Old 04-04-2012, 10:53 AM
Alex83 Alex83 is offline
Newcomer
 
Join Date: Mar 2012
Posts: 5
Default

Hi passel, thanks again for your reply!

I have made some experiments regarding the Picturebox invalidate and I came up with a conclusion: no matter what method I use there won't be any timing difference - the scan line rotates at same speed in all the methods I've tried, which were:
1) Removing the background image completely;
2) PictureBox1.Invalidate() after I read 6 characters from serial port (my 'packet' contain 6 chars.);
3) Invalidating only the quadrants as you suggested and posted the sample code;
4) Invalidating a rectangle equivalent to the area that my scan line moves.
The results of my experiments showed that all the above need the same time to be executed (the maximum difference was less than 0.1 seconds which doesn't REALLY affect my system's overall performance).

Quote:
You should process the serial data in the serial thread.
Thank you for spotting that!!!!!!
My system now is (almost) fully working! But yeah, that was a definitely slowdown! I am now invoking the GUI thread after I receive a complete message (temp.Length = 6).

I got rid of the ResetTransform. I didn't know that it is already reset at the beginning of every paint event.

I have also fixed my scan line start position.

Man you are a life saver!

I have another question for you though. I am now trying to display the detected objects (little circles or squares).
I can display them fine, however I can only display one at each angle (the current position). How can I draw, let's say, 5 dots and then only after 5 the last one gets erased?
To draw them I just added e.Graphics.FillEllipse(Brushes.Red, 0, radius, 5, 5) to the end of PictureBox1_Paint subroutine.

Thanks in advance!
Reply With Quote
  #9  
Old 04-04-2012, 11:48 AM
passel's Avatar
passelDisplaying data graphically (Ultrasonic radar) passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,026
Default

Quote:
Originally Posted by Alex83 View Post
...I have made some experiments regarding the Picturebox invalidate and I came up with a conclusion: no matter what method I use there won't be any timing difference - the scan line rotates at same speed in all the methods I've tried
Which you should expect.
As long as you can update the image within the time between sensor reports, then what manner you actually did the drawing will not be apparent to the user, as it is the reception of sensor reports that set the update rate.
If the reports are coming in at 50hz, it doesn't matter whether you take 10% of the available drawtime, or 90% of the available drawtime, to update the image,the image will still update at 50hz. It is when the drawing time takes longer than the available time that you have a problem.

If a number of different drawing methods meet the time requirement, then choose the easiest to implement, and/or understand.

I kind of figured that once you used a decent baud rate, and fixed the thread swapping, that you would have sufficient bandwidth to fall back to a simple, "erase the whole area, redraw everything, and refresh" paradigm, which is definitely easier to code and understand.

Quote:
Originally Posted by Alex83 View Post
I have another question for you though. I am now trying to display the detected objects (little circles or squares).
I can display them fine, however I can only display one at each angle (the current position). How can I draw, let's say, 5 dots and then only after 5 the last one gets erased?
To draw them I just added e.Graphics.FillEllipse(Brushes.Red, 0, radius, 5, 5) to the end of PictureBox1_Paint subroutine.
Thanks in advance!
Well, if you look back at my code you can see I stored the "radius" value in an array of 250, which is indexed by your 250 sensor steps.

So, you could keep all the positions for the full sweep, as I did, and redraw them all. (You would have an internal loop in the drawing code to loop through all the points, doing a reset, rotate, translate, drawEllipse for each point).
You just replace the values in the array as you sweep around.

Or you could store all the points, but only draw the last five, based on what the current step position is.

Or you could have an array of five, that you use as a circular buffer, to save points in.

Or, if you wanted something that looked like the old RADAR screens from years gone by, where they didn't have digital displays, but depended on long persistance phosphors to see the blips as they faded out, you could, instead of erasing the bitmap, just fill the area using FilledRectangle and a brush with a low (but not 0) alpha level color. If you repeatedly fill the are with a low alpha color, the things drawn, but not redrawn, will "fade" until they are at, or near the low alpha color.
That will increase your drawing time, but it is a neat effect.
A lot of current RADARs don't depend on log persitance phosphors. The returns are collected, and displayed on a raster based screen (which your monitor is), so you see the data the whole sweep, and it is only replaced as the sweep comes around.
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.
Reply With Quote
  #10  
Old 04-04-2012, 11:27 PM
passel's Avatar
passelDisplaying data graphically (Ultrasonic radar) passel is offline
Sinecure Expert

Super Moderator
* Guru *
 
Join Date: Jun 2003
Location: Upstate New York, usa
Posts: 8,026
Default

I'm double posting to give an example of one of the methods I mentioned in the previous post, so if you didn't read that one it probably wouldn't hurt to give it a quick read first.

This example is probably closer in someways to what you already have, in that it plots one beam at a given sweep angle, and plots one "blip" at some distance out that beam.

The earlier examples I posted kept a history of all 250 "RADAR returns" (technically your sensor is SONAR, not RADAR, but we'll ignore that), which I would think would be useful if you're trying to map your environment and analyze what you're seeing. On the other hand, I guess you would actually want more than just the 250 points around your current position, but would be mapping those "polar" coordinates into a cartesian (X,Y) plot or map of the area, so what the radar displayed would not necessarily represent the data collected, but just an ephemeral view of what is currently being detected.

So, along those lines, this example just draws the beam and plots the one blip and does not retain any "memory" of what that value was once the next RADAR return comes in.
But, it does have a short term "visual" memory of the return, because the bitmap the beam and blip is drawn on is not cleared immediately. It uses the process of drawing a mostly transparent dark color (black in this case) repeatedly over the bitmap which has the effect of fading stuff that is not redrawn.
This kind of emulates the long persistence phosphors used in old style RADAR repeaters.

A secondary objective of this application is to give another example of using the BufferedGraphics class to handle the backbuffer duties, vs using a bitmap or image object to serve as the backbuffer.
The program will use a timer, with an Interval less than 16, which should give a 64hz tick event on most machines. The maximum rate that a timer control will fire is at the whim of the hardware manufacturer and the Operating System setup, and I've seen a number of different speeds over the years, but all the machines I currently work on, and have worked on for a number of years now have been based on a 64hz base rate.
That means that when you select an interval (in milliseconds), the actual interval you get will be (assuming the 64hz rate is what your machine is giving), will be a multiple of 1/64th of a second, i.e. 1/64 (64hz), 2/64 (32hz), 3/64 (21 1/3 hz), etc...

So, assuming the clock is firing at 64hz (the 64 ticks per second emulating 64 "RADAR" returns per second) would mean it takes 3906.25 milliseconds to do a full 250 step (360 degree) sweep.
If we can draw fast enough so that we can do 250 screen updates in less than 3906 ms (3.9 seconds), then can keep up with our 64 frames per second rate.

Assuming that we can draw faster than 64hz, then our sweep will update at 64hz, regulated by the timer (+/- any higher priority Windows events).
Since both methods, on my machine, are faster than needed to maintain 64hz, we can't tell how much faster one is over the other because they both update at the same maximum rate that the timer can trigger.
So, the application has a "Test" button, that will stop the timer, then draw a full 360 degree (250 step) sweep as fast as it can in a loop. It will time how long it takes to do the 250 pass loop.
It will then display the elapsed number of milliseconds in the title bar of the form, and restart the timer control so the continuous sweep resumes.
Which method is used for drawing is selected by clicking on the desired radio button.

This example does not draw directly using the graphics context passed to us in the Paint event. While drawing directly on the double buffer provided by the control is fast, in this case where we need a "visual memory" of our drawing that we modify to implement fading is not possible using the standard drawing paradigm.
The standard drawing process has the control doing housekeeping and handing you a freshly prepared surface to draw on in the paint event.
But we don't want a fresh surface to draw on, we want to add to our existing buffer, to accumulate our sweep and blips over time.
So, we need to maintain a memory buffer to draw on, and refresh the control from that.
The common approach is to allocated a bitmap (or image) object in memory, draw on it, and then either assign the bitmap to the control (through it's image property), or do a DrawImage from the bitmap to the control in the Paint event.
Another option, is to assign the bitmap to the control, but then modify the bitmap dynamically in your code, and then just invalidate the control, so it refreshes the screen from the modified bitmap (in which case you don't have to have any code in the Paint Event).
The testing I've done (and I though it reasonable that the last method would be inheritly quick since we didn't put code in the Paint event, the picturebox just updated itself from the image assigned to it earlier) found that the above three methods all result in about the same maximum update rate. None are a clear winner over the other (at least when using a picturebox control. If you roll your own control, there might be other options that can influence the speed of one method over others, but I haven't researched that yet).
So, given that all three are about the same speed, I went with the one where you don't assign any bitmap to the control itself, leaving those objects (.Image and .BackgroundImage empty). Putting something in the BackgroundImage property is a definite performance hit, so shouldn't be done except where the control doesn't need to be updated quickly.
The example just draws on the bitmap in memory, then in the Paint event, DrawImage is used to draw the bitmap on the control.

The second method, we've already discussed, is using a BufferedGraphics Class object.
We allocate a drawing surface using the class, draw on it using the graphics object it provides, and in the Paint event of the control (Picturebox in this case), have it render the drawing.

The bottom line.
On this machine (Win XP-32 Dell Latitude D630 laptop), the Bitmap/DrawImage method takes around 2300ms (2.3 seconds) on average to do a full sweep. At 64hz we have 3.9 seconds to do the sweep, so this method uses up about 60% of the available bandwidth to draw and display the sweep.

Using the BufferedGraphics object method, this machine takes around 600ms on average to do the full sweep (longer the first time the test is run 900 to 1000ms, but I don't know why), so uses about 15.4% of the available bandwidth to draw and display the sweep.

Result: On this machine, for this application, BufferedGrahics is 3 to 4 times faster than using a memory bitmap.

Hopefully, the example is not too long, or too obscure to be of some use to someone in the future.
Attached Files
File Type: zip RadarSweepLongPersistence.zip (19.1 KB, 56 views)
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.
Reply With Quote
  #11  
Old 04-12-2012, 08:50 AM
Alex83 Alex83 is offline
Newcomer
 
Join Date: Mar 2012
Posts: 5
Default

Hi passel,
Sorry about the late reply but I've been busy with some other stuff.

I just would like to say a very BIG thank you for helping me out with this! Your explanations are really clear and much better than the last 3 books I got from my local library!
My system is now fully working and most importantly -> now I understand what's going on and I also understand where I went wrong on the first place.

Again, thank you very much!
Peace!
Reply With Quote
Reply

Tags
ultrasonic radar


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar) Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
 
Displaying data graphically (Ultrasonic radar)
Displaying data graphically (Ultrasonic radar)
 
-->