Avatar billede moine Nybegynder
14. september 2007 - 09:43 Der er 7 kommentarer

Ændre frekvens for indlæsning af data i array

Hej

I skal nok holde tungen lige i munden når I læser dette ;-)

Jeg har lavet et program der opsamler data fra et datakort. Ud over at blive skrevet i en tekstfil, skal der også løbende på skærmen plottes de målte datapunkter i et xy-plan. Når denne graf er fyldt skal de data der er plottet komprimeres så de kun fylder halvdelen og de næste data plottes så i den næste del.

For at spare lidt på hukommelsen forestiller jeg mig at datapunkterne puttes i et array AF EN FAST STØRRELSE. fx 100 rækker. Når arrayet er fyldt op, kasseres hvert andet punkt og de resterende flyttes til de første 50 rækker. Det er lykkedes for mig at få programmet til det (se kode nedenfor).

Det der er mit problem er at indlæse punkterne efterfølgende med dobbelt så lange intervaller, så tidsaksen bliver ved med at "virke" lineær.  Man kunne gange Timer1.Interval med 2 hver gang, men der skal ikke mange fordoblinger til før det bliver for stort.
Programmet skal kunne køre i 10 dage, så med 100 punkter vil det jo kun blive en indlæsning 10 gange i døgnet efter 10 dage, hvorimod der er i den første halve time skal indlæses punkter meget oftere.

En løsning hvor hvert andet målepunkt ikke puttes i arrayet vil være at foretrække. Når arrayet så er fyldt 3. gang, skal kun hvert 4. punkt puttes i arrayet. 4 gang -> hvert 8. punkt osv...

Nedenstående kode (bruger VB6 og ikke noget med .NET tak) skulle gerne illustrere hvad jeg mener.

--------------------------------
Option Explicit

Dim screendata(0 To 99, 0 To 1)
Dim v      As Long
Dim i      As Integer
Dim d      As Long
Dim n      As Integer

Private Sub Form_Load()
d = 1

Command1.Caption = "Start"

Timer1.Interval = 500
Timer1.Enabled = False

With MSChart1
    .ChartData = screendata
    .chartType = VtChChartType2dXY
    .Plot.Axis(VtChAxisIdY).AxisScale.Type = VtChScaleTypeLinear
    .Plot.Axis(VtChAxisIdX).AxisScale.Type = VtChScaleTypeLinear
    .Plot.Axis(VtChAxisIdX).ValueScale.Auto = False
    .Plot.UniformAxis = False
    .Plot.Axis(VtChAxisIdX).AxisGrid.MajorPen.Style = 0
    .Plot.Axis(VtChAxisIdX).AxisGrid.MinorPen.Style = 0
    .Plot.Axis(VtChAxisIdX).ValueScale.Maximum = 100
    .Plot.Axis(VtChAxisIdX).ValueScale.Minimum = 0
    .Plot.Axis(VtChAxisIdX).ValueScale.MajorDivision = 10
    .Plot.Axis(VtChAxisIdX).ValueScale.MinorDivision = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.Auto = False
    .Plot.Axis(VtChAxisIdY).AxisGrid.MajorPen.Style = 0
    .Plot.Axis(VtChAxisIdY).AxisGrid.MinorPen.Style = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.Maximum = 100
    .Plot.Axis(VtChAxisIdY).ValueScale.Minimum = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.MajorDivision = 10
    .Plot.Axis(VtChAxisIdY).ValueScale.MinorDivision = 2
End With

End Sub

Private Sub Command1_Click()
Timer1.Enabled = True
End Sub

Private Sub Timer1_Timer()
'formel der genererer data til testbrug.
'Erstattes med måledata senere
v = 10 * Sin(0.36 * i) + 50

'Data fyldes i arrayet ind til det er fyldt
If i < 99 Then
    i = i + 1
    screendata(i, 0) = i
    screendata(i, 1) = v

'Dernæst skal hvert andet data punkt kasseres
' og resten fyldes ind på de første 50 pladser
Else:
    'pluk værdier med lige index numre og put dem i array screendata's plads 0-50
    For n = 0 To 99 Step 2
        screendata(n / 2, 1) = screendata(n, 1)
    Next n
    'slet værdier i arrayet over 50
    For n = 51 To 99
        screendata(n, 1) = ""
    Next n
    'start indskrivning af nye data ved række 50
    i = 50
   
