Problem z rysowaniem sygnału

0

Witam

Piszę aplikację do monitorowania sygnału EKG.

Zacząłem od stworzenia własnej kontrolki rysującej na "żywo" wykres EKG. Żeby sygnał był rysowany na bieżąco bez żadnych opóźnień i ze stałą częstotliwością rysowanie oparłem na timerach.

Gdy używam timerow z Win Fromsow wykres rysuje się płynnie ale po załączeniu kolejnych wykresów wszystkie strasznie zwalniają. Przy 10 wykresach picturebox odświeża się tylko kilka razy na sekundę. (chciałbym osiągnąć przynajmniej 30, przy min 6 wykresach).
Podczas rysowania jednego wykresu obciążenie procesora wynosi kilka %, przy kilku wykresach dochodzi do 60%

Gdy używam timerow z klasy System.Timers które działają w osobnych wątkach wykresy nie zwalniają już tak bardzo ale i tak nie da się utrzymać stałej prędkości odświeżania. Pojawia się też inny problem. Gdy rysowany jest jeden wykres to działa on w miarę płynnie, obciążenie procesora kilka %. Po załączeniu kolejnych wszystkie zaczynają migać, im więcej uruchomionych wykresów tym gorzej( a obciążenie procesora po dodaniu zaledwie jednego % wzrasta do 50%, przy kilku wykresach do 80%.)
Tak jakby kolejne uruchomione UserControl negatywnie na siebie wpływały (a przecież są to osobne obiekty i każdy z nich ma osobne timery/wątki ??? )

Próbowałem chyba wszystkie kombinacje z uruchamianiem poszczególnych metod na różnych wątkach, używałem locków, delegatów, i innych mechanizmów. Za każdym razem picturboxy "migają"...

Siedzę już nad tym dość długo i kończą mi się pomysły.... nie wiem czy moje podejście jest dobre ?
czy żeby utrzymać stałą prędkość wykresów należy używać timerów czy jest jakiś inny dobry mechanizm. Jak można to zoptymalizować ?

Bardzo proszę o pomoc

W kodzie umieszczone są metody obu typów timerów.
Niedorysowane fragmenty wykresów to właśnie efekt "migania"

<url>http://imageshack.us/photo/my-images/689/beztytuumev.jpg/
</url>


        private void AddSample()
        {
            //przesuniecie wskaznika odczytu na poczatek
            if (it == buforLenght)
            {
                it = 0;
            }

            //przesuniecie okna o jedna probke
            for (int i = 0; i < buforLenght - 1; i++)
            {
                buforChart[i] = buforChart[i + 1];
            }

            //dodanie nowej probki
            buforChart[buforChart.Length - 1] = buforSamples[it];
        }

        //RYSOWANIE NA PICTBOX
        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawImage(bmp, 0, 0);
        }

        ////////////TIMERY -> WIN FORMS
        private void timerBufor_Tick(object sender, EventArgs e)
        {
            AddSample();
            it++;

            DrawBmp();
        }

        private void timerRender_Tick(object sender, EventArgs e)
        {
            pictureBox1.Invalidate();
        }

        ////////////TIMERY -> KLASY SYSTEM.TIMERS
        void systemTimerBufor_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            lock (zamek)
            {
                AddSample();
                it++;

                DrawBmp();
            }
        }

        void systemTimerRender_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            lock (zamek)
            {
                pictureBox1.Invalidate();
            }
        }

        //RYSOWANIE NA BITMAPIE
        private void DrawBmp()
        {
            x0 = 0;
            y0 = 0;
            x = 0;
            y = 0;

            graphics.Clear(System.Drawing.Color.White);

            for (int i = 0; i < buforLenght; i++)
            {
                y = -(float)((buforChart[i] * ampfilter) - (pictureBoxHeight / 2) + signalLocation + signalLocationHidden);

                if (i == 0)
                    y0 = y;

                x = i;

                graphics.DrawLine(Pens.Red, x0, y0, x, y);

                x0 = x;
                y0 = y;
            }
        }
 
1
  1. Timer ma ograniczenie do ok 66Hz (min odstęp pomiędzy tiknięciami to 15ms) i jest to narzucone przez system. Ograniczenia tego nie ma MultimediaTimer ale nie jest zaimplementowany w NET.
  2. Oko i tak nie złapie więcej jak 60Hz (30Hz dla oka to już płynny obraz) - jeśli potrzebujesz "szybszej" animacji to powinieneś w jednej ramce obrazu rysować kilka punktów.
  3. Przesuwanie bufora za każdym razem to ZŁY pomysł - bufor przecież ma stały rozmiar więc zamiast przesuwać cały bufor zapisuj najnowszą próbkę w pozycję x i traktują ją jako koniec bufora a pozycję x+1 jako początek aż do max a potem od 0 do x.

Zastanów się co chcesz osiągnąć:

  • czy odświeżanie wykresu po każdym dodaniu nowej próbki (nawet jeśli te próbki będą spływały z częstotliwością 500Hz)
  • czy też stałe odświeżanie (z zadaną częstotliwością) niezależnie od częstotliwości spływania próbek
  • a może osobno potraktować samo odświeżanie wykresu i ustawić je np. na 40Hz a dodatkowo mieć ustawianą częstotliwość odświeżania próbek (COP) - np. dla COP=80Hz przy jednej ramce musiałbyś narysować 2 próbki, przy COP=40 jedną próbkę, przy COP=20 rysował byś próbkę co dwa odświeżenia ekranu itd
0

Myślałem o stałym odświeżaniu i regulowaną częstotliwością dodawania próbek.Dlatego właśnie użyłem dwóch timerów - jeden odpowiedzialny za przerysowanie picturebox`a a drugi za rysowanie bitmapy ale widać jak działa...
Poprawiłem przesuwanie bufora. Niestety nie dało to żadnego efektu...

0

Po kopać się z koniem gdy są gotowe komponenty? Ot pierwszy z brzegu: http://msdn.microsoft.com/en-us/library/dd456632.aspx

1 użytkowników online, w tym zalogowanych: 0, gości: 1