diff --git a/hardware/BOM/InteractiveBom_V3/Readme.txt b/hardware/BOM/InteractiveBom_V3/Readme.txt
new file mode 100644
index 0000000..63acb78
--- /dev/null
+++ b/hardware/BOM/InteractiveBom_V3/Readme.txt
@@ -0,0 +1,2 @@
+generated with the standalone version of https://github.com/openscopeproject/InteractiveHtmlBom against the eagle .brd files.
+The ibom_tritiled_v31_xpe_mod.html is modified to have a 1uF decoupling capacitor at C2 (in the eagle file is 100nF).
\ No newline at end of file
diff --git a/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_lux_c.html b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_lux_c.html
new file mode 100644
index 0000000..603498e
--- /dev/null
+++ b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_lux_c.html
@@ -0,0 +1,4442 @@
+
+
+
+
+
+
+ Interactive BOM for KiCAD
+
+
+
+
+
+
+
+
+
+
+
+ F
+
+ FB
+
+ B
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Title
+
+
+ Revision
+
+
+
+
+ Company
+
+
+ Date
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_oslon.html b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_oslon.html
new file mode 100644
index 0000000..4efd239
--- /dev/null
+++ b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_oslon.html
@@ -0,0 +1,4442 @@
+
+
+
+
+
+
+ Interactive BOM for KiCAD
+
+
+
+
+
+
+
+
+
+
+
+ F
+
+ FB
+
+ B
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Title
+
+
+ Revision
+
+
+
+
+ Company
+
+
+ Date
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_xpe.html b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_xpe.html
new file mode 100644
index 0000000..db1366c
--- /dev/null
+++ b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v30_xpe.html
@@ -0,0 +1,4442 @@
+
+
+
+
+
+
+ Interactive BOM for KiCAD
+
+
+
+
+
+
+
+
+
+
+
+ F
+
+ FB
+
+ B
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Title
+
+
+ Revision
+
+
+
+
+ Company
+
+
+ Date
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v31_oslon.html b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v31_oslon.html
new file mode 100644
index 0000000..b1e4fae
--- /dev/null
+++ b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v31_oslon.html
@@ -0,0 +1,4442 @@
+
+
+
+
+
+
+ Interactive BOM for KiCAD
+
+
+
+
+
+
+
+
+
+
+
+ F
+
+ FB
+
+ B
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Title
+
+
+ Revision
+
+
+
+
+ Company
+
+
+ Date
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v31_xpe_mod.html b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v31_xpe_mod.html
new file mode 100644
index 0000000..cb13a79
--- /dev/null
+++ b/hardware/BOM/InteractiveBom_V3/ibom_tritiled_v31_xpe_mod.html
@@ -0,0 +1,4442 @@
+
+
+
+
+
+
+ Interactive BOM for KiCAD
+
+
+
+
+
+
+
+
+
+
+
+ F
+
+ FB
+
+ B
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Title
+
+
+ Revision
+
+
+
+
+ Company
+
+
+ Date
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/software/tritiled30/src/calibrate.py b/software/tritiled30/src/calibrate.py
index 13549af..ea26d0c 100755
--- a/software/tritiled30/src/calibrate.py
+++ b/software/tritiled30/src/calibrate.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# calibrate.py: calibrate TritiLED V3.x brightness/current
@@ -17,36 +17,73 @@
# 4. recompile the c-code to generate the runtime image
# 5. download the runtime image to the board
#
-
+#helper function
+def inverse_poly1d(poly1d_param, y):
+ return (y-poly1d_param[1])/poly1d_param[0]
+
import numpy as np
import matplotlib.pyplot as plt
frequencies = np.array([100, 200, 400])
currents = np.loadtxt('currents.dat')*1e-6
+# fit polynomial to data
p = np.polyfit(frequencies, currents, 1)
-print p
+print("currents")
+print(currents)
+print("polyfit")
+print(p)
-Ah = 0.220
+Ah = 0.220 # power in CR2032
years = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
target_current = Ah / (years * 365.25 * 24)
-frequency = (target_current - p[1])/p[0]
-print np.round(frequency, 2)
+# frequency at target current
+frequency = inverse_poly1d(p,target_current)
+print("frequencies")
+print(np.round(frequency, 2))
P = np.poly1d(p)
-xi = np.linspace(0, 1000, 10)
+xi = np.linspace(0, 1000, 100)
+plotYears = np.array(Ah / (P(xi) * 365.25 * 24))
+
+boolAr = np.where(plotYears>0)
+plotYearsTrunc = plotYears[boolAr]
+xiTrunc = xi[boolAr]
+
+
+figure, axis = plt.subplots(1, 2,layout='constrained')
+# plot current over frequency with datapoints and interpolation
+axis[0].plot(frequencies, currents*1e6, 'ro', label="calibration points")
+axis[0].plot(xi, P(xi)*1e6, 'b-', label="linear interpolation")
+axis[0].set_ylabel("current [uA]")
+axis[0].set_xlabel("frequency [Hz]")
+axis[0].legend()
+
+# plot life expectancy over frequency
+current_50Hz = P(50)
+years_50Hz = Ah / (current_50Hz * 365.25 * 24)
+axis[1].plot(frequencies, Ah / (P(frequencies) * 365.25 * 24), 'ro', label="calibration points")
+axis[1].plot(xiTrunc, plotYearsTrunc, 'b-', label="linear interpolation")
+axis[1].plot(50, years_50Hz, 'gx')
+annot = " "+'{:.2f}'.format(years_50Hz)+" years at 50 Hz"
+axis[1].annotate(annot,(50,years_50Hz))
+axis[1].set_ylabel("years [a]")
+axis[1].set_xlabel("frequency [Hz]")
+ylim = axis[1].get_ylim()
+xlim = axis[1].get_xlim()
+axis[1].hlines(years_50Hz,xlim[0],50)
+axis[1].vlines(50,ylim[0],years_50Hz)
+axis[1].set_ylim(ylim)
+axis[1].set_xlim(xlim)
+axis[1].legend()
+
-plt.plot(frequencies, currents, 'ro')
-plt.plot(xi, P(xi), 'b-')
plt.show()
header = open('modes.h', 'w')
header.write('#define N_MODES 10\n')
header.write('struct run_time period_table[N_MODES] = {\n')
-for idx in range(0, 10):
+for idx in range(0,len(years)):#range(0, 10):
header.write(' {%d, (int)(31000./%f + 0.5)},\n' % (years[idx], frequency[idx]))
header.write('};\n')
header.close()
-
-
-