70 constexpr float COL1_WIDTH = 470.0F;
72 const auto now = std::chrono::system_clock::now();
73 ImGui::SetNextItemWidth(COL1_WIDTH);
74 if (
FileWriter::guiConfig(fmt::format(
".obs,.rnx,.{:%y}O", now).c_str(), {
".obs",
".rnx", fmt::format(
".{:%y}O", now) }, size_t(
id),
nameId()))
80 if (ImGui::CollapsingHeader(fmt::format(
"General##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
82 if (ImGui::BeginTable(fmt::format(
"Table1##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
84 ImGui::TableNextColumn();
85 ImGui::BeginDisabled();
86 ImGui::SetNextItemWidth(COL1_WIDTH);
87 if (ImGui::BeginCombo(fmt::format(
"##Version {}",
size_t(
id)).c_str(), fmt::format(
"{}",
_header.version).c_str()))
91 const bool is_selected =
_header.version == version;
92 if (ImGui::Selectable(fmt::format(
"{}##Version {}", version,
size_t(
id)).c_str(), is_selected))
97 if (is_selected) { ImGui::SetItemDefaultFocus(); }
101 ImGui::EndDisabled();
102 ImGui::TableNextColumn();
103 ImGui::TextUnformatted(
"Version");
105 ImGui::TableNextColumn();
106 ImGui::SetNextItemWidth(COL1_WIDTH);
111 ImGui::TableNextColumn();
112 ImGui::TextUnformatted(
"Run By");
114 ImGui::TableNextColumn();
115 ImGui::SetNextItemWidth(COL1_WIDTH);
120 ImGui::TableNextColumn();
121 ImGui::TextUnformatted(
"Observer");
123 ImGui::TableNextColumn();
124 ImGui::SetNextItemWidth(COL1_WIDTH);
129 ImGui::TableNextColumn();
130 ImGui::TextUnformatted(
"Agency");
136 if (ImGui::CollapsingHeader(fmt::format(
"Comments##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
138 std::vector<size_t> commentsToRemove;
139 if (ImGui::BeginTable(fmt::format(
"Table2##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
141 for (
size_t i = 0; i <
_header.comments.size(); i++)
143 ImGui::TableNextRow();
144 ImGui::TableNextColumn();
145 auto& comment =
_header.comments.at(i);
146 float startPos = ImGui::GetCursorPosX();
147 if (ImGui::Button(fmt::format(
"X##Remove comment {} {}", i,
size_t(
id)).c_str()))
149 commentsToRemove.push_back(i);
152 ImGui::SetNextItemWidth(COL1_WIDTH - (ImGui::GetCursorPosX() - startPos));
153 if (
ImGui::InputTextL(fmt::format(
"##Comment {} {}", i,
size_t(
id)).c_str(), &comment, 60))
160 for (
const size_t& idx : commentsToRemove)
162 _header.comments.erase(std::next(
_header.comments.begin(),
static_cast<int>(idx)));
165 if (ImGui::Button(fmt::format(
"Add##Comment {}",
size_t(
id)).c_str()))
167 _header.comments.emplace_back();
172 if (ImGui::CollapsingHeader(fmt::format(
"Marker##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
174 if (ImGui::BeginTable(fmt::format(
"Table3##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
176 ImGui::TableNextColumn();
177 ImGui::SetNextItemWidth(COL1_WIDTH);
183 ImGui::TableNextColumn();
184 ImGui::TextUnformatted(
"Type");
186 ImGui::TableNextColumn();
187 ImGui::SetNextItemWidth(COL1_WIDTH);
193 ImGui::TableNextColumn();
194 ImGui::TextUnformatted(
"User Type");
196 ImGui::Dummy(ImVec2(ImGui::GetContentRegionAvail().x, 0.0F));
198 ImGui::TableNextColumn();
199 ImGui::SetNextItemWidth(COL1_WIDTH);
204 ImGui::TableNextColumn();
205 ImGui::TextUnformatted(
"Name");
207 ImGui::TableNextColumn();
208 ImGui::SetNextItemWidth(COL1_WIDTH);
213 ImGui::TableNextColumn();
214 ImGui::TextUnformatted(
"Number");
220 if (ImGui::CollapsingHeader(fmt::format(
"Receiver##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
222 if (ImGui::BeginTable(fmt::format(
"Table4##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
224 ImGui::TableNextColumn();
225 ImGui::SetNextItemWidth(COL1_WIDTH);
230 ImGui::TableNextColumn();
231 ImGui::TextUnformatted(
"Number");
233 ImGui::TableNextColumn();
234 ImGui::SetNextItemWidth(COL1_WIDTH);
239 ImGui::TableNextColumn();
240 ImGui::TextUnformatted(
"Type");
242 ImGui::TableNextColumn();
243 ImGui::SetNextItemWidth(COL1_WIDTH);
248 ImGui::TableNextColumn();
249 ImGui::TextUnformatted(
"Version");
255 if (ImGui::CollapsingHeader(fmt::format(
"Antenna##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
257 if (ImGui::BeginTable(fmt::format(
"Table5##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
259 ImGui::TableNextColumn();
260 ImGui::SetNextItemWidth(COL1_WIDTH);
265 ImGui::TableNextColumn();
266 ImGui::TextUnformatted(
"Number");
268 ImGui::TableNextColumn();
269 ImGui::SetNextItemWidth(COL1_WIDTH);
274 ImGui::TableNextColumn();
275 ImGui::TextUnformatted(
"Type");
277 ImGui::TableNextColumn();
278 float startPos = ImGui::GetCursorPosX();
279 if (ImGui::Checkbox(fmt::format(
"##ApproxEnabled {}",
size_t(
id)).c_str(), &
_header.approxPositionEnabled))
284 ImGui::SetNextItemWidth(COL1_WIDTH - (ImGui::GetCursorPosX() - startPos));
285 if (!
_header.approxPositionEnabled) { ImGui::BeginDisabled(); }
286 if (
ImGui::InputDouble3L(fmt::format(
"##Approx position XYZ {}",
size_t(
id)).c_str(),
_header.approxPositionXYZ.data(), -99999999.9999, 99999999.9999,
"%.4f m"))
290 if (!
_header.approxPositionEnabled) { ImGui::EndDisabled(); }
291 ImGui::TableNextColumn();
292 ImGui::TextUnformatted(
"Approx position XYZ");
295 "System: ITRS recommended\n"
296 "Optional for moving platforms");
298 ImGui::TableNextColumn();
299 ImGui::SetNextItemWidth(COL1_WIDTH);
300 if (
ImGui::InputDouble3L(fmt::format(
"##Antenna delta HEN {}",
size_t(
id)).c_str(),
_header.antennaDeltaHeightEastNorth.data(), -99999999.9999, 99999999.9999,
"%.4f m"))
304 ImGui::TableNextColumn();
305 ImGui::TextUnformatted(
"Delta HEN");
421 std::string pathTmp =
getFilepath().string() +
".tmp";
423 auto fsTmp = std::ofstream(pathTmp, std::ios_base::trunc | std::ios_base::binary);
424 if (!fsTmp.good()) {
LOG_CRITICAL(
"{}: Could not create temporary file: {}",
nameId(), pathTmp); }
426 auto fsOld = std::ifstream(
getFilepath(), std::ios_base::in);
429 fsTmp <<
_header.generateHeader();
432 bool dataRecords =
false;
436 std::getline(fsOld, line);
441 if (oldTimeSys ==
_header.timeSys)
444 fsTmp << line <<
'\n';
445 fsTmp << fsOld.rdbuf();
449 LOG_DATA(
"{}: Read epoch [{}][{}][{}][{}][{}][{}] [{}]",
nameId(), line.substr(2, 4), line.substr(7, 2), line.substr(10, 2),
450 line.substr(13, 2), line.substr(16, 2), line.substr(18, 11), line.substr(29 + 3, 3));
451 auto epochTime =
InsTime{
static_cast<uint16_t
>(std::stoi(line.substr(2, 4))),
452 static_cast<uint16_t
>(std::stoi(line.substr(7, 2))),
453 static_cast<uint16_t
>(std::stoi(line.substr(10, 2))),
454 static_cast<uint16_t
>(std::stoi(line.substr(13, 2))),
455 static_cast<uint16_t
>(std::stoi(line.substr(16, 2))),
456 std::stold(line.substr(18, 11)),
458 fsTmp <<
_header.epochRecordLine(epochTime, std::stoul(line.substr(29 + 3, 3)));
460 else if (dataRecords)
463 if (!line.empty()) { fsTmp <<
'\n'; }
475 auto obs = std::static_pointer_cast<const GnssObs>(queue.
extract_front());
478 _header.interval = std::min(
_header.interval, std::round(
static_cast<double>((obs->insTime -
_header.timeLastObs).count()) * 1e3) / 1e3);
479 if (
_header.timeFirstObs.empty()) {
_header.timeFirstObs = obs->insTime; }
480 _header.timeLastObs = obs->insTime;
482 std::set<SatId> satellites;
483 bool satelliteSystemDescriptionChanged =
false;
484 for (
const auto& sig : obs->data)
486 auto satId = sig.satSigId.toSatId();
487 satellites.insert(satId);
488 _header.satellites.insert(satId);
489 _header.satSys |= satId.satSys;
496 if (satelliteSystemDescriptionChanged)
505 for (
const auto& satId : satellites)
507 _filestream << fmt::format(
"{0}{1:02d}", satId.satSys.toChar(),
508 satId.satSys ==
SBAS && satId.satNum > 100 ? satId.satNum - 100 : satId.satNum);
509 const auto& obsDescriptions =
_header.systemObsTypes.at(satId.satSys);
510 for (
size_t i = 0; i < obsDescriptions.size(); i++)
512 const auto& obsDesc = obsDescriptions.at(i);
514 auto signal = std::ranges::find_if(obs->data, [&obsDesc, &satId](
const auto& sig) {
515 return sig.satSigId == SatSigId{ obsDesc.code, satId.satNum };
518 bool obsWritten =
false;
519 if (signal != obs->data.end())
521 switch (obsDesc.type)
524 if (signal->pseudorange && signal->pseudorange->value < 100'000'000.0)
527 "obs"_a = signal->pseudorange->value,
528 "SSI"_a = signal->pseudorange->SSI == 0 ?
" " : std::to_string(signal->pseudorange->SSI));
533 if (signal->carrierPhase && signal->carrierPhase->value < 1'000'000'000.0)
535 _filestream << fmt::format(
"{obs:14.3f}{LLI:1}{SSI:1}",
536 "obs"_a = signal->carrierPhase->value,
537 "LLI"_a = signal->carrierPhase->LLI == 0 ?
" " : std::to_string(signal->carrierPhase->LLI),
538 "SSI"_a = signal->carrierPhase->SSI == 0 ?
" " : std::to_string(signal->carrierPhase->SSI));
545 _filestream << fmt::format(
"{:14.3f} ", signal->doppler.value());
552 _filestream << fmt::format(
"{:14.3f} ", signal->CN0.value());
566 if (i == obsDescriptions.size() - 1)