72 constexpr float COL1_WIDTH = 470.0F;
74 const auto now = std::chrono::system_clock::now();
75 ImGui::SetNextItemWidth(COL1_WIDTH);
76 if (
FileWriter::guiConfig(fmt::format(
".obs,.rnx,.{:%y}O", now).c_str(), {
".obs",
".rnx", fmt::format(
".{:%y}O", now) }, size_t(
id),
nameId()))
82 if (ImGui::CollapsingHeader(fmt::format(
"General##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
84 if (ImGui::BeginTable(fmt::format(
"Table1##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
86 ImGui::TableNextColumn();
87 ImGui::BeginDisabled();
88 ImGui::SetNextItemWidth(COL1_WIDTH);
89 if (ImGui::BeginCombo(fmt::format(
"##Version {}",
size_t(
id)).c_str(), fmt::format(
"{}",
_header.version).c_str()))
93 const bool is_selected =
_header.version == version;
94 if (ImGui::Selectable(fmt::format(
"{}##Version {}", version,
size_t(
id)).c_str(), is_selected))
99 if (is_selected) { ImGui::SetItemDefaultFocus(); }
103 ImGui::EndDisabled();
104 ImGui::TableNextColumn();
105 ImGui::TextUnformatted(
"Version");
107 ImGui::TableNextColumn();
108 ImGui::SetNextItemWidth(COL1_WIDTH);
113 ImGui::TableNextColumn();
114 ImGui::TextUnformatted(
"Run By");
116 ImGui::TableNextColumn();
117 ImGui::SetNextItemWidth(COL1_WIDTH);
122 ImGui::TableNextColumn();
123 ImGui::TextUnformatted(
"Observer");
125 ImGui::TableNextColumn();
126 ImGui::SetNextItemWidth(COL1_WIDTH);
131 ImGui::TableNextColumn();
132 ImGui::TextUnformatted(
"Agency");
138 if (ImGui::CollapsingHeader(fmt::format(
"Comments##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
140 std::vector<size_t> commentsToRemove;
141 if (ImGui::BeginTable(fmt::format(
"Table2##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
143 for (
size_t i = 0; i <
_header.comments.size(); i++)
145 ImGui::TableNextRow();
146 ImGui::TableNextColumn();
147 auto& comment =
_header.comments.at(i);
148 float startPos = ImGui::GetCursorPosX();
149 if (ImGui::Button(fmt::format(
"X##Remove comment {} {}", i,
size_t(
id)).c_str()))
151 commentsToRemove.push_back(i);
154 ImGui::SetNextItemWidth(COL1_WIDTH - (ImGui::GetCursorPosX() - startPos));
155 if (
ImGui::InputTextL(fmt::format(
"##Comment {} {}", i,
size_t(
id)).c_str(), &comment, 60))
162 for (
const size_t& idx : commentsToRemove)
164 _header.comments.erase(std::next(
_header.comments.begin(),
static_cast<int>(idx)));
167 if (ImGui::Button(fmt::format(
"Add##Comment {}",
size_t(
id)).c_str()))
169 _header.comments.emplace_back();
174 if (ImGui::CollapsingHeader(fmt::format(
"Marker##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
176 if (ImGui::BeginTable(fmt::format(
"Table3##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
178 ImGui::TableNextColumn();
179 ImGui::SetNextItemWidth(COL1_WIDTH);
185 ImGui::TableNextColumn();
186 ImGui::TextUnformatted(
"Type");
188 ImGui::TableNextColumn();
189 ImGui::SetNextItemWidth(COL1_WIDTH);
195 ImGui::TableNextColumn();
196 ImGui::TextUnformatted(
"User Type");
198 ImGui::Dummy(ImVec2(ImGui::GetContentRegionAvail().x, 0.0F));
200 ImGui::TableNextColumn();
201 ImGui::SetNextItemWidth(COL1_WIDTH);
206 ImGui::TableNextColumn();
207 ImGui::TextUnformatted(
"Name");
209 ImGui::TableNextColumn();
210 ImGui::SetNextItemWidth(COL1_WIDTH);
215 ImGui::TableNextColumn();
216 ImGui::TextUnformatted(
"Number");
222 if (ImGui::CollapsingHeader(fmt::format(
"Receiver##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
224 if (ImGui::BeginTable(fmt::format(
"Table4##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
226 ImGui::TableNextColumn();
227 ImGui::SetNextItemWidth(COL1_WIDTH);
232 ImGui::TableNextColumn();
233 ImGui::TextUnformatted(
"Number");
235 ImGui::TableNextColumn();
236 ImGui::SetNextItemWidth(COL1_WIDTH);
241 ImGui::TableNextColumn();
242 ImGui::TextUnformatted(
"Type");
244 ImGui::TableNextColumn();
245 ImGui::SetNextItemWidth(COL1_WIDTH);
250 ImGui::TableNextColumn();
251 ImGui::TextUnformatted(
"Version");
257 if (ImGui::CollapsingHeader(fmt::format(
"Antenna##{}",
size_t(
id)).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
259 if (ImGui::BeginTable(fmt::format(
"Table5##{}",
size_t(
id)).c_str(), 2, ImGuiTableFlags_SizingFixedFit))
261 ImGui::TableNextColumn();
262 ImGui::SetNextItemWidth(COL1_WIDTH);
267 ImGui::TableNextColumn();
268 ImGui::TextUnformatted(
"Number");
270 ImGui::TableNextColumn();
271 ImGui::SetNextItemWidth(COL1_WIDTH);
276 ImGui::TableNextColumn();
277 ImGui::TextUnformatted(
"Type");
279 ImGui::TableNextColumn();
280 float startPos = ImGui::GetCursorPosX();
281 if (ImGui::Checkbox(fmt::format(
"##ApproxEnabled {}",
size_t(
id)).c_str(), &
_header.approxPositionEnabled))
286 ImGui::SetNextItemWidth(COL1_WIDTH - (ImGui::GetCursorPosX() - startPos));
287 if (!
_header.approxPositionEnabled) { ImGui::BeginDisabled(); }
288 if (
ImGui::InputDouble3L(fmt::format(
"##Approx position XYZ {}",
size_t(
id)).c_str(),
_header.approxPositionXYZ.data(), -99999999.9999, 99999999.9999,
"%.4f m"))
292 if (!
_header.approxPositionEnabled) { ImGui::EndDisabled(); }
293 ImGui::TableNextColumn();
294 ImGui::TextUnformatted(
"Approx position XYZ");
297 "System: ITRS recommended\n"
298 "Optional for moving platforms");
300 ImGui::TableNextColumn();
301 ImGui::SetNextItemWidth(COL1_WIDTH);
302 if (
ImGui::InputDouble3L(fmt::format(
"##Antenna delta HEN {}",
size_t(
id)).c_str(),
_header.antennaDeltaHeightEastNorth.data(), -99999999.9999, 99999999.9999,
"%.4f m"))
306 ImGui::TableNextColumn();
307 ImGui::TextUnformatted(
"Delta HEN");
423 std::string pathTmp =
getFilepath().string() +
".tmp";
425 auto fsTmp = std::ofstream(pathTmp, std::ios_base::trunc | std::ios_base::binary);
426 if (!fsTmp.good()) {
LOG_CRITICAL(
"{}: Could not create temporary file: {}",
nameId(), pathTmp); }
428 auto fsOld = std::ifstream(
getFilepath(), std::ios_base::in);
431 fsTmp <<
_header.generateHeader();
434 bool dataRecords =
false;
438 std::getline(fsOld, line);
443 if (oldTimeSys ==
_header.timeSys)
446 fsTmp << line <<
'\n';
447 fsTmp << fsOld.rdbuf();
451 LOG_DATA(
"{}: Read epoch [{}][{}][{}][{}][{}][{}] [{}]",
nameId(), line.substr(2, 4), line.substr(7, 2), line.substr(10, 2),
452 line.substr(13, 2), line.substr(16, 2), line.substr(18, 11), line.substr(29 + 3, 3));
453 auto epochTime =
InsTime{
static_cast<uint16_t
>(std::stoi(line.substr(2, 4))),
454 static_cast<uint16_t
>(std::stoi(line.substr(7, 2))),
455 static_cast<uint16_t
>(std::stoi(line.substr(10, 2))),
456 static_cast<uint16_t
>(std::stoi(line.substr(13, 2))),
457 static_cast<uint16_t
>(std::stoi(line.substr(16, 2))),
458 std::stold(line.substr(18, 11)),
460 fsTmp <<
_header.epochRecordLine(epochTime, std::stoul(line.substr(29 + 3, 3)));
462 else if (dataRecords)
465 if (!line.empty()) { fsTmp <<
'\n'; }
477 auto obs = std::static_pointer_cast<const GnssObs>(queue.
extract_front());
480 _header.interval = std::min(
_header.interval, std::round(
static_cast<double>((obs->insTime -
_header.timeLastObs).count()) * 1e3) / 1e3);
481 if (
_header.timeFirstObs.empty()) {
_header.timeFirstObs = obs->insTime; }
482 _header.timeLastObs = obs->insTime;
484 std::set<SatId> satellites;
485 bool satelliteSystemDescriptionChanged =
false;
486 for (
const auto& sig : obs->data)
488 auto satId = sig.satSigId.toSatId();
489 satellites.insert(satId);
490 _header.satellites.insert(satId);
491 _header.satSys |= satId.satSys;
498 if (satelliteSystemDescriptionChanged)
507 for (
const auto& satId : satellites)
509 _filestream << fmt::format(
"{0}{1:02d}", satId.satSys.toChar(),
510 satId.satSys ==
SBAS && satId.satNum > 100 ? satId.satNum - 100 : satId.satNum);
511 const auto& obsDescriptions =
_header.systemObsTypes.at(satId.satSys);
512 for (
size_t i = 0; i < obsDescriptions.size(); i++)
514 const auto& obsDesc = obsDescriptions.at(i);
516 auto signal = std::ranges::find_if(obs->data, [&obsDesc, &satId](
const auto& sig) {
517 return sig.satSigId == SatSigId{ obsDesc.code, satId.satNum };
520 bool obsWritten =
false;
521 if (signal != obs->data.end())
523 switch (obsDesc.type)
526 if (signal->pseudorange && signal->pseudorange->value < 100'000'000.0)
529 "obs"_a = signal->pseudorange->value,
530 "SSI"_a = signal->pseudorange->SSI == 0 ?
" " : std::to_string(signal->pseudorange->SSI));
535 if (signal->carrierPhase && signal->carrierPhase->value < 1'000'000'000.0)
537 _filestream << fmt::format(
"{obs:14.3f}{LLI:1}{SSI:1}",
538 "obs"_a = signal->carrierPhase->value,
539 "LLI"_a = signal->carrierPhase->LLI == 0 ?
" " : std::to_string(signal->carrierPhase->LLI),
540 "SSI"_a = signal->carrierPhase->SSI == 0 ?
" " : std::to_string(signal->carrierPhase->SSI));
547 _filestream << fmt::format(
"{:14.3f} ", signal->doppler.value());
554 _filestream << fmt::format(
"{:14.3f} ", signal->CN0.value());
568 if (i == obsDescriptions.size() - 1)