3 Unbekannte effizient ermitteln

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
Thomas
Beiträge: 893
Registriert: 06.07.2006 19:24
Wohnort: Regensburg
Kontaktdaten:

3 Unbekannte effizient ermitteln

Beitrag von Thomas »

Hallo Leute,

in einem anderem Projekt nutze ich ein Raspberry Pico um mit dem internen Timer eine einstellbare Frequenz zu erzeugen.
PureBasic nutze ich gerade um herzauszufinden, wie ich die Parameter effizient errechnen (effizent ausprobieren ist auch ok) kann.

Wunschfrequenz 10Hz - 2MHz

Die Formel (nach f aufgelöst) ist:

Code: Alles auswählen

#CPU_FREQ = 125000000	; 125MHz

; Wertebereiche
; TOP = 0 - 65535	(16 bit)
; DIV_INT = 1 - 255	( 8 bit)
; DIV_FRAC = 0 - 15	( 4 bit)
; => Sollte es ein gutes Ergebnis sowohl mit DIV_FRAC = 0 und DIV_FRAC > 0 geben,
; ist DIV_FRAC = 0 immer vorzuziehen, da DIV_FRAC > 0 zusätzlichen Jitter verursacht

f = #CPU_FREQ / ((TOP + 1) * (DIV_INT + (DIV_FRAC / 16)))
Das Problem: Ich habe eine "Soll-Frequenz" und soll die Parameter TOP, DIV_INT, DIV_FRAC errechnen.

Aktuell sehe ich nur den Weg für 65536x256x16 Schleifendurchläufe:
  1. Alle 65536x256x16 Werte zu durchlaufen
  2. Dabei die Abweichung zur Soll-Frequenz bei den einzelnen Durchläufen zu ermitteln. Die Sache mit dem DIV_FRAC = 0 zu berücksichtigen.
  3. Am Ende die "besten Parameter" auszugeben.
  4. => Dauert sogar auf dem PC ein paar Sekunden, was zu lange ist

Code: Alles auswählen

Beispiel:
für Soll-Frequenz = 18673 Hz
Bestes Ergebnis ermittelt durch Durchlaufen und Prüfen aller möglichen Werte:
DIV_INT = 4
DIV_FRAC = 13
TOP = 1390
Proberechnung ergibt für f = 18672,9160... Hz
Hat jemand eine effiziente Idee für das Problem?
Zuletzt geändert von Thomas am 15.08.2022 15:45, insgesamt 1-mal geändert.
v6.00
Benutzeravatar
Thomas
Beiträge: 893
Registriert: 06.07.2006 19:24
Wohnort: Regensburg
Kontaktdaten:

Re: 3 Unbekannte effizient ermitteln

Beitrag von Thomas »

Ich habe schon ein bisschen getestet, nur ist es einfach viel zu Träge:

Code: Alles auswählen

#CPU_FREQ = 125000000

#FREQ_OUT_MIN = 10    ; 10
#FREQ_OUT_MAX = 11    ; wenns schnell genug ist: 2000000

Procedure Frequency(freq)
  
  temp1.f = 0
  calc_div_int    = 1
  calc_top        = 0
  calc_div_frac   = 0
  calc_freq_out.f = 0
  calc_error.f = 0
  calc_error_alt.f = freq
  
  For n_div_int = 1 To 255        ; 0 bis 255 => 0 Timer aus
    For n_div_frac = 0 To 15       ; 0 bis 15
      temp1 = #CPU_FREQ / freq / (n_div_int + (n_div_frac/16))
      If temp1 < 65536
        For n_top = 0 To 65535       ; 0 bis 65535 => ab 1 machts Sinn, vorher ists iwie aus
          calc_freq_out = #CPU_FREQ / ((n_top + 1) * (n_div_int + (n_div_frac/16)))
          calc_error = Abs(Abs(freq)-Abs(calc_freq_out))
          If calc_error < calc_error_alt
            calc_error_alt = calc_error
            calc_div_frac = n_div_frac
            calc_div_int = n_div_int
            calc_top = n_top
          EndIf
        Next
      EndIf  
    Next
  Next
      
  DIV_INT         = calc_div_int
  TOP             = calc_top
  DIV_FRAC        = calc_div_frac
  FREQ_OUT.f      = #CPU_FREQ / ((TOP + 1) * (DIV_INT + (DIV_FRAC/16)))   ; Formel aus Datenblatt RP2040 Seite 530
  
  error_diff.f    = freq - FREQ_OUT
  error_perc.f    = 100 / freq * error_diff
  error_ppm       = error_perc * 10000
  
  AddGadgetItem(0, -1, Str(freq)+Chr(10)+StrF(DIV_INT)+Chr(10)+StrF(DIV_FRAC)+Chr(10)+StrF(TOP)+Chr(10)+StrF(FREQ_OUT)+Chr(10)+StrF(error_diff)+Chr(10)+Str(error_ppm))

EndProcedure

