Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion amplifier/src/amplifier_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,10 @@ void AmplifierEngine::deserialize(const nlohmann::json &j) {
if (!j.contains("enable_nonlinear") &&
(j.contains("oip2_dBm") || j.contains("oip3_dBm") || j.contains("p1db_dBm")))
m_nonlinear.setEnabled(true);
m_sparam_mode = j.value("sparam_mode", false);
m_sparam_filepath = j.value("sparam_filepath", "");
if (!m_sparam_filepath.empty())
m_sparam_data.load(m_sparam_filepath);
m_sparam_mode = j.value("sparam_mode", false) && m_sparam_data.loaded();
m_sparam_fwd_idx = j.value("sparam_fwd_idx", 0);
m_dirty = true;
}
Expand Down
8 changes: 4 additions & 4 deletions app/src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,10 @@ void RfSimulatorApp::update_dsp() {

for (auto *node : m_view_manager.nodes()) {
if (node) {
node->view_enabled =
std::find_if(probed_sources.begin(), probed_sources.end(),
[node](const SignalSource &ps) { return ps.node == node; }) !=
probed_sources.end();
node->view_enabled = std::find_if(probed_sources.begin(), probed_sources.end(),
[node](const SignalSource &ps) {
return ps.node == node;
}) != probed_sources.end();
}
}

Expand Down
4 changes: 3 additions & 1 deletion attenuator/src/attenuator_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,10 @@ nlohmann::json AttenuatorEngine::serialize() const {
void AttenuatorEngine::deserialize(const nlohmann::json &j) {
m_atten_dB =
j.contains("atten_dB") ? j["atten_dB"].get<double>() : j.value("attenuation_dB", 0.0);
m_sparam_mode = j.value("sparam_mode", false);
m_sparam_path = j.value("sparam_path", "");
if (!m_sparam_path.empty())
m_sparam.load(m_sparam_path);
m_sparam_mode = j.value("sparam_mode", false) && m_sparam.loaded();
m_dirty = true;
}

Expand Down
4 changes: 3 additions & 1 deletion combiner/src/combiner_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ nlohmann::json CombinerEngine::serialize() const {

void CombinerEngine::deserialize(const nlohmann::json &j) {
m_manual_mode = j.value("manual_mode", true);
m_sparam_mode = j.value("sparam_mode", false);
m_sparam_path = j.value("sparam_path", "");
if (!m_sparam_path.empty())
m_sparam.load(m_sparam_path);
m_sparam_mode = j.value("sparam_mode", false) && m_sparam.loaded();
m_dirty = true;
}

Expand Down
8 changes: 6 additions & 2 deletions equalizer/src/equalizer_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,18 @@ nlohmann::json EqualizerEngine::serialize() const {
{"ref_freq_Hz", m_ref_freq_Hz},
{"slope_dB_per_decade", m_slope_dB_per_decade},
{"sparam_mode", m_sparam_mode},
{"sparam_filepath", m_sparam_filepath}};
{"sparam_filepath", m_sparam_filepath},
{"sparam_fwd_idx", m_sparam_fwd_idx}};
}

void EqualizerEngine::deserialize(const nlohmann::json &j) {
m_ref_gain_dB = j.value("ref_gain_dB", 0.0);
m_ref_freq_Hz = j.value("ref_freq_Hz", 1e9);
m_slope_dB_per_decade = j.value("slope_dB_per_decade", 0.0);
m_sparam_mode = j.value("sparam_mode", false);
m_sparam_filepath = j.value("sparam_filepath", "");
if (!m_sparam_filepath.empty())
m_sparam_data.load(m_sparam_filepath);
m_sparam_mode = j.value("sparam_mode", false) && m_sparam_data.loaded();
m_sparam_fwd_idx = j.value("sparam_fwd_idx", 0);
m_dirty = true;
}
4 changes: 3 additions & 1 deletion ideal_filter/src/ideal_filter_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,10 @@ void IdealFilterEngine::deserialize(const nlohmann::json &j) {
} else if (j.contains("fc_low_Hz")) {
setCutoff_Hz(j["fc_low_Hz"].get<double>());
}
m_sparam_mode = j.value("sparam_mode", false);
m_sparam_filepath = j.value("sparam_filepath", "");
if (!m_sparam_filepath.empty())
m_sparam_data.load(m_sparam_filepath);
m_sparam_mode = j.value("sparam_mode", false) && m_sparam_data.loaded();
m_sparam_fwd_idx = j.value("sparam_fwd_idx", 0);
m_dirty = true;
}
Expand Down
3 changes: 1 addition & 2 deletions tests/test_issue42_multi_output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ static int outputPinFor(NodeGraphEngine &graph, SignalNode *node, int port) {
return -1;
}

TEST_CASE_METHOD(ImGuiFixture,
"Splitter OUT2 routes to Combiner IN1 with outputs[1] (issue #42)",
TEST_CASE_METHOD(ImGuiFixture, "Splitter OUT2 routes to Combiner IN1 with outputs[1] (issue #42)",
"[app][issue42]") {
RfSimulatorApp app;
app.newProject();
Expand Down
74 changes: 74 additions & 0 deletions tests/test_project_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,77 @@ TEST_CASE_METHOD(ImGuiFixture, "Round-trip: default component positions are (0,0
}
std::remove(path.c_str());
}

// ---------------------------------------------------------------------------
// 13 — Issue #44: S-param mode survives save/load for amplifier, ideal filter,
// equalizer, attenuator, and combiner. Previously deserialize() restored
// sparam_mode/sparam_filepath but never reloaded the Touchstone file, so
// a reloaded project silently fell back to ideal/manual mode.
// ---------------------------------------------------------------------------
static std::string sparamFixturePath() {
return std::string(PROJECT_SOURCE_DIR) +
"/component_data/amplifiers/adm-3844psm/ADM-8344PSM_SM_A_25C_De_5V_5V_102mA.s2p";
}

TEST_CASE_METHOD(ImGuiFixture, "Round-trip: S-param mode survives save/load (issue #44)",
"[project_file][sparam]") {
auto path = tempPath();
std::remove(path.c_str());
const std::string s2p = sparamFixturePath();
{
RfSimulatorApp app;
app.newProject();

auto &amp = app.testComponents().add<AmplifierEngine>(10001, app.testGraphEngine());
amp.setSParamFilepath(s2p);
REQUIRE(amp.sparamLoaded());

auto &flt = app.testComponents().add<IdealFilterEngine>(10002, app.testGraphEngine());
flt.setSParamFilepath(s2p);
REQUIRE(flt.sparamLoaded());

auto &eq = app.testComponents().add<EqualizerEngine>(10003, app.testGraphEngine());
eq.setSParamFilepath(s2p);
REQUIRE(eq.sparamLoaded());

auto &atten = app.testComponents().add<AttenuatorEngine>(10004, app.testGraphEngine());
atten.setSParamFile(s2p);
REQUIRE(atten.sParamMode());

auto &comb = app.testComponents().add<CombinerEngine>(10005, app.testGraphEngine());
comb.setSParamFile(s2p);
REQUIRE(comb.sParamMode());

REQUIRE(app.componentCount() == 5);
app.saveProject(path);
}
{
RfSimulatorApp app;
app.loadProject(path);
REQUIRE(app.componentCount() == 5);

auto amps = app.testComponents().byType<AmplifierEngine>();
REQUIRE(amps.size() == 1);
CHECK(amps[0]->sparamMode() == true);
CHECK(amps[0]->sparamLoaded() == true);

auto flts = app.testComponents().byType<IdealFilterEngine>();
REQUIRE(flts.size() == 1);
CHECK(flts[0]->sparamMode() == true);
CHECK(flts[0]->sparamLoaded() == true);

auto eqs = app.testComponents().byType<EqualizerEngine>();
REQUIRE(eqs.size() == 1);
CHECK(eqs[0]->sparamMode() == true);
CHECK(eqs[0]->sparamLoaded() == true);

auto attens = app.testComponents().byType<AttenuatorEngine>();
REQUIRE(attens.size() == 1);
CHECK(attens[0]->sParamMode() == true);

auto combs = app.testComponents().byType<CombinerEngine>();
REQUIRE(combs.size() == 1);
CHECK(combs[0]->sParamMode() == true);
}
std::remove(path.c_str());
}