Manchester codifica um fluxo de dados

14

A codificação Manchester é um protocolo de telecomunicações usado em radiocomunicação que garante a transição de bits em intervalos regulares, para que um receptor possa recuperar a taxa de clock dos próprios dados. Dobra a taxa de bits, mas é barato e simples de implementar. É amplamente utilizado por operadores de rádio amador.

O conceito é muito simples: no nível do hardware, o relógio e as linhas de dados são simplesmente combinadas com XOR. No software, isso é retratado como a conversão de um fluxo de bits de entrada em um fluxo de saída de taxa dupla, com cada entrada '1' traduzida para '01' e cada entrada '0' traduzida para '10'.

Esse é um problema fácil, mas aberto a muitas implementações devido à sua natureza de fluxo de bits. Ou seja, a codificação é conceitualmente um processo bit a bit em vez de um processo byte a byte. Portanto, todos concordamos em endianness, os bits menos significativos da entrada se tornam o byte menos significativo da saída.

Tempo de golfe! Escreva uma função que, dada uma matriz arbitrária de bytes, retorne uma matriz desses dados codificados por manchester.

A entrada e a saída devem ser consideradas little-endian, menos bytes significativos primeiro e bits menos significativos primeiro no fluxo de bits.

Desenho de fluxo de bits ASCII :

bit #      5 4 3 2 1 0                                5  4  3  2  1  0
IN ------- 1 0 1 0 1 1 ---> [manchester encoder] ---  01 10 01 10 01 01 ----> OUT

Exemplos :

Example 1 (hex):
       LSB              MSB     <-- least sig BYTE first
IN : [0x10, 0x02]  
OUT: [0xAA, 0xA9, 0xA6, 0xAA]  

Example 1 (binary):
      msb  lsb                      msb  lsb  <-- translated hex, so msb first
BIN: [00010000, 00000010]                     <-- least sig NIBBLE...
BIN: [10101010, 10101001, 10100110, 10101010] <-- becomes least sig BYTE
         LSB                           MSB

Example 2
IN :  [0xFF, 0x00, 0xAA, 0x55]  
OUT: [0x55, 0x55, 0xAA, 0xAA, 0x66, 0x66, 0x99, 0x99]

Example 3
IN : [0x12, 0x34, 0x56, 0x78, 0x90]  
OUT: [0xA6, 0xA9, 0x9A, 0xA5, 0x96, 0x99, 0x6A, 0x95, 0xAA, 0x69] 

Example 4
IN : [0x01, 0x02, 0x03, 0xF1, 0xF2, 0xF3]  
OUT: [0xA9, 0xAA, 0xA6, 0xAA, 0xA5, 0xAA, 0xA9, 0x55, 0xA6, 0x55, 0xA5, 0x55]

Regras :

  • A solução requer apenas algoritmo para converter entrada em saída.
  • A aquisição de entrada e saída de impressão NÃO é uma parte necessária da solução, mas pode estar incluída. Você deve fornecer seu código de teste / impressão, se não estiver incluído na sua solução.
  • A entrada é uma matriz de bytes de 8 bits (o que quer que isso possa significar no seu idioma preferido), NÃO uma sequência de texto. Você pode usar cadeias de caracteres como formato de armazenamento, se for conveniente no seu idioma, mas caracteres não imprimíveis (por exemplo, 0xFF) devem ser suportados. A entrada também pode demorar, se necessário.
  • A memória para saída deve ser alocada pela sua rotina, não fornecida. editar: requisito desnecessário
  • A saída também é uma matriz de bytes de 8 bits e um comprimento, se necessário.
  • Deve suportar pelo menos 16 KB de entrada
  • O desempenho não deve ser muito horrível: <10s para 16 KB
  • Byte menos significativo primeiro na memória.

Desafio de canal lateral :

  • Desafie a resposta de outro usuário, provando que seu código é mais rápido, mais eficiente em memória ou produz um binário menor!

Obter golfe! O código mais curto vence!

mrmekon
fonte
2
"A memória para saída deve ser alocada pela sua rotina, não fornecida." Isso parece um requisito bastante estranho, pois muitos idiomas têm alocação de memória completamente automática.
Aaaaaaaaaaa
O que na Terra você possuía para usar uma ordem de bits tão bizarra?
Peter Taylor
A ordem dos bits faz sentido quando você considera o meio físico para o qual é usado; esse algoritmo é para um fluxo de bits individuais viajando pelo ar. O fato de termos que armazená-lo na memória e de escrevermos hex msb-> lsb torna um pouco complicado acompanhar.
Mrmekon

Respostas:

6

GolfScript 28 caracteres

{2{base}:|~4|43691-~256|~\}%

Versão equivalente sem otimização ofuscante:

{2base 4base 43691-~256base~\}%

