14. Synchronisation¶

Ce chapitre traite de la synchronisation des signaux sans fil en temps et en frĂ©quence, afin de corriger les dĂ©calages de la frĂ©quence porteuse et d’effectuer un alignement temporel au niveau des symboles et des trames. Nous utiliserons la technique de rĂ©cupĂ©ration d’horloge de Mueller et Muller, ainsi que la boucle de Costas, en Python.

Introduction¶

Nous avons vu comment transmettre numĂ©riquement par voie hertzienne, en utilisant un schĂ©ma de modulation numĂ©rique comme la QPSK et en appliquant une mise en forme des impulsions pour limiter la largeur de bande du signal. Le codage de canal peut ĂȘtre utilisĂ© pour traiter les canaux bruyants, par exemple lorsque le rapport signal/bruit est faible au niveau du rĂ©cepteur. Il est toujours utile de filtrer autant que possible le signal avant de le traiter numĂ©riquement. Dans ce chapitre, nous allons Ă©tudier la maniĂšre dont la synchronisation est effectuĂ©e du cĂŽtĂ© de la rĂ©ception. La synchronisation est un ensemble de traitements qui se produisent avant la dĂ©modulation et le dĂ©codage du canal. La chaĂźne globale tx-canal-rx est reprĂ©sentĂ©e ci-dessous, avec les blocs abordĂ©s dans ce chapitre surlignĂ©s en jaune. (Ce diagramme n’est pas exhaustif : la plupart des systĂšmes incluent Ă©galement l’égalisation et le multiplexage).

../_images/sync-diagram.svg

Simulation d’un canal sans fil¶

Avant d’apprendre Ă  mettre en Ɠuvre la synchronisation temporelle et frĂ©quentielle, nous devons rendre nos signaux simulĂ©s plus rĂ©alistes. Sans l’ajout d’un retard alĂ©atoire, la synchronisation dans le temps est triviale. En fait, il suffit de prendre en compte le retard d’échantillonnage de tous les filtres que vous utilisez. Nous voulons Ă©galement simuler un dĂ©calage de frĂ©quence car, comme nous le verrons, les oscillateurs ne sont pas parfaits; il y aura toujours un certain dĂ©calage entre la frĂ©quence centrale de l’émetteur et celle du rĂ©cepteur.

Examinons maintenant le code Python permettant de simuler un retard non entier et un dĂ©calage de frĂ©quence. Le code Python de ce chapitre part du code que nous avons Ă©crit lors de l’exercice Python de mise en forme des impulsions (cliquez ci-dessous si vous en avez besoin); vous pouvez le considĂ©rer comme le point de dĂ©part du code de ce chapitre, et le nouveau code viendra ensuite.

Code Python de la mise en forme des impulsions
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
import math

# cette partie provient de l'exercice des impulsions de mise en forme
num_symbols = 100
sps = 8
bits = np.random.randint(0, 2, num_symbols) # Nos données à transmettre: des 1 et des 0.
pulse_train = np.array([])
for bit in bits:
    pulse = np.zeros(sps)
    pulse[0] = bit*2-1 # définir la premiÚre valeur à 1 ou -1
    pulse_train = np.concatenate((pulse_train, pulse)) # ajouter les 8 Ă©chantillons au signal

# Créer notre filtre à base de cosinus surélevé
num_taps = 101
beta = 0.35
Ts = sps # Supposons que la fréquence d'échantillonnage est de 1Hz, donc la période d'échantillonnage est de 1, donc la période du *symbole* est de 8.
t = np.arange(-51, 52) # n'oubliez pas que le nombre final n'est pas inclus
h = np.sinc(t/Ts) * np.cos(np.pi*beta*t/Ts) / (1 - (2*beta*t/Ts)**2)

# Filtrer notre signal, afin d'appliquer l'impulsion de mise en forme
samples = np.convolve(pulse_train, h)

Nous laisserons de cĂŽtĂ© le code relatif au tracĂ© car vous avez probablement dĂ©jĂ  appris Ă  tracer n’importe quel signal. Pour que les tracĂ©s soient jolis, comme c’est souvent le cas dans ce manuel, il faut beaucoup de code supplĂ©mentaire qu’il n’est pas nĂ©cessaire de comprendre.

Ajouter un délai¶

Nous pouvons facilement simuler un retard en dĂ©calant les Ă©chantillons, mais cela ne simule qu’un retard qui est un multiple entier de notre pĂ©riode d’échantillonnage. Dans le monde rĂ©el, le retard sera une fraction de la pĂ©riode d’échantillonnage. Nous pouvons simuler le retard d’une fraction d’échantillon en crĂ©ant un filtre Ă  “retard fractionnel”, qui laisse passer toutes les frĂ©quences mais retarde les Ă©chantillons d’une certaine quantitĂ© qui n’est pas limitĂ©e Ă  l’intervalle d’échantillonnage. Vous pouvez l’imaginer comme un filtre passe-tout qui applique le mĂȘme dĂ©phasage Ă  toutes les frĂ©quences. (Rappelez-vous qu’un retard temporel et un dĂ©phasage sont Ă©quivalents.) Le code Python permettant de crĂ©er ce filtre est prĂ©sentĂ© ci-dessous:

