Advertisement
Guest User

Status remote decoder

a guest
Aug 15th, 2013
326
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 11.50 KB | None | 0 0
  1. Program remote;
  2. {
  3. This program extracts binary sequences from WAV files for the Status
  4.   remote control or similar devices. It can display handset ID so you can
  5.   use pihat to simulate communications with already paired remote switches.
  6.   WAV audio format must be recorded as PCM 44.1kHz / 48kHz Stereo or Mono.
  7.  
  8.   There several ways to capture audio such as tapping into the remote control
  9.   circuit board, use a coil microphone or telephone pickup coil (untested).
  10.   If you have an FM radio which lacks *AFC drift control then it should be
  11.   possible tune to one of the remote control's transmission harmonics.
  12.   If audio contains lots of background noise then use the -f1 -f2 parameter,
  13.   if no data can be found you'll need to retune your radio and re-record.
  14.  
  15.   *AFC technology is used to lock onto an FM signal to prevent tuning drift
  16.   it does not play nicely with AM broadcasts such as the Status remote
  17.   control and thus audio could degrade as a result.
  18. }
  19.  
  20. {$i-}
  21.  
  22. uses crt;
  23.  
  24. var
  25.    f_read : file;
  26.    read_buffer : array [1..8192] of byte;
  27.    wav_buffer : array [1..4] of byte;
  28.    read_offset, bytes_read : word;
  29.    stereo, bit16, show_status : boolean;
  30.    filter_mode : byte;
  31.    last_msg : string;
  32. Procedure open_wav;
  33. begin
  34.   assign(f_read,paramstr(1));
  35.   filemode := 2;
  36.   reset(f_read,1);
  37.   if ioresult <> 0 then
  38.   begin
  39.     writeln;
  40.     writeln('Error cannot open: ',paramstr(1));
  41.     halt(0);
  42.   end;
  43.   read_offset := 0;
  44. end;
  45.  
  46. Procedure close_wav;
  47. begin
  48.   close(f_read);
  49. end;
  50.  
  51. Function read_byte(var data:byte):boolean;
  52. var
  53.    loop : byte;
  54. begin
  55.   if read_offset = 0 then BlockRead(f_read,read_buffer,Sizeof(read_buffer),bytes_read);
  56.   if bytes_read = 0 then read_byte := true
  57.   else
  58.   begin
  59.     if (bytes_read < Sizeof(read_buffer)) and (read_offset = bytes_read) then read_byte := true
  60.     else
  61.     begin
  62.       inc(read_offset);
  63.       data := read_buffer[read_offset];
  64.       if read_offset = (Sizeof(read_buffer)) then read_offset := 0;
  65.       read_byte := false;
  66.     end;
  67.   end;
  68. end;
  69.  
  70. Function audio_filter(data,method:byte):byte;
  71. var lpf : byte;
  72.     hpf : integer;
  73. begin
  74.   if (method = 0) then lpf := data;
  75.   if (method = 1) then lpf := (data + wav_buffer[1]) div 2;
  76.   if (method = 2) then
  77.   begin
  78.     lpf := (data + wav_buffer[1] + wav_buffer[2] +
  79.                    wav_buffer[3] + wav_buffer[4]) div 5;
  80.     if data > lpf then hpf := data - lpf
  81.                   else hpf := lpf - data;
  82.     hpf := hpf div 2;
  83.     if data > lpf then lpf := lpf + hpf
  84.                   else lpf := lpf - hpf;
  85.   end;
  86.   wav_buffer[4] := wav_buffer[3];
  87.   wav_buffer[3] := wav_buffer[2];
  88.   wav_buffer[2] := wav_buffer[1];
  89.   wav_buffer[1] := data;
  90.   audio_filter := lpf;
  91. end;
  92.  
  93. Function read_wav(var data:byte):boolean;
  94. var
  95.    mono8, left8, right8 : byte;
  96.    mono16, left16, right16 : word;
  97.    eof : boolean;
  98. begin
  99.   eof := read_byte(mono8);
  100.   if stereo and not bit16 then { stereo 8bit }
  101.   begin
  102.     left8 := mono8;
  103.     if not eof then eof := read_byte(right8);
  104.     mono8 := (left8 + right8) div 2; { stereo -> mono }
  105.   end;
  106.   if bit16 then { 16bit audio }
  107.   begin
  108.     mono16 := mono8;
  109.     if not eof then eof := read_byte(mono8);
  110.     mono16 := mono16 + mono8 * 256;
  111.     mono16 := mono16 xor 32768;
  112.     if stereo then { stereo 16bit }
  113.     begin
  114.       left16 := mono16;
  115.       if not eof then eof := read_byte(mono8);
  116.       right16 := mono8;
  117.       if not eof then eof := read_byte(mono8);
  118.       right16 := right16 + mono8 * 256;
  119.       right16 := right16 xor 32768;
  120.       mono16 := (left16 + right16) div 2; { stereo -> mono }
  121.     end;
  122.     mono8 := mono16 div 256; { 16bit -> 8bit }
  123.   end;
  124.   data := audio_filter(mono8,filter_mode);
  125.   read_wav := eof;
  126. end;
  127.  
  128. Procedure help;
  129. begin
  130.   writeln('Remote control binary decoder - redhawk 2013');
  131.   writeln('usage: remote audio.wav [options]');
  132.   writeln('[options]: -s -f1 -f2');
  133.   writeln('-s = decode Status binary data');
  134.   writeln('-f(n) = apply audio filter(n)');
  135.   halt;
  136. end;
  137.  
  138. Procedure wav_error(error_type:byte);
  139. begin
  140. exit;
  141.   case error_type of
  142.     0 : writeln('Not a valid WAV file');
  143.     1 : writeln('WAV format not PCM');
  144.     2 : writeln('Sample rate not 44.1kHz or 48kHz');
  145.     3 : writeln('Only 8bit or 16bit sample files are supported');
  146.     4 : writeln('WAV header missing "data" tag');
  147.   end;
  148.   close_wav;
  149.   halt;
  150. end;
  151.  
  152. Procedure verify_wav;
  153. var
  154.    loop, data : byte;
  155.    eof : boolean;
  156.    header_test : string;
  157.    format_test : word;
  158. begin
  159.   header_test := '';
  160.   for loop := 1 to 4 do
  161.   begin
  162.     eof := read_byte(data);
  163.     if eof then wav_error(0);
  164.     header_test := header_test + chr(data);
  165.   end;
  166.   for loop := 1 to 4 do eof := read_byte(data);
  167.   for loop := 1 to 8 do
  168.   begin
  169.     eof := read_byte(data);
  170.     header_test := header_test + chr(data);
  171.   end;
  172.   if header_test <> 'RIFFWAVEfmt ' then wav_error(0);
  173.   for loop := 1 to 4 do eof := read_byte(data);
  174.   eof := read_byte(data);
  175.   format_test := data;
  176.   eof := read_byte(data);
  177.   format_test := format_test + data * 256;
  178.   if format_test <> 1 then wav_error(1); { not PCM }
  179.   eof := read_byte(data);
  180.   format_test := data;
  181.   eof := read_byte(data);
  182.   format_test := format_test + data * 256;
  183.   if format_test = 2 then stereo := true else stereo := false;
  184.   eof := read_byte(data);
  185.   format_test := data;
  186.   eof := read_byte(data);
  187.   format_test := format_test + data * 256;
  188.   if ((format_test = 44100) or (format_test = 48000)) then else wav_error(2);
  189.   eof := read_byte(data);
  190.   format_test := data;
  191.   eof := read_byte(data);
  192.   format_test := format_test + data * 256;
  193.   if format_test <> 0 then wav_error(2); { wrong sample rate }
  194.   for loop := 1 to 6 do eof := read_byte(data);
  195.   eof := read_byte(data);
  196.   format_test := data;
  197.   eof := read_byte(data);
  198.   format_test := format_test + data * 256;
  199.   if format_test = 16 then bit16 := true
  200.   else
  201.   begin
  202.     bit16 := false;
  203.     if format_test <> 8 then wav_error(3); { not 8bit or 16bit }
  204.   end;
  205.   header_test := '';
  206.   for loop := 1 to 4 do
  207.   begin
  208.     eof := read_byte(data);
  209.     header_test := header_test + chr(data);
  210.   end;
  211.   if header_test <> 'data' then wav_error(4);
  212.   for loop := 1 to 4 do eof := read_byte(data);
  213. end;
  214.  
  215. Function normalise(data:byte;dc_offset:integer;wav_norm:byte):byte;
  216. var idata : longint;
  217.     amp : integer;
  218. begin
  219.   idata := data - dc_offset - 128;
  220.   amp := 10000 div (wav_norm + 1);
  221.   idata := (idata * amp) div 100;
  222.   idata := idata + 128;
  223.   if idata > 255 then idata := 255;
  224.   if idata < 0 then idata := 0;
  225.   normalise := idata;
  226. end;
  227.  
  228. Procedure decode_status(data:string);
  229. var
  230.    loop, state, channel : byte;
  231.    handset_id : longint;
  232. begin
  233.   if not show_status then writeln(data)
  234.   else
  235.   begin
  236.     handset_id := 0;
  237.     channel := 0;
  238.     if ((length(data) = 25) and (last_msg <> data)) then
  239.     begin
  240.       for loop := 2 to 20 do
  241.       begin
  242.         handset_id := handset_id * 2;
  243.         handset_id := handset_id + (ord(data[loop]) - 48);
  244.       end;
  245.       state := ord(data[21]) - 48;
  246.       for loop := 22 to 24 do
  247.       begin
  248.         channel := channel * 2;
  249.         channel := channel + (ord(data[loop]) - 48);
  250.       end;
  251.       write(data, ' --brand=5 --id=',handset_id);
  252.       write(' --channel=',channel);
  253.       if channel = 0 then writeln(' (all off)')
  254.                      else writeln(' --state=',state);
  255.       last_msg := data;
  256.     end;
  257.   end;
  258. end;
  259.  
  260. Function data2bin(data : byte):char;
  261. var
  262.    bin : byte;
  263. begin
  264.   bin := data div 30;
  265.   if bin > 1 then bin := 0;
  266.   data2bin := chr(bin + 48);
  267. end;
  268.  
  269. Procedure profile_wav(var _wav_norm:word;var _dc_offset:longint);
  270. var
  271.    count, data, wav_max, wav_min : byte;
  272.    dc_offset, lcount : longint;
  273.    wav_norm, wav_buffer_start : word;
  274.    eof : boolean;
  275. begin
  276.   for count := 1 to 4 do wav_buffer[count] := 128;
  277.   lcount := 0;
  278.   dc_offset := 0;
  279.   wav_min := 128;
  280.   wav_max := 128;
  281.   wav_buffer_start := read_offset;
  282.   repeat
  283.     eof := read_wav(data);
  284.     if not eof then
  285.     begin
  286.       dc_offset := dc_offset + (data - 127);
  287.       lcount := lcount + 1;
  288.       if data < wav_min then wav_min := data;
  289.       if data > wav_max then wav_max := data;
  290.     end;
  291.   until eof;
  292.   dc_offset := dc_offset div lcount;
  293.   wav_min := 255 - wav_min;
  294.   wav_min := wav_min + dc_offset;
  295.   wav_max := wav_max - dc_offset;
  296.   if wav_max > wav_min then wav_norm := wav_max
  297.                        else wav_norm := wav_min;
  298.   wav_norm := ((wav_norm - 128) * 100) div 128;
  299.   read_offset := 0;
  300.   seek(f_read,wav_buffer_start);
  301.   _wav_norm := wav_norm;
  302.   _dc_offset := dc_offset;
  303. end;
  304.  
  305. Function param_option(data:string):boolean;
  306. var
  307.    loop : byte;
  308.    test : boolean;
  309. begin
  310.   test := false;
  311.   if paramcount > 1 then for loop := 1 to paramcount
  312.     do if paramstr(loop) = data then test := true;
  313.   param_option := test;
  314. end;
  315.  
  316. Procedure main;
  317. const
  318.      trigger_threshold = 32;
  319. var
  320.    bin, data, last_data, last_data2 : byte;
  321.    count, wav_norm : word;
  322.    trigger_type : integer;
  323.    dc_offset : longint;
  324.    eof, trigger_pos, trigger_neg, capture : boolean;
  325.    msg : string;
  326. begin
  327.   if (paramcount > 0) then count := 1
  328.                       else count := 0;
  329.   if param_option('-s') then show_status := true
  330.                         else show_status := false;
  331.   if show_status then count := count + 1;
  332.   if param_option('-f1') then filter_mode := 1
  333.                          else filter_mode := 0;
  334.   if param_option('-f2') then filter_mode := 2;
  335.   if filter_mode <> 0 then count := count + 1;
  336.   if ((paramcount < 1) or (paramcount >count)) then help;
  337.   open_wav;
  338.   verify_wav;
  339.   profile_wav(wav_norm,dc_offset);
  340.   capture := false;
  341.   trigger_type := -1;
  342.   count := 0;
  343.   msg := '';
  344.   last_msg := '';
  345.   for count := 1 to 4 do wav_buffer[count] := 128;
  346.   eof := read_wav(data);
  347.   last_data2 := normalise(data,dc_offset,wav_norm);
  348.   eof := read_wav(data);
  349.   last_data := normalise(data,dc_offset,wav_norm);
  350.   repeat
  351.     eof := read_wav(data);
  352.     data := normalise(data,dc_offset,wav_norm);
  353.     if not eof then
  354.     begin
  355.       if data > (last_data2 + trigger_threshold) then trigger_pos := true
  356.                                                  else trigger_pos := false;
  357.       if data < (last_data2 - trigger_threshold) then trigger_neg := true
  358.                                                  else trigger_neg := false;
  359.       if trigger_type = -1 then
  360.       begin
  361.         if trigger_pos or trigger_neg then count := 0;
  362.         if trigger_pos then trigger_type := 0;
  363.         if trigger_neg then trigger_type := 1;
  364.         capture := true;
  365.       end;
  366.       if trigger_pos or trigger_neg then
  367.       begin
  368.         if trigger_type = 0 then
  369.         begin
  370.           if not trigger_neg then
  371.           begin
  372.             capture := true;
  373.             count := 0;
  374.           end else
  375.           if capture then
  376.           begin
  377.             msg := msg + data2bin(count);
  378.             capture := false;
  379.           end
  380.         end;
  381.         if trigger_type = 1 then
  382.         begin
  383.           if not trigger_pos then
  384.           begin
  385.             capture := true;
  386.             count := 0;
  387.           end else
  388.           if capture then
  389.           begin
  390.             msg := msg + data2bin(count);
  391.             capture := false;
  392.           end;
  393.         end;
  394.       end;
  395.       if count = 200 then
  396.       begin
  397.         if msg <> '' then decode_status(msg);
  398.         msg := '';
  399.         trigger_type := -1;
  400.       end;
  401.       count := count + 1;
  402.       last_data2 := last_data;
  403.       last_data := data;
  404.     end;
  405.   until eof;
  406.   if msg <> '' then decode_status(msg);
  407.   close_wav;
  408. end;
  409.  
  410. Begin
  411.   main{};
  412. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement