{"id":21390043,"url":"https://github.com/k0swe/adif-json-protobuf","last_synced_at":"2025-07-13T16:31:52.558Z","repository":{"id":39579200,"uuid":"278000142","full_name":"k0swe/adif-json-protobuf","owner":"k0swe","description":"Protocol buffer and JSON schema based on the  amateur radio data interchange format (ADIF)","archived":false,"fork":false,"pushed_at":"2024-11-12T15:07:30.000Z","size":194,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-11-12T16:21:18.620Z","etag":null,"topics":["adif","amateur-radio","json","json-schema","protobuf"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/k0swe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-08T05:42:16.000Z","updated_at":"2024-11-12T15:07:33.000Z","dependencies_parsed_at":"2023-11-07T17:35:43.080Z","dependency_job_id":"44b6b344-fbe4-4c96-83d4-f802c3f1f16f","html_url":"https://github.com/k0swe/adif-json-protobuf","commit_stats":{"total_commits":120,"total_committers":3,"mean_commits":40.0,"dds":0.4666666666666667,"last_synced_commit":"112b283e8342f161a26361fd398834bb069b07cd"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k0swe%2Fadif-json-protobuf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k0swe%2Fadif-json-protobuf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k0swe%2Fadif-json-protobuf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k0swe%2Fadif-json-protobuf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/k0swe","download_url":"https://codeload.github.com/k0swe/adif-json-protobuf/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225901193,"owners_count":17542183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adif","amateur-radio","json","json-schema","protobuf"],"created_at":"2024-11-22T13:15:09.771Z","updated_at":"2024-11-22T13:15:10.500Z","avatar_url":"https://github.com/k0swe.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ADIF protocol buffer and JSON schema\n\nThis repository is an unofficial extension to the [ADIF](https://www.adif.org/) amateur radio data\ninterchange specification.\n\n\u003ctable\u003e\n\u003ctr\u003e\u003cth\u003eADIF\u003c/th\u003e\u003cth\u003eJSON\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e\u003cpre\u003e\n\u0026lt;band:3\u003e20m\n\u0026lt;call:4\u003eKK9A\n\u0026lt;cnty:12\u003eNC, Cabarrus\n\u0026lt;cont:2\u003eNA\n\u0026lt;contest_id:10\u003eCQ-WPX-SSB\n\u0026lt;country:13\u003eUnited States\n\u0026lt;dxcc:3\u003e291\n\u0026lt;freq:6\u003e14.282\n\u0026lt;gridsquare:6\u003eEM95re\n\u0026lt;lotw_qsl_rcvd:1\u003eY\n\u0026lt;lotw_qsl_sent:1\u003eY\n\u0026lt;lotw_qslrdate:8\u003e20200402\n\u0026lt;lotw_qslsdate:8\u003e20200406\n\u0026lt;mode:3\u003eSSB\n\u0026lt;my_city:11\u003eWestminster\n\u0026lt;my_cnty:13\u003eCO, Jefferson\n\u0026lt;my_country:13\u003eUnited States\n\u0026lt;my_gridsquare:6\u003eDM79lv\n\u0026lt;my_name:20\u003eChristopher C Keller\n\u0026lt;my_state:2\u003eCO\n\u0026lt;name:12\u003eJOHN P BAYNE\n\u0026lt;qrzcom_qso_upload_date:8\u003e20200329\n\u0026lt;qrzcom_qso_upload_status:1\u003eY\n\u0026lt;qsl_via:6\u003eWD9DZV\n\u0026lt;qso_date:8\u003e20200329\n\u0026lt;qso_date_off:8\u003e20200329\n\u0026lt;qth:7\u003eMIDLAND\n\u0026lt;rst_rcvd:2\u003e59\n\u0026lt;rst_sent:2\u003e59\n\u0026lt;srx:4\u003e1592\n\u0026lt;state:2\u003eNC\n\u0026lt;station_callsign:5\u003eK0SWE\n\u0026lt;stx:1\u003e1\n\u0026lt;time_off:4\u003e0034\n\u0026lt;time_on:4\u003e0034\n\u0026lt;tx_pwr:3\u003e100\n\u0026lt;eor\u003e\n\u003c/pre\u003e\n\u003c/td\u003e\n\u003ctd\u003e\n\n```\n{\n  \"loggingStation\": {\n    \"stationCall\": \"K0SWE\",\n    \"opName\": \"Christopher C Keller\",\n    \"gridSquare\": \"DM79lv\",\n    \"power\": 100.0,\n    \"city\": \"Westminster\",\n    \"county\": \"CO, Jefferson\",\n    \"state\": \"CO\",\n    \"country\": \"United States\",\n  },\n  \"contactedStation\": {\n    \"stationCall\": \"KK9A\",\n    \"opName\": \"JOHN P BAYNE\",\n    \"gridSquare\": \"EM95re\",\n    \"qslVia\": \"WD9DZV\",\n    \"city\": \"MIDLAND\",\n    \"county\": \"NC, Cabarrus\",\n    \"state\": \"NC\",\n    \"country\": \"United States\",\n    \"dxcc\": 291,\n    \"continent\": \"NA\",\n  },\n  \"band\": \"20m\",\n  \"freq\": 14.282,\n  \"mode\": \"SSB\",\n  \"timeOn\": \"2020-03-29T00:34:00Z\",\n  \"timeOff\": \"2020-03-29T00:34:00Z\",\n  \"rstReceived\": \"59\",\n  \"rstSent\": \"59\",\n  \"contest\": {\n    \"contestId\": \"CQ-WPX-SSB\",\n    \"serialSent\": \"1\",\n    \"serialReceived\": \"1592\"\n  },\n  \"qrzcom\": {\n    \"uploadDate\": \"2020-03-29T00:00:00Z\",\n    \"uploadStatus\": \"UPLOAD_COMPLETE\"\n  },\n  \"lotw\": {\n    \"sentDate\": \"2020-04-06T00:00:00Z\",\n    \"sentStatus\": \"Y\",\n    \"receivedDate\": \"2020-04-02T00:00:00Z\",\n    \"receivedStatus\": \"Y\"\n  },\n},\n```\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n## Background\n\nADIF is popular among the amateur radio community for transmitting and exchanging information about\non-air contacts, a.k.a. QSOs. It was conceived in 1996 and has evolved over the years in mostly\nbackward-compatible ways.\n\nThe wire format of the protocol is a bespoke ASCII format based on tags with field width indicators.\nWhile simple to parse, the unique encoding prevents the use of many modern protocol handling tools,\nwhich slows development effort on more modern amateur radio contact logging software. In addition,\nADIF's reliance on ASCII and inability to be modernized for UTF-8 is an eyesore to anyone with \ndiacritics in their name or location. An extension called ADX attempted to encode ADIF data in XML, \nbut ADX has not been widely adopted. I speculate this is because ADIF was considered good enough and\nADX didn't add value, but another complaint among developers is that ADX failed to follow XML best \npractices. Notably, [it does not respect XML namespaces][1], breaking extensibility. Without wide \nadoption from developers, users have had no motivation to entrust their data to ADX. Even if they \ncared about an arguably superior wire format, the fear of incompatibility would have discouraged \nusers from using it.\n\n[1]: http://tlfabian.blogspot.com/2016/07/musings-on-adif-file-format.html\n\n## Why update ADIF?\n\nADIF has served the amateur radio community well for several decades. It's the *lingua franca* of \nham logging software and will continue to be for a long time. Even software written against newer\ninterchange formats will need to speak ADIF for the forseeable future. However, the format is\nshowing its age and does not mesh well with international user bases, or modern software\ndevelopment practices and toolchains. Most programming languages now have mature libraries for\nhandling UTF-8 and JSON data transparently without having to write any custom parsing logic. XML \nlibraries are similarly widely available, but as noted, most people have already abandoned ADX, and \nXML's heyday has passed.\n\nBeyond just tooling improvements, I assert that semantic improvements can be made to ADIF. Rather\nthan representing a QSO as a flat set of 153 key/value pairs (some of which have further internal \nstructure), this is an opportunity to improve the intelligibility of the format for both humans and\ncomputers through nested data types.\n\n## Why JSON? Why protobuf?\n\nSelecting [JSON](https://www.json.org/) as a wire format is an easy choice in today's web-centric\ntechnology space. As RESTful APIs have become the norm for exchanging information between\ninternet-connected services and their clients, JSON has become the *de facto* standard for these\nAPIs. [JSON Schema](https://json-schema.org/) is less widely adopted, but it's a convenient way to\nformalize the document structure and provide a bootstrap for basic validation in many programming\nlanguages. In this repository, the JSON Schema is derived from the protocol buffer schema.\n\nI've chosen [protocol buffers](https://developers.google.com/protocol-buffers) as the primary schema\nrepresentation because:\n \n1. Protobuf is a relatively simple and intuitive schema to read even for the unfamiliar.\n1. Protobuf is programming language neutral and can generate data structures and parsers in many\n   languages.\n1. Protobuf has a well-defined JSON transformation which eases using JSON as a public wire format.\n1. Protobuf is specifically designed to have a compact and efficient binary wire format, which makes\n   it simple and fast to share information between components of the same service in e.g.\n   remote procedure calls (RPCs).\n1. Protobuf messages are extensible which will allow for the inclusion of ADIF's user-defined \n   fields.\n\nThere are other good data serialization formats with IDLs out there that could do the job like \nApache Thrift or Amazon Ion, but I happen to know protobuf.\n\n## Which ADIF fields are included?\n\nMost of the fields from the ADIF 3.1.1 specification are present in this schema. I did not include\nthe `INTL` fields because both protobuf and JSON explicitly support UTF-8 strings, making the `INTL`\nfields redundant. I did not include deprecated fields. Finally, I omitted `SRX` and `TRX` with the\nassertion that all contest serial numbers be stored as strings. You weren't going to do arithmetic \nwith those serial numbers, and this is simpler for everyone, if marginally less storage efficient. \n\nDate/times are represented by specialized data types, which means ADIF's `QSO_DATE` is combined with\n`TIME_ON`, and similarly `QSO_DATE_OFF` with `TIME_OFF`.\n\nI have not included most of ADIF's enumerations. Programs using this schema should still respect\nthose enumerations, but putting those into a codified schema makes it prohibitively difficult to \naccommodate e.g. new political boundaries and new operating modes.  \n\n## Should I use it?\n\nThis is an unofficial extension of ADIF and does not yet have any community support, which is\ncritical to an interchange format. This is a proposal meant to stimulate a discussion, and I'll use\nit internally in my own logging application. \n\nI don't really expect that protocol buffers will become a popular way for end users to store their\ndata on disk or even for developers of software to support importing or exporting binary protobufs.\n\nPerhaps desktop logging software might start using JSON as a disk file format; JSON is both highly \nhuman-readable and highly machine-parsable. However, I think that's questionable for the same reason\nthat ADX failed; ADIF is good enough and already ubiquitous.\n\nWhat I do hope is that amateur radio internet logging services exposing RESTful APIs will consider\nusing this schema for JSON structure rather than classic ADIF, and that developers find this schema\nuseful as an internal data representation.\n\n## Field mapping\n\nADIF QSO field | Protobuf Qso field\n------- | ------\n`A_INDEX` | `propagation.a_index`\n`ADDRESS` | `contacted_station.address`\n`ADDRESS_INTL` | (obsolete)\n`AGE` | `contacted_station.age`\n`ANT_AZ` | `logging_station.antenna_azimuth`\n`ANT_EL` | `logging_station.antenna_elevation`\n`ANT_PATH` | `propagation.ant_path`\n`ARRL_SECT` | `contest.arrl_section`\n`AWARD_GRANTED` | `award_granted[]`\n`AWARD_SUBMITTED` | `award_submitted[]`\n`BAND` | `band`\n`BAND_RX` | `band_rx`\n`CALL` | `contacted_station.station_call`\n`CHECK` | `contest.check`\n`CLASS` | `contest.class`\n`CLUBLOG_QSO_UPLOAD_DATE` | `clublog.upload_date`\n`CLUBLOG_QSO_UPLOAD_STATUS` | `clublog.upload_status`\n`CNTY` | `contacted_station.county`\n`COMMENT` | `comment`\n`COMMENT_INTL` | (obsolete)\n`CONT` | `contacted_station.continent`\n`CONTACTED_OP` | `contacted_station.op_call`\n`CONTEST_ID` | `contest.contest_id`\n`COUNTRY` | `contacted_station.country`\n`COUNTRY_INTL` | (obsolete)\n`CQZ` | `contacted_station.cq_zone`\n`CREDIT_GRANTED` | `credit_granted[]`\n`CREDIT_SUBMITTED` | `credit_submitted[]`\n`DARC_DOK` | `contacted_station.darc_dok`\n`DISTANCE` | `distance`\n`DXCC` | `contacted_station.dxcc`\n`EMAIL` | `contacted_station.email`\n`EQ_CALL` | `contacted_station.owner_callsign`\n`EQSL_QSL_RCVD` | `eqsl.received_status`\n`EQSL_QSL_SENT` | `eqsl.sent_status`\n`EQSL_QSLRDATE` | `eqsl.received_date`\n`EQSL_QSLSDATE` | `eqsl.sent_date`\n`FISTS` | `contacted_station.fists`\n`FISTS_CC` | `contacted_station.fists_cc`\n`FORCE_INIT` | `propagation.force_init`\n`FREQ` | `freq`\n`FREQ_RX` | `freq_rx`\n`GRIDSQUARE` | `contacted_station.grid_square`\n`GUEST_OP` | (obsolete)\n`HRDLOG_QSO_UPLOAD_DATE` | `hrdlog.upload_date`\n`HRDLOG_QSO_UPLOAD_STATUS` | `hrdlog.upload_status`\n`IOTA` | `contacted_station.iota`\n`IOTA_ISLAND_ID` | `contacted_station.iota_island_id`\n`ITUZ` | `contacted_station.itu_zone`\n`K_INDEX` | `propagation.k_index`\n`LAT` | `contacted_station.latitude`\n`LON` | `contacted_station.longitude`\n`LOTW_QSL_RCVD` | `lotw.received_status`\n`LOTW_QSL_SENT` | `lotw.sent_status`\n`LOTW_QSLRDATE` | `lotw.received_date`\n`LOTW_QSLSDATE` | `lotw.sent_date`\n`MAX_BURSTS` | `propagation.max_bursts`\n`MODE` | `mode`\n`MS_SHOWER` | `propagation.ms_shower`\n`MY_ANTENNA` | `logging_station.antenna`\n`MY_ANTENNA_INTL` | (obsolete)\n`MY_CITY` | `logging_station.city`\n`MY_CITY_INTL` | (obsolete)\n`MY_CNTY` | `logging_station.county`\n`MY_COUNTRY` | `logging_station.country`\n`MY_COUNTRY_INTL` | (obsolete)\n`MY_CQ_ZONE` | `logging_station.cq_zone`\n`MY_DXCC` | `logging_station.dxcc`\n`MY_FISTS` | `logging_station.fists`\n`MY_GRIDSQUARE` | `logging_station.grid_square`\n`MY_IOTA` | `logging_station.iota`\n`MY_IOTA_ISLAND_ID` | `logging_station.iota_island_id`\n`MY_ITU_ZONE` | `logging_station.itu_zone`\n`MY_LAT` | `logging_station.latitude`\n`MY_LON` | `logging_station.longitude`\n`MY_NAME` | `logging_station.op_name`\n`MY_NAME_INTL` | (obsolete)\n`MY_POSTAL_CODE` | `logging_station.postal_code`\n`MY_POSTAL_CODE_INTL` | (obsolete)\n`MY_RIG` | `logging_station.rig`\n`MY_RIG_INTL` | (obsolete)\n`MY_SIG` | `logging_station.sig`\n`MY_SIG_INFO` | `logging_station.sig_info`\n`MY_SIG_INFO_INTL` | (obsolete)\n`MY_SIG_INTL` | (obsolete)\n`MY_SOTA_REF` | `logging_station.sota_ref`\n`MY_STATE` | `logging_station.state`\n`MY_STREET` | `logging_station.street`\n`MY_STREET_INTL` | (obsolete)\n`MY_USACA_COUNTIES` | `logging_station.usaca_counties`\n`MY_VUCC_GRIDS` | `logging_station.vucc_grids`\n`NAME` | `contacted_station.op_name`\n`NAME_INTL` | (obsolete)\n`NOTES` | `notes`\n`NOTES_INTL` | (obsolete)\n`NR_BURSTS` | `propagation.nr_bursts`\n`NR_PINGS` | `propagation.nr_pings`\n`OPERATOR` | `logging_station.op_call`\n`OWNER_CALLSIGN` | `logging_station.owner_call`\n`PFX` | `contacted_station.pfx`\n`PRECEDENCE` | `contest.precedence`\n`PROP_MODE` | `propagation.propagation_mode`\n`PUBLIC_KEY` | `public_key`\n`QRZCOM_QSO_UPLOAD_DATE` | `qrzcom.upload_date`\n`QRZCOM_QSO_UPLOAD_STATUS` | `qrzcom.upload_status`\n`QSL_RCVD` | `card.received_status`\n`QSL_RCVD_VIA` | `card.received_via`\n`QSL_SENT` | `card.sent_status`\n`QSL_SENT_VIA` | `card.sent_via`\n`QSL_VIA` | `contacted_station.qsl_via`\n`QSLMSG` | `card.received_message`\n`QSLMSG_INTL` | (obsolete)\n`QSLRDATE` | `card.received_date`\n`QSLSDATE` | `card.sent_date`\n`QSO_COMPLETE` | `complete`\n`QSO_DATE` | `time_on`\n`QSO_DATE_OFF` | `time_off`\n`QSO_RANDOM` | `random`\n`QTH` | `contacted_station.city`\n`QTH_INTL` | (obsolete)\n`REGION` | `contacted_station.region`\n`RIG` | `contacted_station.rig`\n`RIG_INTL` | (obsolete)\n`RST_RCVD` | `rst_received`\n`RST_SENT` | `rst_sent`\n`RX_PWR` | `contacted_station.power`\n`SAT_MODE` | `propagation.sat_mode`\n`SAT_NAME` | `propagation.sat_name`\n`SFI` | `propagation.solar_flux_index`\n`SIG` | `contacted_station.sig`\n`SIG_INFO` | `contacted_station.sig_info`\n`SIG_INFO_INTL` | (obsolete)\n`SIG_INTL` | (obsolete)\n`SILENT_KEY` | `contacted_station.silent_key`\n`SKCC` | `contacted_station.skcc`\n`SOTA_REF` | `contacted_station.sota_ref`\n`SRX` | `contest.serial_received`\n`SRX_STRING` | `contest.serial_received`\n`STATE` | `contacted_station.state`\n`STATION_CALLSIGN` | `logging_station.station_call`\n`STX` | `contest.serial_sent`\n`STX_STRING` | `contest.serial_sent`\n`SUBMODE` | `submode`\n`SWL` | `swl`\n`TEN_TEN` | `contacted_station.ten_ten`\n`TIME_OFF` | `time_off`\n`TIME_ON` | `time_on`\n`TX_PWR` | `logging_station.power`\n`UKSMG` | `contacted_station.uksmg`\n`USACA_COUNTIES` | `contacted_station.usaca_counties`\n`VE_PROV` | (obsolete)\n`VUCC_GRIDS` | `contacted_station.vucc_grids`\n`WEB` | `contacted_station.web`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk0swe%2Fadif-json-protobuf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fk0swe%2Fadif-json-protobuf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk0swe%2Fadif-json-protobuf/lists"}