@@ -3959,108 +3959,29 @@ def on_exec(self, args: argparse.Namespace):
39593959 print (f" PAC/Stanley Card ID: { color_string ((CG , card_id_ascii ))} ({ card_id .hex ().upper ()} )" )
39603960
39613961
3962- @lf_pac .command ('debug' )
3963- class LFPacDebug (ReaderRequiredUnit ):
3964- def args_parser (self ) -> ArgumentParserNoExit :
3965- parser = ArgumentParserNoExit ()
3966- parser .description = 'Capture raw ADC data and analyze NRZ signal for PAC debugging'
3967- return parser
3968-
3969- def on_exec (self , args : argparse .Namespace ):
3970- resp = self .cmd .adc_generic_read ()
3971- if resp is None :
3972- print ("ADC read failed" )
3973- return
3974-
3975- data = list (resp )
3976- min_val = min (data )
3977- max_val = max (data )
3978-
3979- print (f" Samples: { len (data )} " )
3980- print (f" ADC range: { min_val } - { max_val } (spread: { max_val - min_val } )" )
3981-
3982- # Spike removal: clip values above 3x the minimum (LC ringing transients)
3983- spike_cap = min_val * 3 if min_val > 0 else max_val
3984- clipped = [min (v , spike_cap ) for v in data ]
3985- c_min = min (clipped )
3986- c_max = max (clipped )
3987- c_thresh = (c_min + c_max ) / 2
3988-
3989- print (f" Spike cap: { spike_cap } (3x raw min { min_val } )" )
3990- print (f" Clipped range: { c_min } - { c_max } (spread: { c_max - c_min } )" )
3991-
3992- # Moving-average of clipped signal (32-sample window = 1 NRZ bit)
3993- window = 32
3994- if len (clipped ) >= window :
3995- filtered = []
3996- s = sum (clipped [:window ])
3997- for i in range (window , len (clipped )):
3998- filtered .append (s / window )
3999- s += clipped [i ] - clipped [i - window ]
4000- filtered .append (s / window )
4001- f_min = min (filtered )
4002- f_max = max (filtered )
4003- f_thresh = (f_min + f_max ) / 2
4004- f_hyst = (f_max - f_min ) / 4
4005- print (f"\n Spike-free moving average (window={ window } ):" )
4006- print (f" Range: { f_min :.1f} - { f_max :.1f} (spread: { f_max - f_min :.1f} )" )
4007- print (f" Threshold: { f_thresh :.1f} , Hysteresis: { f_hyst :.1f} " )
4008-
4009- # Binary decode with hysteresis
4010- f_binary = []
4011- state = filtered [0 ] > f_thresh
4012- for v in filtered :
4013- if v > f_thresh + f_hyst :
4014- state = True
4015- elif v < f_thresh - f_hyst :
4016- state = False
4017- f_binary .append (1 if state else 0 )
4018-
4019- f_trans = sum (1 for i in range (1 , len (f_binary )) if f_binary [i ] != f_binary [i - 1 ])
4020- f_runs = []
4021- cur = f_binary [0 ]
4022- cnt = 1
4023- for i in range (1 , len (f_binary )):
4024- if f_binary [i ] == cur :
4025- cnt += 1
4026- else :
4027- f_runs .append (cnt )
4028- cur = f_binary [i ]
4029- cnt = 1
4030- f_runs .append (cnt )
4031- print (f" Transitions: { f_trans } " )
4032- print (f" Run lengths: { f_runs [:40 ]} { '...' if len (f_runs ) > 40 else '' } " )
4033- run_bits = [f"{ r / 32 :.1f} " for r in f_runs [:40 ]]
4034- print (f" Run bits: { run_bits } " )
4035-
4036- # Show bit-period averages (each = avg of 32 clipped samples)
4037- print (f"\n Bit-period averages (32 samples each):" )
4038- bit_avgs = []
4039- for i in range (0 , len (clipped ) - window + 1 , window ):
4040- chunk = clipped [i :i + window ]
4041- bit_avgs .append (sum (chunk ) / len (chunk ))
4042- bit_str = " " .join (f"{ a :.0f} " for a in bit_avgs )
4043- print (f" { bit_str } " )
4044-
4045- # Raw hex dump (first 512 bytes)
4046- print (f"\n Raw ADC (8-bit, min={ min_val } max={ max_val } ):" )
4047- for i in range (0 , min (len (data ), 512 ), 50 ):
4048- chunk = data [i :i + 50 ]
4049- hexpart = " " .join (f"{ b :02x} " for b in chunk )
4050- print (f" { i :04x} { hexpart } " )
4051-
4052-
40533962class LFPacIdArgsUnit (DeviceRequiredUnit ):
40543963 @staticmethod
40553964 def add_card_arg (parser : ArgumentParserNoExit , required = False ):
4056- parser .add_argument ("--id" , type = str , required = required , help = "PAC/Stanley card ID (8 ASCII hex chars)" , metavar = "<hex>" )
3965+ parser .add_argument ("--id" , type = str , required = required ,
3966+ help = "PAC/Stanley card ID: 16 hex chars (e.g. 4141414141414141) or 8 ASCII chars (e.g. AAAAAAAA)" ,
3967+ metavar = "<hex|ascii>" )
40573968 return parser
40583969
40593970 def before_exec (self , args : argparse .Namespace ):
40603971 if not super ().before_exec (args ):
40613972 return False
4062- if args .id is not None and not re .match (r"^[a-fA-F0-9]{16}$" , args .id ):
4063- raise ArgsParserError ("ID must include 16 HEX symbols (8 bytes)" )
3973+ if args .id is not None :
3974+ if re .match (r"^[a-fA-F0-9]{16}$" , args .id ):
3975+ pass # already hex
3976+ elif len (args .id ) == 8 :
3977+ # treat as 8 ASCII characters, convert to hex
3978+ args .id = args .id .encode ('ascii' ).hex ()
3979+ else :
3980+ raise ArgsParserError ("ID must be 16 hex chars (8 bytes) or 8 ASCII characters" )
3981+ # PAC uses 7-bit UART frames; MSB of each byte is not encoded
3982+ id_bytes = bytes .fromhex (args .id )
3983+ if any (b > 0x7F for b in id_bytes ):
3984+ raise ArgsParserError ("PAC card IDs are 7-bit ASCII only (each byte must be 0x00-0x7F)" )
40643985 return True
40653986
40663987 def args_parser (self ) -> ArgumentParserNoExit :
@@ -4069,6 +3990,19 @@ def args_parser(self) -> ArgumentParserNoExit:
40693990 def on_exec (self , args : argparse .Namespace ):
40703991 raise NotImplementedError ("Please implement this" )
40713992
3993+ @lf_pac .command ('write' )
3994+ class LFPacWriteT55xx (LFPacIdArgsUnit , ReaderRequiredUnit ):
3995+ def args_parser (self ) -> ArgumentParserNoExit :
3996+ parser = ArgumentParserNoExit ()
3997+ parser .description = 'Write PAC/Stanley id to T55xx'
3998+ return self .add_card_arg (parser , required = True )
3999+
4000+ def on_exec (self , args : argparse .Namespace ):
4001+ id_bytes = bytes .fromhex (args .id )
4002+ self .cmd .pac_write_to_t55xx (id_bytes )
4003+ id_ascii = '' .join (chr (b ) if 0x20 <= b < 0x7f else '.' for b in id_bytes )
4004+ print (f" - PAC/Stanley ID write done: { id_ascii } ({ args .id .upper ()} )" )
4005+
40724006@lf_pac .command ('econfig' )
40734007class LFPacEconfig (SlotIndexArgsAndGoUnit , LFPacIdArgsUnit ):
40744008 def args_parser (self ) -> ArgumentParserNoExit :
@@ -4182,8 +4116,20 @@ def on_exec(self, args: argparse.Namespace):
41824116 for slot in SlotNumber :
41834117 fwslot = SlotNumber .to_fw (slot )
41844118 status = f"({ color_string ((CG , 'active' ))} )" if slot == selected else ""
4185- hf_tag_type = TagSpecificType (slotinfo [fwslot ]['hf' ])
4186- lf_tag_type = TagSpecificType (slotinfo [fwslot ]['lf' ])
4119+ try :
4120+ hf_tag_type = TagSpecificType (slotinfo [fwslot ]['hf' ])
4121+ except ValueError :
4122+ hf_tag_type = TagSpecificType .UNDEFINED
4123+ hf_unknown = slotinfo [fwslot ]['hf' ]
4124+ else :
4125+ hf_unknown = None
4126+ try :
4127+ lf_tag_type = TagSpecificType (slotinfo [fwslot ]['lf' ])
4128+ except ValueError :
4129+ lf_tag_type = TagSpecificType .UNDEFINED
4130+ lf_unknown = slotinfo [fwslot ]['lf' ]
4131+ else :
4132+ lf_unknown = None
41874133 print (f' - { f"Slot { slot } :" :{4 + maxnamelength + 1 }} { status } ' )
41884134
41894135 # HF
@@ -4192,12 +4138,14 @@ def on_exec(self, args: argparse.Namespace):
41924138 print (f' HF: '
41934139 f'{ slotnames [fwslot ]["hf" ]["name" ]:{field_length }} ' , end = '' )
41944140 print (status , end = '' )
4195- if hf_tag_type != TagSpecificType .UNDEFINED :
4141+ if hf_unknown is not None :
4142+ print (color_string ((CR , f"Unknown ({ hf_unknown } )" )))
4143+ elif hf_tag_type != TagSpecificType .UNDEFINED :
41964144 color = CY if enabled [fwslot ]['hf' ] else C0
41974145 print (color_string ((color , hf_tag_type )))
41984146 else :
41994147 print ("undef" )
4200- if (not args .short ) and enabled [fwslot ]['hf' ] and hf_tag_type != TagSpecificType .UNDEFINED :
4148+ if (not args .short ) and enabled [fwslot ]['hf' ] and hf_tag_type != TagSpecificType .UNDEFINED and hf_unknown is None :
42014149 if current != slot :
42024150 self .cmd .set_active_slot (slot )
42034151 current = slot
@@ -4247,12 +4195,14 @@ def on_exec(self, args: argparse.Namespace):
42474195 print (f' LF: '
42484196 f'{ slotnames [fwslot ]["lf" ]["name" ]:{field_length }} ' , end = '' )
42494197 print (status , end = '' )
4250- if lf_tag_type != TagSpecificType .UNDEFINED :
4198+ if lf_unknown is not None :
4199+ print (color_string ((CR , f"Unknown ({ lf_unknown } )" )))
4200+ elif lf_tag_type != TagSpecificType .UNDEFINED :
42514201 color = CY if enabled [fwslot ]['lf' ] else C0
42524202 print (color_string ((color , lf_tag_type )))
42534203 else :
42544204 print ("undef" )
4255- if (not args .short ) and enabled [fwslot ]['lf' ] and lf_tag_type != TagSpecificType .UNDEFINED :
4205+ if (not args .short ) and enabled [fwslot ]['lf' ] and lf_tag_type != TagSpecificType .UNDEFINED and lf_unknown is None :
42564206 if current != slot :
42574207 self .cmd .set_active_slot (slot )
42584208 current = slot
@@ -4310,6 +4260,7 @@ def on_exec(self, args: argparse.Namespace):
43104260 else :
43114261 slot_num = SlotNumber .from_fw (self .cmd .get_active_slot ())
43124262 self .cmd .set_slot_tag_type (slot_num , tag_type )
4263+ self .cmd .set_slot_data_default (slot_num , tag_type )
43134264 print (f' - Set slot { slot_num } tag type success.' )
43144265
43154266
0 commit comments