Skip to content

Commit b1d0827

Browse files
committed
Fix simulation at sample rates that are not even multiple of bitrate
1 parent 58a2104 commit b1d0827

3 files changed

Lines changed: 65 additions & 35 deletions

File tree

scripts/validate_flexray_capture.py

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,39 @@ def summarize_legacy_export(export_path: Path) -> dict[str, object]:
152152
return summary
153153

154154

155-
def find_simulation_device(manager: automation.Manager) -> automation.DeviceDesc:
156-
devices = manager.get_devices(include_simulation_devices=True)
155+
def format_device_type(device: automation.DeviceDesc) -> str:
156+
return getattr(device.device_type, "name", str(device.device_type))
157+
158+
159+
def find_requested_simulation_device(
160+
manager: automation.Manager, requested_device: str, sample_rate: int
161+
) -> automation.DeviceDesc:
162+
devices = [device for device in manager.get_devices(include_simulation_devices=True) if device.is_simulation]
163+
164+
if not devices:
165+
raise RuntimeError("No Logic 2 simulation device was reported by the automation API.")
166+
167+
if requested_device == "auto":
168+
if sample_rate > 100_000_000:
169+
for preferred_type in ("LOGIC_PRO_8", "LOGIC_PRO_16"):
170+
for device in devices:
171+
if format_device_type(device) == preferred_type:
172+
return device
173+
174+
return devices[0]
175+
176+
requested_type = {
177+
"logic8": "LOGIC_8",
178+
"pro8": "LOGIC_PRO_8",
179+
"pro16": "LOGIC_PRO_16",
180+
}[requested_device]
157181

158182
for device in devices:
159-
if device.is_simulation:
183+
if format_device_type(device) == requested_type:
160184
return device
161185

162-
raise RuntimeError("No Logic 2 simulation device was reported by the automation API.")
186+
available = ", ".join(format_device_type(device) for device in devices)
187+
raise RuntimeError(f"Requested simulation device {requested_type} was not available. Found: {available}")
163188

164189

