Fix binary parsers for UTF-16 headers and AC analysis
- Handle UTF-16 LE encoded .raw file headers (Windows/Wine output) - Fix mixed-precision transient data: float64 time + float32 signals - Fix AC analysis: all variables stored as complex128, not mixed - Fix schematic parser losing components at SYMBOL boundaries - Use proper atan2 for phase calculation, report magnitude in dB
This commit is contained in:
parent
50953a4dea
commit
a77874c972
@ -172,28 +172,14 @@ def _parse_binary_data(
|
|||||||
- Complex AC: frequency (float64) + other vars as (float64, float64) pairs
|
- Complex AC: frequency (float64) + other vars as (float64, float64) pairs
|
||||||
"""
|
"""
|
||||||
if is_complex:
|
if is_complex:
|
||||||
# Complex data (AC analysis): freq as double + complex values
|
# Complex data (AC analysis): ALL variables stored as complex128
|
||||||
# freq (8 bytes) + (n_vars-1) * (real + imag) = 8 + (n-1)*16 bytes per point
|
# Each value is (real float64, imag float64) = 16 bytes
|
||||||
bytes_per_point = 8 + (n_vars - 1) * 16
|
bytes_per_point = n_vars * 16
|
||||||
expected_bytes = bytes_per_point * points
|
actual_points = len(data) // bytes_per_point
|
||||||
|
|
||||||
if len(data) >= expected_bytes:
|
# Read as flat complex128 array and reshape
|
||||||
result = np.zeros((n_vars, points), dtype=np.complex128)
|
flat = np.frombuffer(data[:actual_points * bytes_per_point], dtype=np.complex128)
|
||||||
offset = 0
|
return flat.reshape(actual_points, n_vars).T.copy()
|
||||||
|
|
||||||
for p in range(points):
|
|
||||||
# Frequency as double (real)
|
|
||||||
result[0, p] = struct.unpack("<d", data[offset:offset + 8])[0]
|
|
||||||
offset += 8
|
|
||||||
|
|
||||||
# Rest are complex (real, imag) pairs
|
|
||||||
for v in range(1, n_vars):
|
|
||||||
real = struct.unpack("<d", data[offset:offset + 8])[0]
|
|
||||||
imag = struct.unpack("<d", data[offset + 8:offset + 16])[0]
|
|
||||||
result[v, p] = complex(real, imag)
|
|
||||||
offset += 16
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Real data - detect format from data size
|
# Real data - detect format from data size
|
||||||
# Mixed format: time (8 bytes) + other vars (4 bytes each)
|
# Mixed format: time (8 bytes) + other vars (4 bytes each)
|
||||||
|
|||||||
@ -10,6 +10,7 @@ This server provides tools for:
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
@ -181,9 +182,9 @@ def get_waveform(
|
|||||||
|
|
||||||
if x_axis is not None:
|
if x_axis is not None:
|
||||||
sampled = x_axis[::step]
|
sampled = x_axis[::step]
|
||||||
# Convert complex to magnitude for frequency domain
|
# For frequency domain, take real part (imag is 0)
|
||||||
if sampled.dtype == complex:
|
if np.iscomplexobj(sampled):
|
||||||
result["x_axis_data"] = [abs(x) for x in sampled]
|
result["x_axis_data"] = sampled.real.tolist()
|
||||||
else:
|
else:
|
||||||
result["x_axis_data"] = sampled.tolist()
|
result["x_axis_data"] = sampled.tolist()
|
||||||
result["returned_points"] = len(result["x_axis_data"])
|
result["returned_points"] = len(result["x_axis_data"])
|
||||||
@ -194,15 +195,17 @@ def get_waveform(
|
|||||||
if data is not None:
|
if data is not None:
|
||||||
sampled = data[::step]
|
sampled = data[::step]
|
||||||
# Handle complex data (AC analysis)
|
# Handle complex data (AC analysis)
|
||||||
if sampled.dtype == complex:
|
if np.iscomplexobj(sampled):
|
||||||
|
import math
|
||||||
result["signals"][name] = {
|
result["signals"][name] = {
|
||||||
"magnitude": [abs(x) for x in sampled],
|
"magnitude_db": [
|
||||||
"phase_degrees": [
|
20 * math.log10(abs(x)) if abs(x) > 0 else -200
|
||||||
(180 / 3.14159) * (x.imag / x.real if x.real != 0 else 0)
|
for x in sampled
|
||||||
|
],
|
||||||
|
"phase_degrees": [
|
||||||
|
math.degrees(math.atan2(x.imag, x.real))
|
||||||
for x in sampled
|
for x in sampled
|
||||||
],
|
],
|
||||||
"real": [x.real for x in sampled],
|
|
||||||
"imag": [x.imag for x in sampled],
|
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
result["signals"][name] = {"values": sampled.tolist()}
|
result["signals"][name] = {"values": sampled.tolist()}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user