'Tæller der kunne tænkes anvendt til at fjerne punkter som 'beskrevet nedenfor
    d = d * 2
End If

'De aktuelle værdier i arrayet plottes
MSChart1.ChartData = screendata

End Sub
-------------------------------

Håber I forstår hvad jeg mener ... og at nogen kan give et bud på hvilken kode der skal til for kun at putte hvert "d'ende" punkt i arrayet.
Avatar billede tjacob Juniormester
18. september 2007 - 11:20 #1
Uden i øvrigt at kunne hjælpe dig med problematikken omkring 'komprimering' af dit array, har jeg følgende kommentar:

Hvorfor ikke lade arrayet (et andet array) indeholde samtlige målepunkter?
Et målepunkt hver 0,5 sekund = 1728000 punkter på 10 døgn. Selv hvis arrayet holder Doubles vil du ikke belaste

hukommelsen mere end 4-5 Mb. Det er da ikke alverden?

Og med hensyn til at bearbejde arrayet (f.eks. trække 100 punkter ud, jævnt fordelt),
ja så med f.eks. en 1 GHz processor (for at tage et lavt tal), vil du kunne løbe igennem arrayet på nogle

millisekunder. I hvert fald kan du sagtens lave de operationer der skal laves, inden næste tick af timeren.
For i øvrigt skal du ikke løbe igennem hele arrayet -se nedenfor.

Det er svært at sige hvad den samlede belastning på computeren vil være, men det er mit bud, at belastningen på

hukommelsen vil være under 10 Mb (og der er vel ingen i dag der har under 128 Mb RAM), og på processoren vil der så

være en spidsbelastning hvert halve sekund, men det skulle ikke betyde alverden, hvis blot du får nogle DoEvents med i

koden.

Her er en skitse til hvad du så kunne gøre:

Erklær et Basisarray, feks Public BaseArray(2000000) As ?
Erklær en tæller feks Public lCount As Long
Erklær et PlotArray feks Public screendata(99) As ?

I timeren gør du følgende:

lCount=lCount+1
BaseArray(lCount)='Dine måledata'
Dim lFactor As Long

lFactor=Int(lCount/100)        'her skal laves lidt betingelse for lCount<100

'skal kun gøres hver gang lCount er vokset med 100, evt tjek med en anden tæller
For i=0 to 99
    screendata=Basearray(i*lFactor)   
Next i

screendata vil så kun få nye værdier hver gang lCount er vokset med 100, men det gør den jo på bare 50 sekunder.

Dette vil trække 100 punkter ud, jævnt fordelt over den forløbne tid, men jeg kan ikke rigtig gennemskue, om det er det du vil, jævnfør din bemærkning om at "tidsaksen skal virke lineær".

/tjacob
Avatar billede moine Nybegynder
18. september 2007 - 11:40 #2
Jeg skulle nok have fortalt at den computer programmet skal køre på er en meget udtjent sag. Win98, 32 (eller 64 MB) ram. Tror også CPU'en er under 1 GHz.

Desuden bliver der i det endelige program flere dimensioner kolonner i arrayet i det der skal logges flere signaler (10 i alt tror jeg). Det skal så vises som forskellige kurver.

Men tak for dit forslag - jeg kigger lige lidt nærmere på det og skal nok vende tilbage.
Avatar billede mikker Nybegynder
21. september 2007 - 15:27 #3
Den er godt nok anderledes og jeg har ikke adgang til Mschart så dette er et gæt.

Option Explicit

Dim screendata(0 To 99, 0 To 1)
Dim v      As Long
Dim i      As Integer
Dim d      As Long
Dim n      As Integer
Dim i2 As Integer

Private Sub Form_Load()
d = 1
i2 = 2
Command1.Caption = "Start"

Timer1.Interval = 500
Timer1.Enabled = False

