Project

General

Profile

DevelopersDriverNotes » History » Version 4

Dan Smith, 01/10/2025 02:57 PM

1 1 Dan Smith
This is a set of notes about complex driver situations to help with consistency.
2
3
## Implied modes
4
5
Some radios have an annoying behavior where they determine the mode for a channel based on frequency. This is most commonly used to allow AM for airband frequencies, where the radio does not actually store a bit for "mode is AM" but instead just forces the receiver into AM mode in that range.
6
7
Using the airband example, this should be handled by the driver with the following steps:
8
9
- Expose `AM` as an option in `valid_modes`.
10
- Coerce the `Memory.mode` value to `AM` when the frequency is with in the airband range.
11
- Implement `validate_memory()` to check that `Memory.mode` is `AM` in the airband range, and `FM` in all other ranges. If there is a mismatch, return a `chirp_common.ValidationWarning` to indicate to the user that the mismatch will be corrected by the driver.
12
13
Here is an example `validate_memory()` implementation. Try to re-use the same exact strings to minimize translation overhead:
14
15 4 Dan Smith
```python
16 1 Dan Smith
    AIRBAND = (118000000, 136000000)
17
    def validate_memory(self, mem):
18
        msgs = []
19
        in_range = chirp_common.in_range
20
        if in_range(mem.freq, [AIRBAND]) and not mem.mode == 'AM':
21
            msgs.append(chirp_common.ValidationWarning(
22
                _('Frequency in this range requires AM mode')))
23
        if not in_range(mem.freq, [AIRBAND]) and mem.mode == 'AM':
24
            msgs.append(chirp_common.ValidationWarning(
25
                _('Frequency in this range must not be AM mode')))
26
        return msgs + super().validate_memory(mem)
27
```
28
29
## Immutable memory fields
30
31
Immutable fields on a memory can be implemented by setting, for example, `mem.immutable = ['mode']` to prevent the `mode` from being changed. This should be used in cases where the radio has a special memory that has some required settings. Examples of where this is used:
32
33
- Some radios have a per-mode "call" special channel (i.e. `FM`, `DV`, etc) 
34
- Some radios have requirements that the first 22 channels be the GMRS channels where the name and power level are changeable by the user, but the frequency/duplex are not
35
36 2 Dan Smith
Effectively, the decision to mark a field as immutable should generally come down to "is this immutable because of the *location* of the memory?" If the fixed nature is related to other *content* of the memory (i.e. channels in the airband range must be `AM`, see above), then it's best to use `validate_memory()` to check/warn about a required value.
37 3 Dan Smith
38
## Radios that only support TX/RX frequency
39
40 1 Dan Smith
CHIRP requires that drivers behave like a more conventional amateur radio and support Duplex/Offset at least. Supporting TX/RX frequencies with Duplex=split is also supported but Duplex=+/- is necessary. To do this, you should assume a TX/RX split of <= 70MHz to be a duplex mode. Greater separation should be implemented as Duplex=split. See `split_to_offset()` in source:chirp/chirp_common.py which you should use from your driver.
41 4 Dan Smith
42
## Aliases
43
44
You will find legacy code in the tree that looks like this:
45
```python
46
class SomeOtherRadio(chirp_common.Alias):
47
    VENDOR = "Some"
48
    MODEL = "Other Radio"
49
50
@directory.register
51
class MyRadio:
52
    VENDOR = "Acme"
53
    MODEL = "WhizBang"
54
    ALIASES = [SomeOtherRadio]
55
```
56
This is the **wrong** way to do this job in modern chirp, and it is a pattern that existed from before we had stable metadata in the `.img` files. Please do not add new instances of this.
57
58
In modern times, this sort of thing should generally be added to source:chirp/share/model_alias_map.yaml . This source file is used to generate the radio listing on the front page of the site. Models listed here will be reported, along with the pointer to the alternate model the user should select. If the model is a direct clone of another, this is the proper way to denote it. In some cases where the clone is massively more popular than the original, then an empty subclass is to be used (which is normally preferred when there is an actual difference). So the above deprecated `ALIASES=` example would be:
59
```python
60
@directory.register
61
class MyRadio:
62
    VENDOR = "Acme"
63
    MODEL = "WhizBang"
64
    ALIASES = [SomeOtherRadio]
65
66
@directory.register
67
class SomeOtherRadio(MyRadio):
68
    VENDOR = "Some"
69
    MODEL = "Other Radio"
70
71
```