Code GRAY

Programmation d'applications complexes
Avatar de l’utilisateur
PhM
Messages : 118
Inscription : dim. 08/déc./2019 10:50

Code GRAY

Message par PhM »

Bonjour,

Pour la réalisation d'un potentiomètre numérique pour un projet de table de mixage audio, j'ai besoin de connaître la position absolue du curseur numérique (encodeur).

Pour cela, j'ai besoin d'une grille rectiligne codée en code Gray qui est plus adaptée que le code binaire dans une application d'encodeur à valeur absolue qui lit la position du curseur à la différence d'un encodeur incrémental qui ne donne que des impulsions d'un mouvement (rotatif ou rectiligne). Je ne rentre pas plus ici dans la réalisation du potentiomètre qui est fait d'électronique avec des capteurs optiques (RX/TX).

Image

Je détaille ici le programme qui permet l'impression d'une bande rectiligne codée en code Gray de 2 à 10 bits suivant les besoins de chacun. Le programme n'est pas très ergonomique mais, suivant les paramètres modifiables indiqués en début permettront de s'adapter au mode d'impression choisi.
exemple en 8 bits:

Image


La base du décodage de décimale à binaire puis de binaire vers le code Gray est le suivant :

Code : Tout sélectionner

; ---------------------------------------------------
; 8 bits Decimal to Binary to Gray Code translation
;           Philippe Mijon - mai 2023
;                PureBasic 6.01
; ---------------------------------------------------

Debug "Décimale   Code Binaire    Code Gray"

For decimale = 0 To 255     ; 8 bits (0 à 7)
  
  binaire$ = RSet(Bin(decimale), 8, "0")
  
  B0 = Val(Mid(binaire$, 8, 1))
  B1 = Val(Mid(binaire$, 7, 1))
  B2 = Val(Mid(binaire$, 6, 1))
  B3 = Val(Mid(binaire$, 5, 1))
  B4 = Val(Mid(binaire$, 4, 1))
  B5 = Val(Mid(binaire$, 3, 1))
  B6 = Val(Mid(binaire$, 2, 1))
  B7 = Val(Mid(binaire$, 1, 1))
  
  ; Formules XOR (!) pour la conversion Bin to Gray
  G7 = B7
  G6 = B7 ! B6
  G5 = B6 ! B5
  G4 = B5 ! B4
  G3 = B4 ! B3
  G2 = B3 ! B2
  G1 = B2 ! B1
  G0 = B1 ! B0
  
  Gray$ = Str(G7) + Str(G6) + Str(G5) + Str(G4) + Str(G3) + Str(G2) + Str(G1) + Str(G0)
  
  Debug "    " + Str(decimale) + "            " + binaire$ + "         " + Gray$
  
Next

End
Le programme complet utilisé est :

Nota : bien que fonctionnant parfaitement pour l'application visée, je ne suis pas certain que ma procédure BINtoGRAY soit optimisée suivant le nombre de bits choisi. Il aurait peut-être pu être amélioré au travers de l'utilisation d'une procédure de récursivité...

Code : Tout sélectionner

; -------------------------------------------------------------------------------------------------------
;                    Graphique rectiligne coder en code Gray de 2 à 10 bits
;                       Philippe Mjon - mai 2023 PureBasic 6.01 LTS (x64)
; -------------------------------------------------------------------------------------------------------
;               l'image enregistrée est au format jpg ou png et placer sur le bureau
;     à la fin du traitement, elle s'ouvre automatiquement avec votre lecteur d'images jpg/png
; -------------------------------------------------------------------------------------------------------
;
;                                  Paramétres modifiables
; -------------------------------------------------------------------------------------------------------
Nbits       = 8                   ; valeur comprise entre 2 et 10 bits de données
Echelle     = 1                   ; échelle de l'image enregistrée (valeur supérieure ou égale à 1)
Box_x       = 20                  ; dimension en x du "carré" de chaque bit en pixels (par défaut 20)
Box_y       = 20                  ; dimension en y du "carré" de chaque bit en pixels (par défaut 20)
ecart       = 1                   ; épaisseur de la grille séparant les bits (0 sans grille visible)
formatIMAGE = 1                   ; format de l'image enregistrée : 0=png ou 1=jpg (qualité 10)          

; -------------------------------------------------------------------------------------------------------

Global B0, B1, B2, B3, B4, B5, B6, B7, B8, B9
Global Gray$
Global BIN$
Declare BINtoGRAY (Nbits)

