Project

General

Profile

DevelopersDriverNotes » History » Revision 4

Revision 3 (Dan Smith, 12/26/2024 04:53 PM) → Revision 4/5 (Dan Smith, 01/10/2025 02:57 PM)

This is a set of notes about complex driver situations to help with consistency. 

 ## Implied modes 

 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. 

 Using the airband example, this should be handled by the driver with the following steps: 

 - Expose `AM` as an option in `valid_modes`. 
 - Coerce the `Memory.mode` value to `AM` when the frequency is with in the airband range. 
 - 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. 

 Here is an example `validate_memory()` implementation. Try to re-use the same exact strings to minimize translation overhead: 

 ```python ``` 
     AIRBAND = (118000000, 136000000) 
     def validate_memory(self, mem): 
         msgs = [] 
         in_range = chirp_common.in_range 
         if in_range(mem.freq, [AIRBAND]) and not mem.mode == 'AM': 
             msgs.append(chirp_common.ValidationWarning( 
                 _('Frequency in this range requires AM mode'))) 
         if not in_range(mem.freq, [AIRBAND]) and mem.mode == 'AM': 
             msgs.append(chirp_common.ValidationWarning( 
                 _('Frequency in this range must not be AM mode'))) 
         return msgs + super().validate_memory(mem) 
 ``` 

 ## Immutable memory fields 

 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: 

 - Some radios have a per-mode "call" special channel (i.e. `FM`, `DV`, etc)  
 - 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 

 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. 

 ## Radios that only support TX/RX frequency 

 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. 

 ## Aliases 

 You will find legacy code in the tree that looks like this: 
 ```python 
 class SomeOtherRadio(chirp_common.Alias): 
     VENDOR = "Some" 
     MODEL = "Other Radio" 

 @directory.register 
 class MyRadio: 
     VENDOR = "Acme" 
     MODEL = "WhizBang" 
     ALIASES = [SomeOtherRadio] 
 ``` 
 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. 

 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: 
 ```python 
 @directory.register 
 class MyRadio: 
     VENDOR = "Acme" 
     MODEL = "WhizBang" 
     ALIASES = [SomeOtherRadio] 

 @directory.register 
 class SomeOtherRadio(MyRadio): 
     VENDOR = "Some" 
     MODEL = "Other Radio" 

 ```