If OpenWindow(0, 100, 100, 1000, 1000, "Frequenz-Calc", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    ListIconGadget(0, 5, 5, 990, 990, "Freq Soll", 200, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
    AddGadgetColumn(0, 1, "DIV_INT", 100)
    AddGadgetColumn(0, 2, "DIV_FRAC", 100)
    AddGadgetColumn(0, 3, "TOP", 100)
    AddGadgetColumn(0, 4, "FREQ_OUT", 200)
    AddGadgetColumn(0, 5, "error_diff", 100)
    AddGadgetColumn(0, 6, "error_ppm", 100)
    
    ; Einzelne Frequenz
    ; Frequency(18480680)
    
    ; Frequenz-Liste
    For f = #FREQ_OUT_MIN To #FREQ_OUT_MAX
      Frequency(f)
    Next
 
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf
Hier berücksichtige ich die Sache mit dem DIV_FRAC = 0 noch nicht.
Bei 10Hz wären z.B. TOP = 62499, DIV_INT = 200, DIV_FRAC = 0 besser.
v6.00
Benutzeravatar
Thomas
Beiträge: 893
Registriert: 06.07.2006 19:24
Wohnort: Regensburg
Kontaktdaten:

Re: 3 Unbekannte effizient ermitteln

Beitrag von Thomas »

// Update - ich hab es etwas eingegrenzt, nur denke ich geht hier noch mehr:

Code: Alles auswählen

#CPU_FREQ = 125000000

#FREQ_OUT_MIN = 10    ; 10
#FREQ_OUT_MAX = 2000    ; wenns schnell genug ist: 2000000

Procedure Frequency(freq)
  
  temp1.f = 0
  temp2.f = 0
  calc_div_int    = 1
  calc_top        = 0
  calc_div_frac   = 0
  calc_freq_out.f = 0
  calc_error.f = 0
  calc_error_alt.f = freq
  
  For n_div_int = 1 To 255        ; 0 bis 255 => 0 Timer aus
    For n_div_frac = 0 To 15       ; 0 bis 15
      temp1 = #CPU_FREQ / freq / (n_div_int + (n_div_frac/16))
      If temp1 < 65536
        
        
        For n_top = temp1-1 To temp1+1       ; 0 bis 65535 => ab 1 machts Sinn, vorher ists iwie aus
          calc_freq_out = #CPU_FREQ / ((n_top + 1) * (n_div_int + (n_div_frac/16)))
          calc_error = Abs(Abs(freq)-Abs(calc_freq_out))
          
          ; Debug "freq: " + StrF(freq) + " calc_freq_out: " + StrF(calc_freq_out, 6) + " n_div_int: " + StrF(n_div_int) + " n_div_frac: " + StrF(n_div_frac) + " n_top: " + StrF(n_top)
          
          If calc_error <= calc_error_alt
            If (calc_error_alt = 0 And calc_div_frac = 0)
            Else
              calc_error_alt = calc_error
              calc_div_frac = n_div_frac
              calc_div_int = n_div_int
              calc_top = n_top
            EndIf
          EndIf
        Next
      EndIf  
    Next
  Next
      
  DIV_INT         = calc_div_int
  TOP             = calc_top
  DIV_FRAC        = calc_div_frac
  FREQ_OUT.f      = #CPU_FREQ / ((TOP + 1) * (DIV_INT + (DIV_FRAC/16)))   ; Formel aus Datenblatt RP2040 Seite 530
  
  error_diff.f    = freq - FREQ_OUT
  error_perc.f    = 100 / freq * error_diff
  error_ppm       = error_perc * 10000
  
  AddGadgetItem(0, -1, Str(freq)+Chr(10)+StrF(DIV_INT)+Chr(10)+StrF(DIV_FRAC)+Chr(10)+StrF(TOP)+Chr(10)+StrF(FREQ_OUT)+Chr(10)+StrF(error_diff)+Chr(10)+Str(error_ppm))

EndProcedure

If OpenWindow(0, 100, 100, 1000, 1000, "Frequenz-Calc", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    ListIconGadget(0, 5, 5, 990, 990, "Freq Soll", 200, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
    AddGadgetColumn(0, 1, "DIV_INT", 100)
    AddGadgetColumn(0, 2, "DIV_FRAC", 100)
    AddGadgetColumn(0, 3, "TOP", 100)
    AddGadgetColumn(0, 4, "FREQ_OUT", 200)
    AddGadgetColumn(0, 5, "error_diff", 100)
    AddGadgetColumn(0, 6, "error_ppm", 100)
    
    ; Einzelne Frequenz
    ; Frequency(18480680)
    
    ; Frequenz-Liste
    StartTime.q = ElapsedMilliseconds()     
    For f = #FREQ_OUT_MIN To #FREQ_OUT_MAX
      Frequency(f)
      Debug "Frequenz: "+Str(f)+ "     Millisekunden: "+Str(ElapsedMilliseconds()-StartTime)
    Next
 
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf
v6.00
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: 3 Unbekannte effizient ermitteln

Beitrag von Nino »

Tatsächlich muss nur 1 Unbekannte ermittelt, genauer gesagt berechnet werden. :-)
f = #CPU_FREQ / ((TOP + 1) * (DIV_INT + (DIV_FRAC / 16)))
Die Lösung ist einfach, wenn die Formel umgestellt wird.
f ist ja bekannt, auf der linken Seite sollte aber etwas stehen, was unbekannt ist.
Umgeformt:

Code: Alles auswählen

TOP = #CPU_FREQ / (f * (DIV_INT + (DIV_FRAC / 16))) - 1
Als Nebenbedingung haben wir: Wenn möglich DIV_FRAC = 0. Das ergibt:

Code: Alles auswählen

TOP = #CPU_FREQ / (f * DIV_INT) - 1
Also:
  • DIV_FRAC = 0 setzen
  • Für DIV_INT irgendeinen gültigen Wert wählen.
  • Mit Hilfe der letzten Formel oben den dazu gehörenden Wert für TOP berechnen.
:)
Benutzeravatar
Thomas
Beiträge: 893
Registriert: 06.07.2006 19:24
Wohnort: Regensburg
Kontaktdaten:

Re: 3 Unbekannte effizient ermitteln

Beitrag von Thomas »

Vielen Dank, probier ich gleichaus :mrgreen:
v6.00
Antworten