DAQ: Entrées-sorties numériques avec le UM232R

Ce document fait partie d’une série de tutoriels sur l’acquisition de données et le contrôles d’appareils. La partie 1 est disponible ici. Le présent document se retrouve à la fois dans le repo public d’enseignement de dccote et sur le site Web de DCC Lab. Le format Markdown est celui supporté par Typora.io. Une version PDF est disponible.


Le monde numérique

La question la plus simple se répond par oui ou par non. De la même façpn, la tâche la plus simple que l’on peut faire avec un appareil d’acquisition ou de contrôle est de lui faire dire oui ou non. Il y a plusieurs questions qui demandent des réponses binaires exclusives: vrai ou faux, 1 ou 0, « Je suis prêt » ou « Je ne suis pas prêt », etc…

Les pionniers de l’informatique ont dû concevoir un système qui permettrait de représenter les valeurs sans ambiguïtés pour les stocker et les manipuler avec un circuit logique. Pour bien des raisons qui viennent à la fois de l’ingéniosité (il fallait y penser) et de l’opportunité technologique (le transistor semionducteur et son potentiel de fabrication et miniaturisation), il est devenu apparent qu’un système basé sur une représentation binaire des nombres et de leur transformation par des circuits logiques serait idéal. Les premiers circuits électroniques de type TTL (ou Transistor-Transistor-Logic) dans les années 60 sont devenus les premiers circuits logiques, c’est-à-dire des circuits traitant des données d’entrée en représentation binaire pour produire de l’information à la sortie, aussi sous forme binaire. L’ensemble des circuits logiques permet d’implémenter toutes les opérations nécessaires à un calcul. Nous reviendrons plus tard au très passionnant côté hardware de la naissance de l’informatique, mais pour l’instant il suffira de savoir que la représentation réelle des nombres et des instructions dans les chips est sous forme binaire: une tension est à 5V est VRAI (ou 1) et une tension à 0V est FAUX (ou 0)1.

Bits et octets

Un bit a 2 valeurs possibles, 0 ou 1. C’est la plus petite quantité d’information que nous puissions avoir sur quelque chose2. Une collection ordonnée de 8 bits est un octet. Chaque bit pouvant avoir deux valeurs, un octet peut donc avoir $2\times2\times2\times2\times2\times2\times2\times2=2^8 = 256$ valeurs différentes. Par commodité, nous représentons cela sous forme de nombre en base 2 (binaire). La séquence suivante de 8 bits 0 0 1 0 0 1 0 1 peut être prise pour avoir une valeur de:

0 ⨉ 2^7 + 0 ⨉ 2^6 + 1 ⨉ 2^5 + 0 ⨉ 2^4 + 0 ⨉ 2^3 + 1 ⨉ 2^2 + 0 ⨉ 2^1 + 1 ⨉ 2^0 = 37 dec.

Comprenons bien que l’interprétation d’un octet en nombre entier allant de 0 à 255 n’est rien de plus qu’une interprétation: on pourrait aussi interpréter ce nom autrement. Par exemple, on pourrait déterminer que si le dernier bit est 1, une sortie est activée par exemple.

Dans de nombreux textes, une valeur binaire est notée %00100101 (Python: 0b00100101) pour la différencier de la valeur décimale 100101, qui a pour valeur «cent mille cent un». Cependant, la notation binaire n’est pas pratique: elle prend beaucoup de place lorsqu’elle est écrite et il est difficile d’évaluer rapidement une valeur pour le non-expert. Par conséquent, une notation raccourci est l’hexadécimale ou la notation en base 16. Les « chiffres » hexadécimaux sont {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F}. Le nombre ci-dessus peut donc être ré-écrit sous la forme %10100101 = 10 ⨉ 16^1 + 5 ⨉ 16^0 ou 0xA5 en hexadécimal, le préfixe 0x (souvent seulement x) désignant hexadécimal. Évidemment, puisque la base 16 = 2^4on voit que la notation hexadécimale permet de regrouper les bits par groupe de 4: les moins significatifs (à droite) sont représentés par le chiffre 5 = %0101​ et les plus significatifs (à gauche) sont représentés par la lettre 0xA = %1010.