# Créer et appliquer un filtre à retard fractionnel
delay = 0.4 # délai fractionné, en échantillons
N = 21 # nombre de taps
n = np.arange(-N//2, N//2) # ...-3,-2,-1,0,1,2,3...
h = np.sinc(n - delay) # calcul des taps du filtre
h *= np.hamming(N) # fenĂȘtre du filtre pour s'assurer qu'il dĂ©croit vers 0 des deux cĂŽtĂ©s
h /= np.sum(h) # normaliser pour obtenir un gain unitaire, nous ne voulons pas changer l'amplitude/puissance
samples = np.convolve(samples, h) # appliquer le filtre

Comme vous pouvez le voir, nous calculons les prises du filtre Ă  l’aide d’une fonction sinc(). Une fonction sinc dans le domaine temporel est un rectangle dans le domaine frĂ©quentiel, et notre rectangle pour ce filtre couvre toute la gamme de frĂ©quences de notre signal. Ce filtre ne remodĂšle pas le signal, il le retarde simplement dans le temps. Dans notre exemple, nous retardons de 0.4 Ă©chantillon. N’oubliez pas que l’application de n’importe quel filtre retarde un signal de la moitiĂ© des taps du filtre moins un, en raison de la convolution du signal Ă  travers le filtre.

Si nous traçons le graphique “avant” et “aprùs” le filtrage d’un signal, nous pouvons observer le retard fractionnel. Dans notre graphique, nous ne zoomons que sur quelques symboles. Sinon, le retard fractionnel n’est pas visible.

../_images/fractional-delay-filter.svg

Ajout d’un dĂ©calage de frĂ©quence¶

Pour rendre notre signal simulĂ© plus rĂ©aliste, nous allons appliquer un dĂ©calage de frĂ©quence. Disons que notre frĂ©quence d’échantillonnage dans cette simulation est de 1 MHz (la valeur n’a pas vraiment d’importance, mais vous verrez pourquoi il est plus facile de choisir un nombre). Si nous voulons simuler un dĂ©calage de frĂ©quence de 13 kHz (un nombre arbitraire), nous pouvons le faire via le code suivant:

# appliquer un décalage de fréquence
fs = 1e6 # supposons que notre fréquence d'échantillonnage est de 1 MHz
fo = 13000 # simuler le décalage de la fréquence
Ts = 1/fs # période d'échantillonnage
t = np.arange(0, Ts*len(samples), Ts) # créer un vecteur temps
samples = samples * np.exp(1j*2*np.pi*fo*t) # effectuer un décalage de fréquence

La figure ci-dessous montre le signal avant et aprĂšs l’application du dĂ©calage de frĂ©quence.

../_images/sync-freq-offset.svg

Nous n’avons pas reprĂ©sentĂ© graphiquement la partie Q puisque nous transmettions en BPSK, ce qui fait que la partie Q est toujours nulle. Maintenant que nous ajoutons un dĂ©calage de frĂ©quence pour simuler les canaux sans fil, l’énergie s’étend sur I et Q. À partir de maintenant, nous devrions tracer Ă  la fois I et Q. N’hĂ©sitez pas Ă  substituer un dĂ©calage de frĂ©quence diffĂ©rent pour votre code. Si vous abaissez le dĂ©calage Ă  environ 1 kHz, vous serez en mesure de voir la sinusoĂŻde dans l’enveloppe du signal car elle oscille suffisamment lentement pour couvrir plusieurs symboles.

En ce qui concerne le choix d’une frĂ©quence d’échantillonnage arbitraire, si vous examinez le code, vous remarquerez que ce qui importe est le rapport entre fo et fs.

Vous pouvez prétendre que les deux blocs de code présentés précédemment simulent un canal sans fil. Le code devrait venir aprÚs le code cÎté émission (ce que nous avons fait dans le chapitre sur les impulsions de mise en forme) et avant le code cÎté réception, qui est ce que nous allons explorer dans le reste de ce chapitre.

Synchronisation du temps¶

Lorsque nous transmettons un signal sans fil, il arrive au rĂ©cepteur avec un dĂ©phasage alĂ©atoire dĂ» au temps parcouru. Nous ne pouvons pas simplement commencer Ă  Ă©chantillonner les symboles Ă  notre dĂ©bit de symboles car il est peu probable que nous l’échantillonnions au bon endroit dans l’impulsion, comme nous l’avons vu Ă  la fin du chapitre Mise en Forme. Revoyez les trois figures Ă  la fin de ce chapitre si vous ne suivez pas.

La plupart des techniques de synchronisation prennent la forme d’une boucle Ă  verrouillage de phase (ou PLL en anglais pour phase locked loop). Nous n’étudierons pas les PLL ici, mais il est important de connaĂźtre ce terme et vous pouvez vous documenter sur le sujet si vous ĂȘtes intĂ©ressĂ©. Les PLL sont des systĂšmes en boucle fermĂ©e qui utilisent la rĂ©troaction pour ajuster continuellement un paramĂštre; dans notre cas, un dĂ©calage temporel nous permet d’échantillonner au pic des symboles numĂ©riques.

Vous pouvez vous reprĂ©senter la rĂ©cupĂ©ration du temps comme un bloc dans le rĂ©cepteur, qui accepte un flux d’échantillons et sort un autre flux d’échantillons (similaire Ă  un filtre). Nous programmons ce bloc de rĂ©cupĂ©ration du temps avec des informations sur notre signal, la plus importante Ă©tant le nombre d’échantillons par symbole (ou notre meilleure estimation de celui-ci, si nous ne sommes pas sĂ»rs Ă  100 % de ce qui a Ă©tĂ© transmis). Ce bloc agit comme un “dĂ©cimateur”, c’est-Ă -dire que notre Ă©chantillon de sortie sera une fraction du nombre d’échantillons d’entrĂ©e. Nous voulons un Ă©chantillon par symbole numĂ©rique, donc le taux de dĂ©cimation est simplement les Ă©chantillons par symbole. Si l’émetteur transmet Ă  1M symboles par seconde et que nous Ă©chantillonnons Ă  16 Msps, nous recevrons 16 Ă©chantillons par symbole. Ce sera le taux d’échantillonnage entrant dans le bloc de synchronisation. Le taux d’échantillonnage sortant du bloc sera de 1 Msps car nous voulons un Ă©chantillon par symbole numĂ©rique.

La plupart des mĂ©thodes de rĂ©cupĂ©ration du temps reposent sur le fait que nos symboles numĂ©riques montent puis descendent, et que la crĂȘte est le point auquel nous voulons Ă©chantillonner le symbole. En d’autres termes, nous Ă©chantillonnons le point maximum aprĂšs avoir pris la valeur absolue :

../_images/symbol_sync2.png

Il existe de nombreuses mĂ©thodes de rĂ©cupĂ©ration du temps, la plupart ressemblant Ă  une PLL. La diffĂ©rence entre elles rĂ©side gĂ©nĂ©ralement dans l’équation utilisĂ©e pour effectuer la “correction” du dĂ©calage temporel, que nous dĂ©signons par \mu ou mu dans le code. La valeur de mu est mise Ă  jour Ă  chaque itĂ©ration de la boucle. Elle est exprimĂ©e en unitĂ©s d’échantillons, et vous pouvez l’imaginer comme le dĂ©calage que nous devons faire pour pouvoir Ă©chantillonner au moment “parfait”. Ainsi, si mu = 3.61, cela signifie que nous devons dĂ©caler l’entrĂ©e de 3.61 Ă©chantillons pour Ă©chantillonner au bon endroit. Comme nous avons 8 Ă©chantillons par symbole, si mu dĂ©passe 8, il revient simplement Ă  zĂ©ro.

Le code Python suivant implĂ©mente la technique de rĂ©cupĂ©ration d’horloge de Mueller et Muller.

mu = 0 # estimation initiale de la phase de l'Ă©chantillon
out = np.zeros(len(samples) + 10, dtype=np.complex)
out_rail = np.zeros(len(samples) + 10, dtype=np.complex) # stocke les valeurs, à chaque itération nous avons besoin des 2 valeurs précédentes plus la valeur actuelle.
i_in = 0 # index des échantillons d'entrée
i_out = 2 # indice de sortie (les deux premiĂšres sorties sont 0)
while i_out < len(samples) and i_in+16 < len(samples):
    out[i_out] = samples[i_in + int(mu)] # prendre ce que nous pensons ĂȘtre le "meilleur" Ă©chantillon.
    out_rail[i_out] = int(np.real(out[i_out]) > 0) + 1j*int(np.imag(out[i_out]) > 0)
    x = (out_rail[i_out] - out_rail[i_out-2]) * np.conj(out[i_out-1])
    y = (out[i_out] - out[i_out-2]) * np.conj(out_rail[i_out-1])
    mm_val = np.real(y - x)
    mu += sps + 0.3*mm_val
    i_in += int(np.floor(mu)) # arrondir Ă  l'entier le plus proche puisque nous l'utilisons comme un index
    mu = mu - np.floor(mu) # supprimer la partie entiĂšre de mu
    i_out += 1 # incrémenter l'indice de sortie
out = out[2:i_out] # supprimer les deux premiers, et tout ce qui suit i_out (qui n'a jamais été rempli)
samples = out # n'incluez cette ligne que si vous voulez connecter cet extrait de code avec la boucle Costas plus tard

Le bloc de rĂ©cupĂ©ration du timing reçoit les Ă©chantillons “reçus” et produit un Ă©chantillon de sortie un par un (notez que i_out est incrĂ©mentĂ© de 1 Ă  chaque itĂ©ration de la boucle). Le bloc de rĂ©cupĂ©ration n’utilise pas seulement les Ă©chantillons “reçus” l’un aprĂšs l’autre Ă  cause de la façon dont la boucle ajuste i_in. Elle sautera quelques Ă©chantillons pour essayer de tirer le “bon” Ă©chantillon, qui serait celui au pic de l’impulsion. Au fur et Ă  mesure que la boucle traite les Ă©chantillons, elle se synchronise lentement sur le symbole, ou du moins elle tente de le faire en ajustant mu. Étant donnĂ© la structure du code, la partie entiĂšre de mu est ajoutĂ©e Ă  i_in, puis retirĂ©e de mu (gardez Ă  l’esprit que mm_val peut ĂȘtre nĂ©gatif ou positif Ă  chaque boucle). Une fois qu’elle est complĂštement synchronisĂ©e, la boucle ne devrait tirer que l’échantillon central de chaque symbole/impulsion. Vous pouvez ajuster la constante 0.3, qui modifiera la vitesse de rĂ©action de la boucle de rĂ©troaction; une valeur plus Ă©levĂ©e la fera rĂ©agir plus rapidement, mais avec un risque plus Ă©levĂ© de problĂšmes de stabilitĂ©.

Le graphique suivant montre un exemple de sortie oĂč nous avons dĂ©sactivĂ© le dĂ©lai fractionnel ainsi que le dĂ©calage de frĂ©quence. Nous montrons seulement I parce que Q est tout Ă  fait nul avec le dĂ©calage de frĂ©quence dĂ©sactivĂ©. Les trois graphiques sont empilĂ©s les uns sur les autres pour montrer comment les bits sont alignĂ©s verticalement.

Graphique du haut

Symboles BPSK originaux, c’est-Ă -dire des 1 et des -1. Rappelez-vous qu’il y a des zĂ©ros entre les deux car nous voulons 8 Ă©chantillons par symbole.

Graphique du milieu

Echantillons aprùs l’impulsion de mise en forme mais avant le synchronisation.

Graphique du bas

Sortie de la synchronisation de symboles, qui fournit seulement 1 Ă©chantillon par symbole. Cela signifie que ces Ă©chantillons peuvent ĂȘtre introduits directement dans un dĂ©modulateur, qui, pour la BPSK, vĂ©rifie si la valeur est supĂ©rieure ou infĂ©rieure Ă  0.

../_images/time-sync-output.svg

Concentrons-nous sur le graphique du bas, qui est la sortie de la synchronisation. Il a fallu prĂšs de 30 symboles pour que la synchronisation se verrouille sur le bon dĂ©lai. En raison inĂ©vitablement du temps nĂ©cessaire aux synchroniseurs pour se verrouiller, de nombreux protocoles de communication utilisent un prĂ©ambule contenant une sĂ©quence de synchronisation: il sert Ă  annoncer l’arrivĂ©e d’un nouveau paquet et donne au rĂ©cepteur le temps de se synchroniser sur celui-ci. Mais aprĂšs ces ~30 Ă©chantillons, la synchronisation fonctionne parfaitement. Nous nous retrouvons avec des 1 et des -1 parfaits qui correspondent aux donnĂ©es d’entrĂ©e. Il est utile que cet exemple n’ait pas eu de bruit ajoutĂ©. N’hĂ©sitez pas Ă  ajouter du bruit ou des dĂ©calages temporels et voyez comment la synchronisation se comporte. Si nous utilisions la QPSK, nous aurions affaire Ă  des nombres complexes, mais l’approche serait la mĂȘme.

Synchronisation du temps avec interpolation¶

Les synchroniseurs de symboles ont tendance Ă  interpoler les Ă©chantillons d’entrĂ©e par un certain nombre, par exemple 16, afin de pouvoir se dĂ©caler d’une fraction d’échantillon. Le retard alĂ©atoire causĂ© par le canal sans fil ne sera probablement pas un multiple exact d’un Ă©chantillon, de sorte que le pic du symbole peut ne pas se produire rĂ©ellement sur un Ă©chantillon. C’est particuliĂšrement vrai dans le cas oĂč il n’y aurait que 2 ou 4 Ă©chantillons par symbole reçu. L’interpolation des Ă©chantillons nous permet d’échantillonner “entre” les Ă©chantillons rĂ©els, afin d’atteindre le pic de chaque symbole. La sortie du synchroniseur n’est toujours qu’un Ă©chantillon par symbole. Les Ă©chantillons d’entrĂ©e sont eux-mĂȘmes interpolĂ©s.

Le code Python de synchronisation temporelle que nous avons implĂ©mentĂ© ci-dessus n’incluait pas d’interpolation. Pour Ă©tendre notre code, activez le retard temporel fractionnaire que nous avons implĂ©mentĂ© au dĂ©but de ce chapitre afin que notre signal reçu ait un retard plus rĂ©aliste. Laissez le dĂ©calage de frĂ©quence dĂ©sactivĂ© pour le moment. Si vous relancez la simulation, vous constaterez que la synchronisation ne parvient pas Ă  se synchroniser complĂštement sur le signal. C’est parce que nous n’interpolons pas, et que le code n’a aucun moyen “d’échantillonner entre les Ă©chantillons” pour compenser le retard fractionnel. Ajoutons l’interpolation.

Un moyen rapide d’interpoler un signal en Python est d’utiliser signal.resample ou signal.resample_poly de scipy. Ces deux fonctions font la mĂȘme chose mais fonctionnent diffĂ©remment. Nous utiliserons la derniĂšre fonction car elle a tendance Ă  ĂȘtre plus rapide. Interpolons par 16, c’est-Ă -dire que nous allons insĂ©rer 15 Ă©chantillons supplĂ©mentaires entre chaque Ă©chantillon. Cela peut ĂȘtre fait en une ligne de code, et cela devrait se faire avant d’effectuer la synchronisation temporelle (avant le gros extrait de code ci-dessus). Nous allons Ă©galement tracer le graphique avant et aprĂšs pour voir la diffĂ©rence:

samples_interpolated = signal.resample_poly(samples, 16, 1)

# Tracez l'ancien et le nouveau
plt.figure('avant interp')
plt.plot(samples,'.-')
plt.figure('aprĂšs interp')
plt.plot(samples_interpolated,'.-')
plt.show()

Si on zoome beaucoup, on voit que c’est le mĂȘme signal, mais avec 16x plus de points :

../_images/time-sync-interpolated-samples.svg

J’espĂšre que la raison pour laquelle nous devons interpoler Ă  l’intĂ©rieur du bloc de synchronisation temporelle devient claire. Ces Ă©chantillons supplĂ©mentaires nous permettront de prendre en compte une fraction d’un Ă©chantillon de retard. En plus de calculer samples_interpolated, nous devons Ă©galement modifier une ligne de code dans notre synchronisation temporelle. Nous allons changer la premiĂšre ligne Ă  l’intĂ©rieur de la boucle while pour devenir:

out[i_out] = samples_interpolated[i_in*16 + int(mu*16)]

Nous avons fait plusieurs choses ici. D’abord, nous ne pouvons plus utiliser i_in comme index de l’échantillon d’entrĂ©e. Nous devons le multiplier par 16 car nous avons interpolĂ© nos Ă©chantillons d’entrĂ©e par 16. Rappelez-vous que la boucle de rĂ©troaction ajuste la variable mu. Elle reprĂ©sente le dĂ©lai qui nous permet d’échantillonner au bon moment. Rappelez-vous Ă©galement qu’aprĂšs avoir calculĂ© la nouvelle valeur de mu, nous avons ajoutĂ© la partie entiĂšre Ă  i_in. Maintenant, nous allons utiliser la partie restante, qui est un flottant de 0 Ă  1, et qui reprĂ©sente la fraction d’échantillon que nous devons retarder. Avant, nous n’étions pas capables de retarder d’une fraction d’échantillon, mais maintenant nous le pouvons, au moins par incrĂ©ments de 16Ăšme d’échantillon. Il faut donc multiplier mu par 16 pour savoir de combien d’échantillons de notre signal interpolĂ© nous devons retarder. Ensuite, nous devons arrondir ce nombre, car la valeur entre parenthĂšses est finalement un index et doit ĂȘtre un nombre entier. Si ce paragraphe n’a pas eu de sens, essayez de revenir au code initial de rĂ©cupĂ©ration d’horloge de Mueller et Muller, et lisez Ă©galement les commentaires Ă  cĂŽtĂ© de chaque ligne de code.

Le rĂ©sultat du tracĂ© de ce nouveau code devrait ĂȘtre Ă  peu prĂšs le mĂȘme que prĂ©cĂ©demment. Tout ce que nous avons fait, c’est rendre notre simulation plus rĂ©aliste en ajoutant un retard d’échantillon fractionnaire, puis nous avons ajoutĂ© l’interpolateur Ă  la synchronisation afin de compenser ce retard d’échantillon fractionnaire.

N’hĂ©sitez pas Ă  jouer avec diffĂ©rents facteurs d’interpolation, c’est-Ă -dire Ă  remplacer tous les 16 par une autre valeur. Vous pouvez Ă©galement essayer d’activer le dĂ©calage de frĂ©quence, ou d’ajouter un bruit blanc gaussien au signal avant qu’il ne soit reçu, pour voir comment cela affecte les performances de synchronisation (indice : vous devrez peut-ĂȘtre ajuster le multiplicateur de 0.3).

Si nous activons uniquement le décalage de fréquence en utilisant une fréquence de 1kHz, nous obtenons les performances de synchronisation suivantes. Nous devons montrer à la fois I et Q maintenant que nous avons ajouté un décalage de fréquence :

../_images/time-sync-output2.svg

C’est peut-ĂȘtre difficile Ă  voir, mais la synchronisation du temps fonctionne toujours trĂšs bien. Il faut environ 20 Ă  30 symboles avant qu’elle ne soit verrouillĂ©e. Cependant, il y a un motif sinusoĂŻdal parce que nous avons encore un dĂ©calage de frĂ©quence, et nous allons apprendre Ă  le gĂ©rer dans la section suivante.

La figure ci-dessous montre le graphique IQ (aussi appelĂ© constellation) du signal avant et aprĂšs la synchronisation. Rappelez-vous que vous pouvez tracer des Ă©chantillons sur un graphique IQ en utilisant un nuage de points : plt.plot(np.real(samples), np.imag(samples), '.'). Dans l’animation ci-dessous, nous avons spĂ©cifiquement laissĂ© de cĂŽtĂ© les 30 premiers symboles. Ils sont apparus avant la fin de la synchronisation temporelle. Les symboles restants sont tous approximativement sur le cercle des unitĂ©s en raison du dĂ©calage de frĂ©quence.

../_images/time-sync-constellation.svg

Pour en savoir encore plus, nous pouvons observer la constellation dans le temps afin de discerner ce qui arrive rĂ©ellement aux symboles. Au tout dĂ©but, pendant une courte pĂ©riode de temps, les symboles ne sont pas Ă  0 ou sur le cercle unitaire. C’est la pĂ©riode pendant laquelle la synchronisation temporelle trouve le bon dĂ©lai. C’est trĂšs rapide, regardez bien! La rotation est juste le dĂ©calage de frĂ©quence. La frĂ©quence est un changement constant de la phase, donc un dĂ©calage de frĂ©quence provoque une rotation de la BPSK (crĂ©ant un cercle dans le tracĂ© statique/persistant ci-dessus).

../_images/time-sync-constellation-animated.gif

Nous espĂ©rons qu’en voyant un exemple de synchronisation temporelle, vous avez une idĂ©e de ce qu’elle fait et une idĂ©e gĂ©nĂ©rale de son fonctionnement. En pratique, la boucle while que nous avons crĂ©Ă©e ne fonctionnerait que sur un petit nombre d’échantillons Ă  la fois (par exemple, 1000). Vous devez vous souvenir de la valeur de mu entre les appels Ă  la fonction sync, ainsi que des deux derniĂšres valeurs de out et out_rail.

Ensuite, nous allons étudier la synchronisation de la fréquence, que nous divisons en synchro de fréquence grossiÚre et fine. La synchronisation grossiÚre vient généralement avant la synchronisation temporelle, tandis que la synchronisation fine vient aprÚs.

Synchronisation grossiÚre des fréquences¶

MĂȘme si nous demandons Ă  l’émetteur et au rĂ©cepteur de fonctionner sur la mĂȘme frĂ©quence centrale, il y aura un lĂ©ger dĂ©calage de frĂ©quence entre les deux en raison d’imperfections matĂ©rielles (par exemple, l’oscillateur) ou d’un dĂ©calage Doppler dĂ» au mouvement. Ce dĂ©calage de frĂ©quence sera minuscule par rapport Ă  la frĂ©quence porteuse, mais mĂȘme un petit dĂ©calage peut perturber un signal numĂ©rique. Le dĂ©calage Ă©voluera probablement dans le temps, ce qui nĂ©cessite une boucle de rĂ©troaction permanente pour corriger le dĂ©calage. Par exemple, l’oscillateur Ă  l’intĂ©rieur du Pluto a une spĂ©cification de dĂ©calage maximale de 25 PPM. C’est-Ă -dire 25 parties par million par rapport Ă  la frĂ©quence centrale. Si vous ĂȘtes rĂ©glĂ© sur 2.4 GHz, le dĂ©calage maximal serait de +/- 60 kHz. Les Ă©chantillons que notre SDR nous fournit sont en bande de base, ce qui fait que tout dĂ©calage de frĂ©quence se manifeste dans ce signal en bande de base. Un signal BPSK avec un petit dĂ©calage de la porteuse ressemblera au tracĂ© temporel ci-dessous, ce qui n’est Ă©videmment pas idĂ©al pour dĂ©moduler des bits. Nous devons supprimer tout dĂ©calage de frĂ©quence avant la dĂ©modulation.

../_images/carrier-offset.png

La synchronisation de frĂ©quence est gĂ©nĂ©ralement dĂ©composĂ©e en synchronisation grossiĂšre et synchronisation fine, oĂč la synchronisation grossiĂšre corrige les grands dĂ©calages de l’ordre du kHz ou plus, tandis que la synchronisation fine corrige ce qui reste. La synchronisation grossiĂšre intervient avant la synchronisation temporelle, tandis que la synchronisation fine intervient aprĂšs.

MathĂ©matiquement, si nous disposons d’un signal en bande de base s(t) et qu’il subit un dĂ©calage de frĂ©quence (aussi appelĂ© porteuse) de f_o Hz, nous pouvons reprĂ©senter ce qui est reçu comme suit:

r(t) = s(t) e^{j2\pi f_o t} + n(t)

oĂč n(t) est le bruit.

La premiĂšre astuce que nous allons apprendre, afin d’effectuer une estimation grossiĂšre du dĂ©calage de frĂ©quence (si nous pouvons estimer la frĂ©quence de dĂ©calage, alors nous pouvons la compenser), est de prendre le carrĂ© de notre signal. Ignorons le bruit pour l’instant, afin de garder les mathĂ©matiques plus simples :

r^2(t) = s^2(t) e^{j4\pi f_o t}

Voyons ce qui se passe lorsque nous prenons le carrĂ© de notre signal s(t) en considĂ©rant ce que ferait la QPSK. L’élĂ©vation au carrĂ© de nombres complexes donne lieu Ă  un comportement intĂ©ressant, surtout lorsqu’il s’agit de constellations comme la BPSK et la QPSK. L’animation suivante montre ce qui se passe lorsqu’on Ă©lĂšve au carrĂ© une QPSK, puis si on l’élĂšve encore une deuxiĂšme fois. J’ai utilisĂ© spĂ©cifiquement la QPSK au lieu de la BPSK parce que vous pouvez voir que lorsque vous Ă©rigez la QPSK une fois, vous obtenez essentiellement la BPSK. Et aprĂšs un autre carrĂ©, on obtient un cluster. (Merci Ă  http://ventrella.com/ComplexSquaring/ qui a crĂ©Ă© cette belle application web).

../_images/squaring-qpsk.gif

Voyons ce qui se passe lorsqu’on applique Ă  notre signal QPSK une petite rotation de phase et une mise Ă  l’échelle de l’amplitude, ce qui est plus rĂ©aliste :

../_images/squaring-qpsk2.gif

Il s’agit toujours d’un seul groupe, mais avec un dĂ©phasage. Ce qu’il faut retenir, c’est que si vous mettez la QPSK au carrĂ© deux fois (et la BPSK une fois), les quatre groupes de points seront fusionnĂ©s en un seul groupe. Pourquoi cela est-il utile? En fusionnant les groupes, nous supprimons essentiellement la modulation! Si tous les points sont maintenant dans le mĂȘme groupe, c’est comme si on avait un tas de constantes dans une rangĂ©e. C’est comme s’il n’y avait plus de modulation, et que la seule chose qui restait Ă©tait la sinusoĂŻde causĂ©e par le dĂ©calage de frĂ©quence (nous avons aussi du bruit, mais ignorons-le pour l’instant). Il s’avĂšre que vous devez Ă©lever le signal au carrĂ© N fois, oĂč N est l’ordre du schĂ©ma de modulation utilisĂ©, ce qui signifie que cette astuce ne fonctionne que si vous connaissez le schĂ©ma de modulation Ă  l’avance. L’équation est en fait la suivante :

r^N(t) = s^N(t) e^{j2N\pi f_o t}

Pour notre cas de BPSK, nous avons un schĂ©ma de modulation d’ordre 2, nous utiliserons donc l’équation suivante pour notre synchronisation grossiĂšre de la frĂ©quence:

r^2(t) = s^2(t) e^{j4\pi f_o t}

Nous avons dĂ©couvert ce qui arrive Ă  la partie s(t) de l’équation, mais qu’en est-il de la partie sinusoĂŻde (alias exponentielle complexe)? Comme on peut le voir, on ajoute le terme N, ce qui la rend Ă©quivalente Ă  une sinusoĂŻde Ă  une frĂ©quence de Nf_o au lieu de f_o. Une mĂ©thode simple pour dĂ©terminer nf_o est de prendre la FFT du signal aprĂšs l’avoir Ă©levĂ© au carrĂ© N fois et de voir oĂč le pic se produit. Faisons une simulation en Python. Nous allons retourner Ă  la gĂ©nĂ©ration de notre signal BPSK, et au lieu de lui appliquer un retard fractionnel, nous allons appliquer un dĂ©calage de frĂ©quence en multipliant le signal par e^{j2\pi f_o t} comme nous l’avons fait dans le chapitre Filtres pour convertir un filtre passe-bas en un filtre passe-haut.

En utilisant le code du dĂ©but de ce chapitre, appliquez un dĂ©calage de frĂ©quence de +13 kHz Ă  votre signal numĂ©rique. Cela peut se produire juste avant ou juste aprĂšs l’ajout du retard fractionnĂ©; cela n’a pas d’importance. Quoi qu’il en soit, cela doit se faire aprĂšs l’impulsion de mise en forme, mais avant d’effectuer toute fonction cĂŽtĂ© rĂ©ception, comme la synchronisation temporelle.

Maintenant que nous avons un signal avec un dĂ©calage de frĂ©quence de 13kHz, traçons la FFT avant et aprĂšs la mise au carrĂ©, pour voir ce qui se passe. Vous devriez maintenant savoir comment effectuer une FFT, y compris les opĂ©rations abs() et fftshift(). Pour cet exercice, peu importe que vous preniez ou non le logarithme ou que vous Ă©leviez au carrĂ© le signal aprĂšs avoir effectuĂ© l’opĂ©ration abs().

Regardez d’abord le signal avant de l’élever au carrĂ© (juste une FFT normale):

psd = np.fft.fftshift(np.abs(np.fft.fft(samples)))
f = np.linspace(-fs/2.0, fs/2.0, len(psd))
plt.plot(f, psd)
plt.show()
../_images/coarse-freq-sync-before.svg

On ne voit pas vraiment de pic associé au décalage de la porteuse. Il est couvert par notre signal.

Maintenant avec l’élĂ©vation au carrĂ© ajoutĂ©e (juste une puissance de 2 parce que c’est une BPSK) :

# Ajoutez ceci avant la ligne FFT
samples = samples**2

Il faut zoomer pour voir sur quelle fréquence se trouve le pic :

../_images/coarse-freq-sync.svg

Vous pouvez essayer d’augmenter le nombre de symboles simulĂ©s (par exemple, 1000 symboles) afin d’avoir suffisamment d’échantillons pour travailler. Plus il y a d’échantillons dans notre FFT, plus notre estimation du dĂ©calage de frĂ©quence sera prĂ©cise. Pour rappel, le code ci-dessus doit venir avant la synchornisation de temps.

Le pic de frĂ©quence apparaĂźt Ă  Nf_o. Nous devons diviser cette valeur (26.6kHz) par 2 pour trouver notre rĂ©ponse finale, qui est trĂšs proche du dĂ©calage de frĂ©quence de 13kHz que nous avons appliquĂ© au dĂ©but du chapitre! Si vous avez jouĂ© avec ce nombre et qu’il n’est plus de 13kHz, ce n’est pas grave. Assurez-vous simplement que vous ĂȘtes conscient de ce que vous avez rĂ©glĂ©.

Comme notre frĂ©quence d’échantillonnage est de 1 MHz, les frĂ©quences maximales que nous pouvons voir sont de -500kHz Ă  500kHz. Si nous portons notre signal Ă  la puissance N, cela signifie que nous ne pouvons “voir” les dĂ©calages de frĂ©quence que jusqu’à 500e3/N, ou dans le cas de la BPSK +/- 250kHz. Si nous recevions un signal QPSK, il ne serait que de +/- 125kHz, et un dĂ©calage de la porteuse supĂ©rieur ou infĂ©rieur Ă  cette valeur serait hors de notre portĂ©e avec cette technique. Pour vous donner une idĂ©e du dĂ©calage Doppler, si vous transmettez dans la bande des 2.4GHz et que l’émetteur ou le rĂ©cepteur se dĂ©place Ă  96km/h (c’est la vitesse relative qui compte), cela entraĂźnera un dĂ©calage de frĂ©quence de 214Hz. Le dĂ©calage dĂ» Ă  un oscillateur de mauvaise qualitĂ© sera probablement le principal coupable dans cette situation.

En fait, la correction de ce décalage de fréquence se fait exactement comme nous avons simulé le décalage en premier lieu: en multipliant par une exponentielle complexe, mais avec un signe négatif puisque nous voulons supprimer le décalage.

max_freq = f[np.argmax(psd)]
Ts = 1/fs # période d'échantillonnage
t = np.arange(0, Ts*len(samples), Ts) # vecteur de temps
samples = samples * np.exp(-1j*2*np.pi*max_freq*t/2.0)

C’est Ă  vous de dĂ©cider si vous voulez le corriger ou modifier le dĂ©calage de frĂ©quence initial que nous avons appliquĂ© au dĂ©but Ă  un nombre plus petit (comme 500Hz) pour tester la synchronisation de frĂ©quence fine que nous allons maintenant apprendre Ă  faire.

Synchronisation fine de la fréquence¶

Ensuite, nous allons passer Ă  la synchronisation fine de la frĂ©quence. L’astuce prĂ©cĂ©dente est plutĂŽt destinĂ©e Ă  l’évanouissement grossier, et ce n’est pas une opĂ©ration en boucle fermĂ©e (de type feedback). Mais pour la synchronisation fine de la frĂ©quence, nous aurons besoin d’une boucle de rĂ©troaction par laquelle nous ferons passer des Ă©chantillons, ce qui sera une fois de plus une forme de PLL. Notre objectif est de ramener le dĂ©calage de frĂ©quence Ă  zĂ©ro et de l’y maintenir, mĂȘme si le dĂ©calage change au fil du temps. Nous devons continuellement suivre le dĂ©calage. Les techniques de synchronisation fine de la frĂ©quence fonctionnent mieux avec un signal qui a dĂ©jĂ  Ă©tĂ© synchronisĂ© dans le temps au niveau du symbole, donc le code dont nous parlons dans cette section viendra aprĂšs la synchronisation temporelle.

Nous allons utiliser une technique appelĂ©e boucle de Costas. Il s’agit d’une forme de PLL spĂ©cialement conçue pour la correction du dĂ©calage de la frĂ©quence de la porteuse pour les signaux numĂ©riques tels que BPSK et QPSK. Elle a Ă©tĂ© inventĂ©e par John P. Costas chez General Electric dans les annĂ©es 1950 et a eu un impact majeur sur les communications numĂ©riques modernes. La boucle de Costas supprime le dĂ©calage de frĂ©quence tout en fixant le dĂ©calage de phase. L’énergie est alignĂ©e avec l’axe I. La frĂ©quence n’est qu’un changement de phase, ils peuvent donc ĂȘtre suivis comme un tout. La boucle de Costas est rĂ©sumĂ©e Ă  l’aide du diagramme suivant (notez que les 1/2 ont Ă©tĂ© laissĂ©s de cĂŽtĂ© dans les Ă©quations car ils n’ont pas d’importance fonctionnelle).

../_images/costas-loop.svg

L’oscillateur commandĂ© en tension (ou VCO en anglais pour voltage controlled oscillator) est simplement un gĂ©nĂ©rateur d’ondes sin/cos qui utilise une frĂ©quence basĂ©e sur l’entrĂ©e. Dans notre cas, puisque nous simulons un canal sans fil, il ne s’agit pas d’une tension, mais plutĂŽt d’un niveau reprĂ©sentĂ© par une variable. Elle dĂ©termine la frĂ©quence et la phase des ondes sinus et cosinus gĂ©nĂ©rĂ©es. Ce qu’il fait, c’est multiplier le signal reçu par une sinusoĂŻde gĂ©nĂ©rĂ©e en interne, afin de tenter d’annuler le dĂ©calage de frĂ©quence et de phase. Ce comportement est similaire Ă  celui d’une SDR qui effectue une conversion de frĂ©quence et crĂ©e les branches I et Q.

Voici le code Python qui constitue notre boucle Costas:

N = len(samples)
phase = 0
freq = 0
# Ces deux paramÚtres suivants sont ce qu'il faut ajuster, pour rendre la boucle de rétroaction plus rapide ou plus lente (ce qui a un impact sur la stabilité).
alpha = 0.132
beta = 0.00932
out = np.zeros(N, dtype=np.complex)
freq_log = []
for i in range(N):
    out[i] = samples[i] * np.exp(-1j*phase) # ajuster l'échantillon d'entrée par l'inverse du décalage de phase estimé
    error = np.real(out[i]) * np.imag(out[i]) # Voici la formule d'erreur pour une boucle de Costas de 2Ăšme ordre (par exemple pour BPSK)

    # Avancer la boucle (recalculer la phase et le décalage de fréquence)
    freq += (beta * error)
    freq_log.append(freq * fs / (2*np.pi)) # convertir de la vitesse angulaire en Hz pour la journalisation
    phase += freq + (alpha * error)

    # Facultatif: Ajustez la phase de façon à ce qu'elle soit toujours entre 0 et 2pi, rappelez-vous que la phase s'enroule autour de chaque 2pi
    while phase >= 2*np.pi:
        phase -= 2*np.pi
    while phase < 0:
        phase += 2*np.pi

# Tracez la fréquence en fonction du temps pour voir combien de temps il faut pour atteindre le bon décalage.
plt.plot(freq_log,'.-')
plt.show()

Il y a beaucoup de choses ici, alors passons-les en revue. Certaines lignes sont simples et d’autres sont super compliquĂ©es. samples est notre entrĂ©e, et out les Ă©chantillons de sortie. phase et frequency sont comme le mu du code de synchronisation temporelle. Ils contiennent les estimations du dĂ©calage actuel, et Ă  chaque itĂ©ration de la boucle, nous crĂ©ons les Ă©chantillons de sortie en multipliant les Ă©chantillons d’entrĂ©e par np.exp(-1j*phase). La variable error contient la mĂ©trique d’erreur, et pour une boucle de Costas d’ordre 2, c’est une Ă©quation trĂšs simple. Nous multiplions la partie rĂ©elle de l’échantillon (I) par la partie imaginaire (Q), et parce que Q devrait ĂȘtre Ă©gal Ă  zĂ©ro pour la BPSK, la fonction d’erreur est minimisĂ©e lorsqu’il n’y a pas de dĂ©calage de phase ou de frĂ©quence qui fait passer l’énergie de I Ă  Q. Pour une boucle de Costas d’ordre 4, c’est encore relativement simple mais pas tout Ă  fait une ligne, car I et Q auront de l’énergie mĂȘme lorsqu’il n’y a pas de dĂ©calage de phase ou de frĂ©quence, pour la QPSK. Si vous ĂȘtes curieux de voir Ă  quoi cela ressemble, cliquez ci-dessous, mais nous ne l’utiliserons pas dans notre code pour le moment. La raison pour laquelle cela fonctionne pour la QPSK est que lorsque vous prenez la valeur absolue de I et Q, vous obtenez +1+1j, et s’il n’y a pas de dĂ©calage de phase ou de frĂ©quence, la diffĂ©rence entre la valeur absolue de I et Q devrait ĂȘtre proche de zĂ©ro.

Équation d'erreur de la boucle de Costas de l'ordre 4 (pour les curieux)
# For QPSK
def phase_detector_4(sample):
    if sample.real > 0:
        a = 1.0
    else:
        a = -1.0
    if sample.imag > 0:
        b = 1.0
    else:
        b = -1.0
    return a * sample.imag - b * sample.real

Les variables alpha et beta dĂ©finissent la vitesse de mise Ă  jour de la phase et de la frĂ©quence, respectivement. Il y a une certaine thĂ©orie derriĂšre le choix de ces deux valeurs, mais nous ne l’aborderons pas ici. Si vous ĂȘtes curieux, vous pouvez essayer de modifier alpha et/ou beta pour voir ce qui se passe.

Nous enregistrons la valeur de freq Ă  chaque itĂ©ration afin de pouvoir la tracer Ă  la fin, pour voir comment la boucle de Costas converge vers le dĂ©calage de frĂ©quence correct. Nous devons multiplier freq par la frĂ©quence d’échantillonnage et convertir la frĂ©quence angulaire en Hz, en la divisant par 2\pi. Notez que si vous avez effectuĂ© une synchronisation temporelle avant la boucle Costas, vous devrez Ă©galement diviser par votre facteur de surĂ©chantillonnage sps (par exemple, 8), car les Ă©chantillons provenant de la synchronisation temporelle sont Ă  un taux Ă©gal Ă  votre taux d’échantillonnage original divisĂ© par sps.

Enfin, aprĂšs avoir recalculĂ© la phase, nous ajoutons ou supprimons suffisamment de 2 \pi’s pour maintenir la phase entre 0 et 2 \pi’s, ce qui enroule la phase autour.

Notre signal avant et aprĂšs la boucle de Costas ressemble Ă  ceci:

../_images/costas-loop-output.svg

Et l’estimation du dĂ©calage de frĂ©quence au fil du temps, se stabilisant sur le dĂ©calage correct (un dĂ©calage de -300Hz a Ă©tĂ© utilisĂ© dans cet exemple de signal) :

../_images/costas-loop-freq-tracking.svg

Il faut prĂšs de 70 Ă©chantillons pour que l’algorithme se verrouille complĂštement sur le dĂ©calage de frĂ©quence. Vous pouvez voir que dans mon exemple simulĂ©, il restait environ -300 Hz aprĂšs la synchronisation grossiĂšre de la frĂ©quence. Les vĂŽtres peuvent varier. Comme je l’ai dĂ©jĂ  mentionnĂ©, vous pouvez dĂ©sactiver la synchronisation grossiĂšre de la frĂ©quence et dĂ©finir le dĂ©calage initial de la frĂ©quence Ă  la valeur de votre choix et voir si la boucle de Costas s’en rend compte.

La boucle de Costas, en plus de supprimer le dĂ©calage de frĂ©quence, a alignĂ© notre signal BPSK pour qu’il soit sur la partie I, ce qui rend Q Ă  nouveau nul. Il s’agit d’un effet secondaire pratique de la boucle de Costas, et il permet Ă  la boucle de Costas d’agir essentiellement comme notre dĂ©modulateur. Maintenant, tout ce que nous avons Ă  faire est de prendre I et de voir s’il est supĂ©rieur ou infĂ©rieur Ă  zĂ©ro. Nous ne saurons pas vraiment comment transformer un nĂ©gatif et un positif en 0 et 1 parce qu’il peut y avoir ou non une inversion; il n’y a aucun moyen pour la boucle de Costas (ou notre synchronisation temporelle) de le savoir. C’est lĂ  que le codage diffĂ©rentiel entre en jeu. Il lĂšve l’ambiguĂŻtĂ© car les 1 et les 0 sont basĂ©s sur le fait que le symbole a changĂ© ou non, et non sur le fait qu’il Ă©tait +1 ou -1. Si on ajoute le codage diffĂ©rentiel, on utilise toujours la BPSK. Nous ajouterions un bloc de codage diffĂ©rentiel juste avant la modulation du cĂŽtĂ© Tx et juste aprĂšs la dĂ©modulation du cĂŽtĂ© Rx.

Vous trouverez ci-dessous une animation de la synchronisation temporelle et de la synchronisation de frĂ©quence. La synchronisation temporelle se produit presque immĂ©diatement, mais la synchronisation de frĂ©quence prend presque toute l’animation pour s’installer complĂštement, et ce parce que alpha et beta ont Ă©tĂ© rĂ©glĂ©s trop bas, Ă  0.005 et 0.001 respectivement. Le code utilisĂ© pour gĂ©nĂ©rer cette animation peut ĂȘtre trouvĂ© ici.

../_images/costas_animation.gif

Synchronisation des trames¶

Nous avons vu comment corriger les dĂ©calages de temps, de frĂ©quence et de phase dans notre signal reçu. Mais la plupart des protocoles de communication modernes ne se contentent pas de transmettre des bits en continu Ă  un taux d’utilisation de 100%. Ils utilisent plutĂŽt des paquets/trames. Au niveau du rĂ©cepteur, nous devons ĂȘtre en mesure d’identifier le dĂ©but d’une nouvelle trame. Habituellement, l’en-tĂȘte de trame (au niveau de la couche MAC) indique le nombre d’octets contenus dans la trame. Nous pouvons utiliser cette information pour connaĂźtre la longueur de la trame, par exemple, en unitĂ©s d’échantillons ou de symboles. NĂ©anmoins, la dĂ©tection du dĂ©but de la trame est une tĂąche totalement distincte. Vous trouverez ci-dessous un exemple de structure de trame WiFi. Notez que la toute premiĂšre chose transmise est un en-tĂȘte de la couche PHY, et que la premiĂšre moitiĂ© de cet en-tĂȘte est un “prĂ©ambule”. Ce prĂ©ambule contient une sĂ©quence de synchronisation que le rĂ©cepteur utilise pour dĂ©tecter le dĂ©but des trames, et c’est une sĂ©quence connue d’avance par le rĂ©cepteur.

../_images/wifi-frame.png

Une mĂ©thode courante et simple de dĂ©tection de ces sĂ©quences au niveau du rĂ©cepteur consiste Ă  effectuer une corrĂ©lation croisĂ©e entre les Ă©chantillons reçus et la sĂ©quence connue. Lorsque la sĂ©quence se produit, cette intercorrĂ©lation ressemble Ă  une autocorrĂ©lation (avec du bruit ajoutĂ©). Typiquement, les sĂ©quences choisies pour les prĂ©ambules auront de belles propriĂ©tĂ©s d’autocorrĂ©lation, telles que l’autocorrĂ©lation de la sĂ©quence crĂ©e un seul pic fort Ă  0 et aucun autre pic. Les codes de Barker en sont un exemple. Dans la norme 802.11/WiFi, une sĂ©quence de Barker de longueur 11 est utilisĂ©e pour les dĂ©bits de 1 et 2Mbit/sec :

+1 +1 +1 −1 −1 −1 +1 −1 −1 +1 −1

On peut l’assimiler Ă  11 symboles BPSK. Nous pouvons regarder l’autocorrĂ©lation de cette sĂ©quence trĂšs facilement en Python :

import numpy as np
import matplotlib.pyplot as plt
x = [1,1,1,-1,-1,-1,1,-1,-1,1,-1]
plt.plot(np.correlate(x,x,'same'),'.-')
plt.grid()
plt.show()
../_images/barker-code.svg

Vous pouvez voir qu’il y a 11 (longueur de la sĂ©quence) au centre, et -1 ou 0 pour tous les autres dĂ©lais. Il fonctionne bien pour trouver le dĂ©but d’une trame car il intĂšgre essentiellement 11 symboles d’énergie dans une tentative de crĂ©er un pic de 1 bit dans la sortie de la corrĂ©lation croisĂ©e. En fait, la partie la plus difficile de la dĂ©tection du dĂ©but d’une trame est de trouver un bon seuil. Vous ne voulez pas que des trames qui ne font pas rĂ©ellement partie de votre protocole le dĂ©clenchent. Cela signifie qu’en plus de la corrĂ©lation croisĂ©e, vous devez Ă©galement effectuer une sorte de normalisation de la puissance, que nous n’examinerons pas ici. En dĂ©cidant d’un seuil, vous devez faire un compromis entre la probabilitĂ© de dĂ©tection et la probabilitĂ© de fausses alarmes. Rappelez-vous que l’en-tĂȘte de trame lui-mĂȘme contiendra des informations, donc certaines fausses alarmes sont acceptables; vous dĂ©couvrirez rapidement qu’il ne s’agit pas d’une trame lorsque vous dĂ©coderez l’en-tĂȘte et que le CRC Ă©chouera inĂ©vitablement (parce qu’il ne s’agissait pas d’une trame). Cependant, si certaines fausses alarmes sont acceptables, manquer complĂštement la dĂ©tection d’une trame est une mauvaise chose.

Les sĂ©quences de Zadoff-Chu, utilisĂ©es en LTE, sont une autre sĂ©quence prĂ©sentant d’excellentes propriĂ©tĂ©s d’autocorrĂ©lation. Elles ont l’avantage de se prĂ©senter sous forme d’ensembles; vous pouvez avoir plusieurs sĂ©quences diffĂ©rentes qui ont toutes de bonnes propriĂ©tĂ©s d’autocorrĂ©lation, mais elles ne se dĂ©clencheront pas les unes les autres (c’est-Ă -dire qu’elles ont Ă©galement de bonnes propriĂ©tĂ©s de corrĂ©lation croisĂ©e, lorsque vous corrĂšlez diffĂ©rentes sĂ©quences de l’ensemble). GrĂące Ă  cette fonctionnalitĂ©, des sĂ©quences diffĂ©rentes seront attribuĂ©es Ă  diffĂ©rentes stations de bases, de sorte qu’un tĂ©lĂ©phone puisse non seulement trouver le dĂ©but de la trame mais aussi savoir de quelle station il reçoit.