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):
        voltage = n / calibration_points * adc_range
        print(f"Measuring {voltage:.3f}, expecting {expected_codes[n]}.")
        sol_.send_voltage(voltage)
        time.sleep(0.2)

        samples = []
        for s in range(sample_count):
            samples.append(gem.read_adc(adc_channel))
        
        result = statistics.mean(samples)
        
        print(f"Got {result:.1f}, diff {result - expected_codes[n]:.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)
    print(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)
    print(f"Corrected: Gain: {corrected_gain_error:.3f}, Offset: {corrected_offset_error:.1f}")

    if save:
        gem.set_adc_gain_error(gain_error)
        gem.set_adc_offset_error(int(offset_error))
        print("Saved to NVM.")
    else:
        print("Dry run, not saved to NVM.")
    
    gem.enable_adc_error_correction()
    def post_measure(self, value):
        if self._gain_error is None:
            return value

        return adc_errors.apply_correction(value, self._gain_error,
                                           self._offset_error)
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()