L’importance de la notation binaire vient du fait que tout langage de programmation que l’on utilisera pour communiquer avec notre appareil devra invariablement passer par des représentations en binaires et des octets, puisque c’est la représentation interne naturelle de l’ordinateur.

Exercices

    1. Écrivez la valeur suivante en binaire, décimal et hexadécimal:
      a. 12
      b. 0x45
      c. %1001101
      d. 230
      e. 0xF3
    2. Effectuez les opérations suivantes:
      a. % 00101101 +% 00100001
      b. 0xF6 – 0x41
      c. 0x29 – 0x56
    3. Multipliez %00111001 par 2, 4 et 8 et écrivez les réponses en binaire.
    4. Divisez %00111001 par 2 et écrivez la réponse en binaire.
    5. Multipliez 0xF3 par 16.

Premier branchement

Le plan ici est d’obtenir un système pour expérimenter. Il y aura bien sûr des moments où l’on pourra se questionner sur le fonctionnement exact d’un ou l’autre aspect. Cependant, pour l’instant, il s’agit simplement de mettre en place un « banc d’expérimentation » pour aider à la compréhension, pour démystifier certains aspects et pour permettre l’exploration. Ainsi, on utilise le module de communication UM232R de FTDI (specifications) de type UART. Ce module est très intéressant pour plusieurs raisons:

    1. Il se branche simplement dans le port USB de n’importe quel ordinateur
    2. Il ne demande aucun branchement supplémentaire pour être au moins fonctionnel
    3. Il permet de construire un systeme pour communiquer avec d’autres appareils. En fait, il permet de mettre à jour les vieux systèmes RS232 pour USB.
    4. Les drivers (pilotes en français) de FTDI existent pour toutes les plateformes
    5. Les puces USB de FTDI sont utilisées dans un très grand nombre d’appareils
    6. Les librairies de FTDI sont disponibles, simples et sans bugs, et sont supportées par Python.

Pour commencer, il faut se procurer le fameux module UM232R. La façon la plus simple, venez me voir à mon bureau POP-2141 et demandez-le moi, j’en ai quelques uns. Sinon, on peut les commander pour 28$ chez Digi-Key , numéro de pièce Digi-Key 768-1019-ND, ou FTDI UM232R. Idéalement, un petit kit de breadboard comme le 438-1047-ND serait acheté en même temps, mais n’importe quel breadboard fait l’affaire. On remarque:

    1. Il y a 24 lignes (ou pins en anglais), on commence a compter en haut à gauche en tournant dans le sens trigonométrique.
    2. Plusieurs lignes sont identifiées RST, GND, VCC, VIO mais d’autres sont identifiées avec des termes génériques DB0, DB1, etc…
    3. Le manuel indique qu’il y a une ligne nommée TXD (transmission data) et une nommée RXD (receiving data)
    4. Il y a une ligne RESET. Le manuel indique clairement que mettre cette ligne à 0V forcera un « reset » de la puce.
    5. Il y a deux lignes configurées par défaut CB0 et CB1 pour servir d’indicateurs de transmission et de réception avec une DEL.
    6. Un groupe de lignes est identifié comme faisant du hardware handshake: DTR, RTS, DSR, DTS
    7. Bien que nous soyions en 2018, il reste des lignes identifiées comme: Ring Indicator (RI, ligne 6) Data Carrier Detect Control Input (DCDC, ligne de tonalité d’un modem)

 

 

Pour l’instant, mes instructions seront pour macOS/Linux car c’est ce que j’utilise. D’autres instructions devraient suivre pour les autres plateformes, même si la tâche me donne plutôt le goût d’aller chez le dentiste me faire enlever la totalité des dents d’en bas. Sans anesthésie.

Expérience 1: brancher

Si vous branchez un câble USB dans le module, la magie du standard USB3 devrait faire son oeuvre si le driver FTDI est installé (souvent standard, sinon obtenez et installez-le). Votre « puce » apparaîtra comme un port série ou Virtual Comm Port (VCP). Les systèmes qui utilisent le standard POSIX (macOS4 et Linux) le montreront dans /dev/:

Nestor:anaconda2 dccote$ ls -l /dev/*usbserial*
crw-rw-rw-  1 root  wheel   18,  51 14 Oct 22:42 /dev/cu.usbserial-FTCBGW24
crw-rw-rw-  1 root  wheel   18,  50 14 Oct 22:42 /dev/tty.usbserial-FTCBGW24
Nestor:anaconda2 dccote$

Le port série en POSIX apparait en deux formes pour des raisons historiques: cu (callout) et tty (teletype). Nous prendrons cu* pour la communication avec les appareils. Le nom du port série (ici cu.usbserial-FTCBGW24 est programmé par FTDI directement dans la puce à la fabrication). La valeur FTCBGW24 est un numéro de série unique à chaque puce, donc la vôtre sera différente de la mienne.

Expérience 2: débrancher

Si on débranche le câble USB, le port de communication associé au module disparaitra du système car le module n’est plus disponible. On peut faire la même chose à l’aide d’un fil en connectant la ligne RST au GND pour forcer un reset du module. Lorsqu’on le fait, on voit:

Nestor:anaconda2 dccote$ ls -l /dev/*usbserial*
ls: /dev/*FT*: No such file or directory
Nestor:anaconda2 dccote$

Avant de continuer, on enlève la connexion entre RST et GND .

Expérience 3: parler

Le langage de référence dans ces tutoriels est Python, non pas pour sa puissance, sa convivialité ou l’élégance de sa syntaxe mais bien pour son universalité. La façon la plus rapide d’être opérationnel est de télécharger Anaconda2. Vous aurez un environnement et la majorité des modules importants pour travailler, incluant un petit module appelé libftdi. Surprenamment, les routines de port serie PySerial n’y sont pas, on les installe avec: easy_install PySerial. Par la suite, les programmes Python qui suivent peuvent être exécuter.

Le UM232R est une puce pour la communication série de type UART (Universal Asynchronous Receiver-Transmitter), c’est à dire qu’elle sert à transférer l’information vers un récepteur un bit à la fois. Pour l’instant, ignorons les détails et concentrons-nous sur la ligne de transmission TXD (ou DB0). Dans Python, on peut ouvrir la communication avec cette puce UM232R par « le port série ». En effet, tout ce que l’on écrit sera sur la ligne de transmission TXD et tout ce qui est sur la ligne RXD nous pourrons lire par le port série.

import serial
import time

text = 'a'
path = '/dev/cu.usbserial-FTCBGW24'
try:
    port = serial.Serial(path, 9600)
    bytesWritten = port.write(text)
    if bytesWritten == len(text):
        print('Wrote to port: %s' % port.name)
    else:
        print('Error when writing to port: %s' % port.name)

    time.sleep(.1)
    port.close()
except IOError:
    print('Unable to open the port with path: %s' % path)
except:
    print('Unknown error')

Pour commencer, idéalement, on utilise un oscilloscope. Ne vous en faites pas, l’expérience suivante ne nécessite pas d’oscilloscope.

En exécutant le code précédent (python parler.py) avec une sonde d’oscilloscope sur la ligne TXD/DB0 (la premiere ligne en haut à gauche), on verra la ligne osciller entre 0V et 5V, comme sur l’image suivante. La ligne est d’abord à 5V, ensuite descend à 0 pour environ 100 µs, remonte à 5V, redescend pendant 400 µs, remonte pour 200 µs et finalement descend 100 µs et remonte pour rester à 5 V.

 

Nous verrons les details de cette communication UART plus tard, mais on remarque:

    1. environ 100 µs est en fait précisément 104 µs, c’est-à-dire 1/9600 de secondes,
    2. la lettre ‘a’ est codée comme le code ASCII 97. En binaire, 97 s’écrit %10000110,
    3. à partir de la première descente, il y a 9 périodes de 104 µs pour un total de 936 µs sur l’écran d’oscilloscope. Si on remplace les période de 104 µs à 5V par 1 et celle de 104 µs à 0V par zéro, on obtient 0 suivi de 10000110.

On comprendra donc que la communication envoie la lettre ‘a’ (code 97) à 9600 bits per seconde. Dans le cas de la puce ici présente qui implémente le protocole UART, la ligne commence toujours à 5V pour commencer, descend à 0 le temps d’un « coup d’horloge », et ensuite écrit la valeur en binaire:

Rien n’est connecté, donc notre module « parle dans le vide ». On vient de dire au module d’envoyer un « a » sur « ses lignes de sortie » mais cette information n’a pas été utilisée ou capté par personne, sauf notre oscilloscope.

Expérience 4: parler et illuminer

Ce n’est pas tout le monde qui a un oscilloscope. Pour visualiser un peu ce qui se passe, on peut utiliser une diode électroluminescente (DEL) et la connecter directement sur la ligne de transmission avec une résistance de 220 Ohms au ground. Pour bien voir ce qui se passe, on écrit plusieurs 0 en ligne, à la vitesse la plus lente permise (300 bits par seconde). Ainsi, la ligne TXD sera à 5V pour commencer, et descendra à 0 pour 30 ms, remontera brièvement à 5V pour descendre tout de suite, et ce 15 fois en ligne. La DEL sera allumée, ensuite essentiellement éteinte pendant 0.5 seconde (avec un minuscule petit flash de 3 ms à chaque 30 ms) et ensuite rallumée. Voir le video.

import serial
import time

data = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 
path = '/dev/cu.usbserial-FTCBGW24'
try:
    port = serial.Serial(path, 300)
    bytesWritten = port.write(data)
    if bytesWritten == len(data):
        print('Wrote to port: %s' % port.name)
    else:
        print('Error when writing to port: %s' % port.name)

    time.sleep(1)
    port.close()
except IOError:
    print('Unable to open the port with path: %s' % path)
except:
    print('Unknown error')

Expérience 5: écouter dans le vide

Si on essaie de lire le module, on n’obtiendra rien: la ligne read() ne complètera jamais (il n’y a pas de timeout, il est infini par défaut):

import serial

path = '/dev/cu.usbserial-FTCBGW24'
try:
    port = serial.Serial(path)
    text = port.read() ## Bloquera ici. Faites Ctrl-C pour quitter.
    port.close()
except IOError:
    print('Unable to open the port with path: %s' % path)
except:
    print('Unknown error')

En effet, le module n’est aucunement connecté à quoi que ce soit pour lire des données. Il peut transmettre, mais n’a rien à lire.

Expérience 6: écouter l’écho

On peut par contre prendre le module et le connecter pour qu’il s’écoute lui-même. En effet, on connecte la ligne de sortie (TXD) à la ligne d’entrée (RXD). Ainsi, en excéutant le code suivant, on pourra écrire « hello » et lire « hello » par la suite.

import serial

text = 'hello'
path = '/dev/cu.usbserial-FTCBGW24'
try:
    port = serial.Serial(path)
    bytesWritten = port.write(text)

    if bytesWritten == len(text):
        print('Wrote to port: %s' % port.name)
    else:
        print('Error when writing to port: %s' % port.name)

    echo = port.read(bytesWritten)

    if bytesWritten == len(echo):
        print('Read from port: %s' % port.name)
    else:
        print('Error when reading to port: %s' % port.name)

    port.close()
except IOError:
    print('Unable to open the port with path: %s' % path)
except:
    print('Unknown error')

On appelle ce mode le mode ECHO, puisque tout ce qui est écrit sur le port est lu sur le même port. En exécutant le code précédent, on obtient « hello » en lecture après avoir écrit « hello » sur le port série. Le mode ECHO permet de tester notre compréhension de plusieurs façons car nous savons toujours ce qui devrait être lu sur le port: il s’agira de ce que l’on vient d’écrire.

1 En fait, le standard TTL demande une tension supérieure à 2V pour être considéré comme un logique VRAI et moins de 0.8V pour un logique FAUX.
2 Bien qu’il soit omniprésent en informatique, le terme «bit» a été inventé par Claude Shannon, père de la théorie de l’information.
3 Le standard USB est un standard complexe qui n’est pas nécessaire de comprendre pour l’instant. Cependant, un bon ingénieur devra comprendre la reconnaissance des appareils, l’association des drivers, les classes, les vendor ID, product ID, la sérialisation des ports, les endpoints, etc… Bien sur, nous verrons tout cela plus loin lorsque ce sera nécessaire.
4 Apple fournit depuis macOS 10 un driver FTDI avec le système, mais celui-ci n’est pas directement de FTDI mais bien d’Apple. On peut tout de même installer le driver de FTDI. Cependant, depuis macOS 10, qui resserre la sécurité, le driver doit obtenir la permission de l’Administrateur pour pouvoir s’executer.

Tutoriel: Introduction au Contrôle

Au fil des années, j’ai développé le goût de la programmation des « appareils physiques ». J’aime contrôler les choses qui interagissent avec le monde extérieur: une platine de microscope, un laser, une carte d’acquisition, une caméra, une imprimante, etc. Ce goût s’est transformé en expertise au fil des années, et plusieurs étudiants viennent me voir (dans mon groupe, chez Bliq et dans les laboratoires d’enseignement) avec le goût d’en savoir plus. Ce n’est pas toujours facile de transférer ces connaissances, et c’est en partie par la façon dont je les ai acquises.

En effet, mes premiers contacts avec le hardware sont venus dès le Commodore-64 en 1983: l’achat de l’ordinateur chez Distribution au Consommateur était bien, mais l’achat par la suite d’une imprimante Star NX-1000 avait subitement décuplé la puissance de notre machine, qui autrement ne faisait que des dessins et des bips-bips sans vraiment laisser de traces ou changer quoi que ce soit dans le monde qui m’entourait. Si on envoyait les bonnes commandes à l’imprimante, elle bougeait, elle écrivait, elle dessinait. Un jour, j’ai été obligé de sortir un tournevis parce que le joystick n’arrêtait pas de se briser parce qu’on jouait toujours aux Olympiques (le saut à obstacles demandait de brasser le joystick de gauche à droite sans arrêt le plus rapidement possible). Je voulais jouer, je n’avais plus de joystick parce que ma soeur le brasssait trop fort: je n’y connaissais rien, mais en l’ouvrant, j’ai vu un morceau de plastique décollé, je l’ai recollé. C’était ma première réparation. Plus tard, plusieurs de mes amis et moi avons appris à utiliser la « Copie-21-secondes« , qui était une modification hardware du lecteur de disquette qui permettait d’outrepasser la protection en envoyant l’information lue directement à l’ordinateur au lieu de laisser le lecteur la traiter et la bloquer. J’avais des fils qui dépassaient de mon lecteur de disque. Je ne comprenais absolument rien à ce que ça faisait, mis à part que je pouvais copier tous les jeux que je voulais. C’était tout aussi puissant que mystérieux. Éventuellement, j’ai discuté de différentes choses avec d’autres gars de 12 ans sur les BBS de Québec (l’ancêtre d’Internet). Un jour, j’ai découvert ce qui s’appelait le Demo Scene: un monde où l’on s’échangeait des programmes qui n’avait que pour seul but d’impressioner par des prouesses techniques qui semblait dépasser les capacités de la machine. Le commodore-64 était la machine par excellence car elle était beaucoup plus puissante qu’elle n’en avait l’air. J’ai donc appris le langage assembleur du 6502 sur le C-64, en partie grâce à un livre obscur qu’un ami informaticien de ma mère à la Ville de québec m’a prêté. Mais surtout, j’ai passé mes soirées à programmer un peu n’importe quoi, n’importe comment: les interrupts vidéo, la « carte graphique » (ah ah), la chip SID pour le son etc…. C’était tout croche, mais c’était passionnant. À la fin, cependant, ça marchait. Genre. Je trippais.

Quand j’ai commencé mon doctorat en physique à l’Université de Toronto en 1995, il y avait un cours qui s’appelait Microprocessor Interfacing Techniques, donné par le très expérimenté et passionné Jim Drummond. Le but était de nous montrer comment faire l’interface entre le monde extérieur et nos ordinateurs. Un cours de rêve: nous avions les cours magistraux remplis d’anecdotes de Prof. Drummond, ensuite le laboratoire rempli de pièces, de chips, de breadboards, d’ordinateurs PC 8088 Turbo (rien de moins) et de cartes d’acquisition. De plus, nous avions la clef du local pour venir à toute heure du jour et de la nuit. J’ai donc finalement appris la logique TTL, les flip-flop, les compteurs synchrones et asynchrones, la numérisation, la sortie analogique, comment lire des feuilles de spécification d’un chip, etc… Les multiples bribes d’information glanées à gauche et à droite au fil des années ont fini par converger dans un tout cohérent: j’ai réussi à comprendre les petits détails les plus minuscules d’un circuit logique tout en étant capable de faire une abstraction suffisante en blocs fonctionnels pour finalement construire un système complet.

Avec le recul de 30 ans à taponner des ordinateurs, des circuits, péter des fusibles, briser des cartes d’acquisition, sauter des détecteurs, réparer des drivers de stepper motors pour graduer, ou passer des soirées entières juste pour faire allumer une DEL dans un circuit de base, je me rends compte que les étudiants qui viennent me voir n’ont pas souvent eu la chance d’expérimenter avec les ordinateurs. Ceci vient en partie du fait que les ordinateurs sont de plus en plus compliqués, et plusieurs couches de complexité ont été ajoutées depuis le C-64: les processeurs executent des opérations en parallèles, le fameux port d’imprimante ou de modem RS-232 a été remplacé par le port Universal Serial Bus, et les systèmes d’opération, la protection de mémoire, le preemptive multitasking (qui se rappelle du cooperative multitasking?), la sécurité font qu’aujourd’hui, il y a rarement « une case de mémoire » qui correspond à des lignes à l’arrière de l’ordinateur (ce que le C-64 avait), et l’ordre exact de l’exécution des opérations n’est pas toujours connue (ce qu’on pouvait contrôler facilement avec sys 49152 par exemple sur le C-64, la case de mémoire de prédilection pour les jeux). Ainsi, expérimenter en tant que novice devient de plus en plus difficile parce que la marche est beaucoup plus haute qu’en 1983 quand j’ai commencé, ce qui peut être intimidant.

J’espère donc, avec une série de petits tutoriels, faire une introduction de base au Contrôle d’Appareils. Cela va évidemment commencer par des tutoriels extrêmement simples mais qui devraient vous permettre d’acquérir les connaissances pour faire du Contrôle d’Appareils à travers une démarche basée sur l’expérimentation méthodique.

Dans le prochain article, nous commencerons par la construction très simple d’un minuscule circuit à base d’un chip de FTDI UM232R pour obtenir, de façon la plus simple possible, accès à des lignes numériques de sortie et d’entrée.

Les expressions régulières

## Les expressions régulières

Les expressions régulières permettent de reconnaître des patrons dans du texte et d’en extraire l’information. D’une curiosité de programmeur qui a pris racine avec awk et sed, mais surtout le langage Perl, elles sont maintenant standardisées et disponibles dans presque tous les langages de programmation. Il s’agit d’un outil puissant pour travailler avec toute forme de texte. Dans le cas du scientifique ou de l’ingénieur, je nomme les situations suivantes:

  • Traiter des séries de fichiers de façon générale
  • Isoler des dates

Par exemple: Vous voulez extraire le nom et prénom d’une personne dans le texte suivant:

Côté, Daniel

Vous voulez donc le mot avant la virgule, ensuite, après les espaces, l’autre mot. Vous pourriez lire les caractères un à un, mais qu’arrive-til si vous avez le nom suivant?

De Koninck, Yves

L’analyse peut devenir de plus en plus compliquée et tordue. Ainsi, plutôt que de lire les caractères un à un et de faire l’analyse, vous pouvez utiliser les expressions régulières qui ont été inventées justement pour décrire ce genre de patrons. Dans le cas présent, vous pourriez rapidement extraire le nom et prénom avec l’expression régulière suivante:

\s*(\S.+?),\s*(\S.*?)\s*

Les parenthèses dans les expression régulières représentent les matching groups. Dans notre cas, la première expression entre parenthèses est le nom, la deuxième le prénom, sans les espaces qui peuvent être présents ou non avant ou après le nom. Dans le premier cas, on aurait “Côté” et “Daniel”, alors qu’on aurait “De Koninck” et “Yves” dans le deuxième.

Le langage de base des expressions régulières

Dans leur forme la plus simple, une expression régulière est une suite de caractères avec un indicatif de répétition. Les parenthèses de capture permettent de garder le texte reconnu. On peut ensuite y référer d’une façon qui dépend de l’outil de programmation utilisé ($1, $2, … dans Perl, \1 \2, … dans la boite “Find” de TexWrangler, etc…).

Expression Signification Expression Signification
. N’importe quel caractère * 0 ou plusieurs fois
\s Espace blanc (ou tabulation) + 1 un plusieurs fois
\S Tout sauf un espace blanc ? 0 ou 1 fois
\d Un chiffre {n} n fois
\D Tout sauf un chiffre {n,m} entre n et m fois
^ Début de ligne *? 0 ou plusieurs fois, mais priorité au prochain patron
$ Fin de ligne () Parenthèses de capture
Lettre ou chiffre La lettre ou le chiffre (?:) Paranthèse de regroupement sans capture
\. Le point
\\ Le caractère \

Exemples d’expressions régulières

Objet recherché Expression
Un nom de fichier (.\*?)\\.(...)
Un nom de fichier avec le chemin complet (.\*?)/(.\*?)\\.(...)
Une date de la forme AAA-MM-JJ (\\d{4})-(\d\d)-(\d\d)
Un nom de fichier sous la forme fichier-XXX-YYY-ZZZ.tif ou XXX, YYY et ZZZ sont des chiffres fichier-(\\d{3})-(\\d{3})-(\\d{3})\\.tif{1,2}

Où trouve-t-on les regexps ?

MATLAB

Pour obtenir determiner si du texte coorespond a une expression

matchStr = regexp(filename, ’fichier-\d\d\d-\d\d\d-\d\d\d\.tif{1,2}’,'match')

matchStr aura chaque string qui correspond à l’expression. Pour extraire du texte avec les groupes de capture (i.e. les parenthèses), MATLAB a deux méthodes: par l’ordre ou par nom. Par l’ordre, on fait ceci:

[tokens,matches] = regexp(‘stack-001-002-003.tif’,stack-(\d\d\d)-(\d\d\d)-(\d\d\d)\.tif{1,2},’tokens’,’match’);

tokens{1} aura le premier groupe de capture et ainsi de suite (dans le cas ici: ‘001’, ‘002’, ‘003’), et matches{1} aura la chaine de caractères qui a correspondu en premier (dans le cas ici la chaine au complet), ensuite matches{2} aurait la deuxième s’il y a lieu, etc… L’autre méthode fait appel aux “named tokens”. Ce n’est pas standard regexp, mais MATLAB le fait ainsi:

expressionRegexp = ‘(?\d+)/(?\d+)/(?\d+)’
str = '01/11/2000 20-02-2020 03/30/2000 16-04-2020';
expression = ['(?\d+)/(?\d+)/(?\d+)|(?\d+)-(?\d+)-(?\d+)'];
tokenNames = regexp(str,expression,’names');

MATLAB retournera un array de structures avec chaque element de la structure identifié par month, day ou year:tokenNames(1).month. Plus d’information, tapez dans la fenêtre de commande de MATLAB:doc regexp

Les editeurs de texte

La boite Find de BBEdit/TextWrangler/SublimteText/Emacs, permettent de trouver du texte avec des expressions régulières, mais aussi de le remplacer. Par exemple, avec TextWrangler, changer les commentaires dans du texte de C++ à C. Chaque groupe de capture peut être utiliser dans la boîte de remplacement avec \1, \2, \3 etc…:

textWrangler

Python

Python supporte les expressions régulières. On les utilise comme suit pour extraire les valeurs numériques dans une table de deux colonnes dans un fichier Markdown (par exemple:| 2.0 | 4.0 | mais aussi | .02 | 0.01 |):

matchObj = re.match( "\\|\s*(\.?\d+\.?\d*)\s*\\|\s*(\.?\d+\.?\d*)\s*", line)
if matchObj:
value = float(matchObj.group(1))
x.append(value)
value = float(matchObj.group(2))
y.append(value)
continue

Perl

Perl est bâti autour des expressions régulières. On les utilise comme suit:

if ( $text =~ /(?:+1)?\d{3}?\s*(\d{3}-?\d{4})/ ) {
print “Your phone number without area code is $1”;
}

Plus d’information, tapez dans un terminal Unix: perldoc perlre

Cocoa (iOS ou macOS)

Il existe une classe NSRegularExpression pour faire la reconnaissance de texte. Pour plus d’information, NSRegularExpression dans XCode.

Javascript

Les expressions regulières sont supportés directement dans le langage Javascript. Pour plus d’information: <http://www.w3schools.com/jsref/jsref_obj_regexp.asp>

Mot de la fin

Les expressions régulières sont puissantes et permettent de rapidement vous concentrer sur votre tâche plutôt que de gérer les mondanités ennuyantes des nomenclatures de fichiers, ou les détails d’un texte.

Pour encore plus d’information, “regular expression” dans Google avec le nom de votre langage de choix.

« Optique » version iBook, nouvelle version revue et corrigée

Livre optique, Daniel C.Côté

La nouvelle version du livre “Optique” est enfin disponible, toujours gratuitement, sur le site de Apple en format ePub.  Une version PDF  est disponible.

  • Nouvelle section sur l’introduction à l’optique non-linéaire
  • Les DRMs ont été enlevés pour permettre le partage facilement et possiblement la conversion en d’autres formats: si quelqu’un réussit à le convertir pour Android, svp dites-le moi (il s’agit d’un ePub3 modifié pour le iBook store).
  • Plusieurs corrections dans le textes et améliorations.

Félicitations Damon DePaoli

Damon a reçu le prix de la meilleure présentation par poster au 10e Congrès Canadien de Neuromodulation qui s’est tenu du 4 au 6 février 2018 à Whistler en Colombie Britannique.  Le titre de son poster : Fiber-based tissue identification for electrode placement in deep brain stimulation neurosurgery.

 

Daniel Côté laboratoire au Centre de Neurophotonique, Centre de Recherche de l'Institut Universitaire en Santé Mentale de Québec