Расширить графики и общие протоколы CAN и Periph28335 #4
@@ -16,7 +16,8 @@ enum set_plot_operation {
|
|||||||
SET_PLOT_VALUE = 3, /* fraction,low,high,inverted -> value (not clamped) */
|
SET_PLOT_VALUE = 3, /* fraction,low,high,inverted -> value (not clamped) */
|
||||||
SET_PLOT_DRAG = 4, /* initial,deltaPixels,length,low,high,inverted -> clamped value */
|
SET_PLOT_DRAG = 4, /* initial,deltaPixels,length,low,high,inverted -> clamped value */
|
||||||
SET_PLOT_TICK_STEP = 5, /* range,lengthPixels -> nice step */
|
SET_PLOT_TICK_STEP = 5, /* range,lengthPixels -> nice step */
|
||||||
SET_PLOT_DELTA = 6 /* A,B,multiplier -> (B-A)*multiplier */
|
SET_PLOT_DELTA = 6, /* A,B,multiplier -> (B-A)*multiplier */
|
||||||
|
SET_PLOT_DB_DELTA = 7 /* A,B -> 20*log10(abs(B/A)); zero is invalid */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Version of this plot ABI, independently of the transport ABI. */
|
/** Version of this plot ABI, independently of the transport ABI. */
|
||||||
|
|||||||
@@ -30,3 +30,5 @@ data class PlotBounds(val left: Double, val right: Double, val bottom: Double, v
|
|||||||
|
|
||||||
fun plotTickStep(range: Double, pixels: Double): Double = NativePlot.call(5, range, pixels)[0]
|
fun plotTickStep(range: Double, pixels: Double): Double = NativePlot.call(5, range, pixels)[0]
|
||||||
fun plotDelta(a: Double, b: Double, multiplier: Double = 1.0): Double = NativePlot.call(6, a, b, multiplier)[0]
|
fun plotDelta(a: Double, b: Double, multiplier: Double = 1.0): Double = NativePlot.call(6, a, b, multiplier)[0]
|
||||||
|
fun plotDbDelta(a: Double, b: Double): Double? =
|
||||||
|
runCatching { NativePlot.call(7, a, b)[0] }.getOrNull()
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package ru.setcorp.setprotocol.trends
|
package ru.setcorp.setprotocol.trends
|
||||||
|
|
||||||
/** Normalized top-left viewport; independent of pixels, units, toolkit and samples. */
|
/** Normalized top-left viewport; independent of pixels, units, toolkit and samples. */
|
||||||
data class PlotViewport(val x: Double = 0.0, val y: Double = 0.0, val width: Double = 1.0, val height: Double = 1.0) {
|
data class PlotViewport(val x: Double = 0.0, val y: Double = 0.0, val width: Double = 1.0, val height: Double = 1.0,
|
||||||
|
val locked: Boolean = false) {
|
||||||
fun transform(zoomX: Double = 1.0, zoomY: Double = 1.0, panX: Double = 0.0, panY: Double = 0.0,
|
fun transform(zoomX: Double = 1.0, zoomY: Double = 1.0, panX: Double = 0.0, panY: Double = 0.0,
|
||||||
focusX: Double = 0.5, focusY: Double = 0.5): PlotViewport {
|
focusX: Double = 0.5, focusY: Double = 0.5): PlotViewport {
|
||||||
|
if (locked) return this
|
||||||
val result = NativePlot.call(0, x, y, width, height, zoomX, zoomY, panX, panY, focusX, focusY)
|
val result = NativePlot.call(0, x, y, width, height, zoomX, zoomY, panX, panY, focusX, focusY)
|
||||||
return PlotViewport(result[0], result[1], result[2], result[3])
|
return PlotViewport(result[0], result[1], result[2], result[3], locked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,4 +37,12 @@ class PlotContractTest {
|
|||||||
val crossed = markers.move(TrendMarker.A, 1900.0).move(TrendMarker.B, 1100.0)
|
val crossed = markers.move(TrendMarker.A, 1900.0).move(TrendMarker.B, 1100.0)
|
||||||
assertEquals(-800.0, plotDelta(crossed.a!!, crossed.b!!), 0.0)
|
assertEquals(-800.0, plotDelta(crossed.a!!, crossed.b!!), 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun lockedViewportIgnoresZoomAndPanAndDbUsesAmplitudeRatio() {
|
||||||
|
val locked = PlotViewport(locked = true)
|
||||||
|
assertEquals(locked, locked.transform(zoomX = 2.0, panY = 0.2))
|
||||||
|
assertEquals(20.0, plotDbDelta(1.0, 10.0)!!, 1e-10)
|
||||||
|
assertEquals(-20.0, plotDbDelta(10.0, 1.0)!!, 1e-10)
|
||||||
|
assertNull(plotDbDelta(0.0, 1.0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ static int finite_values(const double *v, size_t n) {
|
|||||||
uint32_t set_plot_abi_version(void) { return 1U; }
|
uint32_t set_plot_abi_version(void) { return 1U; }
|
||||||
|
|
||||||
size_t set_plot_eval(uint32_t op, const double *v, size_t n, double *out, size_t cap) {
|
size_t set_plot_eval(uint32_t op, const double *v, size_t n, double *out, size_t cap) {
|
||||||
static const size_t sizes[] = {10, 3, 4, 4, 6, 2, 3};
|
static const size_t sizes[] = {10, 3, 4, 4, 6, 2, 3, 2};
|
||||||
double span, fraction;
|
double span, fraction;
|
||||||
if (op > SET_PLOT_DELTA || !v || !out || n != sizes[op] ||
|
if (op > SET_PLOT_DB_DELTA || !v || !out || n != sizes[op] ||
|
||||||
cap < (op == SET_PLOT_TRANSFORM ? 4U : 1U)) return 0;
|
cap < (op == SET_PLOT_TRANSFORM ? 4U : 1U)) return 0;
|
||||||
if (op == SET_PLOT_TRANSFORM) {
|
if (op == SET_PLOT_TRANSFORM) {
|
||||||
double w, h, fx, fy;
|
double w, h, fx, fy;
|
||||||
@@ -65,6 +65,10 @@ size_t set_plot_eval(uint32_t op, const double *v, size_t n, double *out, size_t
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SET_PLOT_DELTA: out[0] = (v[1] - v[0]) * v[2]; break;
|
case SET_PLOT_DELTA: out[0] = (v[1] - v[0]) * v[2]; break;
|
||||||
|
case SET_PLOT_DB_DELTA:
|
||||||
|
if (v[0] == 0 || v[1] == 0) return 0;
|
||||||
|
out[0] = 20 * log10(fabs(v[1] / v[0]));
|
||||||
|
break;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
return isfinite(out[0]) ? 1 : 0;
|
return isfinite(out[0]) ? 1 : 0;
|
||||||
|
|||||||
3
c/set-protocol/tests/fixtures/plot-v1.json
vendored
3
c/set-protocol/tests/fixtures/plot-v1.json
vendored
@@ -20,6 +20,9 @@
|
|||||||
{"name":"ticks","op":5,"input":[100,800],"output":[20]},
|
{"name":"ticks","op":5,"input":[100,800],"output":[20]},
|
||||||
{"name":"negative_delta","op":6,"input":[10,0,1],"output":[-10]},
|
{"name":"negative_delta","op":6,"input":[10,0,1],"output":[-10]},
|
||||||
{"name":"milliseconds","op":6,"input":[0,0.01,1000],"output":[10]},
|
{"name":"milliseconds","op":6,"input":[0,0.01,1000],"output":[10]},
|
||||||
|
{"name":"db_gain","op":7,"input":[1,10],"output":[20]},
|
||||||
|
{"name":"db_attenuation","op":7,"input":[10,1],"output":[-20]},
|
||||||
|
{"name":"db_zero_reference","op":7,"input":[0,1],"output":null},
|
||||||
{"name":"zero_range","op":2,"input":[1,1,1,0],"output":null},
|
{"name":"zero_range","op":2,"input":[1,1,1,0],"output":null},
|
||||||
{"name":"zero_pixels","op":4,"input":[0,1,0,0,1,0],"output":null},
|
{"name":"zero_pixels","op":4,"input":[0,1,0,0,1,0],"output":null},
|
||||||
{"name":"bad_viewport","op":0,"input":[0,0,0,1,2,1,0,0,0.5,0.5],"output":null}
|
{"name":"bad_viewport","op":0,"input":[0,0,0,1,2,1,0,0,0.5,0.5],"output":null}
|
||||||
|
|||||||
@@ -61,6 +61,12 @@ class PlotMath:
|
|||||||
def delta(self, a: float, b: float, multiplier: float = 1) -> float:
|
def delta(self, a: float, b: float, multiplier: float = 1) -> float:
|
||||||
return self.call(6, a, b, multiplier)[0]
|
return self.call(6, a, b, multiplier)[0]
|
||||||
|
|
||||||
|
def db_delta(self, a: float, b: float) -> Optional[float]:
|
||||||
|
try:
|
||||||
|
return self.call(7, a, b)[0]
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Viewport:
|
class Viewport:
|
||||||
@@ -68,12 +74,15 @@ class Viewport:
|
|||||||
y: float = 0.0
|
y: float = 0.0
|
||||||
width: float = 1.0
|
width: float = 1.0
|
||||||
height: float = 1.0
|
height: float = 1.0
|
||||||
|
locked: bool = False
|
||||||
|
|
||||||
def transform(self, core: PlotMath, zoom_x: float = 1, zoom_y: float = 1,
|
def transform(self, core: PlotMath, zoom_x: float = 1, zoom_y: float = 1,
|
||||||
pan_x: float = 0, pan_y: float = 0,
|
pan_x: float = 0, pan_y: float = 0,
|
||||||
focus_x: float = 0.5, focus_y: float = 0.5) -> "Viewport":
|
focus_x: float = 0.5, focus_y: float = 0.5) -> "Viewport":
|
||||||
|
if self.locked:
|
||||||
|
return self
|
||||||
return Viewport(*core.call(0, self.x, self.y, self.width, self.height,
|
return Viewport(*core.call(0, self.x, self.y, self.width, self.height,
|
||||||
zoom_x, zoom_y, pan_x, pan_y, focus_x, focus_y))
|
zoom_x, zoom_y, pan_x, pan_y, focus_x, focus_y), locked=self.locked)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ class PlotTests(unittest.TestCase):
|
|||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
self.core.pinch_axis(value, 10, 8)
|
self.core.pinch_axis(value, 10, 8)
|
||||||
|
|
||||||
|
def test_locked_viewport_and_decibel_delta(self):
|
||||||
|
locked = Viewport(locked=True)
|
||||||
|
self.assertEqual(locked, locked.transform(self.core, zoom_x=2, pan_y=.2))
|
||||||
|
self.assertAlmostEqual(20, self.core.db_delta(1, 10))
|
||||||
|
self.assertAlmostEqual(-20, self.core.db_delta(10, 1))
|
||||||
|
self.assertIsNone(self.core.db_delta(0, 1))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user