2020-09-27 17:52:52 -04:00
|
|
|
"""
|
|
|
|
Conversion extension for qrm
|
|
|
|
---
|
2021-03-28 13:57:03 -04:00
|
|
|
Copyright (C) 2020 classabbyamp, 0x5c
|
2020-09-27 17:52:52 -04:00
|
|
|
|
|
|
|
This file is part of qrm2 and is released under the terms of
|
|
|
|
the GNU General Public License, version 2.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import math
|
|
|
|
from enum import Enum
|
|
|
|
from typing import Optional
|
2020-10-28 21:06:30 -04:00
|
|
|
from dataclasses import dataclass
|
2020-09-27 17:52:52 -04:00
|
|
|
|
|
|
|
import discord.ext.commands as commands
|
|
|
|
|
|
|
|
import common as cmn
|
|
|
|
from data import options as opt
|
|
|
|
|
|
|
|
|
|
|
|
# not sure why but UnitConverter and Unit need to be defined before DbConvCog and convert()
|
|
|
|
class UnitConverter(commands.Converter):
|
|
|
|
async def convert(self, ctx: commands.Context, argument: str):
|
2020-10-28 21:06:30 -04:00
|
|
|
is_db = None
|
|
|
|
mult = None
|
|
|
|
unit = None
|
|
|
|
utype = None
|
2020-09-27 17:52:52 -04:00
|
|
|
try:
|
2020-10-28 21:06:30 -04:00
|
|
|
s = argument.lower()
|
|
|
|
if len(s) > 2 and s[:2] == "db":
|
|
|
|
is_db = True
|
|
|
|
if s[2:] in units:
|
|
|
|
u = units[s[2:]]
|
|
|
|
mult = u["mult"]
|
|
|
|
unit = u["log"]
|
|
|
|
utype = u["type"]
|
|
|
|
elif s in units:
|
|
|
|
is_db = False
|
|
|
|
u = units[s]
|
|
|
|
mult = u["mult"]
|
|
|
|
unit = u["scalar"]
|
|
|
|
utype = u["type"]
|
|
|
|
else:
|
|
|
|
raise ValueError(f"Invalid unit: {argument}")
|
|
|
|
return Unit(argument, unit, utype, is_db, mult)
|
2020-09-27 17:52:52 -04:00
|
|
|
except ValueError as e:
|
|
|
|
raise commands.BadArgument(message=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
class UnitType(Enum):
|
|
|
|
voltage = 1
|
|
|
|
power = 2
|
|
|
|
antenna = 3
|
|
|
|
|
|
|
|
|
2020-10-28 21:06:30 -04:00
|
|
|
@dataclass
|
|
|
|
class Unit:
|
|
|
|
raw: str
|
|
|
|
unit: str
|
|
|
|
type: UnitType
|
|
|
|
is_db: bool
|
|
|
|
mult: int
|
|
|
|
|
|
|
|
|
2020-09-27 17:52:52 -04:00
|
|
|
class DbConvCog(commands.Cog):
|
|
|
|
def __init__(self, bot: commands.Bot):
|
|
|
|
self.bot = bot
|
|
|
|
|
2021-03-28 09:50:51 -04:00
|
|
|
@commands.command(name="dbconv", aliases=["dbc"], category=cmn.Cats.CALC)
|
2020-09-27 17:52:52 -04:00
|
|
|
async def _db_conv(self, ctx: commands.Context,
|
|
|
|
value: Optional[float] = None,
|
|
|
|
unit_from: Optional[UnitConverter] = None,
|
|
|
|
unit_to: Optional[UnitConverter] = None):
|
|
|
|
"""
|
|
|
|
Convert between decibels and scalar values for voltage, power, and antenna gain.
|
|
|
|
|
|
|
|
**Valid Units**
|
|
|
|
*Voltage:* V, mV, µV, uV, dBV, dBmV, dBµV, dBuV
|
|
|
|
*Power:* fW, mW, W, kW, dBf, dBm, dBW, dBk
|
|
|
|
*Antenna Gain:* dBi, dBd, dBq
|
|
|
|
"""
|
|
|
|
embed = cmn.embed_factory(ctx)
|
|
|
|
if value is not None and unit_from is not None and unit_to is not None:
|
|
|
|
converted = convert(value, unit_from, unit_to)
|
|
|
|
|
2020-10-28 21:06:30 -04:00
|
|
|
embed.title = f"{value:.3g} {unit_from.unit} = {converted:.3g} {unit_to.unit}"
|
2020-09-27 17:52:52 -04:00
|
|
|
embed.colour = cmn.colours.good
|
|
|
|
else:
|
|
|
|
embed.title = "Decibel Quick Reference"
|
|
|
|
embed.description = (
|
|
|
|
"Decibels are a great way to easily represent large quantities that are common in electronics. "
|
|
|
|
"There are a few main types that are used often in radio: voltage, power, and antenna gain. "
|
|
|
|
"Here are some commonly-used reference levels for each type:"
|
|
|
|
)
|
|
|
|
v_db_info = ("**dBV** = relative to 1 V\n"
|
|
|
|
"**dBmV** = relative to 1 mV (1e-3 V)\n"
|
|
|
|
"**dBµV** = relative to 1 µV (1e-6 V)")
|
|
|
|
embed.add_field(name="Voltage Decibels", value=v_db_info, inline=False)
|
|
|
|
p_db_info = ("**dBW** = relative to 1 W\n"
|
|
|
|
"**dBk** = relative to 1 kW (1e3 W)\n"
|
|
|
|
"**dBm** = relative to 1 mW (1e-3 W)\n"
|
|
|
|
"**dBf** = relative to 1 fW (1e-15 W)")
|
|
|
|
embed.add_field(name="Power Decibels", value=p_db_info, inline=False)
|
|
|
|
a_db_info = ("**dBi** = relative to a theoretical __i__sotropic radiator in free space "
|
|
|
|
"(equal radiation in all directions)\n"
|
|
|
|
"**dBd** = relative to a dipole in free space (0 dBd = 2.15 dBi)\n"
|
|
|
|
"**dBq** = relative to a quarter-wave antenna in free space (0 dBq = -0.85 dBi)")
|
|
|
|
embed.add_field(name="Antenna Gain Decibels", value=a_db_info, inline=False)
|
|
|
|
embed.add_field(name="Use the bot to do the conversions",
|
|
|
|
value=f"`{opt.display_prefix}dbconv [value] [unit_from] [unit_to]`",
|
|
|
|
inline=False)
|
|
|
|
await ctx.send(embed=embed)
|
|
|
|
|
|
|
|
|
|
|
|
def setup(bot: commands.Bot):
|
|
|
|
bot.add_cog(DbConvCog(bot))
|
|
|
|
|
|
|
|
|
|
|
|
def convert(initial: float, unit1: Unit, unit2: Unit):
|
|
|
|
if unit1.type == unit2.type:
|
|
|
|
# dB to dB
|
|
|
|
if unit1.is_db and unit2.is_db:
|
|
|
|
if unit1.mult == unit2.mult:
|
|
|
|
return initial
|
|
|
|
elif unit1.type == UnitType.voltage:
|
|
|
|
return _calc_volt_db(_calc_volt(initial, unit1.mult), unit2.mult)
|
|
|
|
elif unit1.type == UnitType.power:
|
|
|
|
return _calc_power_db(_calc_power(initial, unit1.mult), unit2.mult)
|
|
|
|
elif unit1.type == UnitType.antenna:
|
|
|
|
return initial + (unit1.mult - unit2.mult)
|
|
|
|
# V/W to V/W
|
|
|
|
elif not unit1.is_db and not unit2.is_db:
|
|
|
|
if unit1.mult == unit2.mult:
|
|
|
|
return initial
|
|
|
|
return initial * unit1.mult / unit2.mult
|
|
|
|
# dB to V/W
|
|
|
|
elif unit1.is_db and not unit2.is_db:
|
|
|
|
if unit1.type == UnitType.voltage:
|
|
|
|
return _calc_volt(initial, unit1.mult) / unit2.mult
|
|
|
|
elif unit1.type == UnitType.power:
|
|
|
|
return _calc_power(initial, unit1.mult) / unit2.mult
|
|
|
|
# V/W to dB
|
|
|
|
elif not unit1.is_db and unit2.is_db:
|
|
|
|
if unit1.type == UnitType.voltage:
|
|
|
|
return _calc_volt_db(initial * unit1.mult, unit2.mult)
|
|
|
|
elif unit1.type == UnitType.power:
|
|
|
|
return _calc_power_db(initial * unit1.mult, unit2.mult)
|
|
|
|
raise ValueError(f"Can't convert between {unit1} and {unit2}")
|
|
|
|
|
|
|
|
|
|
|
|
units = {
|
|
|
|
# voltage
|
|
|
|
"uv": {"mult": 1e-6, "scalar": "µV", "log": "dBµV", "type": UnitType.voltage},
|
|
|
|
"µv": {"mult": 1e-6, "scalar": "µV", "log": "dBµV", "type": UnitType.voltage},
|
|
|
|
"mv": {"mult": 1e-3, "scalar": "mV", "log": "dBmV", "type": UnitType.voltage},
|
|
|
|
"v": {"mult": 1, "scalar": "V", "log": "dBV", "type": UnitType.voltage},
|
|
|
|
# power
|
|
|
|
"fw": {"mult": 1e-15, "scalar": "fW", "log": "dBf", "type": UnitType.power},
|
|
|
|
"f": {"mult": 1e-15, "scalar": "fW", "log": "dBf", "type": UnitType.power},
|
|
|
|
"mw": {"mult": 1e-3, "scalar": "mW", "log": "dBm", "type": UnitType.power},
|
|
|
|
"m": {"mult": 1e-3, "scalar": "mW", "log": "dBm", "type": UnitType.power},
|
|
|
|
"w": {"mult": 1, "scalar": "W", "log": "dBW", "type": UnitType.power},
|
|
|
|
"kw": {"mult": 1e3, "scalar": "kW", "log": "dBk", "type": UnitType.power},
|
|
|
|
"k": {"mult": 1e3, "scalar": "kW", "log": "dBk", "type": UnitType.power},
|
|
|
|
# antenna
|
|
|
|
"q": {"mult": -0.85, "scalar": None, "log": "dBq", "type": UnitType.antenna},
|
|
|
|
"i": {"mult": 0, "scalar": None, "log": "dBi", "type": UnitType.antenna},
|
|
|
|
"d": {"mult": 2.15, "scalar": None, "log": "dBd", "type": UnitType.antenna},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def _calc_power_db(p: float, ref: float):
|
|
|
|
return 10 * math.log10(p / ref)
|
|
|
|
|
|
|
|
|
|
|
|
def _calc_power(db: float, ref: float):
|
|
|
|
return 10 ** (db / 10) * ref
|
|
|
|
|
|
|
|
|
|
|
|
def _calc_volt_db(v: float, ref: float):
|
|
|
|
return 20 * math.log10(v / ref)
|
|
|
|
|
|
|
|
|
|
|
|
def _calc_volt(db: float, ref: float):
|
|
|
|
return 10 ** (db / 20) * ref
|
|
|
|
|
|
|
|
|
|
|
|
# testing code
|
|
|
|
if __name__ == "__main__":
|
|
|
|
while(True):
|
|
|
|
try:
|
|
|
|
ip = input("> ").split()
|
|
|
|
initial = float(ip[0])
|
|
|
|
unit1 = Unit(ip[1])
|
|
|
|
unit2 = Unit(ip[2])
|
|
|
|
conv = convert(initial, unit1, unit2)
|
|
|
|
print(f"{initial:.2f} {unit1} = {conv:.2f} {unit2}")
|
|
|
|
except ValueError as e:
|
|
|
|
print(e)
|