With MSChart1
    .ChartData = screendata
    .ChartType = VtChChartType2dXY
    .Plot.Axis(VtChAxisIdY).AxisScale.Type = VtChScaleTypeLinear
    .Plot.Axis(VtChAxisIdX).AxisScale.Type = VtChScaleTypeLinear
    .Plot.Axis(VtChAxisIdX).ValueScale.Auto = False
    .Plot.UniformAxis = False
    .Plot.Axis(VtChAxisIdX).AxisGrid.MajorPen.Style = 0
    .Plot.Axis(VtChAxisIdX).AxisGrid.MinorPen.Style = 0
    .Plot.Axis(VtChAxisIdX).ValueScale.Maximum = 100
    .Plot.Axis(VtChAxisIdX).ValueScale.Minimum = 0
    .Plot.Axis(VtChAxisIdX).ValueScale.MajorDivision = 10
    .Plot.Axis(VtChAxisIdX).ValueScale.MinorDivision = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.Auto = False
    .Plot.Axis(VtChAxisIdY).AxisGrid.MajorPen.Style = 0
    .Plot.Axis(VtChAxisIdY).AxisGrid.MinorPen.Style = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.Maximum = 100
    .Plot.Axis(VtChAxisIdY).ValueScale.Minimum = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.MajorDivision = 10
    .Plot.Axis(VtChAxisIdY).ValueScale.MinorDivision = 2
End With

End Sub

Private Sub Command1_Click()
Timer1.Enabled = True
End Sub

Private Sub Timer1_Timer()
'formel der genererer data til testbrug.
'Erstattes med måledata senere
v = 10 * Sin(0.36 * i) + 50

'Data fyldes i arrayet ind til det er fyldt

If i < 99 And i Mod i2 = 0 Then
    i = i + 1
    screendata(i, 0) = i
    screendata(i, 1) = v

'Dernæst skal hvert andet data punkt kasseres
' og resten fyldes ind på de første 50 pladser
Else:
    'pluk værdier med lige index numre og put dem i array screendata's plads 0-50
    i2 = i2 * 2
    For n = 0 To 99 Step 2
        screendata(n / 2, 1) = screendata(n, 1)
    Next n
    'slet værdier i arrayet over 50
    For n = 51 To 99
        screendata(n, 1) = ""
    Next n
    'start indskrivning af nye data ved række 50
    i = 50
   
'Tæller der kunne tænkes anvendt til at fjerne punkter som 'beskrevet nedenfor
    d = d * 2
End If

'De aktuelle værdier i arrayet plottes
MSChart1.ChartData = screendata

End Sub
Avatar billede mikker Nybegynder
21. september 2007 - 15:28 #4
Den vil selvfølgelig låse sig i en løkke så den dutter ej.
Avatar billede mikker Nybegynder
21. september 2007 - 15:30 #5
Måske vil dette virke?

Option Explicit

Dim screendata(0 To 99, 0 To 1)
Dim v      As Long
Dim i      As Integer
Dim d      As Long
Dim n      As Integer
Dim i2, i3 As Integer

Private Sub Form_Load()
d = 1
i2 = 2: i3 = 0
Command1.Caption = "Start"

Timer1.Interval = 500
Timer1.Enabled = False

With MSChart1
    .ChartData = screendata
    .ChartType = VtChChartType2dXY
    .Plot.Axis(VtChAxisIdY).AxisScale.Type = VtChScaleTypeLinear
    .Plot.Axis(VtChAxisIdX).AxisScale.Type = VtChScaleTypeLinear
    .Plot.Axis(VtChAxisIdX).ValueScale.Auto = False
    .Plot.UniformAxis = False
    .Plot.Axis(VtChAxisIdX).AxisGrid.MajorPen.Style = 0
    .Plot.Axis(VtChAxisIdX).AxisGrid.MinorPen.Style = 0
    .Plot.Axis(VtChAxisIdX).ValueScale.Maximum = 100
    .Plot.Axis(VtChAxisIdX).ValueScale.Minimum = 0
    .Plot.Axis(VtChAxisIdX).ValueScale.MajorDivision = 10
    .Plot.Axis(VtChAxisIdX).ValueScale.MinorDivision = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.Auto = False
    .Plot.Axis(VtChAxisIdY).AxisGrid.MajorPen.Style = 0
    .Plot.Axis(VtChAxisIdY).AxisGrid.MinorPen.Style = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.Maximum = 100
    .Plot.Axis(VtChAxisIdY).ValueScale.Minimum = 0
    .Plot.Axis(VtChAxisIdY).ValueScale.MajorDivision = 10
    .Plot.Axis(VtChAxisIdY).ValueScale.MinorDivision = 2
