272 lines
8.7 KiB
Python
272 lines
8.7 KiB
Python
import os
|
|
import math
|
|
|
|
|
|
# Protocol settings: https://phreakerclub.com/447
|
|
class Protocol:
|
|
"""
|
|
Generic OOK communication protocol:
|
|
- name: the protocol's name
|
|
- n_bits: number of bits composing a key
|
|
- transposition_table: how 1s and 0s are translated to .sub format
|
|
- pilot_period: preamble PREPENDED to each key in sub format, empty by default
|
|
- stop_bit: APPENDED to each key in sub format, empty by default
|
|
- frequency: protocol's frequency in Hz, 433920000 by default
|
|
- repetition: number of times to repeat each key, 3 by default
|
|
- key_range: range of keys to generate, None by default
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
name,
|
|
n_bits,
|
|
transposition_table,
|
|
pilot_period="",
|
|
stop_bit="",
|
|
frequency=433920000,
|
|
repetition=3,
|
|
key_range=None,
|
|
):
|
|
self.name = name
|
|
self.n_bits = n_bits
|
|
self.transposition_table = transposition_table
|
|
self.pilot_period = pilot_period
|
|
self.stop_bit = stop_bit
|
|
self.repetition = repetition
|
|
self.key_range = key_range
|
|
self.file_header = (
|
|
"Filetype: Flipper SubGhz RAW File\n"
|
|
+ "Version: 1\n"
|
|
+ f"Frequency: {frequency}\n"
|
|
+ "Preset: FuriHalSubGhzPresetOok650Async\n"
|
|
+ "Protocol: RAW\n"
|
|
)
|
|
|
|
def key_to_sub(self, key):
|
|
bin_str = f"{key:0{self.n_bits}b}"
|
|
return self.key_bin_str_to_sub(bin_str)
|
|
|
|
def key_bin_str_to_sub(self, bin_str, debruijn=False):
|
|
sub = ""
|
|
if not debruijn:
|
|
sub = self.pilot_period
|
|
line_len = 0 # keep lines under 2500 chars
|
|
for bit in bin_str:
|
|
if line_len > 2500:
|
|
sub += "\nRAW_Data: "
|
|
line_len = 0
|
|
sub += self.transposition_table[bit]
|
|
line_len += len(self.transposition_table[bit])
|
|
if not debruijn:
|
|
sub += self.stop_bit
|
|
return sub
|
|
|
|
def de_bruijn(self):
|
|
"""
|
|
de Bruijn binary sequence
|
|
credit: https://www.rosettacode.org/wiki/De_Bruijn_sequences#Python
|
|
"""
|
|
alphabet = "01"
|
|
k = 2
|
|
a = [0] * k * (self.n_bits if self.pilot_period == "" else self.n_bits + 1)
|
|
sequence = []
|
|
|
|
def db(t, p):
|
|
if t > self.n_bits:
|
|
if self.n_bits % p == 0:
|
|
sequence.extend(a[1 : p + 1])
|
|
else:
|
|
a[t] = a[t - p]
|
|
db(t + 1, p)
|
|
for j in range(a[t - p] + 1, k):
|
|
a[t] = j
|
|
db(t + 1, t)
|
|
|
|
db(1, 1)
|
|
db_seq = "".join(alphabet[i] for i in sequence)
|
|
return self.key_bin_str_to_sub(db_seq, True)
|
|
|
|
def generate_sub_files(self, n_folders=6):
|
|
"""
|
|
Generate sub files grouped by split nuber of keys
|
|
Directory structure:
|
|
- sub_files
|
|
- protocol_name
|
|
- split_factor
|
|
- split_factor_id.sub
|
|
|
|
n_folder: number of folders to create,
|
|
folder [1..n_folders] will contain [2^1..2^n_folders] files,
|
|
each file containing [2^n_bits/2^1..2^n_bits/2^n_folders] keys.
|
|
"""
|
|
if (
|
|
self.n_bits > 12 and self.key_range is None
|
|
): # take up too much space for github
|
|
print(f"Skipping {self.name}, takes up too much space for github")
|
|
return
|
|
base_dir = f"sub_files/{self.name}"
|
|
os.makedirs(base_dir, exist_ok=True)
|
|
# If key_range is defined, generate those keys only
|
|
if self.key_range is not None:
|
|
filename = f"{base_dir}/bf_{self.key_range[0]}-{self.key_range[-1]}.sub"
|
|
with open(filename, "w") as f:
|
|
f.write(self.file_header)
|
|
for key in self.key_range:
|
|
f.write(
|
|
"RAW_Data: " + self.key_to_sub(key) * self.repetition + "\n"
|
|
)
|
|
return
|
|
# Create debruijn.sub
|
|
filename = f"{base_dir}/debruijn.sub"
|
|
with open(filename, "w") as f:
|
|
f.write(self.file_header)
|
|
f.write("RAW_Data: " + self.de_bruijn() + "\n")
|
|
# Generate sets of 2^0, 2^1, .., 2^n_folders .sub files
|
|
splits = [
|
|
int(pow(2, self.n_bits) / _) for _ in [pow(2, _) for _ in range(n_folders)]
|
|
]
|
|
[os.makedirs(f"{base_dir}/{split}", exist_ok=True) for split in splits]
|
|
split_files = [None] * len(splits) # current file per split
|
|
for key in range(pow(2, self.n_bits)):
|
|
for idx, split in enumerate(splits):
|
|
if key % split == 0:
|
|
# close previous file if open
|
|
if split_files[idx] is not None:
|
|
split_files[idx].close()
|
|
filename = f"{base_dir}/{split}/{math.floor(key/(split*2)):03d}_{key/split:03.0f}.sub"
|
|
split_files[idx] = open(filename, "w")
|
|
split_files[idx].write(self.file_header)
|
|
split_files[idx].write(
|
|
"RAW_Data: " + self.key_to_sub(key) * self.repetition + "\n"
|
|
)
|
|
# close all files
|
|
[f.close() for f in split_files if f is not None]
|
|
|
|
|
|
protocols = [
|
|
Protocol(
|
|
name="CAME-12bit-433",
|
|
n_bits=12,
|
|
transposition_table={"0": "-320 640 ", "1": "-640 320 "},
|
|
pilot_period="-11520 320 ",
|
|
),
|
|
Protocol(
|
|
name="CAME-12bit-433-fast",
|
|
n_bits=12,
|
|
transposition_table={"0": "-250 500 ", "1": "-500 250 "},
|
|
pilot_period="-9000 250 ",
|
|
),
|
|
Protocol(
|
|
name="CAME-12bit-868",
|
|
n_bits=12,
|
|
transposition_table={"0": "-320 640 ", "1": "-640 320 "},
|
|
pilot_period="-11520 320 ",
|
|
frequency=868350000,
|
|
),
|
|
Protocol(
|
|
name="CAME-12bit-868-fast",
|
|
n_bits=12,
|
|
transposition_table={"0": "-250 500 ", "1": "-500 250 "},
|
|
pilot_period="-9000 250 ",
|
|
frequency=868350000,
|
|
),
|
|
Protocol(
|
|
name="Chamberlain-9bit-315",
|
|
n_bits=10,
|
|
transposition_table={"0": "-1000 3000 ", "1": "-2000 2000 "},
|
|
pilot_period="-39000 1000 ",
|
|
stop_bit="-3000 1000 ",
|
|
frequency=315000000,
|
|
),
|
|
Protocol(
|
|
name="Chamberlain-9bit-390",
|
|
n_bits=10,
|
|
transposition_table={"0": "-1000 3000 ", "1": "-2000 2000 "},
|
|
pilot_period="-39000 1000 ",
|
|
stop_bit="-3000 1000 ",
|
|
frequency=390000000,
|
|
),
|
|
Protocol(
|
|
name="Linear-10bit-300",
|
|
n_bits=10,
|
|
transposition_table={"0": "500 -1500 ", "1": "1500 -500 "},
|
|
stop_bit="1 -21500 ",
|
|
frequency=300000000,
|
|
repetition=5,
|
|
),
|
|
Protocol(
|
|
name="Linear-10bit-310",
|
|
n_bits=10,
|
|
transposition_table={"0": "500 -1500 ", "1": "1500 -500 "},
|
|
stop_bit="1 -21500 ",
|
|
frequency=310000000,
|
|
repetition=5,
|
|
),
|
|
Protocol(
|
|
name="NICE-12bit-433",
|
|
n_bits=12,
|
|
transposition_table={"0": "-700 1400 ", "1": "-1400 700 "},
|
|
pilot_period="-25200 700 ",
|
|
),
|
|
Protocol(
|
|
name="NICE-12bit-868",
|
|
n_bits=12,
|
|
transposition_table={"0": "-700 1400 ", "1": "-1400 700 "},
|
|
pilot_period="-25200 700 ",
|
|
frequency=868350000,
|
|
),
|
|
Protocol(
|
|
name="PT-2240-433",
|
|
n_bits=24,
|
|
transposition_table={"0": "450 -1350 ", "1": "1350 -450 "},
|
|
pilot_period="450 -13950 ",
|
|
),
|
|
Protocol(
|
|
name="Spacca_pager-433",
|
|
n_bits=24,
|
|
transposition_table={"0": "320 -960 ", "1": "960 -320 "},
|
|
pilot_period="320 -9920 ",
|
|
frequency=433650000,
|
|
key_range=range(0x11A01C, 0x11A0E4), # 300 keys
|
|
),
|
|
Protocol(
|
|
name="Ansonic-434",
|
|
n_bits=12,
|
|
transposition_table={"0": "-1111 555 ", "1": "-555 1111 "},
|
|
frequency=434075000,
|
|
pilot_period="-19425 555 ",
|
|
),
|
|
Protocol(
|
|
name="Holtek-315",
|
|
n_bits=12,
|
|
transposition_table={"0": "-870 430 ", "1": "-430 870 "},
|
|
frequency=315000000,
|
|
pilot_period="-15480 430 ",
|
|
),
|
|
Protocol(
|
|
name="Holtek-433",
|
|
n_bits=12,
|
|
transposition_table={"0": "-870 430 ", "1": "-430 870 "},
|
|
pilot_period="-15480 430 ",
|
|
),
|
|
Protocol(
|
|
name="Holtek-868",
|
|
n_bits=12,
|
|
transposition_table={"0": "-870 430 ", "1": "-430 870 "},
|
|
frequency=868350000,
|
|
pilot_period="-15480 430 ",
|
|
),
|
|
Protocol(
|
|
name="Holtek-915",
|
|
n_bits=12,
|
|
transposition_table={"0": "-870 430 ", "1": "-430 870 "},
|
|
frequency=915000000,
|
|
pilot_period="-15480 430 ",
|
|
),
|
|
]
|
|
|
|
for p in protocols:
|
|
# TODO multithread this
|
|
p.generate_sub_files()
|
|
print(f"{p.name} done")
|