joularcore 0.2.0

Joular Core is a platform to measure power and energy across all systems, OSes and devices
Documentation
/*
 * Copyright (c) 2025-2026, Adel Noureddine.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the
 * GNU Lesser General Public License v3.0 only (LGPL-3.0-only)
 * which accompanies this distribution, and is available at
 * https://www.gnu.org/licenses/lgpl-3.0.en.html
 *
 * Author : Adel Noureddine
 */

//! The board list and its measured power models.
//!
//! Pure data: which device-tree strings identify which board, and the
//! polynomial fitted to each one. Adding a board means adding one entry to each
//! table, and a test asserts the two agree.
//!
//! See [`super`] for how the coefficients are used.

/// Device-tree substring, 32-bit model name, 64-bit model name.
///
/// Ordered most specific first, so a revision-specific entry wins over the
/// generic one for the same board.
pub(super) const BOARD_NAMES: &[(&str, &str, &str)] = &[
    ("Raspberry Pi 5 Model B Rev 1.0", "rbp5b1.0", "rbp5b1.0-64"),
    ("Raspberry Pi 5 Model B", "rbp5b1.0", "rbp5b1.0-64"),
    ("Raspberry Pi 400 Rev 1.0", "rbp4001.0", "rbp4001.0-64"),
    ("Raspberry Pi 400", "rbp4001.0", "rbp4001.0-64"),
    ("Raspberry Pi 4 Model B Rev 1.2", "rbp4b1.2", "rbp4b1.2-64"),
    ("Raspberry Pi 4 Model B Rev 1.1", "rbp4b1.1", "rbp4b1.1-64"),
    ("Raspberry Pi 4 Model B", "rbp4b1.2", "rbp4b1.2-64"),
    (
        "Raspberry Pi 3 Model B Plus Rev 1.3",
        "rbp3b+1.3",
        "rbp3b+1.3",
    ),
    ("Raspberry Pi 3 Model B Plus", "rbp3b+1.3", "rbp3b+1.3"),
    ("Raspberry Pi 3 Model B Rev 1.2", "rbp3b1.2", "rbp3b1.2"),
    ("Raspberry Pi 3 Model B", "rbp3b1.2", "rbp3b1.2"),
    ("Raspberry Pi 2 Model B Rev 1.1", "rbp2b1.1", "rbp2b1.1"),
    ("Raspberry Pi 2 Model B", "rbp2b1.1", "rbp2b1.1"),
    (
        "Raspberry Pi Model B Plus Rev 1.2",
        "rbp1b+1.2",
        "rbp1b+1.2",
    ),
    ("Raspberry Pi Model B Plus", "rbp1b+1.2", "rbp1b+1.2"),
    ("Raspberry Pi Model B Rev 2", "rbp1b2", "rbp1b2"),
    ("Raspberry Pi Model B", "rbp1b2", "rbp1b2"),
    ("Raspberry Pi Zero W Rev 1.1", "rbpzw1.1", "rbpzw1.1"),
    ("Raspberry Pi Zero W", "rbpzw1.1", "rbpzw1.1"),
    ("ASUS Tinker Board (S)", "asustbs", "asustbs"),
    ("ASUS Tinker Board", "asustbs", "asustbs"),
];

/// The built-in coefficients for `model`, constant term first.
pub(super) fn builtin_coefficients(model: &str) -> Option<&'static [f64]> {
    BUILTIN_MODELS
        .iter()
        .find(|(name, _)| *name == model)
        .map(|(_, coefficients)| *coefficients)
}