End With

End Sub

Private Sub Command1_Click()
Timer1.Enabled = True
End Sub

Private Sub Timer1_Timer()
'formel der genererer data til testbrug.
'Erstattes med måledata senere
v = 10 * Sin(0.36 * i) + 50

'Data fyldes i arrayet ind til det er fyldt
i3 = i3 + 1
If i < 99 And i3 Mod i2 = 0 Then
    i = i + 1
    screendata(i, 0) = i
    screendata(i, 1) = v

'Dernæst skal hvert andet data punkt kasseres
' og resten fyldes ind på de første 50 pladser
Else:
    'pluk værdier med lige index numre og put dem i array screendata's plads 0-50
    i2 = i2 * 2: i3 = 0
    For n = 0 To 99 Step 2
        screendata(n / 2, 1) = screendata(n, 1)
    Next n
    'slet værdier i arrayet over 50
    For n = 51 To 99
        screendata(n, 1) = ""
    Next n
    'start indskrivning af nye data ved række 50
    i = 50
   
'Tæller der kunne tænkes anvendt til at fjerne punkter som 'beskrevet nedenfor
    d = d * 2
End If

'De aktuelle værdier i arrayet plottes
MSChart1.ChartData = screendata

End Sub
Avatar billede moine Nybegynder
27. september 2007 - 12:26 #6
Hej Mikker

Tak for dit svar. Der var et par bugs i det, men det ledte mig på rette spor og nu virker det :-) Derfor har jeg besluttet at vi deler pointene lige, så hvis du du vil kaste et svar...

For en god ordens skyld præsenterer jeg her den endelige løsning. Har af andre grunde måtte erstatte MSChart med en hjemmetegnet graf af line-commands, så der er nogle små-ændringer pga. dette. Bl.a. er arrayet blevet dynamisk, men med en begrænsning i størrelsen på 800. Koden kan sikkert godt optimeres yderligere, men det tager jeg en anden gang. Here we go:

Option Explicit

Dim v0    As Single
Dim v1    As Single
Dim i      As Integer
Dim d      As Single
Dim n      As Integer
Dim i2    As Integer
Dim i3    As Long
Dim x    As Long
Dim screendata() As Single


Private Sub Form_Load()
i2 = 1
i3 = 0

Timer1.Interval = 2250 * 0.01
ReDim screendata(1)
screendata(0) = 0
Timer1.Enabled = False
Command1.Caption = "Start"
End Sub

Private Sub Command1_Click()
Timer1.Enabled = True
End Sub

Private Sub Timer1_Timer()
'formel der genererer data til testbrug.
'Erstattes med måledata senere
v0 = 4 * 0.036 * x

'Data fyldes i arrayet ind til 800 punkter

If UBound(screendata) < 799 Then

    Select Case d = i3 Mod i2
        Case d = 0
            ReDim Preserve screendata(i)
            screendata(i) = v0
           
            i = i + 1
            'hjemmebrygget sub der plotter data i screendata
            DrawSignal screendata()
        Case d <> 0
    End Select
   
    i3 = i3 + 1

    'Dernæst skal hvert andet data punkt kasseres
    ' og resten fyldes ind på de første 400 pladser
    Else:
   
        'plukker værdier med lige index numre og putter
        'dem i array screendatas plads 0-400
        For n = 0 To 799 Step 2
            screendata(n / 2) = screendata(n)         
        Next n
       
        'slet værdier i arrayet fra index 401
        ReDim Preserve screendata(400)
        'igen tegnes grafen
        DrawSignal screendata()
       
       
        'start indskrivning af nye data ved række 400
        i = 400
        'nulstilling af tællere
        i2 = i2 * 2
        i3 = 0
        x = 0

End If

x = x + 1

End Sub
Avatar billede mikker Nybegynder
05. december 2007 - 12:36 #7
Godt du fik det til at virke. Det er en fantastisk følelse når det lykkes :o)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester