diff --git a/lakeshore/fast_hall_controller.py b/lakeshore/fast_hall_controller.py index 7f2dd7a..ddde61d 100644 --- a/lakeshore/fast_hall_controller.py +++ b/lakeshore/fast_hall_controller.py @@ -2,17 +2,20 @@ import json from .xip_instrument import XIPInstrument, RegisterBase, StatusByteRegister, StandardEventRegister +from .generic_instrument import InstrumentException class FastHallOperationRegister(RegisterBase): """Class object representing the operation status register.""" bit_names = [ - "", - "", - "", - "", - "measuring_Done" + "settling", + "ranging", + "measurement_complete", + "waiting_for_trigger", + "field_control_ramping", + "field_measurement_enabled", + "transient" ] def __init__(self, @@ -37,20 +40,20 @@ class FastHallQuestionableRegister(RegisterBase): bit_names = [ "source_in_compliance_or_at_current_limit", - "negative_resistivity", - "", + "field_control_slew_rate_limit", + "field_control_at_voltage_limit", "", "", "current_measurement_overload", "voltage_measurement_overload", - "", - "", + "invalid_probe", + "invalid_calibration", "inter_processor_communication_error", - "", - "", + "field_measurement_communication_error", + "probe_eeprom_read_error", "r2_less_than_minimum_allowable", - "f_value_out_of_acceptable_range", - "geometry_out_of_acceptable_range" + "", + "" ] def __init__(self, @@ -90,7 +93,7 @@ def __init__(self, measurement_range='AUTO', min_r_squared=0.9999, blanking_time=2e-3): - """The constructor for ContackCheckManualParameters class. + """The constructor for ContactCheckManualParameters class. Args: excitation_type (str): @@ -378,7 +381,7 @@ def __init__(self, self.blanking_time = blanking_time self.max_samples = max_samples self.min_snr = min_snr - self.excitation_reversal = str(int(excitation_reversal)) + self.excitation_reversal = excitation_reversal class DCHallParameters: @@ -450,7 +453,7 @@ def __init__(self, self.compliance_limit = compliance_limit self.averaging_samples = averaging_samples self.user_defined_field = user_defined_field - self.with_field_reversal = str(int(with_field_reversal)) + self.with_field_reversal = with_field_reversal self.resistivity = resistivity self.blanking_time = blanking_time self.sample_thickness = sample_thickness @@ -538,7 +541,7 @@ def __init__(self, class ResistivityLinkParameters: - """Class object representing parameters used for running manual Resistivity measurements.""" + """Class object representing parameters used for running Resistivity Link measurements.""" def __init__(self, measurement_range='AUTO', sample_thickness=0, @@ -594,6 +597,15 @@ def __init__(self, self.operation_register = FastHallOperationRegister self.questionable_register = FastHallQuestionableRegister + def _parse_json_response(self, response, context=""): + """Parse a JSON response from the instrument with error handling.""" + try: + return json.loads(response) + except json.JSONDecodeError as ex: + raise InstrumentException( + f"Failed to parse JSON response{' for ' + context if context else ''}: {ex}" + ) from ex + # Status Methods def get_contact_check_running_status(self): """Indicates if the contact check measurement is running.""" @@ -616,7 +628,7 @@ def get_dc_hall_running_status(self): return bool(int(self.query("HALL:DC:RUNNING?"))) def get_dc_hall_waiting_status(self): - """Indicates if the DC hall measurement is running.""" + """Indicates if the DC hall measurement is waiting.""" return bool(int(self.query("HALL:DC:WAITING?"))) def continue_dc_hall(self): @@ -737,7 +749,7 @@ def start_four_wire(self, settings): f"{str(settings.blanking_time)}," + f"{str(settings.max_samples)}," + f"{str(settings.min_snr)}," + - f"{str(settings.excitation_reversal)}") + f"{str(int(settings.excitation_reversal))}") self.command(command_string) def start_dc_hall_vdp(self, settings): @@ -757,7 +769,7 @@ def start_dc_hall_vdp(self, settings): f"{str(settings.compliance_limit)}," + f"{str(settings.averaging_samples)}," + f"{str(settings.user_defined_field)}," + - f"{str(settings.with_field_reversal)}," + + f"{str(int(settings.with_field_reversal))}," + f"{str(settings.resistivity)}," + f"{str(settings.blanking_time)}," + f"{str(settings.sample_thickness)}") @@ -775,10 +787,10 @@ def start_dc_hall_hbar(self, settings): f"{str(settings.excitation_range)}," + f"{str(settings.excitation_measurement_range)}," + f"{str(settings.measurement_range)}," + - f"{str(settings.compliance_limit)}," + \ + f"{str(settings.compliance_limit)}," + f"{str(settings.averaging_samples)}," + f"{str(settings.user_defined_field)}," + - f"{str(settings.with_field_reversal)}," + + f"{str(int(settings.with_field_reversal))}," + f"{str(settings.resistivity)}," + f"{str(settings.blanking_time)}," + f"{str(settings.sample_thickness)}") @@ -847,7 +859,7 @@ def get_contact_check_setup_results(self): # Parse the JSON query string into a dictionary with only the setup results json_results = self.query('CCHECK:RESULT:JSON? 0') - setup_results = json.loads(json_results).get('Setup') + setup_results = self._parse_json_response(json_results, 'contact check setup').get('Setup', {}) # Generate a Contact Check settings object using the setup result values as the initialization parameters settings = ContactCheckManualParameters(excitation_type=setup_results.get('ExcitationType'), @@ -866,12 +878,12 @@ def get_contact_check_measurement_results(self): # Parse the JSON query string into a dictionary json_results = self.query('CCHECK:RESULT:JSON? 0') - measurement_results = json.loads(json_results) + measurement_results = self._parse_json_response(json_results, 'contact check measurement') # Remove the setup data from the results dictionary - measurement_results.pop('Setup') - measurement_results.pop('OptimizationSetup') - measurement_results.pop('OptimizationDiagnostics') + measurement_results.pop('Setup', None) + measurement_results.pop('OptimizationSetup', None) + measurement_results.pop('OptimizationDiagnostics', None) return measurement_results @@ -880,22 +892,20 @@ def get_fasthall_setup_results(self): # Parse the JSON query string into a dictionary with only the setup results json_results = self.query('FASTHALL:RESULT:JSON? 0') - setup_results = json.loads(json_results).get('Setup') + setup_results = self._parse_json_response(json_results, 'FastHall setup').get('Setup', {}) # Generate a FastHall settings object using the setup result values as the initialization parameters settings = FastHallManualParameters(excitation_type=setup_results.get('ExcitationType'), excitation_value=setup_results.get('ExcitationValue'), excitation_range=setup_results.get('ExcitationRange'), - excitation_measurement_range=setup_results.get('ExcitationMeasurementRange' - ), + excitation_measurement_range=setup_results.get('ExcitationMeasurementRange'), measurement_range=setup_results.get('MeasurementRange'), compliance_limit=setup_results.get('ComplianceLimit'), - max_samples=setup_results.get('MeasurementRange'), + max_samples=setup_results.get('MaximumNumberOfSamples'), user_defined_field=setup_results.get('UserDefinedFieldReadingInTesla'), resistivity=setup_results.get('Resistivity'), blanking_time=setup_results.get('BlankingTimeInSeconds'), - averaging_samples=setup_results.get('NumberOfVoltageCompensationSamplesTo\ - Average'), + averaging_samples=setup_results.get('NumberOfVoltageCompensationSamplesToAverage'), sample_thickness=setup_results.get('SampleThicknessInMeters'), min_hall_voltage_snr=setup_results.get('HallVoltageSnr')) return settings @@ -905,10 +915,10 @@ def get_fasthall_measurement_results(self): # Parse the JSON query string into a dictionary json_results = self.query('FASTHALL:RESULT:JSON? 0') - measurement_results = json.loads(json_results) + measurement_results = self._parse_json_response(json_results, 'FastHall measurement') # Remove the setup data from the results dictionary - measurement_results.pop('Setup') + measurement_results.pop('Setup', None) return measurement_results @@ -917,13 +927,15 @@ def get_four_wire_setup_results(self): # Parse the JSON query string into a dictionary with only the setup results json_results = self.query('FWIRE:RESULT:JSON? 0') - setup_results = json.loads(json_results).get('Setup') + setup_results = self._parse_json_response(json_results, 'four wire setup').get('Setup', {}) # Generate a Four Wire settings object using the setup result values as the initialization parameters - settings = FourWireParameters(contact_point1=setup_results.get('ContactPairExcitation').get('Point1'), - contact_point2=setup_results.get('ContactPairExcitation').get('Point2'), - contact_point3=setup_results.get('ContactPairSense').get('Point1'), - contact_point4=setup_results.get('ContactPairSense').get('Point2'), + contact_pair_excitation = setup_results.get('ContactPairExcitation', {}) + contact_pair_sense = setup_results.get('ContactPairSense', {}) + settings = FourWireParameters(contact_point1=contact_pair_excitation.get('Point1'), + contact_point2=contact_pair_excitation.get('Point2'), + contact_point3=contact_pair_sense.get('Point1'), + contact_point4=contact_pair_sense.get('Point2'), excitation_type=setup_results.get('ExcitationType'), excitation_value=setup_results.get('ExcitationValue'), excitation_range=setup_results.get('ExcitationRange'), @@ -941,10 +953,10 @@ def get_four_wire_measurement_results(self): # Parse the JSON query string into a dictionary json_results = self.query('FWIRE:RESULT:JSON? 0') - measurement_results = json.loads(json_results) + measurement_results = self._parse_json_response(json_results, 'four wire measurement') # Remove the setup data from the results dictionary - measurement_results.pop('Setup') + measurement_results.pop('Setup', None) return measurement_results @@ -953,7 +965,7 @@ def get_dc_hall_setup_results(self): # Parse the JSON query string into a dictionary with only the setup results json_results = self.query('HALL:DC:RESULT:JSON? 0') - setup_results = json.loads(json_results).get('Setup') + setup_results = self._parse_json_response(json_results, 'DC Hall setup').get('Setup', {}) # Generate a DC Hall settings object using the setup result values as the initialization parameters settings = DCHallParameters(excitation_type=setup_results.get('ExcitationType'), @@ -975,10 +987,10 @@ def get_dc_hall_measurement_results(self): # Parse the JSON query string into a dictionary json_results = self.query('HALL:DC:RESULT:JSON? 0') - measurement_results = json.loads(json_results) + measurement_results = self._parse_json_response(json_results, 'DC Hall measurement') # Remove the setup data from the results dictionary - measurement_results.pop('Setup') + measurement_results.pop('Setup', None) return measurement_results @@ -987,19 +999,18 @@ def get_resistivity_setup_results(self): # Parse the JSON query string into a dictionary with only the setup results json_results = self.query('RESISTIVITY:RESULT:JSON? 0') - setup_results = json.loads(json_results).get('Setup') + setup_results = self._parse_json_response(json_results, 'resistivity setup').get('Setup', {}) # Generate a Resistivity settings object using the setup result values as the initialization parameters - settings = ResistivityManualParameters(setup_results.get('ExcitationType'), + settings = ResistivityManualParameters(excitation_type=setup_results.get('ExcitationType'), excitation_value=setup_results.get('ExcitationValue'), excitation_range=setup_results.get('ExcitationRange'), - excitation_measurement_range=setup_results.get('Excitation\ - MeasurementRange'), + excitation_measurement_range=setup_results.get('ExcitationMeasurementRange'), measurement_range=setup_results.get('MeasurementRange'), compliance_limit=setup_results.get('ComplianceLimit'), width=setup_results.get('SampleWidthInMeters'), separation=setup_results.get('SampleArmSeparationInMeters'), - max_samples=setup_results.get('MaxNumberOfSamples'), + max_samples=setup_results.get('MaxNumberOfSamples'), # TODO: verify key from M91 manual (other methods use 'MaximumNumberOfSamples') blanking_time=setup_results.get('BlankingTimeInSeconds'), sample_thickness=setup_results.get('SampleThicknessInMeters'), min_snr=setup_results.get('MinimumSnr')) @@ -1010,10 +1021,10 @@ def get_resistivity_measurement_results(self): # Parse the JSON query string into a dictionary json_results = self.query('RESISTIVITY:RESULT:JSON? 0') - measurement_results = json.loads(json_results) + measurement_results = self._parse_json_response(json_results, 'resistivity measurement') # Remove the setup data from the results dictionary - measurement_results.pop('Setup') + measurement_results.pop('Setup', None) return measurement_results @@ -1059,7 +1070,7 @@ def run_complete_contact_check_manual(self, settings, sample_type): self.start_contact_check_hbar(settings) else: raise ValueError('Invalid value for sample_type argument. ' - 'Sample type must be either "VDP" for a Van der Pauw sample or "HBAR for a Hall bar."') + 'Sample type must be either "VDP" for a Van der Pauw sample or "HBAR" for a Hall bar.') # Loop until measurement has stopped running while self.get_contact_check_running_status(): @@ -1153,7 +1164,7 @@ def run_complete_dc_hall(self, settings, sample_type): self.start_dc_hall_hbar(settings) else: raise ValueError('Invalid value for sample_type argument. ' - 'Sample type must be either "VDP" for a Van der Pauw sample or "HBAR for a Hall bar."') + 'Sample type must be either "VDP" for a Van der Pauw sample or "HBAR" for a Hall bar.') # Loop until measurement has stopped running or waiting while self.get_dc_hall_running_status() or self.get_dc_hall_waiting_status(): @@ -1205,7 +1216,7 @@ def run_complete_resistivity_manual(self, settings, sample_type): self.start_resistivity_hbar(settings) else: raise ValueError('Invalid value for sample_type argument. ' - 'Sample type must be either "VDP" for a Van der Pauw sample or "HBAR for a Hall bar."') + 'Sample type must be either "VDP" for a Van der Pauw sample or "HBAR" for a Hall bar.') # Loop until measurement has stopped running while self.get_resistivity_running_status(): diff --git a/lakeshore/model_224.py b/lakeshore/model_224.py index 8c3a2b8..11ab862 100644 --- a/lakeshore/model_224.py +++ b/lakeshore/model_224.py @@ -113,7 +113,7 @@ class Model224ServiceRequestRegister(RegisterBase): "", "message_available", "event_summary", - "" + "", "operation_summary" ] @@ -135,7 +135,7 @@ class Model224StatusByteRegister(RegisterBase): "", "message_available", "event_summary", - "master_summary_status" + "master_summary_status", "operation_summary" ] diff --git a/lakeshore/model_335.py b/lakeshore/model_335.py index 14161b4..816510e 100644 --- a/lakeshore/model_335.py +++ b/lakeshore/model_335.py @@ -147,8 +147,8 @@ class Model335(Model335Enums, TemperatureController): """A class object representing the Lake Shore Model 335 cryogenic temperature controller.""" # Initiate instrument specific registers - _status_byte_register = Model335StatusByteRegister - _service_request_enable = Model335ServiceRequestEnable + status_byte_register = Model335StatusByteRegister + service_request_enable = Model335ServiceRequestEnable vid_pid = [(0x1FB9, 0x0300)] diff --git a/lakeshore/model_372.py b/lakeshore/model_372.py index e032a34..ad0aad8 100644 --- a/lakeshore/model_372.py +++ b/lakeshore/model_372.py @@ -330,8 +330,8 @@ class Model372(Model372Enums, TemperatureController): vid_pid = [(0x1FB9, 0x0305)] # Initialize registers - _status_byte_register = Model372StatusByteRegister - _service_request_enable = Model372ServiceRequestEnable + status_byte_register = Model372StatusByteRegister + service_request_enable = Model372ServiceRequestEnable def __init__(self, baud_rate,