165190
def validate_demo_summary(summary: dict[str, object]) -> None:
@@ -180,7 +205,7 @@ def run_demo_capture(
180205
output_dir: Path,
181206
manual_rerun_capture_path: Path,
182207
) -> tuple[automation.Capture, automation.AnalyzerHandle, str]:
183-
device = find_simulation_device(manager)
208+
device = find_requested_simulation_device(manager, args.simulation_device, args.sample_rate)
184209
capture = manager.start_capture(
185210
device_id=device.device_id,
186211
device_configuration=automation.LogicDeviceConfiguration(
@@ -204,29 +229,19 @@ def run_demo_capture(
204229
print("You can inspect Logic 2 now, and press Run manually during this pause if needed.")
205230
time.sleep(args.demo_pause_seconds)
206231

207-
if args.manual_rerun:
208-
prepared_capture_path = manual_rerun_capture_path
209-
print(f"Analyzer added to the demo capture at {args.bitrate} bit/s.")
210-
print(f"Rerun the capture in the Logic 2 GUI, save it to {prepared_capture_path}, then press Enter here.")
232+
prepared_capture_path = manual_rerun_capture_path
233+
print(f"Analyzer added to the demo capture at {args.bitrate} bit/s.")
234+
print("Logic 2's automation API does not expose a rerun helper for analyzer demo data.")
235+
print(f"Rerun the capture in the Logic 2 GUI, save it to {prepared_capture_path}, then press Enter here.")
236+
try:
211237
input()
238+
except EOFError as exc:
239+
raise RuntimeError(
240+
"Demo mode requires an interactive terminal so you can rerun the capture in Logic 2 and save the resulting .sal file."
241+
) from exc
212242

213-
if prepared_capture_path.exists() is False:
214-
raise FileNotFoundError(f"Manual rerun capture not found: {prepared_capture_path}")
215-
else:
216-
prepared_capture_path = output_dir / "flexray_demo_auto.sal"
217-
try:
218-
capture.save_capture(str(prepared_capture_path))
219-
except automation.errors.InvalidRequestError as exc:
220-
if 'does not exist' not in str(exc):
221-
raise
222-
223-
prepared_capture_path = manual_rerun_capture_path
224-
print("The demo capture was rerun in the GUI during the pause, so the original automation capture no longer exists.")
225-
print(f"Save the current Logic 2 capture to {prepared_capture_path}, then press Enter here.")
226-
input()
227-
228-
if prepared_capture_path.exists() is False:
229-
raise FileNotFoundError(f"Fallback demo capture not found: {prepared_capture_path}") from exc
243+
if prepared_capture_path.exists() is False:
244+
raise FileNotFoundError(f"Manual rerun capture not found: {prepared_capture_path}")
230245

231246
prepared_capture_for_api = create_capture_without_saved_analyzers(
232247
prepared_capture_path,
@@ -238,14 +253,14 @@ def run_demo_capture(
238253
analyzer = add_flexray_analyzer(capture, args)
239254

240255
source_description = (
256+
f"Simulation: {format_device_type(device)} ({device.device_id})\n"
241257
f"Demo capture: {prepared_capture_path}\n"
242258
f"API capture: {prepared_capture_for_api}\n"
243259
f"Sample rate: {args.sample_rate}\n"
244260
f"Bit rate: {args.bitrate}"
245261
)
246262

247-
if args.manual_rerun is False:
248-
source_description += "\nFlow: automated two-pass demo setup"
263+
source_description += "\nFlow: manual rerun after analyzer add"
249264

250265
return capture, analyzer, source_description
251266

@@ -255,12 +270,23 @@ def main() -> int:
255270

256271
parser = argparse.ArgumentParser(description="Validate the FlexRay analyzer against a Logic 2 capture or demo simulation.")
257272
parser.add_argument("--logic2", help="Path to the Logic 2 executable, .app bundle, or AppImage.")
258-
parser.add_argument("--mode", choices=["capture", "demo"], default="capture", help="Load a .sal capture or record from the Logic 2 simulation device.")
273+
parser.add_argument(
274+
"--mode",
275+
choices=["capture", "demo"],
276+
default="capture",
277+
help="Load a .sal capture or use the Logic 2 simulation device, then manually rerun the demo after adding the analyzer.",
278+
)
259279
parser.add_argument("--capture", default=str(root / "assets" / "SP2018_FlexRay.sal"), help="Path to the .sal capture.")
260280
parser.add_argument("--output-dir", default=str(root / "build" / "automation"), help="Directory for exported CSV files.")
261281
parser.add_argument("--analyzers-dir", default=str(root / "build" / "Analyzers"), help="Directory containing libFlexRayAnalyzer.so.")
262282
parser.add_argument("--channel", type=int, default=0, help="Digital channel index for the FlexRay signal.")
263283
parser.add_argument("--bitrate", type=int, default=10_000_000, choices=[2_500_000, 5_000_000, 10_000_000], help="FlexRay bit rate.")
284+
parser.add_argument(
285+
"--simulation-device",
286+
choices=["auto", "logic8", "pro8", "pro16"],
287+
default="auto",
288+
help="Simulation device to use in demo mode. Auto prefers Pro devices when the requested sample rate exceeds 100 MS/s.",
289+
)
264290
parser.add_argument(
265291
"--sample-rate",
266292
type=int,
@@ -279,7 +305,7 @@ def main() -> int:
279305
parser.add_argument(
280306
"--manual-rerun",
281307
action="store_true",
282-
help="For demo mode: pause after adding the analyzer so you can rerun the capture in the GUI, save it, and then export that saved capture.",
308+
help="Deprecated compatibility flag. Demo mode now always requires a manual rerun after adding the analyzer.",
283309
)
284310
parser.add_argument(
285311
"--manual-rerun-capture",
@@ -357,4 +383,8 @@ def main() -> int:
357383

358384

359385
if __name__ == "__main__":
360-
raise SystemExit(main())
386+
try:
387+
raise SystemExit(main())
388+
except Exception as exc:
389+
print(f"Error: {exc}", file=sys.stderr)
390+
raise SystemExit(1)

src/FlexRaySimulationDataGenerator.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ std::vector<U8> BitsToBytes( const std::vector<U8>& bits )
3131
FlexRaySimulationDataGenerator::FlexRaySimulationDataGenerator()
3232
: mSettings( nullptr ),
3333
mSimulationSampleRateHz( 0 ),
34-
mSamplesPerBit( 0 ),
3534
mFrameCounter( 0 )
3635
{
3736
}
@@ -40,7 +39,7 @@ void FlexRaySimulationDataGenerator::Initialize( U32 simulation_sample_rate, Fle
4039
{
4140
mSimulationSampleRateHz = simulation_sample_rate;
4241
mSettings = settings;
43-
mSamplesPerBit = std::max<U32>( 1, mSimulationSampleRateHz / mSettings->mBitRate );
42+
mBitClock.Init( static_cast<double>( mSettings->mBitRate ), mSimulationSampleRateHz );
4443

4544
mSimulationData.SetChannel( mSettings->mInputChannel );
4645
mSimulationData.SetSampleRate( simulation_sample_rate );
@@ -144,7 +143,7 @@ void FlexRaySimulationDataGenerator::OutputBit( U8 wire_bit, U32 bit_count )
144143
{
145144
const U8 physical_bit = mSettings->mInvertInput ? ( wire_bit ^ 0x1 ) : wire_bit;
146145
mSimulationData.TransitionIfNeeded( physical_bit != 0 ? BIT_HIGH : BIT_LOW );
147-
mSimulationData.Advance( mSamplesPerBit * bit_count );
146+
mSimulationData.Advance( mBitClock.AdvanceByTimeS( static_cast<double>( bit_count ) / static_cast<double>( mSettings->mBitRate ) ) );
148147
}
149148

150149
void FlexRaySimulationDataGenerator::OutputExtendedByte( U8 value )

src/FlexRaySimulationDataGenerator.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef FLEXRAY_SIMULATION_DATA_GENERATOR
22
#define FLEXRAY_SIMULATION_DATA_GENERATOR
33

4+
#include <AnalyzerHelpers.h>
45
#include <SimulationChannelDescriptor.h>
56
#include <vector>
67

@@ -22,7 +23,7 @@ class FlexRaySimulationDataGenerator
2223

2324
FlexRayAnalyzerSettings* mSettings;
2425
U32 mSimulationSampleRateHz;
25-
U32 mSamplesPerBit;
26+
ClockGenerator mBitClock;
2627
U64 mFrameCounter;
2728
SimulationChannelDescriptor mSimulationData;
2829
};

0 commit comments

Comments
 (0)