def _check_firmware_version(gem): latest_release = git.latest_tag() build_id = gem.get_firmware_version() log.info(f"Firmware build ID: {build_id}") if latest_release in build_id: return log.warning("Firmware is out of date, updating it..") gem.reset_into_bootloader() path = pathlib.Path(fs.wait_for_drive("GEMINIBOOT", timeout=60 * 5)) fs.copyfile("../firmware/build/gemini-firmware.uf2", path / "firmware.uf2") fs.flush(path) time.sleep(3) log.success("Firmware updated!")
def run( num_calibration_points, sample_count, strategy, save, ): if strategy == "adc": strategy = DirectADCStrategy() elif strategy == "afe": strategy = ThroughAFEStrategy() else: raise ValueError(f"Unknonw strategy {strategy}.") # Create the list of calibration points and expected codes. voltages = [ n / num_calibration_points * strategy.range_ for n in range(num_calibration_points + 1) ] expected_codes = [ int(voltages[n] / strategy.range_ * (strategy.resolution - 1)) for n in range(num_calibration_points + 1) ] if strategy.invert: expected_codes = [ strategy.resolution - 1 - code for code in expected_codes ] calibration_points = dict(zip(voltages, expected_codes)) gem = gemini.Gemini() sol_ = sol.Sol() sol._setup() sol_.send_voltage(0, channel=strategy.sol_channel) gem.enter_calibration_mode() strategy.setup(gem) measured = _measure_range(gem, sol_, strategy, sample_count, calibration_points) gain_error = adc_errors.calculate_avg_gain_error(expected_codes, list(measured.values())) offset_error = adc_errors.calculate_avg_offset_error( expected_codes, list(measured.values()), gain_error) log.success(f"Measured gain={gain_error:.3f}, offset={offset_error:.1f}") strategy.finish(gem) local_copy = pathlib.Path("calibrations") / strategy.file_name(gem) local_copy.parent.mkdir(parents=True, exist_ok=True) with local_copy.open("w") as fh: json.dump({"gain_error": gain_error, "offset_error": offset_error}, fh) log.info(f"Saved local copy to {local_copy}") if save: strategy.save(gem, gain_error, offset_error) log.success("Saved to NVM.") else: log.warning("Dry run, not saving to NVM.") return # Test out the new calibration log.info("Taking measurements with new calibration...") gem.enable_adc_error_correction() measured = _measure_range(gem, sol_, strategy, sample_count, calibration_points) gain_error = adc_errors.calculate_avg_gain_error(expected_codes, list(measured.values())) offset_error = adc_errors.calculate_avg_offset_error( expected_codes, list(measured.values()), gain_error) log.info(f"Remeasured gain={gain_error:.3f}, offset={offset_error:.1f}") log.success("Done") gem.close()
def run(save): # Oscilloscope setup. log.info("Configuring oscilloscope...") resource_manager = visa.ResourceManager("@ivi") scope = oscilloscope.Oscilloscope(resource_manager) scope.reset() scope.enable_bandwidth_limit() scope.set_intensity(50, 100) # Enable both channels initially so that it's clear if the programming # board isn't connecting to the POGO pins. scope.set_time_division("10ms") scope.enable_channel("c1") scope.enable_channel("c2") scope.set_vertical_cursor("c1", 0, 3.3) scope.set_vertical_cursor("c2", 0, 3.3) scope.set_vertical_division("c1", "800mV") scope.set_vertical_division("c2", "800mV") scope.set_vertical_offset("c1", -1.65) scope.set_vertical_offset("c2", -1.65) # Gemini setup log.info("Connecting to Gemini...") gem = gemini.Gemini() gem.enter_calibration_mode() input( "Connect PROBE ONE to RAMP A\nConnect PROBE TWO to RAMP B\n> press enter to start." ) # Calibrate both oscillators log.section("Calibrating Castor...", depth=2) castor_calibration = _calibrate_oscillator(gem, scope, 0) lowest_voltage = oscillators.charge_code_to_volts( min(castor_calibration.values())) highest_voltage = oscillators.charge_code_to_volts( max(castor_calibration.values())) log.success( f"\nCalibrated:\n- Lowest: {lowest_voltage:.2f}v\n- Highest: {highest_voltage:.2f}v\n" ) log.section("Calibrating Pollux...", depth=2) pollux_calibration = _calibrate_oscillator(gem, scope, 1) lowest_voltage = oscillators.charge_code_to_volts( min(pollux_calibration.values())) highest_voltage = oscillators.charge_code_to_volts( max(pollux_calibration.values())) log.success( f"\nCalibrated:\n- Lowest: {lowest_voltage:.2f}v\n- Highest: {highest_voltage:.2f}v\n" ) log.section("Saving calibration table...", depth=2) local_copy = pathlib.Path( "calibrations") / f"{gem.serial_number}.ramp.json" local_copy.parent.mkdir(parents=True, exist_ok=True) with local_copy.open("w") as fh: data = { "castor": castor_calibration, "pollux": pollux_calibration, } json.dump(data, fh) log.success(f"Saved local copy to {local_copy}") if save: output = tui.Updateable() bar = tui.Bar() log.info("Sending LUT values to device...") with output: for n, timer_period in enumerate(castor_calibration.keys()): progress = n / (len(castor_calibration.values()) - 1) bar.draw( tui.Segment( progress, color=tui.gradient(start_color, end_color, progress), ), ) output.update() castor_code = castor_calibration[timer_period] pollux_code = pollux_calibration[timer_period] gem.write_lut_entry(n, timer_period, castor_code, pollux_code) log.debug( f"Set LUT entry {n} to {timer_period=}, {castor_code=}, {pollux_code=}." ) log.info("Committing LUT to NVM...") gem.write_lut() checksum = 0 for dac_code in castor_calibration.values(): checksum ^= dac_code log.success(f"Calibration table written, checksum: {checksum:04x}") else: log.warning("Dry run enabled, calibration table not saved to device.") gem.close() print("") log.success("Done!")
def run(adc_resolution, invert, adc_channel, save): if invert: high_expected = 0 low_expected = adc_resolution - 1 else: high_expected = adc_resolution - 1 low_expected = 0 gem = gemini.Gemini() gem.enter_calibration_mode() # Knob calibration is done with error correction enabled. gem.enable_adc_error_correction() input( f"Set knob for channel {adc_channel} all the way CCW and press enter.") samples = [] for s in range(512): samples.append(gem.read_adc(adc_channel)) low_measured = statistics.mean(samples) log.info(f"> Measured {low_measured}, expected {low_expected}") input( f"Set knob for channel {adc_channel} all the way CW and press enter.") samples = [] for s in range(512): samples.append(gem.read_adc(adc_channel)) high_measured = statistics.mean(samples) log.info(f"> Measured {high_measured}, expected {high_expected}") gain_error = (high_expected - low_expected) / (high_measured - low_measured) offset_error = (low_measured * gain_error) - low_expected log.success( f"Knob gain error: {gain_error:.3f}, offset error: {offset_error:.1f}") local_copy = pathlib.Path( "calibrations") / f"{gem.serial_number}.knob.json" local_copy.parent.mkdir(parents=True, exist_ok=True) with local_copy.open("w") as fh: json.dump({"gain_error": gain_error, "offset_error": offset_error}, fh) log.success(f"Saved local copy to {local_copy}") if save: settings = gem.read_settings() settings.knob_gain_corr = gain_error settings.knob_offset_corr = offset_error gem.save_settings(settings) log.success("Saved to NVM.") else: log.warning("Dry run, not saved to NVM.") log.success("Done!") gem.close()
def run( calibration_points, sample_count, adc_range, adc_resolution, invert, adc_channel, save, ): voltages = [ n / calibration_points * adc_range for n in range(calibration_points + 1) ] expected_codes = [ int(voltages[n] / adc_range * (adc_resolution - 1)) for n in range(calibration_points + 1) ] if invert: expected_codes = [adc_resolution - 1 - code for code in expected_codes] measured_codes = [] gem = gemini.Gemini() sol_ = sol.Sol() gem.enter_calibration_mode() gem.disable_adc_error_correction() sol_.send_voltage(0) for n in range(calibration_points + 1): expected = expected_codes[n] voltage = n / calibration_points * adc_range log.info(f"Measuring {voltage:.3f}, expecting {expected}.") sol_.send_voltage(voltage) time.sleep(0.1) samples = [] for s in range(sample_count): samples.append(gem.read_adc(adc_channel)) result = statistics.mean(samples) diff = result - expected_codes[n] if abs(diff) > 100: log.error( "ADC reading too far out of range. Expected {expected}, measured: {result:.1f}, diff: {diff:.1f}" ) log.info(f"Measured {result:.1f}, diff {diff:.1f}") measured_codes.append(result) gain_error = adc_errors.calculate_avg_gain_error(expected_codes, measured_codes) offset_error = adc_errors.calculate_avg_offset_error( expected_codes, measured_codes, gain_error) log.info(f"Measured: Gain: {gain_error:.3f}, Offset: {offset_error:.1f}") corrected = adc_errors.apply_correction(measured_codes, gain_error, offset_error) corrected_gain_error = adc_errors.calculate_avg_gain_error( expected_codes, corrected) corrected_offset_error = adc_errors.calculate_avg_offset_error( expected_codes, corrected, corrected_gain_error) log.success( f"Expected after correction: Gain: {corrected_gain_error:.3f}, Offset: {corrected_offset_error:.1f}" ) local_copy = pathlib.Path("calibrations") / f"{gem.serial_number}.adc.json" local_copy.parent.mkdir(parents=True, exist_ok=True) with local_copy.open("w") as fh: json.dump({"gain_error": gain_error, "offset_error": offset_error}, fh) log.info(f"Saved local copy to {local_copy}") if save: gem.set_adc_gain_error(gain_error) gem.set_adc_offset_error(int(offset_error)) log.success("Saved to NVM.") else: log.warning("Dry run, not saved to NVM.") gem.enable_adc_error_correction() # Test out the new calibrated ADC log.info("Taking measurements with new calibration...") measured_codes = [] for n in range(calibration_points + 1): voltage = n / calibration_points * adc_range log.debug(f"Measuring {voltage:.3f}, expecting {expected_codes[n]}.") sol_.send_voltage(voltage) time.sleep(0.1) samples = [] for s in range(sample_count): samples.append(gem.read_adc(adc_channel)) result = statistics.mean(samples) log.info( f"Measured {result:.1f}, diff {result - expected_codes[n]:.1f}") if abs(diff) > 50: log.error( "ADC reading too far out of range. Expected {expected}, measured: {result:.1f}, diff: {diff:.1f}" ) measured_codes.append(result) gain_error = adc_errors.calculate_avg_gain_error(expected_codes, measured_codes) offset_error = adc_errors.calculate_avg_offset_error( expected_codes, measured_codes, gain_error) log.success( f"Measured, corrected: Gain: {gain_error:.3f}, Offset: {offset_error:.1f}" ) log.success("Done") gem.close()