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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ including programming several different radios from the same codeplug.
| **AnyTone AT-D578UV** | DMR + Analog mobile | 2 m / 1.25 m / 70 cm | Direct USB — channels, zones, scan lists, talkgroups, settings, call-sign DB | 4000 channels in 250 zones; 264 menu settings including the FM and DMR APRS setup; 500k-entry caller-ID database. AM channels in 108–136 MHz go to the air-band memory, which only the D578UV **PLUS** receives — that part is written to the published map but has not been checked on a PLUS. Same protocol as the D890UV over a different memory map |
| **Yaesu FT5D** | C4FM (System Fusion) + Analog | VHF / UHF TX, wideband RX | microSD — patches the radio's own backup file | 900 channels in 24 banks; channels, banks, and menu settings |
| **Yaesu FTM-300DR** | C4FM (System Fusion) + Analog | 2 m / 70 cm TX, 108–999 MHz RX | microSD — patches the radio's own backup file | 999 memories; memories and 147 menu settings, including the APRS and SmartBeaconing setup |
| **Yaesu FTM-510DR** | C4FM (System Fusion) + Analog | 2 m / 70 cm TX, 108–550 MHz RX | microSD — patches the radio's own backup file | 999 memories; memories and 284 settings — Setup Menu items, band skip, DTMF memories, and the APRS setup down to digi paths, message texts and status texts |
| **Icom ID-52** | D-STAR + Analog | VHF / UHF TX, 108–174 / 225–479 MHz RX | microSD — patches the radio's own `.icf` file | 1000 memories in 100 groups; memories and menu settings restore in one operation |
| **Icom ID-5100** | D-STAR + Analog | 2 m / 70 cm TX, 118–174 / 375–550 MHz RX | Data cable — read, write, settings | 1000 memories in 26 named banks, programmed by Icom's clone protocol over the cable; 292 menu settings including My Call Sign, TX messages and the D-PRS (APRS) position, object, item and weather setup, written in the same upload as the memories |
| **Kenwood TH-D72** | APRS + Analog | 2 m / 70 cm TX, 118–174 / 320–524 MHz RX | Direct USB — read, write, settings | 1000 memories; 113 menu settings over the radio's own `MU` command |
Expand Down
48 changes: 48 additions & 0 deletions src-tauri/src/commands/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ pub async fn find_memory_cards(format: String) -> Vec<MemoryCard> {
"icom_id52_icf" => id52_cards(),
"kenwood_thd75_sd" => thd75_cards(),
"yaesu_ftm300_sd" => ftm300_cards(),
"yaesu_ftm510_sd" => ftm510_cards(),
_ => Vec::new(),
})
.await
Expand Down Expand Up @@ -570,6 +571,28 @@ fn ftm300_cards() -> Vec<MemoryCard> {
out
}

/// The FTM-510 is the FTM-300's arrangement under its own folder and name:
/// Setup Menu 107 BACKUP → WRITE TO SD → ALL writes this path, READ FROM SD
/// → ALL loads it back.
fn ftm510_cards() -> Vec<MemoryCard> {
let mut out = Vec::new();
for root in mounted_roots() {
let path = root.join("FTM510D").join("BACKUP").join("CLNFTM510D.dat");
let Ok(image) = std::fs::read(&path) else {
continue;
};
if crate::radios::yaesu_ftm510::sd_image::validate_backup(&image).is_err() {
continue;
}
out.push(MemoryCard {
has_original: path.with_extension("dat.orig").exists(),
volume: volume_name(&root),
path: path.to_string_lossy().into_owned(),
});
}
out
}

/// The ID-52 keeps its settings files in one fixed folder but names them
/// itself, so what gets offered is the **folder**, not a file: the write creates
/// a new `SetYYYYMMDD_NN.icf` in it rather than overwriting anything the
Expand Down Expand Up @@ -704,6 +727,31 @@ pub async fn read_ftm300_settings_from_backup(path: String) -> Result<RadioSetti
.estr()?
}

/// The FTM-510's version of [`read_ftm300_settings_from_backup`], reading the
/// `CLNFTM510D.dat` the radio writes with Setup Menu 107 BACKUP → WRITE TO SD →
/// ALL.
#[tauri::command]
pub async fn read_ftm510_settings_from_backup(path: String) -> Result<RadioSettingsRead, String> {
tauri::async_runtime::spawn_blocking(move || {
let image = std::fs::read(&path).map_err(|e| {
format!(
"Could not read {path}: {e}. Pick FTM510D/BACKUP/CLNFTM510D.dat from the \
radio's microSD card."
)
})?;
crate::radios::yaesu_ftm510::sd_image::validate_backup(&image)?;
let settings = crate::radios::yaesu_ftm510::settings::decode_settings(&image);
let count = settings.as_object().map(|o| o.len()).unwrap_or(0);
Ok(RadioSettingsRead {
settings,
count,
backup_path: path,
})
})
.await
.estr()?
}

/// The ID-52's version of [`read_ft5d_settings_from_backup`], reading the `.icf`
/// the radio writes with `SET > SD Card > Save Setting`.
///
Expand Down
10 changes: 5 additions & 5 deletions src-tauri/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ mod tests {
// original set): currently the Baofeng UV-5R, TIDRADIO TD-H3, AnyTone
// AT-D890UV and AT-D578UV, Yaesu FT5D, Icom ID-52, Kenwood TH-D75, Kenwood TH-D72
// the Binteradio BT-9000, Kenwood TM-D710, Icom ID-5100, TYT MD-380 and Yaesu
// FTM-300DR. (0015
// FTM-300DR and FTM-510DR. (0015
// removed the Vero VR-N76 placeholder.) None of the last five has a
// migration of its own — seeding INSERTs new (manufacturer, model)
// rows, so a new model reaches existing databases on the next startup
Expand All @@ -71,9 +71,9 @@ mod tests {
.await
.unwrap();
assert_eq!(
count.0, 13,
count.0, 14,
"expected the UV-5R, TD-H3, AT-D890UV, AT-D578UV, FT5D, ID-52, TH-D75, TH-D72, BT-9000, \
TM-D710, ID-5100, MD-380 and FTM-300DR seeded models"
TM-D710, ID-5100, MD-380, FTM-300DR and FTM-510DR seeded models"
);

let models: Vec<(String,)> =
Expand All @@ -85,7 +85,7 @@ mod tests {
assert_eq!(
names,
vec![
"AT-D578UV", "AT-D890UV", "BT-9000", "FT5D", "FTM-300DR", "ID-5100", "ID-52",
"AT-D578UV", "AT-D890UV", "BT-9000", "FT5D", "FTM-300DR", "FTM-510DR", "ID-5100", "ID-52",
"MD-380", "TD-H3", "TH-D72", "TH-D75", "TM-D710", "UV-5R"
]
);
Expand All @@ -96,7 +96,7 @@ mod tests {
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(count2.0, 13, "seeding should be idempotent");
assert_eq!(count2.0, 14, "seeding should be idempotent");

// A new database starts with NO talkgroups. The BrandMeister list used
// to be compiled in and seeded here; it is downloaded on request now,
Expand Down
Loading
Loading