Skip to content

Commit c57b3cc

Browse files
authored
added alternative scripts, doc and requirements.txt (#30)
"
1 parent 07367e5 commit c57b3cc

7 files changed

Lines changed: 992 additions & 0 deletions

factory/alt_adc_calibration.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
from wintertools import reportcard
2+
from wintertools.print import print
3+
from libgemini import adc_errors, gemini
4+
5+
6+
def get_firmware_and_serial():
7+
print("# Firmware & serial")
8+
9+
gem = gemini.Gemini.get()
10+
fw_version = gem.get_firmware_version()
11+
serial = gem.get_serial_number()
12+
13+
print(f"Firmware version: {fw_version}")
14+
print(f"Serial number: {serial}")
15+
16+
REPORT.ulid = serial
17+
REPORT.sections.append(
18+
reportcard.Section(
19+
name="Firmware",
20+
items=[
21+
reportcard.LabelValueItem(
22+
label="Version", value=fw_version, class_="stack"
23+
),
24+
reportcard.LabelValueItem(
25+
label="Serial number", value=serial, class_="stack"
26+
),
27+
],
28+
)
29+
)
30+
31+
32+
def run():
33+
34+
# The Hubble board wires the chorus pot to board ground. We must do it manually
35+
input("\nSet LFO/CHORUS POT to full CCW, then press Enter to continue...")
36+
ZERO_CODE_CHANNEL = gemini.Gemini.ADC.CHORUS_POT
37+
# The Hubble board wires the pitch pots to board 3.3v. We must do it manually
38+
input("\nSet PITCH POT A (Left hand side) to full CW, then press Enter to continue...")
39+
MAX_CODE_CHANNEL = gemini.Gemini.ADC.CV_A_POT
40+
41+
# Simple ADC calibration since this is really only used to read the
42+
# potentiometers. The pitch CV input has an additionally, more complex
43+
# calibration that's measured and applied.
44+
45+
gem = gemini.Gemini.get()
46+
print("Entering Calibration Mode and disabling ADC error correction...\n")
47+
gem.enter_calibration_mode()
48+
gem.disable_adc_error_correction()
49+
50+
zero_code = gem.read_adc_average(ZERO_CODE_CHANNEL)
51+
max_code = gem.read_adc_average(MAX_CODE_CHANNEL)
52+
53+
54+
gain_error, offset_error = adc_errors.calculate(0, 4095, zero_code, max_code)
55+
56+
print()
57+
print("Pre-calibration measurements:")
58+
print(f"* 0v : {zero_code}")
59+
print(f"* 3.3v : {max_code}")
60+
print(f"* Gain error : {gain_error:0.3f}")
61+
print(f"* Offset error : {offset_error:.0f}")
62+
63+
gem.set_adc_gain_error(gain_error)
64+
gem.set_adc_offset_error(offset_error)
65+
gem.enable_adc_error_correction()
66+
print("\n✓ Saved to device NVM\n")
67+
68+
post_zero_code = gem.read_adc_average(ZERO_CODE_CHANNEL)
69+
post_max_code = gem.read_adc_average(MAX_CODE_CHANNEL)
70+
post_gain_error, post_offset_error = adc_errors.calculate(
71+
0, 4095, post_zero_code, post_max_code
72+
)
73+
74+
print()
75+
print("Post-calibration measurements:")
76+
print(f"* 0v : {post_zero_code}")
77+
print(f"* 3.3v : {post_max_code}")
78+
print(f"* Gain error : {post_gain_error:0.3f}")
79+
print(f"* Offset error : {post_offset_error:.0f}")
80+
81+
passed = post_zero_code < 20 and post_max_code > 4075
82+
83+
print()
84+
if passed:
85+
print.success()
86+
else:
87+
print.failure()
88+
89+
return reportcard.Section(
90+
name="ADC",
91+
items=[
92+
reportcard.PassFailItem(label="Calibration", value=passed),
93+
reportcard.LabelValueItem(label="Gain error", value=f"{gain_error:0.3f}"),
94+
reportcard.LabelValueItem(
95+
label="Offset error", value=f"{offset_error:.0f}"
96+
),
97+
reportcard.LabelValueItem(
98+
label="Adj gain error", value=f"{post_gain_error:0.3f}"
99+
),
100+
reportcard.LabelValueItem(
101+
label="Adj offset error", value=f"{post_offset_error:.0f}"
102+
),
103+
],
104+
)
105+
106+
107+
if __name__ == "__main__":
108+
109+
print()
110+
print("> This script calibrates the ADC using values from the LFO/Chorus Pot and Pitch CV Pot A (Left hand side)")
111+
print("!! Please confirm the following are true, then press ENTER to continue:")
112+
print("* This machine connected to the main board USB port")
113+
print("* There is not a drive visible named GEMINIBOOT. If so, please power cycle the main board")
114+
print("* The main board is connected to eurorack power")
115+
input()
116+
117+
REPORT = reportcard.Report(name="Castor & Pollux")
118+
get_firmware_and_serial()
119+
120+
gem = gemini.Gemini.get()
121+
print()
122+
print("Pre-calibration settings...\n")
123+
print(gem.read_settings())
124+
125+
REPORT.sections.append(run())
126+
127+
print()
128+
print("Post-calibration settings...\n")
129+
print(gem.read_settings())
130+
131+
print(REPORT)

factory/alt_clock_calibration.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Copyright (c) 2021 Alethea Katherine Flowers.
2+
# Published under the standard MIT License.
3+
# Full text available at: https://opensource.org/licenses/MIT
4+
5+
import json
6+
import pathlib
7+
import time
8+
9+
from wintertools.print import print
10+
from wintertools import oscilloscope, reportcard
11+
12+
from libgemini import gemini, oscillators
13+
14+
SCOPE_SETTLE_TIME = 4
15+
OSCILLATOR = 0
16+
TEST_NOTE_FREQ = 880
17+
MAX_DEVIATION = 0.85
18+
19+
def get_firmware_and_serial():
20+
print("# Firmware & serial")
21+
22+
gem = gemini.Gemini.get()
23+
fw_version = gem.get_firmware_version()
24+
serial = gem.get_serial_number()
25+
26+
print(f"Firmware version: {fw_version}")
27+
print(f"Serial number: {serial}")
28+
29+
REPORT.ulid = serial
30+
REPORT.sections.append(
31+
reportcard.Section(
32+
name="Firmware",
33+
items=[
34+
reportcard.LabelValueItem(
35+
label="Version", value=fw_version, class_="stack"
36+
),
37+
reportcard.LabelValueItem(
38+
label="Serial number", value=serial, class_="stack"
39+
),
40+
],
41+
)
42+
)
43+
44+
def run():
45+
# Calibrates the tuning by indirectly measuring the actual frequency of the
46+
# SAMD21's internal 8 MHz oscillator.
47+
48+
input("\nConnect a scope to Castor Output (Left hand side). Then turn up the SAW output on Castor (left hand side) and press enter....")
49+
gem = gemini.Gemini.get()
50+
print(f"Changing Gemini note to {TEST_NOTE_FREQ} Hz")
51+
gem.enter_calibration_mode()
52+
gem.set_osc8m_freq(8_000_000)
53+
gem.set_frequency(OSCILLATOR, TEST_NOTE_FREQ)
54+
gem.set_dac(2048, 2048, 2048, 2048)
55+
56+
print(f"Waiting {SCOPE_SETTLE_TIME}s for scope to settle")
57+
time.sleep(SCOPE_SETTLE_TIME)
58+
59+
# measured_note_freq = scope.get_freq(SCOPE_CHANNEL)
60+
measured_note_freq = float(input("\nEnter Measured Frequency from scope...."))
61+
62+
print(f"Measured note frequency: {measured_note_freq} Hz")
63+
64+
period = oscillators.frequency_to_timer_period(880)
65+
measured_clock_freq = round((period + 1) * measured_note_freq)
66+
67+
print(f"Measured clock frequency: {measured_clock_freq:,.0f} Hz")
68+
69+
print("Re-measuring with adjusted clock")
70+
71+
gem.set_osc8m_freq(measured_clock_freq)
72+
gem.set_frequency(OSCILLATOR, TEST_NOTE_FREQ)
73+
74+
print(f"Waiting {SCOPE_SETTLE_TIME}s for scope to settle")
75+
time.sleep(SCOPE_SETTLE_TIME)
76+
77+
# post_measured_note_freq = scope.get_freq(SCOPE_CHANNEL)
78+
post_measured_note_freq = float(input("\nEnter Measured Frequency from scope...."))
79+
80+
passed = abs(TEST_NOTE_FREQ - post_measured_note_freq) < MAX_DEVIATION
81+
82+
print()
83+
if passed:
84+
print.success()
85+
else:
86+
print.failure()
87+
print(f"Calibration failed: post_zero_code < 20 or post_max_code < 4075")
88+
print()
89+
90+
print(
91+
f"{'✓' if passed else '!!'} Re-measured note frequency: {post_measured_note_freq:.0f} Hz"
92+
)
93+
94+
if passed:
95+
settings = gem.read_settings()
96+
settings.osc8m_freq = round(measured_clock_freq)
97+
gem.save_settings(settings)
98+
print()
99+
print("✓ Saved to device NVM")
100+
101+
local_copy = pathlib.Path("calibrations") / f"{gem.get_serial_number()}.clock.json"
102+
local_copy.parent.mkdir(parents=True, exist_ok=True)
103+
104+
with local_copy.open("w") as fh:
105+
data = {
106+
"expected_note_frequency": TEST_NOTE_FREQ,
107+
"measured_note_frequency": measured_note_freq,
108+
"measured_clock_frequency": measured_clock_freq,
109+
}
110+
json.dump(data, fh)
111+
print(f"[italic]Saved to {local_copy}[/]")
112+
113+
return reportcard.Section(
114+
name="Tuning",
115+
items=[
116+
reportcard.PassFailItem(label="8 MHz clock", value=passed),
117+
reportcard.LabelValueItem(
118+
label="Measured clock",
119+
value=f"{measured_clock_freq:0,.0f} Hz",
120+
),
121+
reportcard.LabelValueItem(
122+
label="Measured note", value=f"{measured_note_freq:.3f} Hz"
123+
),
124+
reportcard.LabelValueItem(
125+
label="Adjusted note",
126+
value=f"{post_measured_note_freq:0.3f} Hz",
127+
),
128+
reportcard.LabelValueItem(
129+
label="Tuning error",
130+
value=f"{abs(TEST_NOTE_FREQ - post_measured_note_freq):0.4f} Hz",
131+
),
132+
],
133+
)
134+
135+
136+
if __name__ == "__main__":
137+
138+
print()
139+
print("> This script calibrates the 8kHz clock by connecting a scope to the Castor output (Left hand side)")
140+
print("!! Please confirm the following are true, then press ENTER to continue:")
141+
print("* This machine connected to the main board USB port")
142+
print("* There is not a drive visible named GEMINIBOOT. If so, please power cycle the main board")
143+
input()
144+
145+
REPORT = reportcard.Report(name="Castor & Pollux")
146+
get_firmware_and_serial()
147+
print()
148+
149+
REPORT.sections.append(run())
150+
print(REPORT)

0 commit comments

Comments
 (0)