O código aceita a entrada como uma matriz de números inteiros e retorna o mesmo.

Para cada número na matriz, o número é convertido na forma de matriz da base 2 e, em seguida, é convertido novamente em um número como se fosse a base 4, o que tem o efeito de espaçar os bits com um 0 entre cada um. 43691 é subtraído do número e o resultado é binário invertido, isso é equivalente a subtrair o número de 43690 (43690 = 0b1010101010101010). O número é então dividido em duas partes, convertendo-o em uma matriz base 256, a matriz é decomposta e a ordem dos dois números resultantes é invertida.

Exemplo de entrada:

[1 2 3 241 242 243]

Exemplo de saída:

[169 170 166 170 165 170 169 85 166 85 165 85]
aaaaaaaaaaaa
fonte
Isso é ridiculamente curto e muito inteligente! Embora não pareça atingir os 16 KB na sugestão de desempenho <10s, pelo menos para mim; o seu leva 43s no meu Mac dual-core para converter uma matriz de 16384 1s. Em comparação, minha implementação maciça de python (2419 caracteres) leva 0,06s para 16 KB.
Mrmekon
Leva menos de 5 segundos na minha máquina (Win 7), e a maior parte disso está convertendo a matriz em saída de texto, o que, tanto quanto li sua pergunta, não faz parte do requisito, mas o GolfScript o faz automaticamente com qualquer coisa restante na pilha após a execução. Pode-se simplesmente fazer com que o código solte o resultado em vez de imprimi-lo (adicione; no final do código). Se você quiser ver a saída (embora isso não faça parte das especificações da pergunta.) Conheço dois truques para acelerar, redirecione-o para um arquivo e imprima-o explicitamente em pequenos pedaços usando os comandos de impressão:{2{base}:|~4|43691-~256|~p p}%
aaaaaaaaaaaa
Em um ubuntu vm (no windows), recebo 8s por 16kb. Em um Mac com uma CPU melhor, demorou 1m18. Eu acho que o rubi de navios com OSX é apenas terrivelmente lento
gnibbler
Parece que a impressão em rubi é repugnantemente lenta na minha máquina. Apenas 2s com impressão desativada e Ruby 1.9 (e 5s com a versão OSX nativa). Isso é muito melhor!
Mrmekon
3

c - 224 caracteres

Eu acredito que isso é funcional, incluindo a alocação de requisitos de memória desde que caiu.

#include <stdlib.h>
int B(char i){int16_t n,o=0xFFFF;for(n=0;n<8;++n)o^=((((i>>n)&1)+1))<<(2*n);
return o;}char* M(char*i,int n){char*o=calloc(n+1,2),*p=o;do{int r=B(*i++);
*p++=0xFF&r;*p++=(0xFF00&r)>>8;}while(--n);return o;}

A parte de trabalho do código é um loop sobre os bits de cada caractere, observando que ((bit + 1) exclusivo-ou 3) é o par de bits de saída e aplica muita lógica de deslocamento e mascaramento para alinhar tudo.

Como é de costume, ele funciona nos dados como caracteres. O andaime de teste não aceita 0 bytes (porque c os trata como final de string), mas o código de trabalho não tem essa limitação.

Pode ser jogado um pouco mais, copiando o trabalho de conversão de bytes em linha.

Execução de teste (com andaime de teste aprimorado):

$ gcc -g manchester_golf.c
$ ./a.out AB xyz U
'AB':
[ 0x41, 0x42 ]
[ 0xa9, 0x9a, 0xa6, 0x9a ]
'xyz':
[ 0x78, 0x79, 0x7a ]
[ 0x6a, 0x95, 0x69, 0x95, 0x66, 0x95 ]
'U':
[ 0x55 ]
[ 0x99, 0x99 ]

Comentado, menos dependente da máquina e com andaime de teste

/* manchester.c
 *
 * Manchester code a bit stream least significant bit first
 *
 * Manchester coding means that bits are expanded as {0,1} --> {10, 01}
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>

/* Caller must insure that out points to a valid, writable two byte
   buffer filled with 0xFF */
int16_t manByte(char i){
  int16_t n,o=0xFFFF;
  printf("Manchester coding byte 0x%hx...\n",i);
  for(n=0; n<CHAR_BIT; ++n)
    o ^= (
      (
       (
        (i>>n)&1) /* nth bit of i*/
       +1) /* +1 */
      ) <<(2*n) /* shifted up 2*n bits */ 
      ;
  printf("\tas 0x%hx\n",o);
  return o;
}

char* manBuf(const char*i, int n){
  char*o=calloc(n+1,2),*p=o;
  do{
    int16_t r=manByte(*i++);
    *p++= 0xFF&r;
    *p++=(0xFF00&r)>>8;
  } while(--n);
  return o;
}

void pbuf(FILE* f, char *buf, int len){
  int i;
  fprintf(f,"[");
  for(i=0; i<len-1; i++)
    fprintf(f," 0x%hhx,",buf[i]);
  fprintf(f," 0x%hhx ]\n",buf[len-1]);
}

int main(int argc, char**argv){
  int i;
  for(i=1; i<argc; i++){
    int l=strlen(argv[i]);
    char *o=manBuf(argv[i],l);
    printf("'%s':\n",argv[i]);
    pbuf(stdout,argv[i],l);
    pbuf(stdout,o,l*2);
    free(o);
  }
  return 0;
}
dmckee --- gatinho ex-moderador
fonte
3

J, 36

,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0)

Esboço da explicação (consulte o Vocabulário J para referência):

  • ,@:(3 :'...'"0)aplica o ... a cada entrada "byte" como y, resultando em dois bytes (inteiros) cada. O resultado é achatado por ,.
  • y#:~8#2é equivalente a 2 2 2 2 2 2 2 2 #: y, ou vetor dos 8 dígitos base-2 menos significativos de y.
  • 4|. troca a frente e para trás 4 bits girando 4 posições.
  • (,.~-.)é equivalente 3 :'(-. y) ,. y'ou não ao argumento 'costurado' ao argumento (assumindo a forma 8 2).
  • #.2 8$, nivela o resultado, fornecendo o fluxo de bits, remodelando para 2 linhas de 8 e convertendo da base 2.

Exemplo de uso (J, interativo):

    ,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0) 1 2 3 241 242 243
,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0) 1 2 3 241 242 243
169 170 166 170 165 170 169 85 166 85 165 85

Informação de velocidade (J, interativa):

   manchester =: ,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0)
manchester =: ,@:(3 :'#.2 8$,(,.~-.)4|.y#:~8#2'"0)
   data =: 256 | i. 16384
data =: 256 | i. 16384
   100 (6!:2) 'manchester data'
100 (6!:2) 'manchester data'
0.243138

O tempo médio para 16kb é um pouco abaixo de 0,25s, Intel Core Duo 1,83Ghz ou similar.

Jesse Millikan
fonte
3

Haskell, 76 caracteres

import Bits
z a=170-sum[a.&.p*p|p<-[1,2,4,8]]
y a=[z a,z$a`div`16]
m=(>>=y)

Execuções de teste:

> testAll 
input      [10, 02]
encoded    [AA, A9, A6, AA]
  pass
input      [FF, 00, AA, 55]
encoded    [55, 55, AA, AA, 66, 66, 99, 99]
  pass
input      [12, 34, 56, 78, 90]
encoded    [A6, A9, 9A, A5, 96, 99, 6A, 95, AA, 69]
  pass
input      [01, 02, 03, F1, F2, F3]
encoded    [A9, AA, A6, AA, A5, AA, A9, 55, A6, 55, A5, 55]
  pass

O desempenho está dentro das especificações. a 1 MB em ~ 1,2 s no meu laptop antigo. Ele sofre porque a entrada é convertida para e de uma lista, e não processada como a ByteArray.

> dd bs=1m count=1 if=/dev/urandom | time ./2040-Manchester > /dev/null
1+0 records in
1+0 records out
1048576 bytes transferred in 1.339130 secs (783028 bytes/sec)
        1.20 real         1.18 user         0.01 sys

A fonte, 2040-Manchester.hs , inclui o código, testes e função principal de um filtro de linha de comando.

MtnViewMark
fonte
3

OCaml + baterias, 138 117 caracteres

let m s=Char.(String.(of_enum[?chr(170-Enum.sum[?d land
p*p|p<-List:[1;2;4;8]?])|c<-enum s/@code;d<-List:[c;c/16]?]))

Testes:

Com

let hex s = String.(enum s/@(Char.code|-Printf.sprintf "%02x")|>List.of_enum|>join" ")

Os resultados são:

m "\x12\x34\x56\x78\x90" |> hex;;
- : string = "a6 a9 9a a5 96 99 6a 95 aa 69"
m "\x10\x02" |> hex;;
- : string = "aa a9 a6 aa"
m "\xFF\x00\xAA\x55" |> hex;;
- : string = "55 55 aa aa 66 66 99 99"
m "\x12\x34\x56\x78\x90" |> hex;;
- : string = "a6 a9 9a a5 96 99 6a 95 aa 69"
m "\x01\x02\x03\xF1\xF2\xF3" |> hex;;  
- : string = "a9 aa a6 aa a5 aa a9 55 a6 55 a5 55"

Como referência, com:

let benchmark n =
  let t = Unix.gettimeofday() in
  assert(2*n == String.(length (m (create n))));
  Unix.gettimeofday() -. t

Eu recebo:

# benchmark 16_384;;
- : float = 0.115520954132080078

no meu MacBook.

Matías Giovannini
fonte
1

Python, 87 caracteres

Mé a função solicitada no problema. Ele chama Ncada nybble e junta tudo de volta em uma lista.

N=lambda x:170-(x&1|x*2&4|x*4&16|x*8&64)
M=lambda A:sum([[N(a),N(a>>4)]for a in A],[])

print map(hex,M([0x10,0x02]))
print map(hex,M([0xff,0x00,0xaa,0x55]))
print map(hex,M([0x12, 0x34, 0x56, 0x78, 0x90]))
print map(hex,M([0x01, 0x02, 0x03, 0xF1, 0xF2, 0xF3]))

gera

['0xaa', '0xa9', '0xa6', '0xaa']
['0x55', '0x55', '0xaa', '0xaa', '0x66', '0x66', '0x99', '0x99']
['0xa6', '0xa9', '0x9a', '0xa5', '0x96', '0x99', '0x6a', '0x95', '0xaa', '0x69']
['0xa9', '0xaa', '0xa6', '0xaa', '0xa5', '0xaa', '0xa9', '0x55', '0xa6', '0x55', '0xa5', '0x55']
Keith Randall
fonte
1

APL (Dyalog Extended) , 22 bytes

∊(⌽(2256)⊤43690-4⊥⊤)¨

Experimente online!

Porta da resposta GolfScript.

∊(⌽(2256)⊤43690-4⊥⊤)¨       Monadic train:
  ⌽(2256)⊤43690-4⊥⊤         Define a helper function taking an integer n:
                               Convert n to base 2. Monadic  is an Extended feature.
                  4            Convert the result from base 4.
                                This puts the 1 digits of n 
                                in odd indices of the intermediate result.
            43960-              Subtract from 43690.
    (2256)⊤                    Convert to 2 base-256 digits, corresponding to
                                nibbles of n.
                              Reverse the order of these bytes.
 (                          Call the helper function for each element of the input
                             and flatten the results into a list.
lirtosiast
fonte
0

C, 164 bytes

Recebe uma série de bytes hexadecimais e converte em fluxo binário de Manchester.

#include <stdio.h>
main(int c,char **v){int i,b,x,j=0;while(++j<c{sscanf(v[j],"%x",&b);x=b^0xff;for(i=9;--i;){printf("%d%d",x&1,b&1);x=x>>1;b=b>>1;}printf("\n");}}

#include <stdio.h>
main(int c,char **v){
int i,b,x,j=0;
while(++j<c){
    sscanf(v[j],"%x",&b);
    x=b^0xff;
    for(i=9;--i;){
        printf("%d%d",x&1,b&1);
        x=x>>1;b=b>>1;}
    printf("\n");}}

Teste:

./a.out 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0

Resultado:

1010101010101010
0110101010101010
1001101010101010
0101101010101010
1010011010101010
0110011010101010
1001011010101010
0101011010101010
1010100110101010
0110100110101010
1001100110101010
0101100110101010
1010010110101010
0110010110101010
1001010110101010
0101010110101010
1010101010101010
1010101001101010
1010101010011010
1010101001011010
1010101010100110
1010101001100110
1010101010010110
1010101001010110
1010101010101001
1010101001101001
1010101010011001
1010101001011001
1010101010100101
1010101001100101
1010101010010101
1010101001010101

Gerador de conjunto de dados de teste de 16kb:

test_data.c:

#include <stdio.h>
void main()
{
int i=16*1024;
while(i--)
{
    printf("0x%02x ", i&0xFF);
}
printf("\n");
}

cc test_data.c -o test_data

1.6G i5dual core contra-relógio:

time ./a.out `./test_data` > test.out 
real    0m0.096s
user    0m0.090s
sys 0m0.011s
zeddee
fonte
Bom primeiro post, mas geralmente não tentamos ofuscar nosso código. Mais curto sim, mais difícil de ler não.
Rɪᴋᴇʀ
0

PHP, 156 bytes

function f($i){foreach($i as$n){$b=str_split(str_replace([0,1,2],[2,'01',10],
str_pad(decbin($n),8,0,0)),8);$o[]=bindec($b[1]);$o[]=bindec($b[0]);}return$o;}

Dada a entrada [0, 1, 2, 3, 4, 5], ela retorna:

[170, 170, 169, 170, 166, 170, 165, 170, 154, 170, 153, 170]

Ele codifica 16 KiB de dados em 0,015 segundos e 1 MiB de dados em cerca de 0,9 segundos.

O código não-bloqueado, outra implementação (mais longa e cerca de duas vezes mais lenta) e os casos de teste podem ser encontrados na minha página de soluções de código-golfe no Github.

axiac
fonte