; dimensions adaptées de l'image enregistrée suivant le Nb de bits (pixels)
If Nbits = 2
  xx= 75 : yy = 82
ElseIf  Nbits = 3
  xx= 100 : yy = 165
ElseIf  Nbits = 4
  xx= 120 : yy = 335
ElseIf  Nbits = 5
  xx= 145 : yy = 670
ElseIf  Nbits = 6
  xx= 170 : yy = 1345
ElseIf  Nbits = 7
  xx= 195 : yy = 2690
ElseIf  Nbits = 8
  xx= 215 : yy = 5375
ElseIf  Nbits = 9
  xx= 240 : yy = 10750
ElseIf  Nbits = 10
  xx= 265 : yy = 21500
EndIf

UsePNGImageEncoder()
UseJPEGImageEncoder()

; -------------------------------------------------------------------------------------------------------

If Nbits <11 And Nbits >1     ; graphique Gray possible de 2 à 10 bits
  
  If OpenWindow(0, 10, 10, 250, 350, "Code Gray rectiligne de " + Nbits + " bits", #PB_Window_SystemMenu)   
    
    If CreateImage(0, xx, yy) And StartDrawing(ImageOutput(0))
      
      Nbdec = Pow(2,Nbits)-1
      y = 0 : x = 0
      
      For g = 0 To Nbdec
        
        BIN$ = RSet(Bin(g), Nbits, "0")       ; DEC to BIN
        BINtoGRAY (Nbits)                     ; BIN to GRAY (XOR)
        
        For i = 1 To Nbits
          
          If Mid(Gray$, i, 1) = "1"
            coul = 0
          Else
            coul = 255
          EndIf
          
          Box(x, y, Box_x, Box_y, RGB(coul, coul, coul))      
          x + Box_y + ecart
          
        Next
        
        DrawText(x+20, y, Str(g), RGB(200, 200, 200))
        x = 0
        y + Box_y + ecart
        
      Next
      
      StopDrawing()
      
      ImageGadget(0, 0, 0, xx, yy, ImageID(0))
      ResizeImage(0, xx*Echelle, yy*Echelle)
      
      If formatIMAGE = 0
        ; Image enregistrée au format png
        SaveImage(0, GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + ".png", #PB_ImagePlugin_PNG)
        RunProgram(GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + ".png")
      ElseIf formatIMAGE = 1
        ; Image enregistrée au format jpg en qualité 10
        SaveImage(0, GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + ".jpg", #PB_ImagePlugin_JPEG, 10)
        RunProgram(GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + ".jpg")
      EndIf
      
    EndIf
    
    Repeat
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
    
  EndIf
  
Else
  
  MessageRequester("Erreur", "Le nombre de bits doit être compris entre 2 et 10 !", #PB_MessageRequester_Warning)
  End
  
EndIf

End

; -------------------------------------------------------------------------------------------------------

Procedure BINtoGRAY (Nbits) ; Traitements adaptés XOR (!) pour la conversion Bin to Gray
  
  B0 = Val(Mid(BIN$, Nbits, 1))
  B1 = Val(Mid(BIN$, Nbits-1, 1))
  B2 = Val(Mid(BIN$, Nbits-2, 1))
  B3 = Val(Mid(BIN$, Nbits-3, 1))
  B4 = Val(Mid(BIN$, Nbits-4, 1))
  B5 = Val(Mid(BIN$, Nbits-5, 1))
  B6 = Val(Mid(BIN$, Nbits-6, 1))
  B7 = Val(Mid(BIN$, Nbits-7, 1))
  B8 = Val(Mid(BIN$, Nbits-8, 1))
  B9 = Val(Mid(BIN$, Nbits-9, 1))
  
  If Nbits = 2 
    G1 = B1
    G0 = B1 ! B0  
    Gray$ = Str(G1) + Str(G0)
    
  ElseIf Nbits = 3
    G2 = B2
    G1 = B2 ! B1
    G0 = B1 ! B0  
    Gray$ = Str(G2) + Str(G1) + Str(G0)
    
  ElseIf Nbits = 4
    G3 = B3
    G2 = B3 ! B2
    G1 = B2 ! B1
    G0 = B1 ! B0  
    Gray$ = Str(G3) + Str(G2) + Str(G1) + Str(G0) 
    
  ElseIf Nbits = 5 
    G4 = B4
    G3 = B4 ! B3
    G2 = B3 ! B2
    G1 = B2 ! B1
    G0 = B1 ! B0  
    Gray$ = Str(G4) + Str(G3) + Str(G2) + Str(G1) + Str(G0)
    
  ElseIf Nbits = 6
    G5 = B5
    G4 = B5 ! B4
    G3 = B4 ! B3
    G2 = B3 ! B2
    G1 = B2 ! B1
    G0 = B1 ! B0  
    Gray$ = Str(G5) + Str(G4) + Str(G3) + Str(G2) + Str(G1) + Str(G0)
    
  ElseIf Nbits = 7 
    G6 = B6
    G5 = B6 ! B5
    G4 = B5 ! B4
    G3 = B4 ! B3
    G2 = B3 ! B2
    G1 = B2 ! B1
    G0 = B1 ! B0  
    Gray$ = Str(G6) + Str(G5) + Str(G4) + Str(G3) + Str(G2) + Str(G1) + Str(G0)
    
  ElseIf Nbits = 8    
    G7 = B7
    G6 = B7 ! B6
    G5 = B6 ! B5
    G4 = B5 ! B4
    G3 = B4 ! B3
    G2 = B3 ! B2
    G1 = B2 ! B1
    G0 = B1 ! B0
    Gray$ = Str(G7) + Str(G6) + Str(G5) + Str(G4) + Str(G3) + Str(G2) + Str(G1) + Str(G0)
    
  ElseIf Nbits = 9  
    G8 = B8
    G7 = B8 ! B7
    G6 = B7 ! B6
    G5 = B6 ! B5
    G4 = B5 ! B4
    G3 = B4 ! B3
    G2 = B3 ! B2
    G1 = B2 ! B1
    G0 = B1 ! B0
    Gray$ = Str(G8) + Str(G7) + Str(G6) + Str(G5) + Str(G4) + Str(G3) + Str(G2) + Str(G1) + Str(G0)
    
  ElseIf Nbits = 10 
    G9 = B9
    G8 = B9 ! B8
    G7 = B8 ! B7
    G6 = B7 ! B6
    G5 = B6 ! B5
    G4 = B5 ! B4
    G3 = B4 ! B3
    G2 = B3 ! B2
    G1 = B2 ! B1
    G0 = B1 ! B0
    Gray$ = Str(G9) + Str(G8) + Str(G7) + Str(G6) + Str(G5) + Str(G4) + Str(G3) + Str(G2) + Str(G1) + Str(G0)
    
  EndIf
  
EndProcedure

boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Code GRAY

Message par boddhi »

Bonsoir,

Je n'ai pas étudié ton code en détail.
Toutefois, puisque tu travailles avec des bits, il serait intéressant que tu utilises les opérateurs "<<", ">>" et "&".
Cela t'évitera les étapes d'affectation de valeurs numériques en valeurs chaînes pour les réaffecter ensuite en valeurs numériques :

Un petit exemple qui évite d'utiliser la fonction Bin() :

Code : Tout sélectionner

Valeur.a=135
Debug Bin(Valeur)

For Compteur.a=0 To 7
  Debug "Octet "+Compteur+" = "+Str(Valeur >> Compteur & 1) ; Str() n'est utilisée ici que pour permettre l'affichage d'une concaténation d'une chaîne et d'une opération mathématique dans le débogueur
  If Valeur>>Compteur&1
    Debug "  Bit = 1"
  Else
    Debug "  Bit = 0"
  EndIf
Next
 
Autres petites remarques qui n'engagent que moi et qui ne mettent nullement en cause l'aspect fonctionnel actuel de ton code :
• Quand il y a plusieurs ElseIf dans une condition de vérification constante d'une expression, je préfère privilégier Select...Case...EndSelect

Code : Tout sélectionner

If Valeur=0
  ...
ElseIf Valeur=1
  ...
ElseIf Valeur=2
  ...
ElseIf ...
  ...
EndIf
peut être remplacé par

Code : Tout sélectionner

Select Valeur
  Case 0
    ...
  Case 1
    ...
  Case 2
    ...
  Case ...
    ...
EndIf
 
• Quand il est possible de factoriser l'appel à une ou plusieurs commandes, fonctions, procédures, etc. pour lesquelles seul un ou plusieurs termes diffèrent, je préfère utiliser des variables pour lesdits termes. Cela permet d'éviter des différences ou des erreurs dans l'une des formulations.
 
Ainsi, ton code :

Code : Tout sélectionner

If formatIMAGE = 0
  ; Image enregistrée au format png
  SaveImage(0, GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + ".png", #PB_ImagePlugin_PNG)
  RunProgram(GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + ".png")
ElseIf formatIMAGE = 1
  ; Image enregistrée au format jpg en qualité 10
  SaveImage(0, GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + ".jpg", #PB_ImagePlugin_JPEG, 10)
  RunProgram(GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + ".jpg")
EndIf
 
pourrait, par exemple, "se factoriser"/être optimisé de la manière suivante :"

Code : Tout sélectionner

Fichier.s=GetUserDirectory(#PB_Directory_Desktop) + "Gray " + Nbits + "_" + Nbdec + "."
If formatIMAGE = 0  ; On peut aussi utiliser un Select...EndSelect
  Fichier+"png"
  TypePlugin.l=#PB_ImagePlugin_PNG
ElseIf formatIMAGE = 1
  Fichier+"jpg"
  TypePlugin=#PB_ImagePlugin_JPEG
Endif

SaveImage(0, Fichier, TypePlugin, 10) ; Ici, pas grave s'il y a 10 et que c'est du PNG, PB n'en tiendra pas compte
RunProgram(Fichier)
Edit du 09/05/23 : Correction du typage de la variable TypePlugin (de .a en .l)
Dernière modification par boddhi le mar. 09/mai/2023 15:59, modifié 1 fois.
Avatar de l’utilisateur
PhM
Messages : 118
Inscription : dim. 08/déc./2019 10:50

Re: Code GRAY

Message par PhM »

Bonjour boddhi et merci pour tes remarques averties.
Je testerai tout cela pour le plaisir de mieux faire...
Avatar de l’utilisateur
GallyHC
Messages : 1708
Inscription : lun. 17/déc./2007 12:44

Re: Code GRAY

Message par GallyHC »

Bonjour,

Un vieux test qui peu, peut-être vous aider.

Code : Tout sélectionner

EnableExplicit

Enumeration
  #Window
  #Canvas
EndEnumeration

#DEFINE_SINCOS_MAX  = 359

Define.i i
Global Dim _Sin.f   (#DEFINE_SINCOS_MAX)
Global Dim _Cos.f   (#DEFINE_SINCOS_MAX)
For i=0 To #DEFINE_SINCOS_MAX
  _Sin(i) = Sin(i * (2 * #PI / 360))
  _Cos(i) = Cos(i * (2 * #PI / 360))  
Next i

Procedure ACircle(x.i, y.i, radius.d, antilenh.d, color.i)
  
  Define.l lR, lG, lB
  Define.d dNormal, dValue

  If radius <= 0
    radius = 0.00001
  EndIf
  If antilenh <= 0
    antilenh = 0.00001
  EndIf

  ResetGradientColors()
  DrawingMode(#PB_2DDrawing_AlphaBlend | #PB_2DDrawing_Gradient)

  lR      = Red  (color)
  lG      = Green(color)
  lB      = Blue (color)
  dNormal = 1 / radius
  dValue  = 1 - antilenh * dNormal

  GradientColor(   0  , RGBA(lR, lG, lB, 255))
  GradientColor(dValue, RGBA(lR, lG, lB, 255))
  GradientColor(   1  , RGBA(lR, lG, lB, 0  ))

  CircularGradient(x, y, radius)
  Circle          (x, y, radius)
  
EndProcedure

Procedure DrawValue(x.l, y.l, rayonx, rayony .l, segment.l, ilen.l, ldensity.l, value)
  
  Define.i i, j, new_x, new_y
  If segment < 4
    segment = 4
  EndIf
  
  For i = 20 To 340 Step 20
    j = (i + 180) % #DEFINE_SINCOS_MAX
    new_x = x + _Sin(j) * (rayonx - ilen)
    new_y = y - _Cos(j) * (rayony - ilen)
    If i < value
      ACircle(new_x,        new_y,        8, 8, RGB(255,0,0))
      ACircle(new_x,        new_y,        5, 2, RGB(255,0,0))
      ACircle(new_x,        new_y,        2, 2, RGB(255,100,100))
    Else
      ACircle(new_x,        new_y,        5, 2, RGB(100,0,0))
      ACircle(new_x,        new_y,        2, 2, RGB(200,0,0))
    EndIf
  Next i

EndProcedure

Procedure DrawCircular(WindowW, WindowH, x, y)
  
  Define.i ircircle = WindowW / 7
  Define.i idensity = WindowW / 100
  Define.i dx       = x  -  (WindowW * 0.5)
  Define.i dy       = y  -  (WindowW * 0.5)
  Define.i F        = Degree(ATan2(dx, dy)) - 80
  If F < 0
    F = 360 + F
  EndIf
  Define.i ivalue   = (F + 260) % #DEFINE_SINCOS_MAX
  Define new_x      = (WindowW * 0.5) - _Cos(ivalue) * ((ircircle * 2) - ircircle + 15)
  Define new_y      = (WindowH * 0.5) - _Sin(ivalue) * ((ircircle * 2) - ircircle + 15)

  StartDrawing(CanvasOutput(#Canvas))
    Box(0, 0, WindowW, WindowH, $f0f0f0)
    ACircle(WindowW*0.5,        WindowH*0.5,        WindowW * 0.5 - ircircle + 21, 2, RGB(0,   0,  0))
    ACircle(WindowW*0.5,        WindowH*0.5,        WindowW * 0.5 - ircircle + 20, 2, RGB(60, 60, 60))
    ACircle(WindowW*0.5 + 6,    WindowH*0.5 + 10,   WindowW * 0.5 - ircircle + 0, 2,  RGB(50, 50, 50))
    ACircle(WindowW*0.5 + 4,    WindowH*0.5 + 8,    WindowW * 0.5 - ircircle + 0, 2,  RGB(40, 40, 40))
    ACircle(WindowW*0.5 + 2,    WindowH*0.5 + 6,    WindowW * 0.5 - ircircle + 0, 2,  RGB(30, 30, 30))
    DrawValue(WindowW*0.5,      WindowH*0.5,        WindowW * 0.5 - (ircircle / 2) + 9, WindowW * 0.5 - (ircircle / 2) + 9, 100, ircircle / 2, idensity, F)
    ACircle(WindowW*0.5,        WindowH*0.5,        WindowW * 0.5 - ircircle,     2,  RGB(  0,   0,   0))
    ACircle(WindowW*0.5,        WindowH*0.5,        WindowW * 0.5 - ircircle - 1, 2,  RGB(255, 255, 255)) 
    ACircle(WindowW*0.5,        WindowH*0.5,        WindowW * 0.5 - ircircle - 3, 2,  RGB(200, 200, 200)) 
    ACircle(WindowW*0.5,        WindowH*0.5,        WindowW * 0.5 - ircircle - 5, 2,  RGB(230, 230, 230)) 
    ACircle(new_x,        new_y,        ircircle - 5, 2,  RGB(  0,   0,   0))
    ACircle(new_x,        new_y,        ircircle - 6, 2,  RGB(160, 160, 160))
    ACircle(new_x,        new_y,        ircircle - 7, 2,  RGB(180, 180, 180))
    ACircle(new_x,        new_y,        ircircle - 10, 2, RGB(200, 200, 200))
  StopDrawing()

EndProcedure

Define.i event, eveng, evenp, WindowW = 200, WindowH = 200
  
If OpenWindow(#Window, 0, 0, WindowW, WindowH, "potentiomètre circulaire", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(#Canvas, 0, 0, WindowW, WindowH, #PB_Canvas_Keyboard)
  DrawCircular(WindowW, WindowH, WindowW * 0.5, WindowH)
  
  Repeat
    event = WaitWindowEvent()
    evenp = EventType()
    eveng = EventGadget()
    If eveng = #Canvas
      If evenp = #PB_EventType_LeftButtonDown Or (evenp = #PB_EventType_MouseMove And GetGadgetAttribute(#Canvas, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton)
        DrawCircular(WindowW, WindowH, GetGadgetAttribute(#Canvas, #PB_Canvas_MouseX), GetGadgetAttribute(#Canvas, #PB_Canvas_MouseY))
      EndIf
    EndIf
  Until Event = #PB_Event_CloseWindow
EndIf
End
Cordialement,
Configuration : Tower: Windows 10 (Processeur: i7 "x64") (Mémoire: 16Go) (GeForce GTX 760 - 2Go) - PureBasic 5.72 (x86 et x64)
Avatar de l’utilisateur
PhM
Messages : 118
Inscription : dim. 08/déc./2019 10:50

Re: Code GRAY

Message par PhM »

Merci pour cet exemple mais, je n'avais pas l'intention de créer des formes circulaires pour mon besoin de potentiomètre à déplacement rectiligne.
Néanmoins, je garde l'exemple qui pourra toujours servir...
Répondre