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
|
||||
"""
|
||||
if is_complex:
|
||||
# Complex data (AC analysis): freq as double + complex values
|
||||
# freq (8 bytes) + (n_vars-1) * (real + imag) = 8 + (n-1)*16 bytes per point
|
||||
bytes_per_point = 8 + (n_vars - 1) * 16
|
||||
expected_bytes = bytes_per_point * points
|
||||
# Complex data (AC analysis): ALL variables stored as complex128
|
||||
# Each value is (real float64, imag float64) = 16 bytes
|
||||
bytes_per_point = n_vars * 16
|
||||
actual_points = len(data) // bytes_per_point
|
||||
|
||||
if len(data) >= expected_bytes:
|
||||
result = np.zeros((n_vars, points), dtype=np.complex128)
|
||||
offset = 0
|
||||
|
||||
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
|
||||
# Read as flat complex128 array and reshape
|
||||
flat = np.frombuffer(data[:actual_points * bytes_per_point], dtype=np.complex128)
|
||||
return flat.reshape(actual_points, n_vars).T.copy()
|
||||
|
||||
# Real data - detect format from data size
|
||||
# Mixed format: time (8 bytes) + other vars (4 bytes each)
|
||||
|
||||
@ -10,6 +10,7 @@ This server provides tools for:
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from . import __version__
|
||||
@ -181,9 +182,9 @@ def get_waveform(
|
||||
|
||||
if x_axis is not None:
|
||||
sampled = x_axis[::step]
|
||||
# Convert complex to magnitude for frequency domain
|
||||
if sampled.dtype == complex:
|
||||
result["x_axis_data"] = [abs(x) for x in sampled]
|
||||
# For frequency domain, take real part (imag is 0)
|
||||
if np.iscomplexobj(sampled):
|
||||
result["x_axis_data"] = sampled.real.tolist()
|
||||
else:
|
||||
result["x_axis_data"] = sampled.tolist()
|
||||
result["returned_points"] = len(result["x_axis_data"])
|
||||
@ -194,15 +195,17 @@ def get_waveform(
|
||||
if data is not None:
|
||||
sampled = data[::step]
|
||||
# Handle complex data (AC analysis)
|
||||
if sampled.dtype == complex:
|
||||
if np.iscomplexobj(sampled):
|
||||
import math
|
||||
result["signals"][name] = {
|
||||
"magnitude": [abs(x) for x in sampled],
|
||||
"phase_degrees": [
|
||||
(180 / 3.14159) * (x.imag / x.real if x.real != 0 else 0)
|
||||
"magnitude_db": [
|
||||
20 * math.log10(abs(x)) if abs(x) > 0 else -200
|
||||
for x in sampled
|
||||
],
|
||||
"phase_degrees": [
|
||||
math.degrees(math.atan2(x.imag, x.real))
|
||||
for x in sampled
|
||||
],
|
||||
"real": [x.real for x in sampled],
|
||||
"imag": [x.imag for x in sampled],
|
||||
}
|
||||
else:
|
||||
result["signals"][name] = {"values": sampled.tolist()}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user