/// Polynomial fits measured on each supported board, constant term first.
const BUILTIN_MODELS: &[(&str, &[f64])] = &[
    (
        "rbp5b1.0-64",
        &[
            8.71648242592654,
            -186.5634686,
            2676.08397456,
            -18839.18873015,
            75110.3949109,
            -180373.73562042,
            267380.23597063,
            -240403.81341249,
            120509.01405772,
            -25877.41360901,
        ],
    ),
    (
        "rbp4001.0-64",
        &[
            2.6630056198236938,
            0.82814554,
            -112.17687631,
            1753.99173239,
            -10992.65341181,
            35988.45610911,
            -66254.20051068,
            69071.21138567,
            -38089.87171735,
            8638.45610698,
        ],
    ),
    (
        "rbp4b1.2",
        &[
            2.58542069543335,
            12.335449,
            -248.010554,
            2379.83232,
            -11962.419149,
            34444.268647,
            -58455.266502,
            57698.685016,
            -30618.557703,
            6752.265368,
        ],
    ),
    (
        "rbp4b1.2-64",
        &[
            3.039940056604439,
            -3.074225,
            47.753114,
            -271.974551,
            879.966571,
            -1437.466442,
            1133.325791,
            -345.134888,
        ],
    ),
    (
        "rbp4b1.1",
        &[
            2.5718068562852086,
            2.794871,
            -58.954883,
            838.875781,
            -5371.428686,
            18168.842874,
            -34369.583554,
            36585.681749,
            -20501.30764,
            4708.33149,
        ],
    ),
    (
        "rbp4b1.1-64",
        &[
            3.405685008777926,
            -11.834416,
            137.312822,
            -775.891511,
            2563.399671,
            -4783.024354,
            4974.960753,
            -2691.923074,
            590.355251,
        ],
    ),
    (
        "rbp3b+1.3",
        &[
            2.484396997449118,
            2.933542,
            -150.400134,
            2278.69031,
            -15008.559279,
            51537.315529,
            -98756.887779,
            106478.929766,
            -60432.910139,
            14053.677709,
        ],
    ),
    (
        "rbp3b1.2",
        &[
            1.524116907651687,
            10.053851,
            -234.18693,
            2516.322119,
            -13733.555536,
            41739.918887,
            -73342.794259,
            74062.644914,
            -39909.425362,
            8894.110508,
        ],
    ),
    (
        "rbp2b1.1",
        &[
            1.3596870187778196,
            5.13509,
            -103.296366,
            1027.169748,
            -5323.639404,
            15592.036875,
            -26675.601585,
            26412.963366,
            -14023.471809,
            3089.7862,
        ],
    ),
    (
        "rbp1b+1.2",
        &[
            1.2513999338064061,
            1.857815,
            -18.109537,
            101.531231,
            -346.386617,
            749.560352,
            -1028.802514,
            863.877618,
            -403.270951,
            79.925932,
        ],
    ),
    (
        "rbp1b2",
        &[
            2.826093843916506,
            3.539891,
            -43.586963,
            282.48856,
            -1074.116844,
            2537.679443,
            -3761.784242,
            3391.045904,
            -1692.84087,
            357.800968,
        ],
    ),
    (
        "rbpzw1.1",
        &[
            0.8551610676717238,
            7.207151,
            -135.517893,
            1254.808001,
            -6329.450524,
            18502.371291,
            -32098.028941,
            32554.67989,
            -17824.350159,
            4069.178175,
        ],
    ),
    (
        "asustbs",
        &[
            3.9146162374630173,
            -19.85430796,
            141.7306532,
            -298.12713091,
            -1115.76983141,
            8238.27573132,
            -20976.13898406,
            27132.90930519,
            -17741.01303757,
            4640.69530931,
        ],
    ),
];

#[cfg(test)]
mod tests {
    use super::*;

    // The two tables are joined by a plain string, so nothing but this test
    // stops a new board from naming a model that was never added.
    #[test]
    fn every_board_name_has_a_built_in_model() {
        for &(device_tree_name, name_32, name_64) in BOARD_NAMES {
            for name in [name_32, name_64] {
                assert!(
                    builtin_coefficients(name).is_some(),
                    "{device_tree_name} maps to {name}, which has no model"
                );
            }
        }
    }
}