Examples¶
Real-world examples of using RepeaterBook for common amateur radio tasks.
Example 1: Build a Repeater Directory Website¶
Create a web-based repeater directory with search functionality.
import asyncio
from flask import Flask, jsonify, request
from repeaterbook import RepeaterBook, Repeater
from repeaterbook.services import RepeaterBookAPI
from repeaterbook.models import ExportQuery, Status
from repeaterbook.queries import square, filter_radius, band, Bands
from repeaterbook.utils import LatLon, Radius
import pycountry
app = Flask(__name__)
rb = RepeaterBook(database="repeaters.db")
async def initialize_database():
"""Download and populate database on startup."""
api = RepeaterBookAPI()
# Download data for multiple countries
countries = [
pycountry.countries.get(alpha_2="US"),
pycountry.countries.get(alpha_2="CA"),
pycountry.countries.get(alpha_2="MX"),
]
all_repeaters = []
for country in countries:
repeaters = await api.download(
query=ExportQuery(countries={country})
)
all_repeaters.extend(repeaters)
rb.populate(all_repeaters)
print(f"Database initialized with {len(all_repeaters)} repeaters")
@app.route('/api/search')
def search_repeaters():
"""Search repeaters by location and filters."""
# Get query parameters
lat = float(request.args.get('lat'))
lon = float(request.args.get('lon'))
distance = float(request.args.get('distance', 50))
mode = request.args.get('mode') # 'dmr', 'p25', 'nxdn', 'analog'
band_filter = request.args.get('band') # '2m', '70cm', etc.
# Build query
radius = Radius(origin=LatLon(lat=lat, lon=lon), distance=distance)
conditions = [
square(radius),
Repeater.operational_status == Status.ON_AIR
]
# Add mode filter
if mode == 'dmr':
conditions.append(Repeater.dmr_capable == True)
elif mode == 'p25':
conditions.append(Repeater.apco_p_25_capable == True)
elif mode == 'nxdn':
conditions.append(Repeater.nxdn_capable == True)
# Add band filter
if band_filter == '2m':
conditions.append(band(Bands.M_2))
elif band_filter == '70cm':
conditions.append(band(Bands.CM_70))
# Execute query
results = rb.query(*conditions)
nearby = filter_radius(results, radius)
# filter_radius returns repeaters sorted by distance
# Limit to first 50 results
sorted_results = nearby[:50]
# Convert to JSON
from haversine import haversine
return jsonify([{
'callsign': r.callsign,
'frequency': r.frequency,
'location': r.location_nearest_city,
'distance': round(haversine(radius.origin, (r.latitude, r.longitude), unit=radius.unit), 2),
'ctcss': r.pl_ctcss_uplink,
'dmr': r.dmr_capable,
'dmr_id': r.dmr_id,
'dmr_cc': r.dmr_color_code,
} for r in sorted_results])
if __name__ == '__main__':
# Initialize database
asyncio.run(initialize_database())
# Start server
app.run(debug=True)
Example 2: Generate Codeplug for DMR Radio¶
Create a CSV file for importing into a DMR radio codeplug.
import asyncio
import csv
from repeaterbook import RepeaterBook, Repeater
from repeaterbook.services import RepeaterBookAPI
from repeaterbook.models import ExportQuery, Status, Use
from repeaterbook.queries import square, filter_radius, band, Bands
from repeaterbook.utils import LatLon, Radius
import pycountry
async def generate_codeplug():
"""Generate DMR codeplug from RepeaterBook data."""
# Download California repeaters
api = RepeaterBookAPI()
usa = pycountry.countries.get(alpha_2="US")
repeaters = await api.download(
query=ExportQuery(countries={usa}, state_ids={"06"}) # California FIPS code
)
# Store in database
rb = RepeaterBook()
rb.populate(repeaters)
# Find DMR repeaters near San Francisco
sf = LatLon(lat=37.7749, lon=-122.4194)
radius = Radius(origin=sf, distance=100)
dmr_repeaters = rb.query(
square(radius),
Repeater.dmr_capable == True,
Repeater.operational_status == Status.ON_AIR,
Repeater.use_membership == Use.OPEN,
band(Bands.CM_70) # 70cm only
)
nearby_dmr = filter_radius(dmr_repeaters, radius)
# filter_radius returns repeaters sorted by distance
# Generate CSV for Anytone/TYT radios
with open('dmr_codeplug.csv', 'w', newline='') as f:
writer = csv.writer(f)
# Header row
writer.writerow([
'No.', 'Channel Name', 'Receive Frequency', 'Transmit Frequency',
'Channel Type', 'Transmit Power', 'Band Width', 'CTCSS/DCS Decode',
'CTCSS/DCS Encode', 'Contact', 'Contact Call Type', 'Radio ID',
'Busy Lock/TX Permit', 'Squelch Mode', 'Optional Signal',
'DTMF ID', 'Color Code', 'Slot', 'Scan List', 'Group List',
'GPS System', 'Emergency System'
])
# Data rows
for idx, rep in enumerate(nearby_dmr[:100], start=1):
# Calculate offset
offset = rep.frequency - rep.input_frequency
# Channel name
name = f"{rep.callsign} {rep.location_nearest_city[:20]}"
writer.writerow([
idx, # No.
name, # Channel Name
f"{rep.frequency:.5f}", # Receive Frequency
f"{rep.input_frequency:.5f}", # Transmit Frequency
'D-Digital', # Channel Type
'High', # Transmit Power
'12.5K', # Band Width
f"{rep.pl_ctcss_uplink:.1f}" if rep.pl_ctcss_uplink else '', # CTCSS Decode
f"{rep.pl_ctcss_uplink:.1f}" if rep.pl_ctcss_uplink else '', # CTCSS Encode
'Worldwide', # Contact
'Group Call', # Contact Call Type
'None', # Radio ID
'Always', # Busy Lock/TX Permit
'Carrier', # Squelch Mode
'Off', # Optional Signal
'1', # DTMF ID
rep.dmr_color_code or 1, # Color Code
'2', # Slot (usually TS2 for Worldwide)
'All', # Scan List
'Worldwide', # Group List
'None', # GPS System
'None' # Emergency System
])
print(f"Generated codeplug with {len(nearby_dmr[:100])} channels")
print(f"Import dmr_codeplug.csv into your radio programming software")
if __name__ == '__main__':
asyncio.run(generate_codeplug())
Example 3: Repeater Coverage Map¶
Generate a heatmap of repeater coverage using folium.
import asyncio
import folium
from folium.plugins import HeatMap
from repeaterbook import RepeaterBook, Repeater
from repeaterbook.services import RepeaterBookAPI
from repeaterbook.models import ExportQuery, Status
import pycountry
async def create_coverage_map():
"""Create an interactive map of repeater locations."""
# Download repeater data
api = RepeaterBookAPI()
uk = pycountry.countries.get(alpha_2="GB")
repeaters = await api.download(query=ExportQuery(countries={uk}))
# Store in database
rb = RepeaterBook()
rb.populate(repeaters)
# Get operational repeaters
operational = rb.query(Repeater.operational_status == Status.ON_AIR)
# Create base map centered on UK
m = folium.Map(
location=[54.5, -4.0],
zoom_start=6,
tiles='OpenStreetMap'
)
# Add markers for each repeater
for rep in operational:
# Determine icon color by mode
if rep.dmr_capable:
color = 'blue'
icon = 'info-sign'
elif rep.apco_p_25_capable:
color = 'green'
icon = 'info-sign'
elif rep.nxdn_capable:
color = 'orange'
icon = 'info-sign'
else:
color = 'red'
icon = 'record'
# Create popup with repeater info
popup_html = f"""
<div style="width:200px">
<h4>{rep.callsign}</h4>
<p><b>Frequency:</b> {rep.frequency:.4f} MHz</p>
<p><b>Location:</b> {rep.location_nearest_city}</p>
<p><b>Input Tone:</b> {rep.pl_ctcss_uplink or 'None'}</p>
<p><b>Use:</b> {rep.use_membership.value}</p>
</div>
"""
folium.Marker(
location=[rep.latitude, rep.longitude],
popup=folium.Popup(popup_html, max_width=250),
tooltip=rep.callsign,
icon=folium.Icon(color=color, icon=icon)
).add_to(m)
# Add heatmap layer
heat_data = [[r.latitude, r.longitude] for r in operational]
HeatMap(heat_data, radius=15).add_to(m)
# Save map
m.save('repeater_coverage.html')
print("Map saved to repeater_coverage.html")
if __name__ == '__main__':
asyncio.run(create_coverage_map())
Example 4: Repeater Statistics Dashboard¶
Analyze repeater data and generate statistics.
import asyncio
import pandas as pd
import matplotlib.pyplot as plt
from repeaterbook import RepeaterBook, Repeater
from repeaterbook.services import RepeaterBookAPI
from repeaterbook.models import ExportQuery
import pycountry
async def generate_statistics():
"""Generate statistics and visualizations from repeater data."""
# Download data for multiple European countries
api = RepeaterBookAPI()
countries = [
pycountry.countries.get(name="Germany"),
pycountry.countries.get(name="France"),
pycountry.countries.get(name="Italy"),
pycountry.countries.get(name="Spain"),
pycountry.countries.get(name="United Kingdom"),
]
all_repeaters = []
for country in countries:
repeaters = await api.download(query=ExportQuery(countries={country}))
all_repeaters.extend(repeaters)
# Store in database
rb = RepeaterBook()
rb.populate(all_repeaters)
# Query all repeaters
all_data = rb.query()
# Convert to DataFrame
df = pd.DataFrame([r.model_dump() for r in all_data])
# Statistics
print("=== Repeater Statistics ===\n")
print(f"Total Repeaters: {len(df)}")
print(f"\nBy Status:")
print(df['operational_status'].value_counts())
print(f"\nBy Use/Membership:")
print(df['use_membership'].value_counts())
print(f"\nDigital Mode Capabilities:")
print(f"DMR: {df['dmr_capable'].sum()}")
print(f"P25: {df['apco_p_25_capable'].sum()}")
print(f"NXDN: {df['nxdn_capable'].sum()}")
print(f"Analog: {df['analog_capable'].sum()}")
# Visualizations
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 1. Frequency distribution
df['frequency'].hist(bins=50, ax=axes[0, 0])
axes[0, 0].set_title('Frequency Distribution')
axes[0, 0].set_xlabel('Frequency (MHz)')
axes[0, 0].set_ylabel('Count')
# 2. Status pie chart
df['operational_status'].value_counts().plot.pie(
ax=axes[0, 1],
autopct='%1.1f%%',
title='Operational Status'
)
# 3. Digital modes bar chart
modes = {
'DMR': df['dmr_capable'].sum(),
'P25': df['apco_p_25_capable'].sum(),
'NXDN': df['nxdn_capable'].sum(),
'Analog': df['analog_capable'].sum(),
}
pd.Series(modes).plot.bar(ax=axes[1, 0], title='Mode Capabilities')
axes[1, 0].set_ylabel('Count')
# 4. Access type distribution
df['use_membership'].value_counts().plot.bar(
ax=axes[1, 1],
title='Access Type'
)
axes[1, 1].set_ylabel('Count')
plt.tight_layout()
plt.savefig('repeater_statistics.png', dpi=300)
print("\nStatistics chart saved to repeater_statistics.png")
if __name__ == '__main__':
asyncio.run(generate_statistics())
Example 5: Travel Planner¶
Find repeaters along a route for road trips.
import asyncio
from typing import List
from repeaterbook import RepeaterBook, Repeater
from repeaterbook.services import RepeaterBookAPI
from repeaterbook.models import ExportQuery, Status, Use
from repeaterbook.queries import filter_radius, square, band, Bands
from repeaterbook.utils import LatLon, Radius
import pycountry
class TravelPlanner:
"""Find repeaters along a travel route."""
def __init__(self, rb: RepeaterBook):
self.rb = rb
def find_along_route(
self,
waypoints: List[LatLon],
search_distance: float = 50,
preferred_band: Bands = Bands.M_2,
dmr_only: bool = False
) -> dict:
"""Find repeaters along a route."""
results = {}
for idx, waypoint in enumerate(waypoints):
print(f"Searching around waypoint {idx + 1}...")
# Build query
radius = Radius(origin=waypoint, distance=search_distance)
conditions = [
square(radius),
Repeater.operational_status == Status.ON_AIR,
Repeater.use_membership == Use.OPEN,
band(preferred_band)
]
if dmr_only:
conditions.append(Repeater.dmr_capable == True)
# Execute query
candidates = self.rb.query(*conditions)
nearby = filter_radius(candidates, radius)
# filter_radius returns repeaters sorted by distance
# Take top 5 closest
closest_repeaters = nearby[:5]
results[f"Waypoint {idx + 1}"] = {
'location': waypoint,
'repeaters': closest_repeaters
}
return results
def generate_report(self, results: dict) -> str:
"""Generate a text report of repeaters along route."""
report = ["=" * 80]
report.append("REPEATER TRAVEL PLAN")
report.append("=" * 80)
for waypoint_name, data in results.items():
report.append(f"\n{waypoint_name}:")
report.append(f"Location: {data['location']}")
report.append(f"\nTop Repeaters:")
for rep in data['repeaters']:
# Calculate distance for display
from haversine import haversine
distance = haversine(data['location'], (rep.latitude, rep.longitude))
report.append(f"\n {rep.callsign} - {rep.frequency:.4f} MHz")
report.append(f" Location: {rep.location_nearest_city}")
report.append(f" Distance: {distance:.1f} km")
if rep.pl_ctcss_uplink:
report.append(f" Tone: {rep.pl_ctcss_uplink} Hz")
if rep.dmr_capable:
report.append(f" DMR: CC{rep.dmr_color_code}, ID {rep.dmr_id}")
report.append(f" Notes: {rep.notes or 'None'}")
report.append("-" * 80)
return "\n".join(report)
async def plan_road_trip():
"""Plan repeater coverage for a road trip."""
# Download repeater data
api = RepeaterBookAPI()
usa = pycountry.countries.get(alpha_2="US")
# FIPS codes: CA=06, NV=32, AZ=04, UT=49
states = {"06", "32", "04", "49"}
print("Downloading repeater data...")
repeaters = await api.download(
query=ExportQuery(countries={usa}, state_ids=states)
)
# Initialize database
rb = RepeaterBook()
rb.populate(repeaters)
# Define route (San Francisco to Las Vegas to Phoenix)
route = [
LatLon(lat=37.7749, lon=-122.4194), # San Francisco
LatLon(lat=39.5296, lon=-119.8138), # Reno
LatLon(lat=36.1699, lon=-115.1398), # Las Vegas
LatLon(lat=33.4484, lon=-112.0740), # Phoenix
]
# Create planner
planner = TravelPlanner(rb)
# Find repeaters along route
print("\nFinding repeaters along route...")
results = planner.find_along_route(
waypoints=route,
search_distance=50,
preferred_band=Bands.M_2,
dmr_only=False
)
# Generate and save report
report = planner.generate_report(results)
print(report)
with open('travel_plan.txt', 'w') as f:
f.write(report)
print("\nReport saved to travel_plan.txt")
if __name__ == '__main__':
asyncio.run(plan_road_trip())
Example 6: Emergency Communications Planning¶
Identify repeaters with emergency capabilities.
import asyncio
from repeaterbook import RepeaterBook, Repeater
from repeaterbook.services import RepeaterBookAPI
from repeaterbook.models import ExportQuery, Status, Use
from repeaterbook.queries import square, filter_radius
from repeaterbook.utils import LatLon, Radius
import pycountry
async def emergency_planning():
"""Identify emergency communication resources."""
# Download state data
api = RepeaterBookAPI()
usa = pycountry.countries.get(alpha_2="US")
repeaters = await api.download(
query=ExportQuery(countries={usa}, state_ids={"12"}) # Florida FIPS code
)
# Initialize database
rb = RepeaterBook()
rb.populate(repeaters)
# Find emergency-capable repeaters (ARES/RACES/SKYWARN)
emergency_repeaters = rb.query(
Repeater.operational_status == Status.ON_AIR,
(Repeater.ares == True) | (Repeater.races == True) | (Repeater.skywarn == True),
Repeater.use_membership.in_([Use.OPEN, Use.PRIVATE])
)
print(f"Found {len(emergency_repeaters)} emergency-capable repeaters\n")
# Group by county/location
by_location = {}
for rep in emergency_repeaters:
location = rep.location_nearest_city
if location not in by_location:
by_location[location] = []
by_location[location].append(rep)
# Generate report
print("=" * 80)
print("EMERGENCY COMMUNICATIONS RESOURCES")
print("=" * 80)
for location, reps in sorted(by_location.items()):
print(f"\n{location}:")
for rep in sorted(reps, key=lambda r: r.frequency):
print(f" {rep.callsign:10s} {rep.frequency:8.4f} MHz", end="")
if rep.pl_ctcss_uplink:
print(f" Tone: {rep.pl_ctcss_uplink:6.1f}", end="")
print(f" Use: {rep.use_membership.value}")
if rep.notes:
print(f" Notes: {rep.notes}")
# Find repeaters near major cities
print("\n" + "=" * 80)
print("COVERAGE NEAR MAJOR CITIES")
print("=" * 80)
cities = {
"Miami": LatLon(lat=25.7617, lon=-80.1918),
"Tampa": LatLon(lat=27.9506, lon=-82.4572),
"Orlando": LatLon(lat=28.5383, lon=-81.3792),
"Jacksonville": LatLon(lat=30.3322, lon=-81.6557),
}
for city_name, city_loc in cities.items():
radius = Radius(origin=city_loc, distance=25)
candidates = rb.query(
square(radius),
Repeater.operational_status == Status.ON_AIR,
(Repeater.ares == True) | (Repeater.races == True) | (Repeater.skywarn == True)
)
nearby = filter_radius(candidates, radius)
print(f"\n{city_name}: {len(nearby)} emergency repeaters within 25km")
if __name__ == '__main__':
asyncio.run(emergency_planning())
Example 7: DMR Repeater Analysis¶
Analyze DMR repeater distribution and color codes.
import asyncio
from collections import Counter
from repeaterbook import RepeaterBook, Repeater
from repeaterbook.services import RepeaterBookAPI
from repeaterbook.models import ExportQuery, Status
import pycountry
async def analyze_dmr():
"""Analyze DMR repeater distribution."""
# Download data
api = RepeaterBookAPI()
usa = pycountry.countries.get(alpha_2="US")
repeaters = await api.download(query=ExportQuery(countries={usa}))
rb = RepeaterBook()
rb.populate(repeaters)
# Get DMR repeaters
dmr_repeaters = rb.query(
Repeater.dmr_capable == True,
Repeater.operational_status == Status.ON_AIR
)
print("=" * 80)
print("DMR REPEATER ANALYSIS")
print("=" * 80)
print(f"\nTotal Operational DMR Repeaters: {len(dmr_repeaters)}")
# Count by state
states = Counter(r.state for r in dmr_repeaters if r.state)
print(f"\nTop 10 States by DMR Repeaters:")
for state, count in states.most_common(10):
percentage = (count / len(dmr_repeaters)) * 100
print(f" {state:25s} {count:5d} ({percentage:5.1f}%)")
# Find most popular color codes
color_codes = Counter(r.dmr_color_code for r in dmr_repeaters if r.dmr_color_code)
print(f"\nColor Code Distribution:")
for cc, count in sorted(color_codes.items()):
percentage = (count / len(dmr_repeaters)) * 100
bar = "█" * int(percentage)
print(f" CC{cc}: {bar} {count:4d} ({percentage:5.1f}%)")
# Count repeaters with DMR IDs
with_dmr_id = sum(1 for r in dmr_repeaters if r.dmr_id)
print(f"\nRepeaters with DMR ID: {with_dmr_id} ({with_dmr_id/len(dmr_repeaters)*100:.1f}%)")
if __name__ == '__main__':
asyncio.run(analyze_dmr())
Next Steps¶
- Getting Started - Tutorial for beginners
- Usage Guide - Comprehensive usage examples
- Architecture - Understanding the internals
- API Reference - Complete API documentation
- FAQ - Common questions and troubleshooting