from dataclasses import dataclass from functools import cached_property import math from scipy import optimize from apc_perf import RPM, ApcPerfdata VOLT_DUTY_MAX = 0.97 @dataclass(frozen=True) class MotorOppoint: voltage: float current: float rpm: float torque: float @cached_property def rot_speed(self) -> float: return self.rpm * RPM @property def power_in(self) -> float: return self.voltage * self.current @property def power_out(self) -> float: return self.torque * self.rot_speed @property def efficiency(self) -> float: return self.power_out/self.power_in @dataclass(frozen=True) class MotorData: mot_kv: float mot_ir: float mot_idle_i: float @cached_property def mot_kv_si(self) -> float: return self.mot_kv * RPM @property def effective_ir(self) -> float: return self.mot_ir*1.5 return self.mot_ir def mot_full_pizda(self, rpm: float, voltage: float) -> MotorOppoint: rotspd = rpm * RPM voltage = VOLT_DUTY_MAX * voltage u_active = (rotspd / self.mot_kv_si) u_heat = voltage - u_active current = u_heat / self.effective_ir torq = (current-self.mot_idle_i) / self.mot_kv_si return MotorOppoint( voltage = voltage, current = current, rpm = rpm, torque = torq, ) def op_full_pizda(mot: MotorData, prop: ApcPerfdata, speed: float, volt: float): def f(rpm): return prop.get_op_interp(rpm, speed).torque - mot.mot_full_pizda(rpm, volt).torque opt = optimize.root_scalar(f, bracket = prop.rpm_range_at_speed(speed)) # print(opt) rpm = opt.root p_op = prop.get_op_interp(rpm, speed) m_op = mot.mot_full_pizda(rpm, volt) # print(p_op) # print(m_op) return p_op, m_op