Source code for jii_multispeq_protocols.protocols.calibrations.main_body_leds_calibration

"""
Main Body LEDs Calibration
==========================

new version of CaliQ LED output calibration using PAR matching...

.. warning:: This calibration requires a CaliQ

2280
"""

import numpy as np
from scipy import stats
from jii_multispeq.analysis import GetProtocolByLabel

_protocol = [
  {
    "v_arrays": [
      [
        1200,
        20000,
        1500,
        12000
      ],
      [
        40,
        600
      ],
      [
        600,
        1200
      ],
      [
        40,
        600,
        3000
      ],
      [
        600,
        3000,
        9000
      ],
      [
        50,
        300
      ],
      [
        300,
        500
      ],
      [
        40,
        600,
        3000
      ],
      [
        600,
        3000,
        9000
      ]
    ],
    "set_repeats": 1,
    "_protocol_set_": [
      {
        "label": "cal_led_1",
        "qpar_led_cal": [
          1,
          "@p1",
          "@p2",
          20
        ],
        "protocol_repeats": "#l1"
      },
      {
        "label": "cal_led_2",
        "qpar_led_cal": [
          2,
          "@p3",
          "@p4",
          20
        ],
        "protocol_repeats": "#l3"
      },
      {
        "label": "cal_led_3",
        "qpar_led_cal": [
          3,
          "@p5",
          "@p6",
          20
        ],
        "protocol_repeats": "#l5"
      },
      {
        "label": "cal_led_4",
        "qpar_led_cal": [
          4,
          "@p7",
          "@p8",
          20
        ],
        "protocol_repeats": "#l7"
      }
    ]
  }
]

