Perché gli stessi byte danno quattro risultati CRC-16 diversi
CRC-16 è il nome di una famiglia, non di un algoritmo, e i membri di quella famiglia non vanno d’accordo tra loro. Passa gli stessi byte a MODBUS, a CCITT-FALSE e a XMODEM e ti tornano indietro tre numeri a 16 bit che non hanno nulla in comune.
A decidere quale membro stai eseguendo sono cinque costanti: poly, init, refin, refout e xorout. Cambiane una e il risultato cambia del tutto: non resta nessuna somiglianza parziale a metterti sull’avviso. Quasi tutte le domande del tipo “perché il mio CRC non coincide con quello del dispositivo” finiscono lì: le due parti eseguono varianti diverse e nessuno ha messo per iscritto quale.
Ogni valore di questa guida esce da un modello CRC parametrico eseguito con Python 3.14.7 su macOS, verificato per quattro strade: contro
zlib.crc32ebinascii.crc_hqxdella libreria standard, contro i valori di check pubblicati nel catalogo RevEng CRC e contro la documentazione dei produttori per ciascun set di parametri.
1. Stesso frame, quattro risultati CRC-16
Ecco una vera richiesta Modbus RTU. Slave 01, codice funzione 03 (lettura degli holding register), indirizzo iniziale 0x0000, quantità 0x000A:
01 03 00 00 00 0A
Gli stessi sei byte passati attraverso quattro varianti:
| Variante | Risultato | In trasmissione (byte basso per primo) |
|---|---|---|
| CRC-16/MODBUS | 0xCDC5 | C5 CD |
| CRC-16/IBM-3740 (CCITT-FALSE) | 0x0428 | 28 04 |
| CRC-16/XMODEM | 0x0A38 | 38 0A |
| CRC-16/ARC | 0xD6C5 | C5 D6 |
Niente lega quei quattro valori. Non c’è uno schema di nibble in comune né una differenza costante tra i valori, e nemmeno riordinando i byte si passa dall’uno all’altro. Due cominciano con lo stesso byte in trasmissione, ma è un caso.
Quindi la stringa “CRC-16” su un datasheet non porta quasi nessuna informazione. Ti dice che l’uscita è larga 16 bit e si ferma lì. Se vuoi guardare un risultato in un’altra base mentre lo confronti con un dispositivo che stampa in binario, il convertitore di basi numeriche passa un valore a 16 bit da esadecimale a binario senza costringerti a ragionare sul padding.
2. Che cosa cambia ciascuno dei cinque parametri
Sotto ogni variante c’è la stessa macchina: uno shift register che divora un bit alla volta e fa lo XOR di una costante ogni volta che un 1 cade fuori dalla cima. I parametri decidono che cosa entra nel registro e in quale direzione viaggiano i bit, più un ultimo XOR in uscita.
Il motore intero sta in quattordici righe di Python. È un’implementazione bit per bit, lenta ma leggibile, e riproduce ogni valore di questo articolo:
def crc(data, width, poly, init, refin, refout, xorout):
top = 1 << (width - 1)
mask = (1 << width) - 1
reg = init
for byte in data:
if refin:
byte = int(f"{byte:08b}"[::-1], 2)
reg ^= byte << (width - 8)
for _ in range(8):
reg = ((reg << 1) ^ poly) if reg & top else (reg << 1)
reg &= mask
if refout:
reg = int(f"{reg:0{width}b}"[::-1], 2)
return reg ^ xorout
Puoi riscontrare l’uscita del motore con la libreria standard di Python. zlib.crc32 e binascii.crc_hqx danno ciascuno una risposta indipendente, e tutte e tre coincidono:
zlib.crc32(b"123456789") = 0xCBF43926 engine above = 0xCBF43926 MATCH
binascii.crc_hqx(b"123456789", 0x0000) = 0x31C3 CRC-16/XMODEM = 0x31C3 MATCH
binascii.crc_hqx(b"123456789", 0xFFFF) = 0x29B1 CCITT-FALSE = 0x29B1 MATCH
poly: due schieramenti, 0x8005 e 0x1021
Il polinomio è la costante di cui si fa lo XOR nel registro. Si scrive in forma “normale”, con il bit più alto implicito, perciò 0x8005 significa x^16 + x^15 + x^2 + 1 e 0x1021 significa x^16 + x^12 + x^5 + 1. Il ciclo di shift e XOR è una divisione lunga tra polinomi su GF(2): il polinomio è il divisore, il registro tiene il resto parziale e ogni bit del messaggio fa avanzare la divisione di un passo.
Quasi ogni CRC-16 che incontri usa uno di questi due. 0x8005 copre ARC, MODBUS e USB. 0x1021 copre tutto il groviglio CCITT più XMODEM, KERMIT e le varianti RFID. Il polinomio da solo non basta a identificare una variante.
init: il valore di partenza del registro
Dominano due valori, 0x0000 e 0xFFFF. La differenza pesa più di quanto sembri. Parti da zero e un byte a zero lascia il registro a zero, quindi 00 00 01 e 01 producono CRC identici. Un messaggio che durante il transito guadagna o perde zeri iniziali supera il controllo. Partire da 0xFFFF elimina quel punto cieco, ed è il motivo per cui Modbus, CCITT-FALSE e USB lo usano tutti.
refin e refout: ordine dei bit, non dei byte
refin inverte gli otto bit di ogni byte in ingresso prima che entri nel registro. refout inverte il registro finale prima dell’ultimo XOR. L’hardware che fa uscire i dati con il bit meno significativo per primo ottiene queste riflessioni gratis, perciò le varianti riflesse tendono a essere quelle nate dai protocolli seriali.
È la coppia di parametri che viene confusa più spesso con l’endianness. Sono cose diverse, a livelli diversi, e la sezione 6 le separa.
xorout: lo XOR finale
È l’ultimo passo, applicato al registro dopo refout. Per CRC-16 vale di solito 0x0000 oppure 0xFFFF; la famiglia CRC-32 usa 0xFFFFFFFF. È il parametro che costa meno sbagliare, perché una discrepanza qui è identica a una discrepanza in qualunque altro punto.
width: 8, 16 o 32 bit
La larghezza fissa il tetto della capacità di rilevamento. Un CRC di larghezza n intercetta ogni errore a raffica lungo fino a n bit e lascia passare una corruzione casuale con probabilità circa 2^-n. Fa 1 su 65.536 per CRC-16 e 1 su 4,3 miliardi per CRC-32.
Partendo da CRC-16/XMODEM e cambiando esattamente un parametro alla volta, sull’input di prova standard 123456789:
baseline CRC-16/XMODEM = 0x31C3
init 0x0000 -> 0xFFFF (becomes CCITT-FALSE) = 0x29B1
refin/refout -> true (becomes KERMIT) = 0x2189
xorout 0x0000 -> 0xFFFF = 0xCE3C
poly 0x1021 -> 0x8005 = 0xFEE8
Un solo flag invertito dà un risultato completamente diverso. Il CRC non ha alcuna nozione di “vicino”. Due valori o coincidono, oppure non ti dicono nulla su quanto fossero distanti gli input, perciò notare una discrepanza non ne localizza la causa. Il ciclo interno è solo XOR e shift; la guida completa alle operazioni bit a bit tratta quegli operatori in sé, e questo articolo li dà per scontati.
3. “CCITT” è un brutto nome
A settembre 2026 il catalogo RevEng CRC elenca 31 definizioni distinte di CRC a 16 bit, e tre di esse portano l’etichetta CCITT. Nessuna delle tre concorda con le altre. Le quattro righe qui sotto condividono polinomio e input, e danno quattro risultati senza alcun legame tra loro:
| Nome comune | Nome nel catalogo | check | init | refin/refout | xorout |
|---|---|---|---|---|---|
| CCITT-FALSE | CRC-16/IBM-3740 | 0x29B1 | 0xFFFF | false | 0x0000 |
| XMODEM | CRC-16/XMODEM | 0x31C3 | 0x0000 | false | 0x0000 |
| “il vero” CCITT | CRC-16/KERMIT | 0x2189 | 0x0000 | true | 0x0000 |
| — | CRC-16/MCRF4XX | 0x6F91 | 0xFFFF | true | 0x0000 |
La storia è breve e poco utile. Il catalogo registra CRC-16/KERMIT con gli alias CRC-CCITT e CRC-16/CCITT-TRUE, e quella variante è riflessa. Sotto il nome CCITT circolava largamente anche un’implementazione non riflessa con init 0xFFFF, ed è il motivo per cui il catalogo riporta “CCITT-FALSE” come alias di CRC-16/IBM-3740. XMODEM sta in mezzo: stesso polinomio, nessuna riflessione, init a zero.
Perciò quando un datasheet dice CCITT, ti ha detto che il polinomio è 0x1021 e nient’altro. Ti restano quattro candidati, e sceglierne uno sbagliato ti dà un valore plausibile esattamente quanto quello giusto.
Cita i parametri, non i nomi. Scrivere poly=0x1021, init=0xFFFF, refin=false, refout=false, xorout=0x0000 nel documento di protocollo occupa una riga e chiude la questione per sempre. Scrivere “CRC-16/CCITT” non chiude niente.
4. Come capire quale variante di CRC-16 hai davanti
Il catalogo risolve il problema con un’impronta digitale. Ogni voce pubblica un valore di check: il CRC dei nove byte ASCII 123456789. Fai passare quella stringa nell’implementazione che hai davanti e cerca il risultato in tabella.
| Variante | check | poly | init | refin | refout | xorout |
|---|---|---|---|---|---|---|
| CRC-16/ARC (IBM/LHA) | 0xBB3D | 0x8005 | 0x0000 | true | true | 0x0000 |
| CRC-16/MODBUS | 0x4B37 | 0x8005 | 0xFFFF | true | true | 0x0000 |
| CRC-16/USB | 0xB4C8 | 0x8005 | 0xFFFF | true | true | 0xFFFF |
| CRC-16/IBM-3740 (CCITT-FALSE) | 0x29B1 | 0x1021 | 0xFFFF | false | false | 0x0000 |
| CRC-16/XMODEM | 0x31C3 | 0x1021 | 0x0000 | false | false | 0x0000 |
| CRC-16/KERMIT (il vero CCITT) | 0x2189 | 0x1021 | 0x0000 | true | true | 0x0000 |
| CRC-16/GENIBUS | 0xD64E | 0x1021 | 0xFFFF | false | false | 0xFFFF |
| CRC-16/MCRF4XX | 0x6F91 | 0x1021 | 0xFFFF | true | true | 0x0000 |
| CRC-32/ISO-HDLC (zip, PNG, zlib) | 0xCBF43926 | 0x04C11DB7 | 0xFFFFFFFF | true | true | 0xFFFFFFFF |
| CRC-32/BZIP2 | 0xFC891918 | 0x04C11DB7 | 0xFFFFFFFF | false | false | 0xFFFFFFFF |
| CRC-32C (Castagnoli, iSCSI) | 0xE3069283 | 0x1EDC6F41 | 0xFFFFFFFF | true | true | 0xFFFFFFFF |
| CRC-8/SMBUS | 0xF4 | 0x07 | 0x00 | false | false | 0x00 |
| CRC-8/MAXIM-DOW (1-Wire) | 0xA1 | 0x31 | 0x00 | true | true | 0x00 |
C’è un dettaglio che fa fallire parecchi di questi confronti: 123456789 indica i nove byte 31 32 33 34 35 36 37 38 39, non il numero 123456789 e non una stringa terminata da null. Se il tuo linguaggio passa alla funzione una stringa wide o aggiunge un terminatore, stai calcolando su un input diverso e ogni riga mancherà il bersaglio. I payload non-ASCII aggiungono una seconda trappola: lo stesso testo codificato in UTF-8 e in Windows-1252 dà due sequenze di byte diverse, quindi due CRC diversi. La tabella ASCII con convertitore mostra il byte che sta dietro a ogni carattere.
Se il valore di check non corrisponde a nessuna riga, controlla le cause banali prima di sospettare un polinomio personalizzato:
- Stai leggendo il risultato al contrario rispetto a come viaggia in trasmissione.
- Chi trasmette include o esclude un byte di indirizzo, un byte di lunghezza o il campo CRC stesso.
- L’
initè proprietario, né0x0000né0xFFFF. Succede nei protocolli chiusi dei contatori, ed è di solito sepolto in una nota a piè di pagina.
Ricavare i parametri per forza bruta
Quando il produttore non te lo dice e non puoi leggere il suo firmware, chiedi una cosa sola: un frame catturato più il CRC che il loro dispositivo ha prodotto per quel frame. Poi enumera. Due polinomi, due valori di init, refin e refout indipendenti e due valori di xorout fanno 32 combinazioni, che un computer esaurisce all’istante:
target = 0x4B37 # il valore restituito dalla loro implementazione
for poly in (0x8005, 0x1021):
for init in (0x0000, 0xFFFF):
for refin in (False, True):
for refout in (False, True):
for xorout in (0x0000, 0xFFFF):
if crc(b"123456789", 16, poly, init, refin, refout, xorout) == target:
print(f"poly=0x{poly:04X} init=0x{init:04X} "
f"refin={refin} refout={refout} xorout=0x{xorout:04X}")
poly=0x8005 init=0xFFFF refin=True refout=True xorout=0x0000
Un solo riscontro, ed è CRC-16/MODBUS. Dagli in pasto un frame vero invece di 123456789 e la stessa scansione a 32 vie funziona su qualsiasi messaggio di cui hai un CRC noto e corretto. Se più combinazioni sopravvivono a un campione, prova con un secondo frame e interseca i risultati.
5. Modbus RTU nella pratica
Modbus RTU usa CRC-16/MODBUS: polinomio 0x8005, init 0xFFFF, riflesso in ingresso e in uscita, nessuno XOR finale. Per la nostra richiesta di sei byte il CRC è 0xCDC5, e il frame completo è:
01 03 00 00 00 0A C5 CD
L’ordine in trasmissione è il rovescio di come scrivi il valore
Il valore è 0xCDC5. I byte accodati al frame sono C5 CD. Modbus prescrive prima il byte basso del CRC, cioè l’opposto dell’ordine in cui leggi il numero esadecimale, e coglie in fallo un sacco di gente. Ogni altro campo multi-byte dello stesso frame, compreso quel conteggio di registri 00 0A, è big-endian. Il CRC è l’eccezione.
Questo ha un effetto collaterale utile. Calcola il CRC-16/MODBUS sull’intero frame, byte del CRC inclusi, e il risultato è:
0x0000
È l’intera routine di validazione del ricevitore. Non serve staccare i due byte finali, invertirli e confrontarli con un valore calcolato in locale. Fai girare il CRC su tutto quello che è arrivato e controlla che venga zero. Meno passaggi significa meno punti in cui invertire i byte nel modo sbagliato.
Perché la documentazione Modbus mostra 0xA001
Apri quasi qualsiasi implementazione Modbus e la costante nel sorgente è 0xA001, non 0x8005. Sono corrette entrambe. 0xA001 è 0x8005 con i suoi 16 bit invertiti, e appartiene alla forma riflessa dell’algoritmo, dove il registro scorre a destra anziché a sinistra e i byte in ingresso non hanno bisogno di alcuna inversione. Le due implementazioni producono un output identico; cambia solo il meccanismo interno. 0xA001 nel sorgente è un indizio affidabile di un’implementazione CRC-16 riflessa.
In una cattura può capitarti anche Modbus ASCII, che è un framing a parte: trasporta ogni byte come due caratteri esadecimali fra un 3A (:) iniziale e un 0D 0A finale, e usa un LRC anziché un CRC. Se la tua traccia mostra 3A 30 31 30 33 dove ti aspettavi 01 03, sei in modalità ASCII e nessuna variante di CRC coinciderà mai. La tabella ASCII riporta quei byte direttamente ai caratteri.
Lo stesso algoritmo in C
Il firmware Modbus non usa quasi mai il modello Python di sopra. Usa direttamente la forma riflessa, scorrendo a destra e facendo lo XOR con 0xA001:
uint16_t crc16_modbus(const uint8_t *buf, size_t len) {
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < len; i++) {
crc ^= buf[i];
for (int b = 0; b < 8; b++)
crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
}
return crc;
}
Alimentata con il frame 01 03 00 00 00 0A, questa funzione restituisce 0xCDC5, lo stesso valore prodotto dal motore Python. La stringa di check 123456789 dà 0x4B37. Entrambi sono stati eseguiti con Apple clang 21 e coincidono bit per bit con la tabella della sezione 4.
6. refin/refout non è l’endianness
Modbus contiene entrambe le cose nello stesso frame, ed è per questo che confonderle costa caro. L’endianness riguarda i byte all’interno di un valore multi-byte: se 0xCDC5 viene memorizzato o trasmesso come CD C5 oppure come C5 CD. Dentro un byte non si muove nulla. È il livello che big-endian contro little-endian tratta per esteso, e questo articolo non lo ripete.
La riflessione riguarda i bit all’interno di un singolo byte. refin inverte gli otto bit di ogni byte prima che il registro lo veda: il bit 0 diventa il bit 7. Il byte mantiene la sua posizione nel messaggio. Nessuna delle due impostazioni ha effetto sull’altra.
In un frame Modbus succedono entrambe le cose, in modo indipendente:
- Dentro l’algoritmo
refinerefoutvalgono true, quindi i bit vengono invertiti all’interno dei byte. - In trasmissione il CRC finito viene accodato con il byte basso per primo, e questa è una decisione sull’ordine dei byte presa dal protocollo.
Disattivare la riflessione per “sistemare” un problema di ordine dei byte produce una variante del tutto diversa, e scambiare i byte finali del frame per compensare una riflessione sbagliata produce un valore sbagliato in un modo nuovo. Diagnostica una cosa alla volta: prima fai tornare il valore di check con il solo algoritmo, poi occupati della disposizione del frame.
7. CRC-32 contro CRC-16: quale dovresti usare?
CRC-32 ha lo stesso problema di CRC-16, solo in modo meno visibile, perché una variante domina così tanto che quasi nessuno scopre mai che ce ne sono altre.
| Variante | check | poly | refin/refout |
|---|---|---|---|
| CRC-32/ISO-HDLC | 0xCBF43926 | 0x04C11DB7 | true |
| CRC-32/BZIP2 | 0xFC891918 | 0x04C11DB7 | false |
| CRC-32C (Castagnoli) | 0xE3069283 | 0x1EDC6F41 | true |
ISO-HDLC è quello che calcola zlib.crc32, ed è ovunque sul lato web dello stack. Zip lo memorizza per ogni voce nella central directory, e ogni chunk PNG termina con uno che copre tipo e dati del chunk. La frame check sequence di Ethernet usa lo stesso set di parametri in fondo a ogni frame che il tuo server invia. BZIP2 è quello stesso polinomio con le riflessioni disattivate, e produce un valore che non condivide nulla con ISO-HDLC.
Anche CRC-16 compare da queste parti: Redis Cluster instrada una chiave verso uno dei suoi 16.384 slot con CRC16(key) mod 16384, ed è il motivo per cui le chiavi con lo stesso hash tag finiscono sullo stesso nodo.
Perché CRC-32C ha vinto la lotteria dell’hardware
CRC-32C usa un polinomio diverso, con una capacità di rilevamento migliore sui blocchi corti che i protocolli di storage e di rete si scambiano. Questo gli è valso iSCSI, i checksum dei metadati di ext4, SCTP e Btrfs. Poi Intel l’ha messo nel silicio: l’istruzione crc32 di SSE4.2 calcola direttamente CRC-32C, il che lo trasforma in un controllo di integrità di fatto gratuito sulle x86 moderne. Se oggi devi scegliere un CRC per codice nuovo e non hai vincoli di compatibilità, prendi questo.
Nessuno di questi è un hash con cui verificare un download. Quando vuoi un checksum che confermi che un file è arrivato intatto, il generatore di hash MD5 è lo strumento più familiare, e MD5 contro SHA-256 spiega di quale digest fidarsi e per cosa. Un CRC risponde alla domanda “si è corrotto?”; un hash crittografico risponde a “è esattamente il contenuto che mi aspetto?”.
8. Un CRC non ferma le manomissioni
Questi due messaggi JSON hanno significato opposto e lo stesso CRC-32:
message A = b'{"to":"alice","amount":100} H\xf6\xc0E'
message B = b'{"to":"mallory","amount":999}\xb2\xb2\xc2\xc2'
CRC-32(A) = 0x12345678
CRC-32(B) = 0x12345678
SHA-256 sugli stessi due:
A -> a83b90f5e3f21dd32039e0e693a8b15864eda83e258c115deeee50a60c09ae23
B -> f9319ee0507d719c7260535dff33df721ddd9c0e1857690f3d8e02f92601b742
Niente di tutto questo esce da una ricerca per tentativi: i byte finali si ricavano per via algebrica. Il CRC è una funzione lineare, e accodare quattro byte a un messaggio si mappa sull’output CRC-32 come una biiezione: per qualsiasi messaggio e qualsiasi valore obiettivo esiste esattamente un suffisso di quattro byte che ti ci porta, e trovarlo è aritmetica.
Accodando i quattro byte ricavati 46 C9 6E 0B a hello world:
CRC-32(b'hello world' + 46 C9 6E 0B) = 0xDEADBEEF target 0xDEADBEEF MATCH
Chi può modificare il tuo payload può quindi anche aggiustarti il CRC, in tempo costante, senza dover cercare niente. Mettere un segreto in testa al messaggio non lo salva: per una modifica che conserva la lunghezza, la correzione da applicare al CRC dipende solo dai bit cambiati, quindi si può calcolare senza conoscere il segreto. È la forma dell’attacco che ha demolito WEP.
Il CRC difende dal rumore di trasmissione, che è casuale e non ha obiettivi. Un avversario è tutt’altro: sceglie di preciso i bit da cambiare, e contro di lui un CRC non ti dà nulla. Quando il requisito è l’autenticità e non l’integrità, ti serve una costruzione con chiave: il generatore HMAC ne produce una su qualsiasi payload e chiave, e perché la verifica della firma di un webhook fallisce passa in rassegna i punti che fanno inciampare quando la colleghi a un endpoint reale.
9. FAQ
CRC-16/CCITT è la stessa cosa di CRC-16/CCITT-FALSE?
No, CRC-16/CCITT-FALSE e CRC-16/CCITT sono varianti diverse. CCITT-FALSE è CRC-16/IBM-3740: init 0xFFFF, non riflesso, valore di check 0x29B1. La variante che di solito si intende con il semplice CCITT è CRC-16/KERMIT: init 0x0000, riflesso, check 0x2189. Condividono il polinomio 0x1021 e non concordano su nient’altro.
Perché un calcolatore online non concorda con il mio dispositivo?
Parametri diversi, quasi sempre. Lo strumento parte da una variante e il dispositivo ne implementa un’altra. Fai passare i byte ASCII 123456789 in entrambi, confronta i due valori di check con la tabella della sezione 4 e il parametro che non torna di solito salta fuori in meno di un minuto.
Perché Modbus mette per primo il byte basso del CRC?
Perché lo dice la specifica, ed è l’unico campo del frame che si comporta così. Indirizzi e conteggi di registri sono big-endian; il CRC in coda no. Calcola il CRC-16/MODBUS sull’intero frame, quei due byte compresi, e controlla che venga 0x0000 invece di riordinarli a mano.
Che cosa invertono esattamente refin e refout?
I bit dentro un byte, mai i byte dentro un messaggio. refin inverte gli otto bit di ogni byte in ingresso prima dell’elaborazione; refout inverte il registro finale prima dell’ultimo XOR. Entrambi sono indipendenti dall’endianness, che invece governa come un valore multi-byte viene disposto in trasmissione.
Dovrei usare CRC-16 o CRC-32?
CRC-16 lascia passare una corruzione casuale all’incirca una volta su 65.536, CRC-32 una volta su 4,3 miliardi. Per frame seriali corti di qualche decina di byte CRC-16 va bene, ed è comunque quasi sempre imposto dal protocollo. Per file, frame di rete e qualsiasi cosa superi i pochi kilobyte, usa CRC-32.
Posso usare un CRC come firma di API?
No, un CRC non funziona come firma di API. Il CRC è lineare, quindi chiunque alteri il payload può ricalcolare un valore che coincide, e accodare quattro byte scelti apposta raggiunge qualsiasi valore CRC-32 tu indichi. Le firme richiedono una chiave segreta e una costruzione non lineare. Usa HMAC con SHA-256.
Si possono recuperare i dati originali da un valore CRC?
No, i dati originali non si possono recuperare da un valore CRC. Un CRC-16 comprime qualsiasi input in 16 bit, quindi infiniti messaggi condividono ciascun valore. La direzione inversa resta comunque utile a chi attacca: dato un valore obiettivo puoi costruire un messaggio che lo produce, ed è esattamente il motivo per cui un CRC non autentica nulla.
Il documento del produttore dice solo “CRC-16”. Come faccio a fissare i parametri?
Chiedi un frame catturato più il CRC che il loro dispositivo ha calcolato per quel frame, poi esegui su quella coppia la scansione delle 32 combinazioni della sezione 4. Se sopravvive più di un set di parametri, ripeti con un secondo frame e tieni l’intersezione. Due campioni sono quasi sempre decisivi.