From a1d5d6faff94621f5839e6e3052b1c3aacf9db40 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Thu, 29 Dec 2022 19:01:10 -0800 Subject: [PATCH 1/2] TX with Yardstick One HW --- rf-send.py | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100755 rf-send.py diff --git a/rf-send.py b/rf-send.py new file mode 100755 index 0000000..dc22e98 --- /dev/null +++ b/rf-send.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 + +""" + Send Security+ v2 +""" + +# pylint: disable=invalid-name + +from __future__ import print_function + +import sys +# from rflib import * +import argparse +import rflib +import secplus +# import pprint + +__author__ = "Peter Shipley" + +verbose = 0 + +# bit len = 250 +# 250 * 10^-6 = .0002500 +# 1/.0002500 = 4000 +DRATE = 4000 +CMD_REPEAT = 1 +DEFAULT_RF_FREQ = 315000000 + +DEFAULT_BUTTON = 91 + +def bitstring_to_bytes8(s): + return int(s, 2).to_bytes(len(s) // 8, byteorder='big') + + +def init_RfCat(data_len=60, freq=None, chan_bw=240000, tx_power=96, dat_rate=DRATE): + """ + create RfCat obj + """ + # Start up RfCat + rfc = rflib.RfCat() + + # Set Modulation. We using On-Off Keying here + rfc.setMdmModulation(rflib.MOD_ASK_OOK) + + # Configure the radio + rfc.makePktFLEN(data_len) # Set the RFData packet length + rfc.setMdmDRate(dat_rate) # Set the Baud Rate + rfc.setMdmSyncMode(0) # Disable preamble + rfc.setFreq(freq) # Set the frequency + rfc.setEnableMdmManchester(False) + + # d.setMaxPower() + if tx_power: + rfc.setPower(tx_power) + + if chan_bw: + rfc.setMdmChanBW(chan_bw) + + if verbose: + dr = rfc.getMdmDRate() + bw = rfc.getMdmChanBW() + f1 = rfc.getFreq() + print("DRate:", dr, "ChanBW", bw) + print("Freq:", f1[0]) + rfc.printRadioConfig() + #print("# Freq delta {:0.5f}".format(fq - fr), file=sys.stderr) + + return rfc + +def send_secplus_v2(fixed_dat=1234567890, freq=315000000, roll_dat=123456789, cmd_repeat=1): + + # Join the prefix and the data for the full pwm key, must repeat 3 time + pkt_seq = [0] * 100 + secplus.encode_v2_manchester(roll_dat, fixed_dat) * 3 + [0] * 100 + + bit_seq = "".join(map(str, pkt_seq)) + bit_seq += "0" * (len(bit_seq) % 8) # pad to bits + + if verbose: + print("bit_seq", len(bit_seq), bit_seq) + + # Convert the data to bin + rf_data = bitstring_to_bytes8(bit_seq) + + d = init_RfCat(freq=freq, data_len=len(bit_seq)) + + if d is None: + print("rflib Error") + sys.exit(0) + # return -1 + + if verbose: + print("Repeat:", cmd_repeat) + + with open("rf_data.dat", "wb") as fd: + fd.write(rf_data) + + d.RFxmit(rf_data, repeat=cmd_repeat) + + d.setModeIDLE() + + d = None + return 0 + +def get_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("-F", "--Freq", "--freq", dest="freq", type=int, + default=DEFAULT_RF_FREQ, + help="Set Frequency [default=%(default)r]") + + parser.add_argument("--rolling", dest="rolling", type=int, + default=123456789, + help="Set Rolling code [default=%(default)r]") + + parser.add_argument("-b", "--button", metavar='button_id', + dest="button", + default=0, + help="Button [default=91]") + + fixed_grp = parser.add_mutually_exclusive_group() + + fixed_grp.add_argument("-f", "--fixed", metavar='fixed_code', dest="fixed", + type=int, default=1234567890, + help="Set Fixed code [default=%(default)r]") + + fixed_grp.add_argument("-i", "--id", metavar='remote_id', dest="id", + default=None, + type=int, + help="Remote-ID") + + parser.add_argument('-v', '--verbose', dest="verb", + default=0, + help='Increase debug verbosity', action='count') + + return parser.parse_args() + +if __name__ == "__main__": + + args = get_args() + + if args.verb: + verbose = args.verb + + if args.id: + + butt = args.button or DEFAULT_BUTTON + + # (3**28)>>32 = 5326 + if butt > 5326: + raise ValueError("Button code must be less than 5326") + + # (3**28) & 0xffffffff = 1796636465 + if args.id > 1796636465: + raise ValueError("Remote ID must be less than 1796636465") + + fixed_code = (args.id & 0xffffffff) | (butt << 32) + else: + fixed_code = args.fixed + + send_secplus_v2(fixed_dat=fixed_code, freq=args.freq, roll_dat=args.rolling) + + sys.exit(0) From 08f2732877f364c4fbfe35a26b63b0dbc916add4 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Sun, 8 Jan 2023 23:36:36 -0800 Subject: [PATCH 2/2] working --- secplus_send.py | 150 +++++++++++++++++++++ rf-send.py => secplus_v2-send.py | 53 +++++--- secplus_v2_reciv.py | 222 +++++++++++++++++++++++++++++++ 3 files changed, 407 insertions(+), 18 deletions(-) create mode 100755 secplus_send.py rename rf-send.py => secplus_v2-send.py (78%) create mode 100755 secplus_v2_reciv.py diff --git a/secplus_send.py b/secplus_send.py new file mode 100755 index 0000000..bb7dfa1 --- /dev/null +++ b/secplus_send.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 + +""" + Send Security+ v1 +""" + +# pylint: disable=invalid-name + +from __future__ import print_function + +import sys +# from rflib import * +import argparse +import rflib +import secplus +# import pprint + +__author__ = "Peter Shipley" + +verbose = 0 +noop = 0 + + +# bit len = 500 +# 500 * 10^-6 = .00050 +# 1/.00050 = 2000 +DRATE = 2000 +CMD_REPEAT = 1 +DEFAULT_RF_FREQ = 315000000 + + +def bitstring_to_bytes8(s): + return int(s, 2).to_bytes(len(s) // 8, byteorder='big') + + +def init_RfCat(data_len=60, freq=None, chan_bw=240000, tx_power=96, dat_rate=DRATE): + """ + create RfCat obj + """ + # Start up RfCat + rfc = rflib.RfCat() + + # Set Modulation. We using On-Off Keying here + rfc.setMdmModulation(rflib.MOD_ASK_OOK) + + # Configure the radio + rfc.makePktFLEN(data_len) # Set the RFData packet length + rfc.setMdmDRate(dat_rate) # Set the Baud Rate + rfc.setMdmSyncMode(0) # Disable preamble + rfc.setFreq(freq) # Set the frequency + rfc.setEnableMdmManchester(False) + + # d.setMaxPower() + if tx_power: + rfc.setPower(tx_power) + + if chan_bw: + rfc.setMdmChanBW(chan_bw) + + if verbose: + dr = rfc.getMdmDRate() + bw = rfc.getMdmChanBW() + f1 = rfc.getFreq() + print("DRate:", dr, "ChanBW", bw) + print("Freq:", f1[0]) + rfc.printRadioConfig() + #print("# Freq delta {:0.5f}".format(fq - fr), file=sys.stderr) + + return rfc + +def send_secplus_v1(fixed_dat=1234567890, freq=315000000, roll_dat=123456789, cmd_repeat=1): + + # Join the prefix and the data for the full pwm key, must repeat 3 time + pkt_seq = [0]*100 + secplus.encode_ook(roll_dat, fixed_dat, fast=False)*4 + [0]*100 + + bit_seq = "".join(map(str, pkt_seq)) + bit_seq += "0" * (len(bit_seq) % 8) # pad to bits + + if verbose: + print("bit_seq", len(bit_seq), bit_seq) + + # Convert the data to bin + rf_data = bitstring_to_bytes8(bit_seq) + + if noop: + return 0 + + d = init_RfCat(freq=freq, data_len=len(bit_seq)) + + if d is None: + print("rflib Error") + sys.exit(0) + # return -1 + + if verbose: + print("Repeat:", cmd_repeat) + + with open("rf_data.dat", "wb") as fd: + fd.write(rf_data) + + d.RFxmit(rf_data, repeat=cmd_repeat) + + d.setModeIDLE() + + d = None + return 0 + +def get_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("-F", "--Freq", "--freq", dest="freq", type=int, + default=DEFAULT_RF_FREQ, + help="Set Frequency [default=%(default)r]") + + parser.add_argument('-r', "--rolling", dest="rolling", type=int, + default=123456789, + help="Set Rolling code [default=%(default)r]") + + parser.add_argument("-f", "--fixed", metavar='fixed_code', dest="fixed", + type=int, default=1234567890, + help="Set Fixed code [default=%(default)r]") + + parser.add_argument("-n", "--noop", dest="noop", action='store_true', + help="No op/Do not transmit") + + # fixed_grp = parser.add_mutually_exclusive_group() + + parser.add_argument('-v', '--verbose', dest="verb", + default=0, + help='Increase debug verbosity', action='count') + + return parser.parse_args() + +if __name__ == "__main__": + + args = get_args() + + noop = args.noop + + if args.verb: + verbose = args.verb + + if args.rolling >= 4294967296: + raise ValueError("Rolling code must be less than 2^32") + if args.fixed >= 3486784401: + raise ValueError("Fixed code must be less than 3^20") + + send_secplus_v1(fixed_dat=args.fixed, freq=args.freq, roll_dat=args.rolling) + + sys.exit(0) diff --git a/rf-send.py b/secplus_v2-send.py similarity index 78% rename from rf-send.py rename to secplus_v2-send.py index dc22e98..c95e1e2 100755 --- a/rf-send.py +++ b/secplus_v2-send.py @@ -18,6 +18,7 @@ __author__ = "Peter Shipley" verbose = 0 +noop = 0 # bit len = 250 # 250 * 10^-6 = .0002500 @@ -26,6 +27,8 @@ CMD_REPEAT = 1 DEFAULT_RF_FREQ = 315000000 +DEFAULT_ROLLING = 1 +DEFAULT_FIXED = 1234567890 DEFAULT_BUTTON = 91 def bitstring_to_bytes8(s): @@ -81,6 +84,9 @@ def send_secplus_v2(fixed_dat=1234567890, freq=315000000, roll_dat=123456789, cm # Convert the data to bin rf_data = bitstring_to_bytes8(bit_seq) + if noop: + return 0 + d = init_RfCat(freq=freq, data_len=len(bit_seq)) if d is None: @@ -109,7 +115,7 @@ def get_args(): help="Set Frequency [default=%(default)r]") parser.add_argument("--rolling", dest="rolling", type=int, - default=123456789, + default=0, help="Set Rolling code [default=%(default)r]") parser.add_argument("-b", "--button", metavar='button_id', @@ -117,14 +123,17 @@ def get_args(): default=0, help="Button [default=91]") + parser.add_argument("-n", "--noop", dest="noop", action='store_true', + help="No op/Do not transmit") + fixed_grp = parser.add_mutually_exclusive_group() fixed_grp.add_argument("-f", "--fixed", metavar='fixed_code', dest="fixed", - type=int, default=1234567890, + type=int, default=0, # 1234567890, help="Set Fixed code [default=%(default)r]") fixed_grp.add_argument("-i", "--id", metavar='remote_id', dest="id", - default=None, + default=0, type=int, help="Remote-ID") @@ -132,31 +141,39 @@ def get_args(): default=0, help='Increase debug verbosity', action='count') - return parser.parse_args() + + args = parser.parse_args() + + # (3**28) & 0xffffffff = 1796636465 + if args.id > 1796636465: + raise ValueError("Remote ID must be less than 1796636465") + + # 2**28 268435456 + if args.rolling >= 2**28: + raise ValueError("Rolling code must be less than 2^28") + + if args.fixed >= 2**40: + raise ValueError("Fixed code must be less than 2^40") + + return args if __name__ == "__main__": args = get_args() + noop = args.noop + if args.verb: verbose = args.verb if args.id: - - butt = args.button or DEFAULT_BUTTON - - # (3**28)>>32 = 5326 - if butt > 5326: - raise ValueError("Button code must be less than 5326") - - # (3**28) & 0xffffffff = 1796636465 - if args.id > 1796636465: - raise ValueError("Remote ID must be less than 1796636465") - - fixed_code = (args.id & 0xffffffff) | (butt << 32) + button_id = args.button_id or DEFAULT_BUTTON + fixed_code = (args.id & 0xffffffff) | (button_id << 32) else: - fixed_code = args.fixed + fixed_code = args.fixed or DEFAULT_FIXED + + rolling_code = args.rolling or DEFAULT_ROLLING - send_secplus_v2(fixed_dat=fixed_code, freq=args.freq, roll_dat=args.rolling) + send_secplus_v2(fixed_dat=fixed_code, freq=args.freq, roll_dat=rolling_code) sys.exit(0) diff --git a/secplus_v2_reciv.py b/secplus_v2_reciv.py new file mode 100755 index 0000000..159d494 --- /dev/null +++ b/secplus_v2_reciv.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +import sys +import argparse +import datetime +import signal +# import traceback +import rflib +import secplus + +# pylint: disable=W0614,unused-import + + +DRATE = 4000 +DEFAULT_FREQ = 315000000 +chan_bw = 540000 # 240000 # 93750 # 640000 +dataWhitening = 0 +pktLen = 90 +verbose = 0 +print_time = 0 + +def parse_args(): + parser = argparse.ArgumentParser( + description='receive security+ V2 packets') + + parser.add_argument("-f", "-F", "--freq", dest="freq", type=int, + default=DEFAULT_FREQ, + help="Set Frequency [default=%(default)r]") + + parser.add_argument('-t', '--time', dest="time", + help='Log time', + action='store_true', default=False) + + parser.add_argument('-v', '--verbose', + help='Increase debug verbosity', action='count') + + return parser.parse_args() + + +def configure_RfCat(freq): + + rf = rflib.RfCat(debug=False) + # rf.setMdmChanBW(chan_bw) + + rf.setMdmDRate(DRATE) + #- rf.setBSLimit(BSCFG_BS_LIMIT_6) # 0 3 6 12 + + rf.setEnableMdmManchester(False) + + # rf.makePktFLEN(pktLen) + + #-rf.setEnablePktDataWhitening(dataWhitening) + # rf.setEnableMdmFEC(1) + + + + # 00011110 -> 1010100101010110 + syncword = 0b1010101010101010 #10101010 # 1010101010101010101 0xAAAA + # syncword_b = f"{syncword:016b}" + rf.setMdmSyncWord(syncword) + + #- rf.setMdmChanBW(chan_bw) + + rf.setFreq(freq) + rf.setMdmModulation(rflib.MOD_ASK_OOK) + + # rf.setMdmSyncMode(rflib.SYNCM_CARRIER_15_of_16) + rf.setMdmSyncMode(rflib.SYNCM_CARRIER) + + # SYNCM_NONE: None + # SYNCM_15_of_16: 15 of 16 bits must match + # SYNCM_16_of_16: 16 of 16 bits must match + # SYNCM_30_of_32: 30 of 32 sync bits must match + # SYNCM_CARRIER: Carrier Detect + # SYNCM_CARRIER_15_of_16: Carrier Detect and 15 of 16 sync bits must match + # SYNCM_CARRIER_16_of_16: Carrier Detect and 16 of 16 sync bits must match + # SYNCM_CARRIER_30_of_32: Carrier Detect and 30 of 32 sync bits must match + + + if verbose: + # rf.printRadioConfig() + print( "\n== Frequency Configuration ==") + print( rf.reprFreqConfig()) + print( "\n== Modem Configuration ==") + print( rf.reprModemConfig()) + print( "\n== Packet Configuration ==") + print( rf.reprPacketConfig()) + sys.stdout.flush() + + return rf + + +def demanchester(st): + x = 1 + b = [] + slen = len(st) + + # Note that since 1 starts as "1" + # we are testing the 2nd bit for '1' or '0' + while x < slen: + if st[x] == st[x-1] : + break + # x += 1 + # continue + if st[x] == "1": + b.append("1") + else: + b.append("0") + x = x + 2 + + return "".join(b) + +def keystop(delay=0): + return len(rflib.select.select([sys.stdin], [], [], delay)[0]) + +if __name__ == "__main__": + args = parse_args() + + use_freq = args.freq + + verbose = args.verbose + print_time = args.time + d = None + + def sig_handler(sig=None, _frame=None): + if d: + d.setModeIDLE() + if sig: + print(f"Sig = {sig}") + sys.exit(0) + + signal.signal(signal.SIGABRT, sig_handler) + signal.signal(signal.SIGSEGV, sig_handler) + signal.signal(signal.SIGINT, sig_handler) + signal.signal(signal.SIGTERM, sig_handler) + + d = configure_RfCat(freq=use_freq) + + + pat = "000011110" + + # 00011110 => 1010100101010110 + # 000000011110 + patm = "101010101010100101010110" + patm_len = len(patm) + + z = 0 + o = 0 + pkt_1 = pkt_2 = "" + prev_data = "" + while not keystop(): + try: + y, t = d.RFrecv(timeout=2000) + s = '' + idx = 0 + s = prev_data[-32:] + ''.join(f"{x:08b}" for x in bytearray(y)) + + prev_data = s[:32] + + + idx = 0 + if patm in s: + while patm in s[idx:]: + idx = s.find(patm, idx) + + pkt = demanchester(s[idx:idx + 120]) + + if len(pkt) < 40: + idx += patm_len + continue + + i = pkt.find(pat) + pkt = pkt[i + 8: i + 50] # skip preamble and trim + + idx += patm_len + + if pkt[0] != "0" or pkt[2:4] != "00": + print("SKIP bit check", pkt[:4], "\n\n") + continue + + if pkt[:2] == "00" and len(pkt) > 40: + pkt_1 = pkt[2:] + + elif pkt[:2] == "01" and len(pkt) > 40: + pkt_2 = pkt[2:] + + else: + continue + + if pkt_1 and pkt_2: + try: + full_pkt = pkt_1[:40] + pkt_2[:40] + full_pkt_list = list(map(int, list(full_pkt))) + + rolling_out, fixed_out, _data = secplus.decode_v2(full_pkt_list) + + pretty_out = secplus.pretty_v2(rolling_out, fixed_out) + if print_time: + print(f"{datetime.datetime.now()}") + print(f"{pretty_out}\n") + pkt_1 = pkt_2 = "" + except ValueError as _e: + # print("ValueError", _e) + continue + + except rflib.ChipconUsbTimeoutException: + o = 0 + if verbose: + print(".", end="") + sys.stdout.flush() + + except KeyboardInterrupt: + print("Please press to stop") + + except IndexError as _e: + #print("Index fail: ", str(_e)) + pass + + + # sys.stdin.read(1) + + d.setModeIDLE() + d = None