[docs] def _analyze( _data ): """ * Macro for data evaluation on PhotosynQ.org * by: David M. Kramer * created: July 17, 2020 10:10 AM """ ## Define the output object here output = {} ## The MultispeQ uses the following fixed PAR values ## as reference points for linear interpolation of the ## DAC settings for desired PAR values. set_points = [0,150,300,600,1200,3000,6000,10000] ## Check if the key time exists in json if "time" in _data: ## Add key time and value to output output["time"] = _data["time"] all_labels = [] _set = _data["set"] output["toDevice"] = "" for i in range(len(_set)): if "label" in _set[i]: if "cal_led_" in _set[i]["label"]: all_labels.append(_set[i]["label"]) unique_LEDs = list( set(all_labels) ) v_arrays = _data["v_arrays"] max_allowed_par = v_arrays[0] output["max_allowed_par"] = repr(max_allowed_par) output["toDevice"] = "" for led_index, _ in enumerate(unique_LEDs): led_label = unique_LEDs[led_index] set_number = 0 led2 = GetProtocolByLabel(led_label, _data, True) LED = led2[0]["led_to_qpar0"][0] all_settings = [] all_amplitudes = [] all_slopes=[] all_b = [] for set_number, _ in enumerate(led2): fit_points = [] settings = [] for i in range( len(led2[set_number]["qpar_led_cal"]) ): if led2[set_number]["qpar_led_cal"][i][1] > 0: fit_points.append(led2[set_number]["qpar_led_cal"][i][1]) settings.append(led2[set_number]["qpar_led_cal"][i][0]) all_settings.append(settings) all_amplitudes.append(fit_points) slope, intercept, r_value, p_value, std_err = stats.linregress(fit_points, settings) output[("r2_%s:%s") % (led_label,set_number)] = np.round(r_value**2, 4) all_b.append(intercept) all_slopes.append(slope) ## find the ranges over which each equation applies. ## there should be n+2 ranges, where n is the number of entries ## in the v_arrays. ## PAR = 0 to PAR = element 0 ## element 0 to element 1 ## ... ## last element to PAR = 10,000 ## BUT, these par_ranges = [] lower_bounds_ranges = led_index * 2 + 1 upper_bounds_ranges = led_index * 2 + 2 par_ranges.append([0,v_arrays[lower_bounds_ranges][0]]) for range_index in range( len(v_arrays[lower_bounds_ranges])): par_ranges.append([v_arrays[lower_bounds_ranges][range_index], v_arrays[upper_bounds_ranges][range_index]]) par_ranges.append([v_arrays[upper_bounds_ranges][len(v_arrays[upper_bounds_ranges]) -1], max_allowed_par[led_index]]) set_point_values = [] set_point_indexes = [] set_points_to_fit = [] ## The set point for dark (PAR=0) is jus the Y-intercept for the ## first series of LED PAR vs. settings set. set_point_values.append(np.round(all_b[0]-1,0)) ## The maximum setting is that for the max_allowed_par, using the ## final (highest) LED PAR versus setting curve. max_par_range_index = len(all_slopes) - 1 m = all_slopes[max_par_range_index] b = all_b[max_par_range_index] max_calibrated_setpoint = np.round(m * max_allowed_par[led_index] + b,0) if max_calibrated_setpoint > 4095: max_calibrated_setpoint=4095 if max_calibrated_setpoint < 0: max_calibrated_setpoint=0 output["max_setpoint_for_led:%s" % led_index] = np.round(max_calibrated_setpoint,0) ## Go through the remainting set points and determine which line to use ## for estimating the setting value. for set_point_index in range(1, len(set_points)): set_point_par = set_points[set_point_index] ## the PAR value we are trying to fit. set_points_to_fit.append(set_point_par) ## go through each PAR range and find the one that fits for par_range_index,_ in enumerate(par_ranges): if set_point_par >= max_allowed_par[led_index]: set_point_values.append(max_calibrated_setpoint) set_point_indexes.append(par_range_index) break elif set_point_par >= par_ranges[par_range_index][0]: set_point_indexes.append(par_range_index) m = all_slopes[par_range_index] b = all_b[par_range_index] calibrated_setpoint = m * set_point_par + b set_point_values.append(np.round(calibrated_setpoint,0)) break output["toDevice"] += "par_to_dac_lin+%s+" % LED for cal_index in range(len(set_point_values)): output["toDevice"] += "%s+" % set_point_values[cal_index] output["toDevice"] += "par_max_setting+%s+%s+" % (LED, max_calibrated_setpoint) ## end of led loop ## Return data return output
_example = { "time": "1629320993633", "device_name": "MultispeQ", "device_version": "2", "device_id": "31:f4:e8:4d", "device_battery": 63, "device_firmware": 2.345, "sample": [ [ { "time": "1629320993635", "v_arrays": [ [ 1200, 20000, 1500, 12000 ], [ 40, 600 ], [ 600, 1200 ], [ 40, 600, 3000 ], [ 600, 3000, 9000 ], [ 50, 300 ], [ 300, 500 ], [ 40, 600, 3000 ], [ 600, 3000, 9000 ] ], "set_repeats": 1, "protocol_id": "editor", "set": [ { "time": "1629320993656", "led_to_qpar0": [ 1, 40, 74.01, 75 ], "led_to_qpar1": [ 1, 600, 647.17, 267 ], "qpar_led_cal": [ [ 75, 40 ], [ 84, 90 ], [ 93, 119 ], [ 102, 131 ], [ 111, 172 ], [ 120, 189 ], [ 129, 208 ], [ 138, 249 ], [ 147, 266 ], [ 156, 295 ], [ 165, 326 ], [ 174, 347 ], [ 183, 382 ], [ 192, 412 ], [ 201, 428 ], [ 210, 471 ], [ 219, 488 ], [ 228, 511 ], [ 237, 551 ], [ 246, 570 ], [ 255, 602 ], [ 264, 630 ] ], "label": "cal_led_1", "data_raw": [ ] }, { "time": "1629321017842", "led_to_qpar0": [ 1, 600, 657.19, 267 ], "led_to_qpar1": [ 1, 1200, 1585.55, 567 ], "qpar_led_cal": [ [ 267, 389 ], [ 282, 677 ], [ 297, 736 ], [ 312, 766 ], [ 327, 827 ], [ 342, 871 ], [ 357, 899 ], [ 372, 972 ], [ 387, 1005 ], [ 402, 1036 ], [ 417, 1110 ], [ 432, 1140 ], [ 447, 1198 ], [ 462, 1247 ], [ 477, 1276 ], [ 492, 1354 ], [ 507, 1385 ], [ 522, 1424 ], [ 537, 1491 ], [ 552, 1520 ], [ 567, 1573 ], [ 582, 1625 ] ], "label": "cal_led_1", "data_raw": [ ] }, { "time": "1629321044494", "led_to_qpar0": [ 2, 40, 0, 50 ], "led_to_qpar1": [ 2, 600, 817.4, 315 ], "qpar_led_cal": [ [ 50, 0 ], [ 63, 4 ], [ 76, 17 ], [ 89, 64 ], [ 102, 89 ], [ 115, 108 ], [ 128, 182 ], [ 141, 208 ], [ 154, 268 ], [ 167, 300 ], [ 180, 330 ], [ 193, 395 ], [ 206, 425 ], [ 219, 469 ], [ 232, 527 ], [ 245, 555 ], [ 258, 623 ], [ 271, 659 ], [ 284, 683 ], [ 297, 753 ], [ 310, 781 ], [ 323, 822 ] ], "label": "cal_led_2", "data_raw": [ ] }, { "time": "1629321074822", "led_to_qpar0": [ 2, 600, 672.2, 267 ], "led_to_qpar1": [ 2, 3000, 3564.23, 1062 ], "qpar_led_cal": [ [ 267, 664 ], [ 306, 758 ], [ 345, 860 ], [ 384, 1068 ], [ 423, 1155 ], [ 462, 1294 ], [ 501, 1474 ], [ 540, 1563 ], [ 579, 1651 ], [ 618, 1875 ], [ 657, 1966 ], [ 696, 2190 ], [ 735, 2283 ], [ 774, 2417 ], [ 813, 2601 ], [ 852, 2691 ], [ 891, 2872 ], [ 930, 3022 ], [ 969, 3112 ], [ 1008, 3341 ], [ 1047, 3446 ], [ 1086, 3557 ] ], "label": "cal_led_2", "data_raw": [ ] }, { "time": "1629321101474", "led_to_qpar0": [ 2, 3000, 3571.28, 1062 ], "led_to_qpar1": [ 2, 9000, 12641.5, 3434 ], "qpar_led_cal": [ [ 1062, 636 ], [ 1180, 4056 ], [ 1298, 4317 ], [ 1416, 5023 ], [ 1534, 5304 ], [ 1652, 5721 ], [ 1770, 6312 ], [ 1888, 6603 ], [ 2006, 7157 ], [ 2124, 7623 ], [ 2242, 7913 ], [ 2360, 8639 ], [ 2478, 8941 ], [ 2596, 9305 ], [ 2714, 10010 ], [ 2832, 10277 ], [ 2950, 10826 ], [ 3068, 11421 ], [ 3186, 11808 ], [ 3304, 12439 ], [ 3422, 12606 ], [ 3540, 12820 ] ], "label": "cal_led_2", "data_raw": [ ] }, { "time": "1629321137130", "led_to_qpar0": [ 3, 50, 5.78, 50 ], "led_to_qpar1": [ 3, 300, 299.28, 708 ], "qpar_led_cal": [ [ 50, 2 ], [ 82, 21 ], [ 114, 31 ], [ 146, 44 ], [ 178, 68 ], [ 210, 78 ], [ 242, 97 ], [ 274, 117 ], [ 306, 128 ], [ 338, 147 ], [ 370, 162 ], [ 402, 172 ], [ 434, 198 ], [ 466, 204 ], [ 498, 216 ], [ 530, 233 ], [ 562, 245 ], [ 594, 254 ], [ 626, 268 ], [ 658, 273 ], [ 690, 288 ], [ 722, 295 ] ], "label": "cal_led_3", "data_raw": [ ] }, { "time": "1629321160913", "led_to_qpar0": [ 3, 300, 302.69, 708 ], "led_to_qpar1": [ 3, 500, 469.76, 1593 ], "qpar_led_cal": [ [ 708, 212 ], [ 752, 276 ], [ 796, 304 ], [ 840, 313 ], [ 884, 320 ], [ 928, 339 ], [ 972, 340 ], [ 1016, 338 ], [ 1060, 361 ], [ 1104, 361 ], [ 1148, 372 ], [ 1192, 373 ], [ 1236, 374 ], [ 1280, 383 ], [ 1324, 380 ], [ 1368, 380 ], [ 1412, 382 ], [ 1456, 382 ], [ 1500, 381 ], [ 1544, 380 ], [ 1588, 375 ], [ 1632, 371 ] ], "label": "cal_led_3", "data_raw": [ ] }, { "time": "1629321180197", "led_to_qpar0": [ 4, 40, 0, 50 ], "led_to_qpar1": [ 4, 600, 704.31, 252 ], "qpar_led_cal": [ [ 50, 0 ], [ 60, 0 ], [ 70, 0 ], [ 80, 0 ], [ 90, 0 ], [ 100, 0 ], [ 110, 4 ], [ 120, 22 ], [ 130, 52 ], [ 140, 111 ], [ 150, 140 ], [ 160, 208 ], [ 170, 255 ], [ 180, 289 ], [ 190, 368 ], [ 200, 406 ], [ 210, 449 ], [ 220, 529 ], [ 230, 560 ], [ 240, 620 ], [ 250, 683 ], [ 260, 717 ] ], "label": "cal_led_4", "data_raw": [ ] }, { "time": "1629321210522", "led_to_qpar0": [ 4, 600, 1043.74, 315 ], "led_to_qpar1": [ 4, 3000, 3343.39, 708 ], "qpar_led_cal": [ [ 315, 848 ], [ 334, 1088 ], [ 353, 1247 ], [ 372, 1319 ], [ 391, 1394 ], [ 410, 1545 ], [ 429, 1606 ], [ 448, 1708 ], [ 467, 1833 ], [ 486, 1896 ], [ 505, 2030 ], [ 524, 2122 ], [ 543, 2186 ], [ 562, 2337 ], [ 581, 2398 ], [ 600, 2487 ], [ 619, 2610 ], [ 638, 2674 ], [ 657, 2785 ], [ 676, 2882 ], [ 695, 2940 ], [ 714, 3090 ] ], "label": "cal_led_4", "data_raw": [ ] }, { "time": "1629321235942", "led_to_qpar0": [ 4, 3000, 4745.81, 1062 ], "led_to_qpar1": [ 4, 9000, 10300.67, 2390 ], "qpar_led_cal": [ [ 1062, 3925 ], [ 1128, 4976 ], [ 1194, 5164 ], [ 1260, 5637 ], [ 1326, 5828 ], [ 1392, 6073 ], [ 1458, 6481 ], [ 1524, 6660 ], [ 1590, 6976 ], [ 1656, 7291 ], [ 1722, 7465 ], [ 1788, 7895 ], [ 1854, 8086 ], [ 1920, 8277 ], [ 1986, 8694 ], [ 2052, 8866 ], [ 2118, 9141 ], [ 2184, 9465 ], [ 2250, 9625 ], [ 2316, 9973 ], [ 2382, 10208 ], [ 2448, 10367 ] ], "label": "cal_led_4", "data_raw": [ ] } ], "data_raw": [ ] } ] ], "app_os": "macOS 18.7.0", "app_name": "PhotosynQ", "app_version": "1.10.59", "app_device": "x64", "location": False, "time_offset": "America/Detroit", "protocol": "[{\"v_arrays\":[[1200,20000,1500,12000],[40,600],[600,1200],[40,600,3000],[600,3000,9000],[50,300],[300,500],[40,600,3000],[600,3000,9000]],\"set_repeats\":1,\"_protocol_set_\":[{\"label\":\"cal_led_1\",\"qpar_led_cal\":[1,\"@p1\",\"@p2\",20],\"protocol_repeats\":\"#l1\"},{\"label\":\"cal_led_2\",\"qpar_led_cal\":[2,\"@p3\",\"@p4\",20],\"protocol_repeats\":\"#l3\"},{\"label\":\"cal_led_3\",\"qpar_led_cal\":[3,\"@p5\",\"@p6\",20],\"protocol_repeats\":\"#l5\"},{\"label\":\"cal_led_4\",\"qpar_led_cal\":[4,\"@p7\",\"@p8\",20],\"protocol_repeats\":\"#l7\"}],\"protocol_id\":2280}]" }